From nobody Thu Oct 2 21:41:26 2025 Received: from fllvem-ot04.ext.ti.com (fllvem-ot04.ext.ti.com [198.47.19.246]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 82EC432038B; Thu, 11 Sep 2025 11:37:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.47.19.246 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757590627; cv=none; b=A0yB8W/7WgqWbEjvYRDGWl9aFcOGx6lpQO3I5MnQbNZW9ZFaOcH3DAUNGwuZPSP+TZoQCMXiuv/SMD96u7KL2DsSzpjS0RQuAawhF8jcNHxq5iiQDgd1G3XHjiv79FOfjpdokHIBaZRmK3RNGP8D7R95yl8OfhWfjqFvncpgYsM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757590627; c=relaxed/simple; bh=3JuQddPCunUMjQrT6vQYeUt75KQpUx3lNt2KEVfeukw=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=kAK3B7vJyJLPAMHiG/QLjjaof+QAGYLriDIl+e4FG+VtpqYw8fXsOTqO+EwouzSIcHiuGRX29zX2pS+bcGegnnMXSxQoo2544kUmrfLix5uNfy+WdJ6kRNJwzabNtRJ80mgoObeBxW2ECzXFtYmWwm+EK9iKSUh1M8Xchh+lvQA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com; spf=pass smtp.mailfrom=ti.com; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b=GrpcF4J3; arc=none smtp.client-ip=198.47.19.246 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ti.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b="GrpcF4J3" Received: from fllvem-sh04.itg.ti.com ([10.64.41.54]) by fllvem-ot04.ext.ti.com (8.15.2/8.15.2) with ESMTP id 58BBaR7J735480; Thu, 11 Sep 2025 06:36:27 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1757590587; bh=TM6mT9th3rVCAn81L1mGY3S50wIg3OwdQwzQAAB5wWs=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=GrpcF4J3GXqL6HKj3ZXdFxclmYLZ0treeZYcL4W0Frg1Bywkp1mLF8JfvfHh9d5bY 06iXriz6+wmEXrNs75rzPibf6qZT8+AWFVg/WFGgK55GUm+Fe+2yufO5G82KuVGnDg kVVVUqH7s5zT9ef3I5d9UxabSPlOtZra58PxH1IA= Received: from DLEE109.ent.ti.com (dlee109.ent.ti.com [157.170.170.41]) by fllvem-sh04.itg.ti.com (8.18.1/8.18.1) with ESMTPS id 58BBaQfO1908833 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA256 bits=128 verify=FAIL); Thu, 11 Sep 2025 06:36:26 -0500 Received: from DLEE203.ent.ti.com (157.170.170.78) by DLEE109.ent.ti.com (157.170.170.41) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.55; Thu, 11 Sep 2025 06:36:25 -0500 Received: from lelvem-mr05.itg.ti.com (10.180.75.9) by DLEE203.ent.ti.com (157.170.170.78) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.2562.20 via Frontend Transport; Thu, 11 Sep 2025 06:36:26 -0500 Received: from fllv0122.itg.ti.com (fllv0122.itg.ti.com [10.247.120.72]) by lelvem-mr05.itg.ti.com (8.18.1/8.18.1) with ESMTP id 58BBaPkY2062025; Thu, 11 Sep 2025 06:36:26 -0500 Received: from localhost (danish-tpc.dhcp.ti.com [172.24.231.152]) by fllv0122.itg.ti.com (8.14.7/8.14.7) with ESMTP id 58BBaPlT032075; Thu, 11 Sep 2025 06:36:25 -0500 From: MD Danish Anwar To: "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Jonathan Corbet , Nishanth Menon , Vignesh Raghavendra , Tero Kristo , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Andrew Lunn , MD Danish Anwar , Mengyuan Lou , Lei Wei , Xin Guo , Michael Ellerman , Fan Gong , Lorenzo Bianconi , Parthiban Veerasooran , Lukas Bulwahn , Geert Uytterhoeven CC: , , , , Subject: [PATCH net-next v4 4/7] net: rpmsg-eth: Add netdev ops Date: Thu, 11 Sep 2025 17:06:09 +0530 Message-ID: <20250911113612.2598643-5-danishanwar@ti.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250911113612.2598643-1-danishanwar@ti.com> References: <20250911113612.2598643-1-danishanwar@ti.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-C2ProcessedOrg: 333ef613-75bf-4e12-a4b1-8e3623f5dcea Content-Type: text/plain; charset="utf-8" Add netdev ops for rpmsg-eth driver. This patch introduces the netdev operations for the rpmsg-eth driver, enabling the driver to interact with the Linux networking stack. The following functionalities are implemented: 1. `ndo_open` and `ndo_stop`: - Handles the initialization and cleanup of the network device during open and stop operations. - Manages the state transitions of the rpmsg-eth driver. 2. `ndo_start_xmit`: - Implements the transmit functionality by copying data from the skb to the shared memory buffer and updating the head index. 3. `ndo_set_mac_address`: - Allows setting the MAC address of the network device and sends the updated MAC address to the remote processor. 4. RX Path: - Adds a timer-based mechanism to poll for received packets in shared memory. - Implements NAPI-based packet processing to handle received packets efficiently. 5. State Machine: - Introduces a state machine to manage the driver's state transitions, such as PROBE, OPEN, READY, and RUNNING. 6. Initialization: - Adds necessary initialization for locks, timers, and work structures. - Registers the network device and sets up NAPI and RX timer. 7. Cleanup: - Ensures proper cleanup of resources during driver removal, including NAPI and timers. This patch enhances the rpmsg-eth driver to function as a fully operational network device in the Linux kernel. Signed-off-by: MD Danish Anwar --- drivers/net/ethernet/rpmsg_eth.c | 318 +++++++++++++++++++++++++++++++ drivers/net/ethernet/rpmsg_eth.h | 2 + 2 files changed, 320 insertions(+) diff --git a/drivers/net/ethernet/rpmsg_eth.c b/drivers/net/ethernet/rpmsg_= eth.c index f599318633ea..43196e1d7eeb 100644 --- a/drivers/net/ethernet/rpmsg_eth.c +++ b/drivers/net/ethernet/rpmsg_eth.c @@ -63,6 +63,108 @@ static int rpmsg_eth_validate_handshake(struct rpmsg_et= h_port *port, return 0; } =20 +static int create_request(struct rpmsg_eth_common *common, + enum rpmsg_eth_rpmsg_type rpmsg_type) +{ + struct message *msg =3D &common->send_msg; + + msg->msg_hdr.src_id =3D common->port->port_id; + msg->req_msg.type =3D rpmsg_type; + + switch (rpmsg_type) { + case RPMSG_ETH_REQ_SHM_INFO: + msg->msg_hdr.msg_type =3D RPMSG_ETH_REQUEST_MSG; + break; + case RPMSG_ETH_REQ_SET_MAC_ADDR: + msg->msg_hdr.msg_type =3D RPMSG_ETH_REQUEST_MSG; + ether_addr_copy(msg->req_msg.mac_addr.addr, + common->port->ndev->dev_addr); + break; + case RPMSG_ETH_NOTIFY_PORT_UP: + case RPMSG_ETH_NOTIFY_PORT_DOWN: + msg->msg_hdr.msg_type =3D RPMSG_ETH_NOTIFY_MSG; + break; + default: + dev_err(common->dev, "Invalid RPMSG request\n"); + return -EINVAL; + } + return 0; +} + +static int rpmsg_eth_create_send_request(struct rpmsg_eth_common *common, + enum rpmsg_eth_rpmsg_type rpmsg_type, + bool wait) +{ + unsigned long flags; + int ret; + + if (wait) + reinit_completion(&common->sync_msg); + + spin_lock_irqsave(&common->send_msg_lock, flags); + ret =3D create_request(common, rpmsg_type); + if (ret) + goto release_lock; + + ret =3D rpmsg_send(common->rpdev->ept, (void *)(&common->send_msg), + sizeof(common->send_msg)); + if (ret) { + dev_err(common->dev, "Failed to send RPMSG message\n"); + goto release_lock; + } + + spin_unlock_irqrestore(&common->send_msg_lock, flags); + if (wait) { + ret =3D wait_for_completion_timeout(&common->sync_msg, + RPMSG_ETH_REQ_TIMEOUT_JIFFIES); + + if (!ret) { + dev_err(common->dev, "Failed to receive response within %ld jiffies\n", + RPMSG_ETH_REQ_TIMEOUT_JIFFIES); + return -ETIMEDOUT; + } + ret =3D 0; + } + return ret; +release_lock: + spin_unlock_irqrestore(&common->send_msg_lock, flags); + return ret; +} + +static void rpmsg_eth_state_machine(struct work_struct *work) +{ + struct delayed_work *dwork =3D to_delayed_work(work); + struct rpmsg_eth_common *common; + struct rpmsg_eth_port *port; + int ret; + + common =3D container_of(dwork, struct rpmsg_eth_common, state_work); + port =3D common->port; + + mutex_lock(&common->state_lock); + + switch (common->state) { + case RPMSG_ETH_STATE_PROBE: + break; + case RPMSG_ETH_STATE_OPEN: + rpmsg_eth_create_send_request(common, RPMSG_ETH_REQ_SHM_INFO, false); + break; + case RPMSG_ETH_STATE_CLOSE: + break; + case RPMSG_ETH_STATE_READY: + ret =3D rpmsg_eth_create_send_request(common, RPMSG_ETH_REQ_SET_MAC_ADDR= , false); + if (!ret) { + napi_enable(&port->rx_napi); + netif_carrier_on(port->ndev); + mod_timer(&port->rx_timer, RX_POLL_TIMEOUT_JIFFIES); + } + break; + case RPMSG_ETH_STATE_RUNNING: + break; + } + mutex_unlock(&common->state_lock); +} + static int rpmsg_eth_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int = len, void *priv, u32 src) { @@ -99,6 +201,17 @@ static int rpmsg_eth_rpmsg_cb(struct rpmsg_device *rpde= v, void *data, int len, msg->resp_msg.shm_info.num_pkt_bufs; port->rx_max_buffers =3D msg->resp_msg.shm_info.num_pkt_bufs; + + mutex_lock(&common->state_lock); + common->state =3D RPMSG_ETH_STATE_READY; + mutex_unlock(&common->state_lock); + + mod_delayed_work(system_wq, + &common->state_work, + STATE_MACHINE_TIME_JIFFIES); + + break; + case RPMSG_ETH_RESP_SET_MAC_ADDR: break; } break; @@ -106,6 +219,20 @@ static int rpmsg_eth_rpmsg_cb(struct rpmsg_device *rpd= ev, void *data, int len, rpmsg_type =3D msg->notify_msg.type; dev_dbg(common->dev, "Msg type =3D %d, RPMsg type =3D %d, Src Id =3D %d,= Msg Id =3D %d\n", msg_type, rpmsg_type, msg->msg_hdr.src_id, msg->notify_msg.id); + switch (rpmsg_type) { + case RPMSG_ETH_NOTIFY_REMOTE_READY: + mutex_lock(&common->state_lock); + common->state =3D RPMSG_ETH_STATE_RUNNING; + mutex_unlock(&common->state_lock); + + mod_delayed_work(system_wq, + &common->state_work, + STATE_MACHINE_TIME_JIFFIES); + break; + case RPMSG_ETH_NOTIFY_PORT_UP: + case RPMSG_ETH_NOTIFY_PORT_DOWN: + break; + } break; default: dev_err(common->dev, "Invalid msg type\n"); @@ -172,6 +299,181 @@ static int rpmsg_eth_get_shm_info(struct rpmsg_eth_co= mmon *common) return 0; } =20 +static void rpmsg_eth_rx_timer(struct timer_list *timer) +{ + struct rpmsg_eth_port *port =3D timer_container_of(port, timer, rx_timer); + struct napi_struct *napi; + int num_pkts =3D 0; + u32 head, tail; + + head =3D readl(port->shm + port->rx_offset + HEAD_IDX_OFFSET); + tail =3D readl(port->shm + port->rx_offset + + TAIL_IDX_OFFSET(port->rx_max_buffers)); + + num_pkts =3D tail - head; + num_pkts =3D num_pkts >=3D 0 ? num_pkts : + (num_pkts + port->rx_max_buffers); + + napi =3D &port->rx_napi; + if (num_pkts && likely(napi_schedule_prep(napi))) + __napi_schedule(napi); + else + mod_timer(&port->rx_timer, RX_POLL_JIFFIES); +} + +static int rpmsg_eth_rx_packets(struct napi_struct *napi, int budget) +{ + struct rpmsg_eth_port *port =3D container_of(napi, struct rpmsg_eth_port,= rx_napi); + u32 count, process_pkts; + struct sk_buff *skb; + u32 head, tail; + int num_pkts; + u32 pkt_len; + + head =3D readl(port->shm + port->rx_offset + HEAD_IDX_OFFSET); + tail =3D readl(port->shm + port->rx_offset + + TAIL_IDX_OFFSET(port->rx_max_buffers)); + + num_pkts =3D head - tail; + + num_pkts =3D num_pkts >=3D 0 ? num_pkts : + (num_pkts + port->rx_max_buffers); + process_pkts =3D min(num_pkts, budget); + count =3D 0; + while (count < process_pkts) { + memcpy_fromio((void *)&pkt_len, + port->shm + port->rx_offset + MAGIC_NUM_SIZE_TYPE + + PKT_START_OFFSET((tail + count) % port->rx_max_buffers), + PKT_LEN_SIZE_TYPE); + /* Start building the skb */ + skb =3D napi_alloc_skb(napi, pkt_len); + if (!skb) { + port->ndev->stats.rx_dropped++; + goto rx_dropped; + } + + skb->dev =3D port->ndev; + skb_put(skb, pkt_len); + memcpy_fromio((void *)skb->data, + port->shm + port->rx_offset + PKT_LEN_SIZE_TYPE + + MAGIC_NUM_SIZE_TYPE + + PKT_START_OFFSET((tail + count) % port->rx_max_buffers), + pkt_len); + + skb->protocol =3D eth_type_trans(skb, port->ndev); + + /* Push skb into network stack */ + napi_gro_receive(napi, skb); + + count++; + port->ndev->stats.rx_packets++; + port->ndev->stats.rx_bytes +=3D skb->len; + } + +rx_dropped: + + if (num_pkts) { + writel((tail + count) % port->rx_max_buffers, + port->shm + port->rx_offset + + TAIL_IDX_OFFSET(port->rx_max_buffers)); + + if (num_pkts < budget && napi_complete_done(napi, count)) + mod_timer(&port->rx_timer, RX_POLL_TIMEOUT_JIFFIES); + } + + return count; +} + +static int rpmsg_eth_ndo_open(struct net_device *ndev) +{ + struct rpmsg_eth_common *common =3D rpmsg_eth_ndev_to_common(ndev); + + mutex_lock(&common->state_lock); + common->state =3D RPMSG_ETH_STATE_OPEN; + mutex_unlock(&common->state_lock); + mod_delayed_work(system_wq, &common->state_work, msecs_to_jiffies(100)); + + return 0; +} + +static int rpmsg_eth_ndo_stop(struct net_device *ndev) +{ + struct rpmsg_eth_common *common =3D rpmsg_eth_ndev_to_common(ndev); + struct rpmsg_eth_port *port =3D rpmsg_eth_ndev_to_port(ndev); + + mutex_lock(&common->state_lock); + common->state =3D RPMSG_ETH_STATE_CLOSE; + mutex_unlock(&common->state_lock); + + netif_carrier_off(port->ndev); + + cancel_delayed_work_sync(&common->state_work); + timer_delete_sync(&port->rx_timer); + napi_disable(&port->rx_napi); + + return 0; +} + +static netdev_tx_t rpmsg_eth_start_xmit(struct sk_buff *skb, struct net_de= vice *ndev) +{ + struct rpmsg_eth_port *port =3D rpmsg_eth_ndev_to_port(ndev); + u32 head, tail; + int num_pkts; + u32 len; + + len =3D skb_headlen(skb); + head =3D readl(port->shm + port->tx_offset + HEAD_IDX_OFFSET); + tail =3D readl(port->shm + port->tx_offset + + TAIL_IDX_OFFSET(port->tx_max_buffers)); + + /* If the buffer queue is full, then drop packet */ + num_pkts =3D head - tail; + num_pkts =3D num_pkts >=3D 0 ? num_pkts : + (num_pkts + port->tx_max_buffers); + + if ((num_pkts + 1) =3D=3D port->tx_max_buffers) { + netdev_warn(ndev, "Tx buffer full %d\n", num_pkts); + goto ring_full; + } + /* Copy length */ + memcpy_toio(port->shm + port->tx_offset + PKT_START_OFFSET(head) + MAGIC_= NUM_SIZE_TYPE, + (void *)&len, PKT_LEN_SIZE_TYPE); + /* Copy data to shared mem */ + memcpy_toio(port->shm + port->tx_offset + PKT_START_OFFSET(head) + MAGIC_= NUM_SIZE_TYPE + + PKT_LEN_SIZE_TYPE, (void *)skb->data, len); + writel((head + 1) % port->tx_max_buffers, + port->shm + port->tx_offset + HEAD_IDX_OFFSET); + + ndev->stats.tx_packets++; + ndev->stats.tx_bytes +=3D skb->len; + + dev_consume_skb_any(skb); + return NETDEV_TX_OK; + +ring_full: + return NETDEV_TX_BUSY; +} + +static int rpmsg_eth_set_mac_address(struct net_device *ndev, void *addr) +{ + struct rpmsg_eth_common *common =3D rpmsg_eth_ndev_to_common(ndev); + int ret; + + ret =3D eth_mac_addr(ndev, addr); + + if (ret < 0) + return ret; + + return rpmsg_eth_create_send_request(common, RPMSG_ETH_REQ_SET_MAC_ADDR, = false); +} + +static const struct net_device_ops rpmsg_eth_netdev_ops =3D { + .ndo_open =3D rpmsg_eth_ndo_open, + .ndo_stop =3D rpmsg_eth_ndo_stop, + .ndo_start_xmit =3D rpmsg_eth_start_xmit, + .ndo_set_mac_address =3D rpmsg_eth_set_mac_address, +}; + static int rpmsg_eth_init_ndev(struct rpmsg_eth_common *common) { struct device *dev =3D &common->rpdev->dev; @@ -195,6 +497,7 @@ static int rpmsg_eth_init_ndev(struct rpmsg_eth_common = *common) =20 ndev_priv =3D netdev_priv(port->ndev); ndev_priv->port =3D port; + port->ndev->netdev_ops =3D &rpmsg_eth_netdev_ops; SET_NETDEV_DEV(port->ndev, dev); =20 port->ndev->min_mtu =3D RPMSG_ETH_MIN_PACKET_SIZE; @@ -206,6 +509,8 @@ static int rpmsg_eth_init_ndev(struct rpmsg_eth_common = *common) } =20 netif_carrier_off(port->ndev); + netif_napi_add(port->ndev, &port->rx_napi, rpmsg_eth_rx_packets); + timer_setup(&port->rx_timer, rpmsg_eth_rx_timer, 0); err =3D register_netdev(port->ndev); if (err) dev_err(dev, "error registering rpmsg_eth net device %d\n", err); @@ -235,6 +540,12 @@ static int rpmsg_eth_probe(struct rpmsg_device *rpdev) if (ret) return ret; =20 + spin_lock_init(&common->send_msg_lock); + spin_lock_init(&common->recv_msg_lock); + mutex_init(&common->state_lock); + INIT_DELAYED_WORK(&common->state_work, rpmsg_eth_state_machine); + init_completion(&common->sync_msg); + /* Register the network device */ ret =3D rpmsg_eth_init_ndev(common); if (ret) @@ -245,6 +556,13 @@ static int rpmsg_eth_probe(struct rpmsg_device *rpdev) =20 static void rpmsg_eth_remove(struct rpmsg_device *rpdev) { + struct rpmsg_eth_common *common =3D dev_get_drvdata(&rpdev->dev); + struct rpmsg_eth_port *port =3D common->port; + + netif_napi_del(&port->rx_napi); + timer_delete_sync(&port->rx_timer); + unregister_netdev(port->ndev); + dev_dbg(&rpdev->dev, "rpmsg-eth client driver is removed\n"); } =20 diff --git a/drivers/net/ethernet/rpmsg_eth.h b/drivers/net/ethernet/rpmsg_= eth.h index 0a0695e857df..80fa07be1678 100644 --- a/drivers/net/ethernet/rpmsg_eth.h +++ b/drivers/net/ethernet/rpmsg_eth.h @@ -219,6 +219,7 @@ enum rpmsg_eth_state { * @data: Vendor specific data * @state: Interface state * @state_work: Delayed work for state machine + * @sync_msg: Completion for synchronous message */ struct rpmsg_eth_common { struct rpmsg_device *rpdev; @@ -235,6 +236,7 @@ struct rpmsg_eth_common { /** @state_lock: Lock for changing interface state */ struct mutex state_lock; struct delayed_work state_work; + struct completion sync_msg; }; =20 /** --=20 2.34.1