You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2026/01/19 15:22:53 UTC
(nuttx) 01/04: net/netdev: modify for hardware checksum offload
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 <da...@xiaomi.com>
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 <da...@xiaomi.com>
---
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 */