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 */
