This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit d2dde8a29a8875fca06c67054a3a228683d73904
Author: daichuan <[email protected]>
AuthorDate: Sat Aug 30 01:17:47 2025 +0800

    net/netdev: modify for hardware checksum offload
    
    Implementation of main hardware verification and uninstallation functions
    
    Signed-off-by: daichuan <[email protected]>
---
 include/nuttx/net/netdev.h   |   9 ++
 net/devif/ipv4_input.c       |   3 +-
 net/inet/ipv4_build_header.c |   1 +
 net/netdev/Kconfig           |   9 ++
 net/netdev/netdev_checksum.c | 195 +++++++++++++++++++++++++++++++++++++++++++
 net/tcp/tcp_input.c          |   3 +-
 net/tcp/tcp_send.c           |  46 +++++++++-
 net/udp/udp_input.c          |   8 +-
 net/udp/udp_send.c           |  23 ++++-
 9 files changed, 287 insertions(+), 10 deletions(-)

diff --git a/include/nuttx/net/netdev.h b/include/nuttx/net/netdev.h
index deccb9a6563..cfb9c84f901 100644
--- a/include/nuttx/net/netdev.h
+++ b/include/nuttx/net/netdev.h
@@ -80,6 +80,11 @@
  * Pre-processor Definitions
  ****************************************************************************/
 
+/* Hardware features bits */
+
+#define NETDEV_TX_CSUM  (1 << 1) /* Netdev support hardware tx checksum */
+#define NETDEV_RX_CSUM  (1 << 2) /* Netdev support hardware rx checksum */
+
 /* Determine the largest possible address */
 
 #if defined(CONFIG_WIRELESS_IEEE802154) && defined(CONFIG_WIRELESS_PKTRADIO)
@@ -341,6 +346,10 @@ struct net_driver_s
 
   uint32_t d_flags;
 
+  /* Hardware features. See NETDEV_* definitions */
+
+  uint8_t d_features;
+
   /* Multi network devices using multiple link layer protocols are
    * supported
    */
diff --git a/net/devif/ipv4_input.c b/net/devif/ipv4_input.c
index 7fbf8d003be..7b8537bcd00 100644
--- a/net/devif/ipv4_input.c
+++ b/net/devif/ipv4_input.c
@@ -447,7 +447,8 @@ static int ipv4_in(FAR struct net_driver_s *dev)
 #endif
 
 #ifdef CONFIG_NET_IPV4_CHECKSUMS
-  if (ipv4_chksum(IPv4BUF) != 0xffff)
+  if (((dev->d_features & NETDEV_RX_CSUM) == 0)
+      && (ipv4_chksum(IPv4BUF) != 0xffff))
     {
       /* Compute and check the IP header checksum. */
 
diff --git a/net/inet/ipv4_build_header.c b/net/inet/ipv4_build_header.c
index be697258ff7..4849d7bff88 100644
--- a/net/inet/ipv4_build_header.c
+++ b/net/inet/ipv4_build_header.c
@@ -59,6 +59,7 @@ static uint16_t g_ipid;
  *   src_ip     Source IPv4 address
  *   dst_ip     Destination IPv4 address
  *   ttl        Time to live(IPv4)
+ *   tos        Type of Service(IPv4)
  *   opt        IPv4 options
  *
  * Returned Value:
diff --git a/net/netdev/Kconfig b/net/netdev/Kconfig
index 227b5c2d58f..9e293e15747 100644
--- a/net/netdev/Kconfig
+++ b/net/netdev/Kconfig
@@ -99,4 +99,13 @@ config NETDOWN_NOTIFIER
                notifier, but was developed specifically to support SIGHUP 
poll()
                logic.
 
+config NETDEV_CHECKSUM
+       bool "netdev hardware checksum"
+       default n
+       ---help---
+               To support hardware checksum calculation for network cards, we
+               need to know the starting position of the L4 layer header in
+               the iob buffer, as well as the offset of the checksum field
+               within the L4 layer.
+
 endmenu # Network Device Operations
diff --git a/net/netdev/netdev_checksum.c b/net/netdev/netdev_checksum.c
new file mode 100644
index 00000000000..b8bf44e0c83
--- /dev/null
+++ b/net/netdev/netdev_checksum.c
@@ -0,0 +1,195 @@
+/****************************************************************************
+ * net/netdev/netdev_checksum.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/net/netdev.h>
+
+#include "netdev/netdev.h"
+
+#ifdef CONFIG_NETDEV_CHECKSUM
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: hardware_chksum_start
+ *
+ * Description:
+ *   get checksum start offset position with iob buffer
+ *
+ * Input Parameters:
+ *   dev       -  The driver structure
+ *   iphdrlen  -  ipv4/ipv6 header length
+ *
+ * Returned Value:
+ *   The checksum start offset position
+ *
+ ****************************************************************************/
+
+static int32_t hardware_chksum_start(FAR struct iob_s *iob,
+                                     uint16_t iphdrlen)
+{
+  int32_t start = 0;
+
+  if (iphdrlen > iob->io_len)
+    {
+      return -EINVAL;
+    }
+
+  if (iob != NULL)
+    {
+      start = iob->io_offset + iphdrlen;
+    }
+
+  return start;
+}
+
+/****************************************************************************
+ * Name: hardware_chksum_get_proto
+ *
+ * Description:
+ *   get proto with dev.
+ *
+ * Input Parameters:
+ *   dev  -  The driver structure
+ *
+ * Returned Value:
+ *   The proto value
+ *
+ ****************************************************************************/
+
+static uint8_t hardware_chksum_get_proto(FAR struct net_driver_s *dev)
+{
+  uint8_t proto;
+
+  if (IFF_IS_IPv6(dev->d_flags))
+    {
+      FAR struct ipv6_hdr_s *ipv6 = IPv6BUF;
+      proto = ipv6->proto;
+    }
+  else
+    {
+      FAR struct ipv4_hdr_s *ipv4 = IPv4BUF;
+      proto = ipv4->proto;
+    }
+
+  return proto;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: netdev_checksum_start
+ *
+ * Description:
+ *   get checksum start offset position with iob, then hardwear can
+ *   use to calculate the package payload checksum value.
+ *
+ * Input Parameters:
+ *   dev  -  The driver structure
+ *
+ * Returned Value:
+ *   The checksum start offset position, -EINVAL is mean not need calculate
+ *   with hardware
+ *
+ ****************************************************************************/
+
+int netdev_checksum_start(FAR struct net_driver_s *dev)
+{
+  int start;
+
+  if (IFF_IS_IPv6(dev->d_flags))
+    {
+      FAR struct ipv6_hdr_s *ipv6 =
+        (FAR struct ipv6_hdr_s *)(IOB_DATA(dev->d_iob));
+
+      if ((ipv6->proto == IP_PROTO_UDP) || (ipv6->proto == IP_PROTO_TCP))
+        {
+          start = hardware_chksum_start(dev->d_iob, IPv6_HDRLEN);
+        }
+      else
+        {
+          return -EINVAL;
+        }
+    }
+  else
+    {
+      FAR struct ipv4_hdr_s *ipv4 =
+        (FAR struct ipv4_hdr_s *)(IOB_DATA(dev->d_iob));
+
+      if ((ipv4->proto == IP_PROTO_UDP) || (ipv4->proto == IP_PROTO_TCP))
+        {
+          start = hardware_chksum_start(dev->d_iob,
+                                        ((ipv4->vhl & IPv4_HLMASK) << 2));
+        }
+      else
+        {
+          return -EINVAL;
+        }
+    }
+
+  return start;
+}
+
+/****************************************************************************
+ * Name: netdev_checksum_offset
+ *
+ * Description:
+ *   get checksum field offset with tcp/udp header.
+ *
+ * Input Parameters:
+ *   dev  -  The driver structure
+ *
+ * Returned Value:
+ *   The checksum field offset with L4, -EINVAL is mean not need calculate
+ *   with hardware
+ *
+ ****************************************************************************/
+
+int netdev_checksum_offset(FAR struct net_driver_s *dev)
+{
+  int offset = 0;
+  uint8_t proto = hardware_chksum_get_proto(dev);
+
+  if (proto == IP_PROTO_UDP)
+    {
+      offset = offsetof(struct udp_hdr_s, udpchksum);
+    }
+  else if (proto == IP_PROTO_TCP)
+    {
+      offset = offsetof(struct tcp_hdr_s, tcpchksum);
+    }
+  else
+    {
+      return -EINVAL;
+    }
+
+  return offset;
+}
+
+#endif /* CONFIG_NETDEV_CHECKSUM */
diff --git a/net/tcp/tcp_input.c b/net/tcp/tcp_input.c
index 54cbd2e73d8..003665cfa1c 100644
--- a/net/tcp/tcp_input.c
+++ b/net/tcp/tcp_input.c
@@ -726,7 +726,8 @@ static void tcp_input(FAR struct net_driver_s *dev, uint8_t 
domain,
 #ifdef CONFIG_NET_TCP_CHECKSUMS
   /* Start of TCP input header processing code. */
 
-  if (tcp_chksum(dev) != 0xffff)
+  if (((dev->d_features & NETDEV_RX_CSUM) == 0)
+      && (tcp_chksum(dev) != 0xffff))
     {
       /* Compute and check the TCP checksum. */
 
diff --git a/net/tcp/tcp_send.c b/net/tcp/tcp_send.c
index 5713d2d7eed..574691e935e 100644
--- a/net/tcp/tcp_send.c
+++ b/net/tcp/tcp_send.c
@@ -200,7 +200,17 @@ static void tcp_sendcommon(FAR struct net_driver_s *dev,
       tcp->tcpchksum = 0;
 
 #ifdef CONFIG_NET_TCP_CHECKSUMS
-      tcp->tcpchksum = ~tcp_ipv6_chksum(dev);
+      if ((dev->d_features & NETDEV_TX_CSUM) == 0)
+        {
+          tcp->tcpchksum = ~tcp_ipv6_chksum(dev);
+        }
+      else
+        {
+          uint16_t chksum = ipv6_upperlayer_header_chksum(dev,
+                                                          IP_PROTO_TCP,
+                                                          IPv6_HDRLEN);
+          tcp->tcpchksum = HTONS(chksum);
+        }
 #endif
 
 #ifdef CONFIG_NET_STATISTICS
@@ -224,7 +234,16 @@ static void tcp_sendcommon(FAR struct net_driver_s *dev,
       tcp->tcpchksum = 0;
 
 #ifdef CONFIG_NET_TCP_CHECKSUMS
-      tcp->tcpchksum = ~tcp_ipv4_chksum(dev);
+      if ((dev->d_features & NETDEV_TX_CSUM) == 0)
+        {
+          tcp->tcpchksum = ~tcp_ipv4_chksum(dev);
+        }
+      else
+        {
+          uint16_t chksum = ipv4_upperlayer_header_chksum(dev,
+                                                          IP_PROTO_TCP);
+          tcp->tcpchksum = HTONS(chksum);
+        }
 #endif
 
 #ifdef CONFIG_NET_STATISTICS
@@ -507,7 +526,17 @@ void tcp_reset(FAR struct net_driver_s *dev, FAR struct 
tcp_conn_s *conn)
       tcp->tcpchksum = 0;
 
 #ifdef CONFIG_NET_TCP_CHECKSUMS
-      tcp->tcpchksum = ~tcp_ipv6_chksum(dev);
+      if ((dev->d_features & NETDEV_TX_CSUM) == 0)
+        {
+          tcp->tcpchksum = ~tcp_ipv6_chksum(dev);
+        }
+      else
+        {
+          uint16_t chksum = ipv6_upperlayer_header_chksum(dev,
+                                                          IP_PROTO_TCP,
+                                                          IPv6_HDRLEN);
+          tcp->tcpchksum = HTONS(chksum);
+        }
 #endif
     }
 #endif /* CONFIG_NET_IPv6 */
@@ -527,7 +556,16 @@ void tcp_reset(FAR struct net_driver_s *dev, FAR struct 
tcp_conn_s *conn)
       tcp->tcpchksum = 0;
 
 #ifdef CONFIG_NET_TCP_CHECKSUMS
-      tcp->tcpchksum = ~tcp_ipv4_chksum(dev);
+      if ((dev->d_features & NETDEV_TX_CSUM) == 0)
+        {
+          tcp->tcpchksum = ~tcp_ipv4_chksum(dev);
+        }
+      else
+        {
+          uint16_t chksum = ipv4_upperlayer_header_chksum(dev,
+                                                          IP_PROTO_TCP);
+          tcp->tcpchksum = HTONS(chksum);
+        }
 #endif
     }
 #endif /* CONFIG_NET_IPv4 */
diff --git a/net/udp/udp_input.c b/net/udp/udp_input.c
index 15534e37b97..1ad1724a54e 100644
--- a/net/udp/udp_input.c
+++ b/net/udp/udp_input.c
@@ -217,7 +217,7 @@ static int udp_input(FAR struct net_driver_s *dev, unsigned 
int iplen)
   unsigned int udpiplen;
   unsigned int udpdatalen = dev->d_len - iplen;
 #ifdef CONFIG_NET_UDP_CHECKSUMS
-  uint16_t chksum;
+  uint16_t chksum = 0;
 #endif
   int ret = OK;
 
@@ -256,7 +256,11 @@ static int udp_input(FAR struct net_driver_s *dev, 
unsigned int iplen)
   dev->d_appdata = IPBUF(udpiplen);
 
 #ifdef CONFIG_NET_UDP_CHECKSUMS
-  chksum = udp->udpchksum;
+  if ((dev->d_features & NETDEV_RX_CSUM) == 0)
+    {
+      chksum = udp->udpchksum;
+    }
+
   if (chksum != 0)
     {
 #ifdef CONFIG_NET_IPv6
diff --git a/net/udp/udp_send.c b/net/udp/udp_send.c
index 8b59be7fb2f..aaa1c5a09df 100644
--- a/net/udp/udp_send.c
+++ b/net/udp/udp_send.c
@@ -255,7 +255,16 @@ void udp_send(FAR struct net_driver_s *dev, FAR struct 
udp_conn_s *conn)
       if (IFF_IS_IPv4(dev->d_flags))
 #endif
         {
-          udp->udpchksum = ~udp_ipv4_chksum(dev);
+          if ((dev->d_features & NETDEV_TX_CSUM) == 0)
+            {
+              udp->udpchksum = ~udp_ipv4_chksum(dev);
+            }
+          else
+            {
+              uint16_t chksum = ipv4_upperlayer_header_chksum(dev,
+                                                              IP_PROTO_UDP);
+              udp->udpchksum = HTONS(chksum);
+            }
         }
 #endif /* CONFIG_NET_IPv4 */
 
@@ -264,7 +273,17 @@ void udp_send(FAR struct net_driver_s *dev, FAR struct 
udp_conn_s *conn)
       else
 #endif
         {
-          udp->udpchksum = ~udp_ipv6_chksum(dev);
+          if ((dev->d_features & NETDEV_TX_CSUM) == 0)
+            {
+              udp->udpchksum = ~udp_ipv6_chksum(dev);
+            }
+          else
+            {
+              uint16_t chksum = ipv6_upperlayer_header_chksum(dev,
+                                                              IP_PROTO_UDP,
+                                                              IPv6_HDRLEN);
+              udp->udpchksum = HTONS(chksum);
+            }
         }
 #endif /* CONFIG_NET_IPv6 */
 

Reply via email to