From nobody Fri Oct 3 11:22:53 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 7E1852EBDCA; Tue, 2 Sep 2025 09:08:49 +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=1756804131; cv=none; b=LBg4qz08bKVJp9vo06YAbtI/s8OI17bHJv8pciX1eO84XfOdC4R7x0eMpd2iJcZ0OrZcrkA1RNXL4aYBbOx1WO6qthX57kp2NnUGD9mWBfflU2y4qFyhG5aILBG6zsPhjQH7/X5a0f27IhhCdssnzUmUTqPPgGlT7MHdtfIaG8g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756804131; c=relaxed/simple; bh=iQDWWZxWEk4ID2ssHt6eQ1WX9jzJVEtWf0D0AxDQveg=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=JVRSILm64XKyAJfq5ptutaEYYKhI8b9wF4hkuBHVQC4BokYGUyJYku/hds1y/vFyo37uRRTEXVXEul6h7+EeMsbB0ij4KmHHunRLAW1tY5Xf5yvL6CU9YEBB5khLDoBLkPYtbry02k0a0KEXDjN6lmXOYocI7qc0Oa3g6N1xNls= 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=Dp1fPXr3; 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="Dp1fPXr3" Received: from lelvem-sh02.itg.ti.com ([10.180.78.226]) by fllvem-ot04.ext.ti.com (8.15.2/8.15.2) with ESMTP id 582985td2950974; Tue, 2 Sep 2025 04:08:05 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1756804085; bh=pQmmNW/NjvgUvNlzsS75BzdlKCJbzFFzNK+LUSrH+Iw=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=Dp1fPXr36jIL4JKH+W3ecJnINBrWlAt6POg1NGHPn2+Nr87r5qQJkl6k39fNszXBf DRulYy6d+OCKLcrN6AiytLHWxlUDfm7hVte4JaFVv+/htJYdzDT0iJJyyLGKNFyF1s BE/j6a7puhwdk29vQnD5AViXjap8AySVSySr1Dos= Received: from DFLE106.ent.ti.com (dfle106.ent.ti.com [10.64.6.27]) by lelvem-sh02.itg.ti.com (8.18.1/8.18.1) with ESMTPS id 582985fB2729681 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA256 bits=128 verify=FAIL); Tue, 2 Sep 2025 04:08:05 -0500 Received: from DFLE100.ent.ti.com (10.64.6.21) by DFLE106.ent.ti.com (10.64.6.27) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.55; Tue, 2 Sep 2025 04:08:04 -0500 Received: from lelvem-mr06.itg.ti.com (10.180.75.8) by DFLE100.ent.ti.com (10.64.6.21) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.55 via Frontend Transport; Tue, 2 Sep 2025 04:08:04 -0500 Received: from lelv0854.itg.ti.com (lelv0854.itg.ti.com [10.181.64.140]) by lelvem-mr06.itg.ti.com (8.18.1/8.18.1) with ESMTP id 582984MK3546744; Tue, 2 Sep 2025 04:08:04 -0500 Received: from localhost (danish-tpc.dhcp.ti.com [172.24.231.152]) by lelv0854.itg.ti.com (8.14.7/8.14.7) with ESMTP id 582983rX020884; Tue, 2 Sep 2025 04:08:03 -0500 From: MD Danish Anwar To: Andrew Lunn , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Bjorn Andersson , Mathieu Poirier , Simon Horman , Jonathan Corbet , Nishanth Menon , Vignesh Raghavendra , Mengyuan Lou , MD Danish Anwar , Xin Guo , Lei Wei , Lee Trager , Michael Ellerman , Fan Gong , Lorenzo Bianconi , Geert Uytterhoeven , Lukas Bulwahn , Parthiban Veerasooran , Suman Anna CC: Tero Kristo , , , , , , , , Roger Quadros Subject: [PATCH net-next v2 6/8] net: rpmsg-eth: Add netdev ops Date: Tue, 2 Sep 2025 14:37:44 +0530 Message-ID: <20250902090746.3221225-7-danishanwar@ti.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250902090746.3221225-1-danishanwar@ti.com> References: <20250902090746.3221225-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 b6fe5628933d..a2248e21cf22 100644 --- a/drivers/net/ethernet/rpmsg_eth.c +++ b/drivers/net/ethernet/rpmsg_eth.c @@ -49,6 +49,109 @@ 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; + int ret =3D 0; + + 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: + ret =3D -EINVAL; + dev_err(common->dev, "Invalid RPMSG request\n"); + } + return ret; +} + +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 =3D 0; + + 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) { @@ -85,6 +188,17 @@ static int rpmsg_eth_rpmsg_cb(struct rpmsg_device *rpde= v, void *data, int len, dev_err(common->dev, "RPMSG handshake failed %d\n", ret); return ret; } + + 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; @@ -92,6 +206,20 @@ static int rpmsg_eth_rpmsg_cb(struct rpmsg_device *rpde= v, 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"); @@ -163,6 +291,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; + ret =3D rpmsg_eth_create_send_request(common, RPMSG_ETH_REQ_SET_MAC_ADDR,= false); + return ret; +} + +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; @@ -186,6 +489,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; @@ -197,6 +501,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); @@ -225,6 +531,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) @@ -235,6 +547,12 @@ 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); + 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 0c2ae89fbfbf..992d05bd9386 100644 --- a/drivers/net/ethernet/rpmsg_eth.h +++ b/drivers/net/ethernet/rpmsg_eth.h @@ -209,6 +209,7 @@ enum rpmsg_eth_state { * @dev: Device * @state: Interface state * @state_work: Delayed work for state machine + * @sync_msg: Completion for synchronous message */ struct rpmsg_eth_common { struct rpmsg_device *rpdev; @@ -224,6 +225,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