diff --git a/arch/sim/src/sim/sim_netdriver.c b/arch/sim/src/sim/sim_netdriver.c index d98c6fa99b..999459a63d 100644 --- a/arch/sim/src/sim/sim_netdriver.c +++ b/arch/sim/src/sim/sim_netdriver.c @@ -72,6 +72,11 @@ #include "sim_internal.h" +#if CONFIG_IOB_BUFSIZE >= (MAX_NETDEV_PKTSIZE + \ + CONFIG_NET_GUARDSIZE + CONFIG_NET_LL_GUARDSIZE) +# define SIM_NETDEV_IOB_OFFLOAD +#endif + /**************************************************************************** * Private Function Prototypes ****************************************************************************/ @@ -95,12 +100,23 @@ static struct net_driver_s g_sim_dev[CONFIG_SIM_NETDEV_NUMBER]; * Private Functions ****************************************************************************/ -static void netdriver_reply(struct net_driver_s *dev) +static void netdriver_send(struct net_driver_s *dev) { int devidx = (intptr_t)dev->d_private; +#ifdef SIM_NETDEV_IOB_OFFLOAD + uint8_t *buf = &dev->d_iob->io_data[CONFIG_NET_LL_GUARDSIZE - + NET_LL_HDRLEN(dev)]; +#else + uint8_t *buf = dev->d_buf; +#endif UNUSED(devidx); + sim_netdev_send(devidx, buf, dev->d_len); +} + +static void netdriver_reply(struct net_driver_s *dev) +{ /* If the receiving resulted in data that should be sent out on * the network, the field d_len is set to a value > 0. */ @@ -110,7 +126,7 @@ static void netdriver_reply(struct net_driver_s *dev) /* Send the packet */ NETDEV_TXPACKETS(dev); - sim_netdev_send(devidx, dev->d_buf, dev->d_len); + netdriver_send(dev); NETDEV_TXDONE(dev); } } @@ -131,6 +147,14 @@ static void netdriver_recv_work(void *arg) while (sim_netdev_avail(devidx)) { +#ifdef SIM_NETDEV_IOB_OFFLOAD + if (netdev_iob_prepare(dev, false, 0) != OK) + { + netdriver_txdone_interrupt(dev); + break; + } +#endif + /* sim_netdev_read will return 0 on a timeout event and > 0 * on a data received event */ @@ -142,6 +166,10 @@ static void netdriver_recv_work(void *arg) { NETDEV_RXPACKETS(dev); +#ifdef SIM_NETDEV_IOB_OFFLOAD + iob_update_pktlen(dev->d_iob, dev->d_len - NET_LL_HDRLEN(dev)); +#endif + /* Data received event. Check for valid Ethernet header with * destination == our MAC address */ @@ -208,7 +236,7 @@ static void netdriver_recv_work(void *arg) if (dev->d_len > 0) { - sim_netdev_send(devidx, dev->d_buf, dev->d_len); + netdriver_send(dev); } } else @@ -224,6 +252,10 @@ static void netdriver_recv_work(void *arg) NETDEV_RXERRORS(dev); } } + +#ifdef SIM_NETDEV_IOB_OFFLOAD + netdev_iob_release(dev); +#endif } net_unlock(); @@ -231,14 +263,10 @@ static void netdriver_recv_work(void *arg) static int netdriver_txpoll(struct net_driver_s *dev) { - int devidx = (intptr_t)dev->d_private; - - UNUSED(devidx); - /* Send the packet */ NETDEV_TXPACKETS(dev); - sim_netdev_send(devidx, dev->d_buf, dev->d_len); + netdriver_send(dev); NETDEV_TXDONE(dev); /* If zero is returned, the polling will continue until all connections @@ -321,9 +349,8 @@ static void netdriver_rxready_interrupt(void *priv) int sim_netdriver_init(void) { struct net_driver_s *dev; - void *pktbuf; - int pktsize; int devidx; + for (devidx = 0; devidx < CONFIG_SIM_NETDEV_NUMBER; devidx++) { dev = &g_sim_dev[devidx]; @@ -334,22 +361,19 @@ int sim_netdriver_init(void) netdriver_txdone_interrupt, netdriver_rxready_interrupt); - /* Update the buffer size */ - - pktsize = dev->d_pktsize ? dev->d_pktsize : - (MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE); - /* Allocate packet buffer */ - pktbuf = kmm_malloc(pktsize); - if (pktbuf == NULL) +#ifndef SIM_NETDEV_IOB_OFFLOAD + dev->d_buf = kmm_malloc(dev->d_pktsize != 0 ? + dev->d_pktsize : + (MAX_NETDEV_PKTSIZE + + CONFIG_NET_GUARDSIZE)); + if (dev->d_buf == NULL) { return -ENOMEM; } +#endif - /* Set callbacks */ - - dev->d_buf = pktbuf; dev->d_ifup = netdriver_ifup; dev->d_ifdown = netdriver_ifdown; dev->d_txavail = netdriver_txavail; diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c index a90e203420..55e14aa7c6 100644 --- a/drivers/net/loopback.c +++ b/drivers/net/loopback.c @@ -82,7 +82,6 @@ struct lo_driver_s ****************************************************************************/ static struct lo_driver_s g_loopback; -static uint8_t g_iobuffer[NET_LO_PKTSIZE + CONFIG_NET_GUARDSIZE]; /**************************************************************************** * Private Function Prototypes @@ -334,7 +333,6 @@ int localhost_initialize(void) priv->lo_dev.d_addmac = lo_addmac; /* Add multicast MAC address */ priv->lo_dev.d_rmmac = lo_rmmac; /* Remove multicast MAC address */ #endif - priv->lo_dev.d_buf = g_iobuffer; /* Attach the IO buffer */ priv->lo_dev.d_private = priv; /* Used to recover private state from dev */ /* Register the loopabck device with the OS so that socket IOCTLs can b diff --git a/include/nuttx/mm/iob.h b/include/nuttx/mm/iob.h index 0231a1cbbc..fa672d0cd5 100644 --- a/include/nuttx/mm/iob.h +++ b/include/nuttx/mm/iob.h @@ -127,7 +127,7 @@ struct iob_s #if CONFIG_IOB_HEADSIZE > 0 uint8_t io_head[CONFIG_IOB_HEADSIZE]; #endif - uint8_t io_data[CONFIG_IOB_BUFSIZE]; + uint8_t io_data[CONFIG_IOB_BUFSIZE] aligned_data(CONFIG_IOB_ALIGNMENT); }; #if CONFIG_IOB_NCHAINS > 0 diff --git a/include/nuttx/net/netdev.h b/include/nuttx/net/netdev.h index 435480b99b..ebdceb6d4c 100644 --- a/include/nuttx/net/netdev.h +++ b/include/nuttx/net/netdev.h @@ -59,8 +59,11 @@ #include #include +#include #include +#include #include +#include #ifdef CONFIG_NET_IGMP # include @@ -307,6 +310,20 @@ struct net_driver_s net_ipv6addr_t d_ipv6draddr; /* Default router IPv6 address */ net_ipv6addr_t d_ipv6netmask; /* Network IPv6 subnet mask */ #endif + /* This is a new design that uses d_iob as packets input and output + * buffer which used by some NICs such as celluler net driver. Case for + * data input, note that d_iob maybe a linked chain only when using + * d_iob to store reassembled datagrams, otherwise d_iob uses only + * one buffer to hold the entire frame data. Case for data output, note + * that d_iob also maybe a linked chain only when using d_iob to + * store fragmented datagrams, otherwise d_iob uses only one buffer + * to hold the entire frame data. + * + * In all cases mentioned above, d_buf always points to the beginning + * of the first buffer of d_iob. + */ + + FAR struct iob_s *d_iob; /* The d_buf array is used to hold incoming and outgoing packets. The * device driver should place incoming data into this buffer. When sending @@ -427,10 +444,6 @@ struct net_driver_s typedef CODE int (*devif_poll_callback_t)(FAR struct net_driver_s *dev); -/**************************************************************************** - * Public Data - ****************************************************************************/ - /**************************************************************************** * Public Function Prototypes ****************************************************************************/ @@ -560,6 +573,19 @@ int sixlowpan_input(FAR struct radio_driver_s *ieee, * return 0; * } * + * Compatible with all old flat buffer NICs + * + * tcp_poll()/udp_poll()/pkt_poll()/...(l3/l4) + * / \ + * / \ + * devif_poll_[l3/l4]_connections() devif_iob_send() (nocopy:udp/icmp/..) + * / \ (copy:tcp) + * / \ + * devif_iob_poll(devif_poll_callback()) devif_poll_callback() + * / \ + * / \ + * devif_poll("NIC"_txpoll) "NIC"_send()(dev->d_buf) + * ****************************************************************************/ int devif_poll(FAR struct net_driver_s *dev, devif_poll_callback_t callback); @@ -640,6 +666,26 @@ int netdev_carrier_off(FAR struct net_driver_s *dev); uint16_t chksum(uint16_t sum, FAR const uint8_t *data, uint16_t len); +/**************************************************************************** + * Name: chksum_iob + * + * Description: + * Calculate the Internet checksum over an iob chain buffer. + * + * Input Parameters: + * sum - Partial calculations carried over from a previous call to + * chksum(). This should be zero on the first time that check + * sum is called. + * iob - An iob chain buffer over which the checksum is to be computed. + * offset - Specifies the byte offset of the start of valid data. + * + * Returned Value: + * The updated checksum value. + * + ****************************************************************************/ + +uint16_t chksum_iob(uint16_t sum, FAR struct iob_s *iob, uint16_t offset); + /**************************************************************************** * Name: net_chksum * @@ -668,6 +714,35 @@ uint16_t chksum(uint16_t sum, FAR const uint8_t *data, uint16_t len); uint16_t net_chksum(FAR uint16_t *data, uint16_t len); +/**************************************************************************** + * Name: net_chksum_iob + * + * Description: + * Calculate the Internet checksum over an iob chain buffer. + * + * The Internet checksum is the one's complement of the one's complement + * sum of all 16-bit words in the buffer. + * + * See RFC1071. + * + * If CONFIG_NET_ARCH_CHKSUM is defined, then this function must be + * provided by architecture-specific logic. + * + * Input Parameters: + * sum - Partial calculations carried over from a previous call to + * chksum(). This should be zero on the first time that check + * sum is called. + * iob - An iob chain buffer over which the checksum is to be computed. + * offset - Specifies the byte offset of the start of valid data. + * + * Returned Value: + * The Internet checksum of the given iob chain buffer. + * + ****************************************************************************/ + +uint16_t net_chksum_iob(uint16_t sum, FAR struct iob_s *iob, + uint16_t offset); + /**************************************************************************** * Name: ipv4_upperlayer_chksum * @@ -802,4 +877,54 @@ void net_incr32(FAR uint8_t *op32, uint16_t op16); int netdev_lladdrsize(FAR struct net_driver_s *dev); +/**************************************************************************** + * Name: netdev_iob_prepare + * + * Description: + * Prepare data buffer for a given NIC + * The iob offset will be updated to l2 gruard size by default: + * ---------------------------------------------------------------- + * | iob entry | + * ---------------------------------------------------------------| + * |<--- CONFIG_NET_LL_GUARDSIZE -->|<--- io_len/io_pktlen(0) --->| + * ---------------------------------------------------------------| + * + * Assumptions: + * The caller has locked the network. + * + * Returned Value: + * A non-zero copy is returned on success. + * + ****************************************************************************/ + +int netdev_iob_prepare(FAR struct net_driver_s *dev, bool throttled, + unsigned int timeout); + +/**************************************************************************** + * Name: netdev_iob_clear + * + * Description: + * Clean up buffer resources for a given NIC + * + * Assumptions: + * The caller has locked the network and dev->d_iob has been + * released or taken away. + * + ****************************************************************************/ + +void netdev_iob_clear(FAR struct net_driver_s *dev); + +/**************************************************************************** + * Name: netdev_iob_release + * + * Description: + * Release buffer resources for a given NIC + * + * Assumptions: + * The caller has locked the network. + * + ****************************************************************************/ + +void netdev_iob_release(FAR struct net_driver_s *dev); + #endif /* __INCLUDE_NUTTX_NET_NETDEV_H */ diff --git a/mm/iob/Kconfig b/mm/iob/Kconfig index 31b8b1091e..6a1d07de70 100644 --- a/mm/iob/Kconfig +++ b/mm/iob/Kconfig @@ -41,7 +41,7 @@ config IOB_HEADSIZE config IOB_ALIGNMENT int "Alignment size of each I/O buffer" - default 1 + default 4 ---help--- The member io_head of all I/O buffers is aligned to the value specified by this configuration. diff --git a/net/Kconfig b/net/Kconfig index c38539fcbf..5a8f0c0e2d 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -117,6 +117,15 @@ config NET_GUARDSIZE packet size will be chopped down to the size indicated in the TCP header. +config NET_LL_GUARDSIZE + int "Data Link Layer(L2) Guard size of Network buffer(IOB)" + default 14 if NET_ETHERNET + default 0 + ---help--- + This is reserved l2 buffer header size of network buffer to isolate + the L2/L3 (MAC/IP) data on Network layer, which will be beneficial + to L3 network layer protocol transparent transmission and forwarding + config NET_RECV_BUFSIZE int "Net Receive buffer size" default 0 diff --git a/net/arp/arp_format.c b/net/arp/arp_format.c index 458a013ead..50782eaae4 100644 --- a/net/arp/arp_format.c +++ b/net/arp/arp_format.c @@ -97,6 +97,10 @@ void arp_format(FAR struct net_driver_s *dev, in_addr_t ipaddr) eth->type = HTONS(ETHTYPE_ARP); dev->d_len = sizeof(struct arp_hdr_s) + ETH_HDRLEN; + + /* Update device buffer length */ + + iob_update_pktlen(dev->d_iob, sizeof(struct arp_hdr_s)); } #endif /* CONFIG_NET_ARP */ diff --git a/net/can/can.h b/net/can/can.h index b1180c11aa..62a47cf59a 100644 --- a/net/can/can.h +++ b/net/can/can.h @@ -209,10 +209,8 @@ uint16_t can_callback(FAR struct net_driver_s *dev, * receive the data. * * Input Parameters: + * dev - The device which as active when the event was detected. * conn - A pointer to the CAN connection structure - * buffer - A pointer to the buffer to be copied to the read-ahead - * buffers - * buflen - The number of bytes to copy to the read-ahead buffer. * * Returned Value: * The number of bytes actually buffered is returned. This will be either @@ -225,8 +223,8 @@ uint16_t can_callback(FAR struct net_driver_s *dev, * ****************************************************************************/ -uint16_t can_datahandler(FAR struct can_conn_s *conn, FAR uint8_t *buffer, - uint16_t buflen); +uint16_t can_datahandler(FAR struct net_driver_s *dev, + FAR struct can_conn_s *conn); /**************************************************************************** * Name: can_recvmsg diff --git a/net/can/can_callback.c b/net/can/can_callback.c index a5f3e17937..b080e4da6f 100644 --- a/net/can/can_callback.c +++ b/net/can/can_callback.c @@ -61,10 +61,9 @@ static inline uint16_t can_data_event(FAR struct net_driver_s *dev, FAR struct can_conn_s *conn, uint16_t flags) { - uint16_t ret; - FAR uint8_t *buffer = dev->d_appdata; int buflen = dev->d_len; uint16_t recvlen; + uint16_t ret; ret = (flags & ~CAN_NEWDATA); @@ -72,7 +71,7 @@ can_data_event(FAR struct net_driver_s *dev, FAR struct can_conn_s *conn, * partial packets will not be buffered. */ - recvlen = can_datahandler(conn, buffer, buflen); + recvlen = can_datahandler(dev, conn); if (recvlen < buflen) { /* There is no handler to receive new data and there are no free @@ -120,21 +119,6 @@ uint16_t can_callback(FAR struct net_driver_s *dev, if (conn) { -#ifdef CONFIG_NET_TIMESTAMP - /* TIMESTAMP sockopt is activated, create timestamp and copy to iob */ - - if (conn->timestamp) - { - struct timespec *ts = (struct timespec *) - &dev->d_appdata[dev->d_len]; - struct timeval *tv = (struct timeval *) - &dev->d_appdata[dev->d_len]; - dev->d_len += sizeof(struct timeval); - clock_systime_timespec(ts); - tv->tv_usec = ts->tv_nsec / 1000; - } -#endif - /* Try to lock the network when successful send data to the listener */ if (net_trylock() == OK) @@ -149,6 +133,34 @@ uint16_t can_callback(FAR struct net_driver_s *dev, if ((flags & CAN_NEWDATA) != 0) { +#ifdef CONFIG_NET_TIMESTAMP + /* TIMESTAMP sockopt is activated, + * create timestamp and copy to iob + */ + + if (conn->timestamp) + { + struct timeval tv; + FAR struct timespec *ts = (FAR struct timespec *)&tv; + int len; + + clock_systime_timespec(ts); + tv.tv_usec = ts->tv_nsec / 1000; + + len = iob_trycopyin(dev->d_iob, (FAR uint8_t *)&tv, + sizeof(struct timeval), 0, false); + if (len != sizeof(struct timeval)) + { + dev->d_len = 0; + return flags & ~CAN_NEWDATA; + } + else + { + dev->d_len += len; + } + } + +#endif /* Data was not handled.. dispose of it appropriately */ flags = can_data_event(dev, conn, flags); @@ -168,10 +180,8 @@ uint16_t can_callback(FAR struct net_driver_s *dev, * receive the data. * * Input Parameters: + * dev - The device which as active when the event was detected. * conn - A pointer to the CAN connection structure - * buffer - A pointer to the buffer to be copied to the read-ahead - * buffers - * buflen - The number of bytes to copy to the read-ahead buffer. * * Returned Value: * The number of bytes actually buffered is returned. This will be either @@ -184,58 +194,28 @@ uint16_t can_callback(FAR struct net_driver_s *dev, * ****************************************************************************/ -uint16_t can_datahandler(FAR struct can_conn_s *conn, FAR uint8_t *buffer, - uint16_t buflen) +uint16_t can_datahandler(FAR struct net_driver_s *dev, + FAR struct can_conn_s *conn) { - FAR struct iob_s *iob; + FAR struct iob_s *iob = dev->d_iob; int ret; - /* Try to allocate on I/O buffer to start the chain without waiting (and - * throttling as necessary). If we would have to wait, then drop the - * packet. - */ - - iob = iob_tryalloc(true); - if (iob == NULL) - { - nerr("ERROR: Failed to create new I/O buffer chain\n"); - return 0; - } - - /* Copy the new appdata into the I/O buffer chain (without waiting) */ - - ret = iob_trycopyin(iob, buffer, buflen, 0, true); - if (ret < 0) - { - /* On a failure, iob_copyin return a negated error value but does - * not free any I/O buffers. - */ - - nerr("ERROR: Failed to add data to the I/O buffer chain: %d\n", ret); - iob_free_chain(iob); - return 0; - } - - /* Add the new I/O buffer chain to the tail of the read-ahead queue (again - * without waiting). - */ + /* Concat the iob to readahead */ ret = iob_tryadd_queue(iob, &conn->readahead); - if (ret < 0) + if (ret >= 0) { - nerr("ERROR: Failed to queue the I/O buffer chain: %d\n", ret); - iob_free_chain(iob); - return 0; +#ifdef CONFIG_NET_CAN_NOTIFIER + /* Provide notification(s) that additional CAN read-ahead data is + * available. + */ + + can_readahead_signal(conn); +#endif + ret = iob->io_pktlen; } -#ifdef CONFIG_NET_CAN_NOTIFIER - /* Provide notification(s) that additional CAN read-ahead data is - * available. - */ - - can_readahead_signal(conn); -#endif - return buflen; + return ret; } #endif /* CONFIG_NET && CONFIG_NET_CAN */ diff --git a/net/can/can_recvmsg.c b/net/can/can_recvmsg.c index 5ad1bfbb48..304535e885 100644 --- a/net/can/can_recvmsg.c +++ b/net/can/can_recvmsg.c @@ -120,8 +120,9 @@ static inline void can_add_recvlen(FAR struct can_recvfrom_s *pstate, ****************************************************************************/ static size_t can_recvfrom_newdata(FAR struct net_driver_s *dev, - FAR struct can_recvfrom_s *pstate) + FAR struct can_recvfrom_s *pstate) { + unsigned int offset; size_t recvlen; if (dev->d_len > pstate->pr_buflen) @@ -135,7 +136,13 @@ static size_t can_recvfrom_newdata(FAR struct net_driver_s *dev, /* Copy the new packet data into the user buffer */ - memcpy(pstate->pr_buffer, dev->d_buf, recvlen); + offset = (dev->d_appdata - dev->d_iob->io_data) - dev->d_iob->io_offset; + + recvlen = iob_copyout(pstate->pr_buffer, dev->d_iob, recvlen, offset); + + /* Trim the copied buffers */ + + dev->d_iob = iob_trimhead(dev->d_iob, recvlen + offset); /* Update the accumulated size of the data read */ @@ -175,34 +182,7 @@ static inline void can_newdata(FAR struct net_driver_s *dev, if (recvlen < dev->d_len) { - FAR struct can_conn_s *conn = pstate->pr_conn; - FAR uint8_t *buffer = dev->d_appdata + recvlen; - uint16_t buflen = dev->d_len - recvlen; -#ifdef CONFIG_DEBUG_NET - uint16_t nsaved; - - nsaved = can_datahandler(conn, buffer, buflen); -#else - can_datahandler(conn, buffer, buflen); -#endif - - /* There are complicated buffering issues that are not addressed fully - * here. For example, what if up_datahandler() cannot buffer the - * remainder of the packet? In that case, the data will be dropped but - * still ACKed. Therefore it would not be resent. - * - * This is probably not an issue here because we only get here if the - * read-ahead buffers are empty and there would have to be something - * serioulsy wrong with the configuration not to be able to buffer a - * partial packet in this context. - */ - -#ifdef CONFIG_DEBUG_NET - if (nsaved < buflen) - { - nerr("ERROR: packet data not saved (%d bytes)\n", buflen - nsaved); - } -#endif + can_datahandler(dev, pstate->pr_conn); } /* Indicate no data in the buffer */ diff --git a/net/devif/Make.defs b/net/devif/Make.defs index 3fcfbf6135..89460d80c5 100644 --- a/net/devif/Make.defs +++ b/net/devif/Make.defs @@ -20,39 +20,44 @@ # Network device interface source files -NET_CSRCS += devif_initialize.c devif_send.c devif_poll.c devif_callback.c -NET_CSRCS += devif_loopback.c +NET_CSRCS += devif_initialize.c devif_callback.c # Device driver IP packet receipt interfaces -ifeq ($(CONFIG_NET_IPv4),y) -NET_CSRCS += ipv4_input.c -endif - -ifeq ($(CONFIG_NET_IPv6),y) -NET_CSRCS += ipv6_input.c -endif - -# IP forwarding - -ifeq ($(CONFIG_NET_IPFORWARD),y) -NET_CSRCS += devif_forward.c -endif - -# I/O buffer chain support required? - ifeq ($(CONFIG_MM_IOB),y) -NET_CSRCS += devif_iobsend.c -endif -# Raw packet socket support + # IP packet -ifeq ($(CONFIG_NET_PKT),y) -NET_CSRCS += devif_pktsend.c -endif + NET_CSRCS += devif_send.c devif_loopback.c -ifeq ($(CONFIG_NET_CAN),y) -NET_CSRCS += devif_cansend.c + ifeq ($(CONFIG_NET_IPv4),y) + NET_CSRCS += ipv4_input.c + endif + + ifeq ($(CONFIG_NET_IPv6),y) + NET_CSRCS += ipv6_input.c + endif + + # IP forwarding + + ifeq ($(CONFIG_NET_IPFORWARD),y) + NET_CSRCS += devif_forward.c + endif + + # I/O buffer chain support required? + + NET_CSRCS += devif_poll.c + NET_CSRCS += devif_iobsend.c + + # Raw packet socket support + + ifeq ($(CONFIG_NET_PKT),y) + NET_CSRCS += devif_pktsend.c + endif + + ifeq ($(CONFIG_NET_CAN),y) + NET_CSRCS += devif_cansend.c + endif endif # Include network device interface build support diff --git a/net/devif/devif.h b/net/devif/devif.h index 6b926813b7..378585f071 100644 --- a/net/devif/devif.h +++ b/net/devif/devif.h @@ -449,7 +449,8 @@ uint16_t devif_dev_event(FAR struct net_driver_s *dev, uint16_t flags); * ****************************************************************************/ -void devif_send(FAR struct net_driver_s *dev, FAR const void *buf, int len); +void devif_send(FAR struct net_driver_s *dev, FAR const void *buf, + int len, unsigned int offset); /**************************************************************************** * Name: devif_iob_send @@ -469,7 +470,8 @@ void devif_send(FAR struct net_driver_s *dev, FAR const void *buf, int len); #ifdef CONFIG_MM_IOB struct iob_s; void devif_iob_send(FAR struct net_driver_s *dev, FAR struct iob_s *buf, - unsigned int len, unsigned int offset); + unsigned int len, unsigned int offset, + unsigned int target_offset); #endif /**************************************************************************** @@ -560,6 +562,40 @@ int devif_poll_out(FAR struct net_driver_s *dev, int devif_loopback(FAR struct net_driver_s *dev); +/**************************************************************************** + * Name: netdev_input + * + * Description: + * This function will copy the flat buffer that does not support + * Scatter/gather to the iob vector buffer. + * + * Compatible with all old flat buffer NICs: + * + * [tcp|udp|icmp|...]ipv[4|6]_data_handler() + * | (iob_concat/append to readahead) + * | + * pkt/ipv[4/6]_in()/... + * | + * | + * netdev_input() // new interface, Scatter/gather flat/iobs + * | + * | + * pkt/ipv[4|6]_input()/... + * | + * | + * NICs io vector receive(Orignal flat buffer) + * + * Input Parameters: + * callback - Input callback of L3 stack + * + * Returned Value: + * A non-zero copy is returned on success. + * + ****************************************************************************/ + +int netdev_input(FAR struct net_driver_s *dev, + devif_poll_callback_t callback, bool reply); + #undef EXTERN #ifdef __cplusplus } diff --git a/net/devif/devif_cansend.c b/net/devif/devif_cansend.c index b82956c851..753868fe82 100644 --- a/net/devif/devif_cansend.c +++ b/net/devif/devif_cansend.c @@ -32,34 +32,6 @@ #if defined(CONFIG_NET_CAN) -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -/**************************************************************************** - * Private Type Declarations - ****************************************************************************/ - -/**************************************************************************** - * Private Function Prototypes - ****************************************************************************/ - -/**************************************************************************** - * Public Constant Data - ****************************************************************************/ - -/**************************************************************************** - * Public Data - ****************************************************************************/ - -/**************************************************************************** - * Private Constant Data - ****************************************************************************/ - -/**************************************************************************** - * Private Data - ****************************************************************************/ - /**************************************************************************** * Public Functions ****************************************************************************/ @@ -83,16 +55,25 @@ void devif_can_send(FAR struct net_driver_s *dev, FAR const void *buf, unsigned int len) { - DEBUGASSERT(dev && len > 0 && len <= NETDEV_PKTSIZE(dev)); + unsigned int limit = NETDEV_PKTSIZE(dev) - + CONFIG_NET_LL_GUARDSIZE; - /* Copy the data into the device packet buffer */ + if (dev == NULL || len == 0 || len > limit) + { + nerr("ERROR: devif_pkt_send fail: %p, sndlen: %u, pktlen: %u\n", + dev, len, limit); + return; + } - memcpy(dev->d_buf, buf, len); + iob_update_pktlen(dev->d_iob, 0); - /* Set the number of bytes to send */ + /* Copy the data into the device packet buffer and set the number of + * bytes to send + */ - dev->d_len = len; - dev->d_sndlen = len; + dev->d_sndlen = iob_copyin(dev->d_iob, buf, len, 0, false) == len ? + len : 0; + dev->d_len = dev->d_sndlen; } #endif /* CONFIG_NET_CAN */ diff --git a/net/devif/devif_iobsend.c b/net/devif/devif_iobsend.c index 496bb1df8f..96bf272749 100644 --- a/net/devif/devif_iobsend.c +++ b/net/devif/devif_iobsend.c @@ -53,19 +53,78 @@ ****************************************************************************/ void devif_iob_send(FAR struct net_driver_s *dev, FAR struct iob_s *iob, - unsigned int len, unsigned int offset) + unsigned int len, unsigned int offset, + unsigned int target_offset) { - if (dev == NULL || len == 0 || len >= NETDEV_PKTSIZE(dev)) + unsigned int limit = NETDEV_PKTSIZE(dev) - + NET_LL_HDRLEN(dev) - target_offset; + unsigned int copyin; + int ret; + + if (dev == NULL || len == 0 || len > limit) { - nerr("devif_iob_send error, %p, send len: %u, pkt len: %u\n", - dev, len, NETDEV_PKTSIZE(dev)); + if (dev->d_iob == NULL) + { + iob_free_chain(iob); + } + + nerr("devif_iob_send error, %p, send len: %u, limit len: %u\n", + dev, len, limit); return; } - /* Copy the data from the I/O buffer chain to the device buffer */ + /* Append the send buffer after device buffer */ - iob_copyout(dev->d_appdata, iob, len, offset); - dev->d_sndlen = len; + if (dev->d_iob != NULL) + { + /* Skip the l3/l4 offset before append */ + + iob_update_pktlen(dev->d_iob, target_offset); + + /* Skip to the I/O buffer containing the data offset */ + + while (iob != NULL && offset > iob->io_len) + { + offset -= iob->io_len; + iob = iob->io_flink; + } + + dev->d_sndlen = len; + + /* Clone the iob to target device buffer */ + + while (iob != NULL && len > 0) + { + copyin = (len > iob->io_len - offset) ? + iob->io_len - offset : len; + + ret = iob_copyin(dev->d_iob, iob->io_data + + iob->io_offset + offset, + copyin, target_offset, false); + if (ret != copyin) + { + netdev_iob_release(dev); + dev->d_sndlen = 0; + nerr("devif_iob_send error, not enough iob entries, " + "send len: %u\n", len); + return; + } + + target_offset += copyin; + len -= copyin; + offset = 0; + iob = iob->io_flink; + } + } + else + { + /* Send the iob directly if no device buffer */ + + dev->d_iob = iob; + dev->d_sndlen = len; + dev->d_buf = &iob->io_data[CONFIG_NET_LL_GUARDSIZE - + NET_LL_HDRLEN(dev)]; + } #ifdef CONFIG_NET_TCP_WRBUFFER_DUMP /* Dump the outgoing device buffer */ diff --git a/net/devif/devif_loopback.c b/net/devif/devif_loopback.c index a2e6be82d5..7df7a20731 100644 --- a/net/devif/devif_loopback.c +++ b/net/devif/devif_loopback.c @@ -31,6 +31,17 @@ #include #include +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* This is a helper pointer for accessing the contents of the ip header */ + +#define LOIPv4BUF ((FAR struct ipv4_hdr_s *) \ + &dev->d_iob->io_data[CONFIG_NET_LL_GUARDSIZE]) +#define LOIPv6BUF ((FAR struct ipv6_hdr_s *) \ + &dev->d_iob->io_data[CONFIG_NET_LL_GUARDSIZE]) + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -40,16 +51,16 @@ static bool is_loopback(FAR struct net_driver_s *dev) if (dev->d_len > 0) { #ifdef CONFIG_NET_IPv4 - if ((IPv4BUF->vhl & IP_VERSION_MASK) == IPv4_VERSION && - net_ipv4addr_hdrcmp(IPv4BUF->destipaddr, &dev->d_ipaddr)) + if ((LOIPv4BUF->vhl & IP_VERSION_MASK) == IPv4_VERSION && + net_ipv4addr_hdrcmp(LOIPv4BUF->destipaddr, &dev->d_ipaddr)) { return true; } #endif #ifdef CONFIG_NET_IPv6 - if ((IPv6BUF->vtc & IP_VERSION_MASK) == IPv6_VERSION && - net_ipv6addr_hdrcmp(IPv6BUF->destipaddr, dev->d_ipv6addr)) + if ((LOIPv6BUF->vtc & IP_VERSION_MASK) == IPv6_VERSION && + net_ipv6addr_hdrcmp(LOIPv6BUF->destipaddr, dev->d_ipv6addr)) { return true; } @@ -99,7 +110,7 @@ int devif_loopback(FAR struct net_driver_s *dev) /* We only accept IP packets of the configured type */ #ifdef CONFIG_NET_IPv4 - if ((IPv4BUF->vhl & IP_VERSION_MASK) == IPv4_VERSION) + if ((LOIPv4BUF->vhl & IP_VERSION_MASK) == IPv4_VERSION) { ninfo("IPv4 frame\n"); @@ -109,7 +120,7 @@ int devif_loopback(FAR struct net_driver_s *dev) else #endif #ifdef CONFIG_NET_IPv6 - if ((IPv6BUF->vtc & IP_VERSION_MASK) == IPv6_VERSION) + if ((LOIPv6BUF->vtc & IP_VERSION_MASK) == IPv6_VERSION) { ninfo("IPv6 frame\n"); diff --git a/net/devif/devif_pktsend.c b/net/devif/devif_pktsend.c index 81df8e2591..8c98699e69 100644 --- a/net/devif/devif_pktsend.c +++ b/net/devif/devif_pktsend.c @@ -32,34 +32,6 @@ #ifdef CONFIG_NET_PKT -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -/**************************************************************************** - * Private Type Declarations - ****************************************************************************/ - -/**************************************************************************** - * Private Function Prototypes - ****************************************************************************/ - -/**************************************************************************** - * Public Constant Data - ****************************************************************************/ - -/**************************************************************************** - * Public Data - ****************************************************************************/ - -/**************************************************************************** - * Private Constant Data - ****************************************************************************/ - -/**************************************************************************** - * Private Data - ****************************************************************************/ - /**************************************************************************** * Public Functions ****************************************************************************/ @@ -83,16 +55,25 @@ void devif_pkt_send(FAR struct net_driver_s *dev, FAR const void *buf, unsigned int len) { - DEBUGASSERT(dev && len > 0 && len < NETDEV_PKTSIZE(dev)); + unsigned int limit = NETDEV_PKTSIZE(dev) - + CONFIG_NET_LL_GUARDSIZE; - /* Copy the data into the device packet buffer */ + if (dev == NULL || len == 0 || len > limit) + { + nerr("ERROR: devif_pkt_send fail: %p, sndlen: %u, pktlen: %u\n", + dev, len, limit); + return; + } - memcpy(dev->d_buf, buf, len); + iob_update_pktlen(dev->d_iob, 0); - /* Set the number of bytes to send */ + /* Copy the data into the device packet buffer and set the number of + * bytes to send + */ - dev->d_len = len; - dev->d_sndlen = len; + dev->d_sndlen = iob_copyin(dev->d_iob, buf, len, 0, false) == len ? + len : 0; + dev->d_len = dev->d_sndlen; } #endif /* CONFIG_NET_PKT */ diff --git a/net/devif/devif_poll.c b/net/devif/devif_poll.c index 17daae4cf0..fef4e48bcf 100644 --- a/net/devif/devif_poll.c +++ b/net/devif/devif_poll.c @@ -618,11 +618,7 @@ static inline int devif_poll_tcp_connections(FAR struct net_driver_s *dev, #endif /**************************************************************************** - * Public Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: devif_poll + * Name: devif_poll_connections * * Description: * This function will traverse each active network connection structure and @@ -646,9 +642,10 @@ static inline int devif_poll_tcp_connections(FAR struct net_driver_s *dev, * ****************************************************************************/ -int devif_poll(FAR struct net_driver_s *dev, devif_poll_callback_t callback) +static int devif_poll_connections(FAR struct net_driver_s *dev, + devif_poll_callback_t callback) { - int bstop = false; + int bstop; /* Traverse all of the active packet connections and perform the poll * action. @@ -772,6 +769,217 @@ int devif_poll(FAR struct net_driver_s *dev, devif_poll_callback_t callback) return bstop; } +/**************************************************************************** + * Name: devif_iob_poll + * + * Description: + * This function will traverse each active network connection structure and + * will perform network polling operations. devif_poll() may be called + * asynchronously with the network driver can accept another outgoing + * packet. + * + * This function will call the provided callback function for every active + * connection. Polling will continue until all connections have been polled + * or until the user-supplied function returns a non-zero value (which it + * should do only if it cannot accept further write data). + * + * When the callback function is called, there may be an outbound packet + * waiting for service in the device packet buffer, and if so the d_len + * field is set to a value larger than zero. The device driver should then + * send out the packet. + * + * This is the iob buffer version of devif_input(), + * this function will support send/receive iob vectors directly between + * the driver and l3/l4 stack to avoid unnecessary memory copies, + * especially on hardware that supports Scatter/gather, which can + * greatly improve performance + * this function will uses d_iob as packets input which used by some + * NICs such as celluler net driver. + * + * If NIC hardware support Scatter/gather transfer + * + * tcp_poll()/udp_poll()/pkt_poll()/...(l3/l4) + * / \ + * / \ + * devif_poll_[l3|l4]_connections() devif_iob_send() (nocopy:udp/icmp/...) + * / \ (copy:tcp) + * / \ + * devif_iob_poll("NIC"_txpoll) callback() // "NIC"_txpoll + * + * + * Assumptions: + * This function is called from the MAC device driver with the network + * locked. + * + ****************************************************************************/ + +static int devif_iob_poll(FAR struct net_driver_s *dev, + devif_poll_callback_t callback) +{ + int bstop; + + /* Device polling, prepare iob buffer */ + + if (netdev_iob_prepare(dev, false, 0) != OK) + { + return true; + } + + /* Perform all connections poll */ + + bstop = devif_poll_connections(dev, callback); + + /* Device polling completed, release iob */ + + netdev_iob_release(dev); + + dev->d_buf = NULL; + + return bstop; +} + +/**************************************************************************** + * Name: devif_poll_callback + * + * Description: + * This function will help us to gather multiple iob memory slices into a + * linear device buffer. if devices with small memory, this function will + * trigger a memory copy if net device start transmit the iob slices to + * flat buffer + * + ****************************************************************************/ + +static int devif_poll_callback(FAR struct net_driver_s *dev) +{ + if (dev->d_len > 0) + { + return true; + } + + return false; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: devif_poll + * + * Description: + * This function will traverse each active network connection structure and + * will perform network polling operations. devif_poll() may be called + * asynchronously with the network driver can accept another outgoing + * packet. + * + * This function will call the provided callback function for every active + * connection. Polling will continue until all connections have been polled + * or until the user-supplied function returns a non-zero value (which it + * should do only if it cannot accept further write data). + * + * When the callback function is called, there may be an outbound packet + * waiting for service in the device packet buffer, and if so the d_len + * field is set to a value larger than zero. The device driver should then + * send out the packet. + * + * Compatible with all old flat buffer NICs + * + * tcp_poll()/udp_poll()/pkt_poll()/...(l3|l4) + * / \ + * / \ + * devif_poll_[l3|l4]_connections() devif_iob_send() (nocopy:udp/icmp/..) + * / \ (copy:tcp) + * / \ + * devif_iob_poll(devif_poll_callback()) devif_poll_callback() + * / \ + * / \ + * devif_poll("NIC"_txpoll) "NIC"_send()(dev->d_buf) + * + * + * Assumptions: + * This function is called from the MAC device driver with the network + * locked. + * + ****************************************************************************/ + +int devif_poll(FAR struct net_driver_s *dev, devif_poll_callback_t callback) +{ + uint16_t llhdrlen; + int bstop = false; + FAR uint8_t *buf; + + if (dev->d_buf == NULL) + { + return devif_iob_poll(dev, callback); + } + + buf = dev->d_buf; + + /* Device polling, prepare iob buffer */ + + if (netdev_iob_prepare(dev, false, 0) != OK) + { + return true; + } + + llhdrlen = NET_LL_HDRLEN(dev); + + do + { + /* Perform all connections poll */ + + bstop = devif_poll_connections(dev, devif_poll_callback); + if (dev->d_len > 0) + { + /* Copy iob to flat buffer */ + + iob_copyout(buf + llhdrlen, + dev->d_iob, dev->d_len, 0); + + /* Copy l2 header (arp out) */ + + memcpy(buf, dev->d_iob->io_data + + (CONFIG_NET_LL_GUARDSIZE - llhdrlen), llhdrlen); + + /* Restore flat buffer pointer */ + + dev->d_buf = buf; + + /* Call the real device callback */ + + bstop = callback(dev); + + /* Flat buffer changed by NIC ? */ + + if (dev->d_buf != buf) + { + if (dev->d_buf == NULL) + { + break; + } + + buf = dev->d_buf; + } + + /* Finish copy, reset iob */ + + netdev_iob_prepare(dev, false, 0); + iob_update_pktlen(dev->d_iob, 0); + } + } + while (bstop); + + /* Device polling completed, release iob */ + + netdev_iob_release(dev); + + /* Restore the flat buffer */ + + dev->d_buf = buf; + + return bstop; +} + /**************************************************************************** * Name: devif_out * diff --git a/net/devif/devif_send.c b/net/devif/devif_send.c index 11841b41ed..020e843909 100644 --- a/net/devif/devif_send.c +++ b/net/devif/devif_send.c @@ -65,10 +65,23 @@ * ****************************************************************************/ -void devif_send(struct net_driver_s *dev, const void *buf, int len) +void devif_send(FAR struct net_driver_s *dev, FAR const void *buf, + int len, unsigned int offset) { - DEBUGASSERT(dev != NULL && len > 0 && len < NETDEV_PKTSIZE(dev)); + unsigned int limit = NETDEV_PKTSIZE(dev) - + CONFIG_NET_LL_GUARDSIZE - offset; - memcpy(dev->d_appdata, buf, len); - dev->d_sndlen = len; + if (dev == NULL || len == 0 || len > limit) + { + nerr("ERROR: devif_send fail: %p, sndlen: %u, pktlen: %u\n", + dev, len, limit); + return; + } + + iob_update_pktlen(dev->d_iob, offset); + + /* Copy in iob to target device buffer */ + + dev->d_sndlen = iob_copyin(dev->d_iob, buf, len, offset, false) == len ? + len : 0; } diff --git a/net/devif/ipv4_input.c b/net/devif/ipv4_input.c index 1afec12ad5..d95974870f 100644 --- a/net/devif/ipv4_input.c +++ b/net/devif/ipv4_input.c @@ -116,13 +116,23 @@ ****************************************************************************/ /**************************************************************************** - * Public Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: ipv4_input + * Name: ipv4_in * * Description: + * Receive an IPv4 packet from the network device. Verify and forward to + * L3 packet handling logic if the packet is destined for us. + * + * This is the iob buffer version of ipv4_input(), + * this function will support send/receive iob vectors directly between + * the driver and l3/l4 stack to avoid unnecessary memory copies, + * especially on hardware that supports Scatter/gather, which can + * greatly improve performance + * this function will uses d_iob as packets input which used by some + * NICs such as celluler net driver. + * + * Input Parameters: + * dev - The device on which the packet was received and which contains + * the IPv4 packet. * * Returned Value: * OK - The packet was processed (or dropped) and can be discarded. @@ -133,11 +143,10 @@ * ****************************************************************************/ -int ipv4_input(FAR struct net_driver_s *dev) +static int ipv4_in(FAR struct net_driver_s *dev) { FAR struct ipv4_hdr_s *ipv4 = IPv4BUF; in_addr_t destipaddr; - uint16_t llhdrlen; uint16_t totlen; int ret = OK; @@ -175,15 +184,12 @@ int ipv4_input(FAR struct net_driver_s *dev) /* Get the size of the packet minus the size of link layer header */ - llhdrlen = NET_LL_HDRLEN(dev); - if ((llhdrlen + IPv4_HDRLEN) > dev->d_len) + if (IPv4_HDRLEN > dev->d_len) { nwarn("WARNING: Packet shorter than IPv4 header\n"); goto drop; } - dev->d_len -= llhdrlen; - /* Make sure that all packet processing logic knows that there is an IPv4 * packet in the device buffer. */ @@ -198,11 +204,12 @@ int ipv4_input(FAR struct net_driver_s *dev) */ totlen = (ipv4->len[0] << 8) + ipv4->len[1]; - if (totlen <= dev->d_len) + if (totlen < dev->d_len) { + iob_update_pktlen(dev->d_iob, totlen); dev->d_len = totlen; } - else + else if (totlen > dev->d_len) { nwarn("WARNING: IP packet shorter than length in IP header\n"); goto drop; @@ -420,4 +427,52 @@ drop: dev->d_len = 0; return OK; } + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ipv4_input + * + * Description: + * Receive an IPv4 packet from the network device. Verify and forward to + * L3 packet handling logic if the packet is destined for us. + * + * Input Parameters: + * dev - The device on which the packet was received and which contains + * the IPv4 packet. + * + * Returned Value: + * OK - The packet was processed (or dropped) and can be discarded. + * ERROR - Hold the packet and try again later. There is a listening + * socket but no receive in place to catch the packet yet. The + * device's d_len will be set to zero in this case as there is + * no outgoing data. + * + ****************************************************************************/ + +int ipv4_input(FAR struct net_driver_s *dev) +{ + FAR uint8_t *buf; + int ret; + + if (dev->d_iob != NULL) + { + buf = dev->d_buf; + + /* Set the device buffer to l2 */ + + dev->d_buf = &dev->d_iob->io_data[CONFIG_NET_LL_GUARDSIZE - + NET_LL_HDRLEN(dev)]; + ret = ipv4_in(dev); + + dev->d_buf = buf; + + return ret; + } + + return netdev_input(dev, ipv4_in, true); +} + #endif /* CONFIG_NET_IPv4 */ diff --git a/net/devif/ipv6_input.c b/net/devif/ipv6_input.c index 94a2130d94..914ff43872 100644 --- a/net/devif/ipv6_input.c +++ b/net/devif/ipv6_input.c @@ -185,16 +185,20 @@ static bool check_destipaddr(FAR struct net_driver_s *dev, } /**************************************************************************** - * Public Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: ipv6_input + * Name: ipv6_in * * Description: * Receive an IPv6 packet from the network device. Verify and forward to * L3 packet handling logic if the packet is destined for us. * + * This is the iob buffer version of ipv6_input(), + * this function will support send/receive iob vectors directly between + * the driver and l3/l4 stack to avoid unnecessary memory copies, + * especially on hardware that supports Scatter/gather, which can + * greatly improve performance + * this function will uses d_iob as packets input which used by some + * NICs such as celluler net driver. + * * Input Parameters: * dev - The device on which the packet was received and which contains * the IPv6 packet. @@ -214,11 +218,10 @@ static bool check_destipaddr(FAR struct net_driver_s *dev, * ****************************************************************************/ -int ipv6_input(FAR struct net_driver_s *dev) +static int ipv6_in(FAR struct net_driver_s *dev) { FAR struct ipv6_hdr_s *ipv6 = IPv6BUF; FAR uint8_t *payload; - uint16_t llhdrlen; uint16_t iphdrlen; uint16_t paylen; uint8_t nxthdr; @@ -251,15 +254,12 @@ int ipv6_input(FAR struct net_driver_s *dev) /* Get the size of the packet minus the size of link layer header */ - llhdrlen = NET_LL_HDRLEN(dev); - if ((llhdrlen + IPv6_HDRLEN) > dev->d_len) + if (IPv6_HDRLEN > dev->d_len) { nwarn("WARNING: Packet shorter than IPv6 header\n"); goto drop; } - dev->d_len -= llhdrlen; - /* Make sure that all packet processing logic knows that there is an IPv6 * packet in the device buffer. */ @@ -287,11 +287,12 @@ int ipv6_input(FAR struct net_driver_s *dev) paylen = ((uint16_t)ipv6->len[0] << 8) + (uint16_t)ipv6->len[1] + IPv6_HDRLEN; - if (paylen <= dev->d_len) + if (paylen < dev->d_len) { + iob_update_pktlen(dev->d_iob, paylen); dev->d_len = paylen; } - else + else if (paylen > dev->d_len) { nwarn("WARNING: IP packet shorter than length in IP header\n"); goto drop; @@ -525,4 +526,57 @@ drop: dev->d_len = 0; return OK; } + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ipv6_input + * + * Description: + * Receive an IPv6 packet from the network device. Verify and forward to + * L3 packet handling logic if the packet is destined for us. + * + * Input Parameters: + * dev - The device on which the packet was received and which contains + * the IPv6 packet. + * Returned Value: + * OK - The packet was processed (or dropped) and can be discarded. + * ERROR - Hold the packet and try again later. There is a listening + * socket but no receive in place to catch the packet yet. The + * device's d_len will be set to zero in this case as there is + * no outgoing data. + * + * If this function returns to the network driver with dev->d_len > 0, + * that is an indication to the driver that there is an outgoing response + * to this input. + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +int ipv6_input(FAR struct net_driver_s *dev) +{ + FAR uint8_t *buf; + int ret; + + if (dev->d_iob != NULL) + { + buf = dev->d_buf; + + /* Set the device buffer to l2 */ + + dev->d_buf = &dev->d_iob->io_data[CONFIG_NET_LL_GUARDSIZE - + NET_LL_HDRLEN(dev)]; + ret = ipv6_in(dev); + + dev->d_buf = buf; + + return ret; + } + + return netdev_input(dev, ipv6_in, true); +} #endif /* CONFIG_NET_IPv6 */ diff --git a/net/icmp/icmp_input.c b/net/icmp/icmp_input.c index e44089a158..9e0be69b90 100644 --- a/net/icmp/icmp_input.c +++ b/net/icmp/icmp_input.c @@ -100,28 +100,14 @@ static uint16_t icmp_datahandler(FAR struct net_driver_s *dev, FAR struct ipv4_hdr_s *ipv4; struct sockaddr_in inaddr; FAR struct iob_s *iob; - uint16_t offset; - uint16_t buflen; uint16_t iphdrlen; - uint8_t addrsize; + uint16_t buflen; int ret; - /* Try to allocate on I/O buffer to start the chain without waiting (and - * throttling as necessary). If we would have to wait, then drop the - * packet. - */ - - iob = iob_tryalloc(true); - if (iob == NULL) - { - nerr("ERROR: Failed to create new I/O buffer chain\n"); - goto drop; - } - /* Put the IPv4 address at the beginning of the read-ahead buffer */ - ipv4 = IPv4BUF; - + iob = dev->d_iob; + ipv4 = IPv4BUF; inaddr.sin_family = AF_INET; inaddr.sin_port = 0; @@ -129,57 +115,23 @@ static uint16_t icmp_datahandler(FAR struct net_driver_s *dev, net_ip4addr_conv32(ipv4->srcipaddr)); memset(inaddr.sin_zero, 0, sizeof(inaddr.sin_zero)); - /* Copy the src address info into the I/O buffer chain. We will not wait - * for an I/O buffer to become available in this context. It there is - * any failure to allocated, the entire I/O buffer chain will be discarded. - */ - - addrsize = sizeof(struct sockaddr_in); - ret = iob_trycopyin(iob, &addrsize, sizeof(uint8_t), 0, true); - if (ret < 0) - { - /* On a failure, iob_trycopyin return a negated error value but does - * not free any I/O buffers. - */ - - nerr("ERROR: Failed to length to the I/O buffer chain: %d\n", ret); - goto drop_with_chain; - } - - offset = sizeof(uint8_t); - - ret = iob_trycopyin(iob, (FAR const uint8_t *)&inaddr, - sizeof(struct sockaddr_in), offset, true); - if (ret < 0) - { - /* On a failure, iob_trycopyin return a negated error value but does - * not free any I/O buffers. - */ - - nerr("ERROR: Failed to source address to the I/O buffer chain: %d\n", - ret); - goto drop_with_chain; - } - - offset += sizeof(struct sockaddr_in); - /* Get the IP header length (accounting for possible options). */ iphdrlen = (ipv4->vhl & IPv4_HLMASK) << 2; + /* Copy the src address info into the front of I/O buffer chain which + * overwrites the contents of the packet header field. + */ + + memcpy(iob->io_data, &inaddr, sizeof(struct sockaddr_in)); + /* Copy the new ICMP reply into the I/O buffer chain (without waiting) */ buflen = ICMPSIZE(iphdrlen); - ret = iob_trycopyin(iob, IPBUF(iphdrlen), buflen, offset, true); - if (ret < 0) - { - /* On a failure, iob_copyin return a negated error value but does - * not free any I/O buffers. - */ - nerr("ERROR: Failed to add data to the I/O buffer chain: %d\n", ret); - goto drop_with_chain; - } + /* Trim l3 header */ + + iob = iob_trimhead(iob, iphdrlen); /* Add the new I/O buffer chain to the tail of the read-ahead queue (again * without waiting). @@ -189,19 +141,18 @@ static uint16_t icmp_datahandler(FAR struct net_driver_s *dev, if (ret < 0) { nerr("ERROR: Failed to queue the I/O buffer chain: %d\n", ret); - goto drop_with_chain; + iob_free_chain(iob); + } + else + { + ninfo("Buffered %d bytes\n", buflen); } - ninfo("Buffered %d bytes\n", buflen + addrsize + 1); - dev->d_len = 0; + /* Device buffer must be enqueue or freed, clear the handle */ + + netdev_iob_clear(dev); + return buflen; - -drop_with_chain: - iob_free_chain(iob); - -drop: - dev->d_len = 0; - return 0; } #endif diff --git a/net/icmp/icmp_recvmsg.c b/net/icmp/icmp_recvmsg.c index 000a5e5bd9..e0f2d4289c 100644 --- a/net/icmp/icmp_recvmsg.c +++ b/net/icmp/icmp_recvmsg.c @@ -227,10 +227,8 @@ static inline ssize_t icmp_readahead(FAR struct icmp_conn_s *conn, FAR struct sockaddr_in *from, FAR socklen_t *fromlen) { - FAR struct sockaddr_in bitbucket; FAR struct iob_s *iob; ssize_t ret = -ENODATA; - int recvlen; /* Check there is any ICMP replies already buffered in a read-ahead * buffer. @@ -238,68 +236,26 @@ static inline ssize_t icmp_readahead(FAR struct icmp_conn_s *conn, if ((iob = iob_peek_queue(&conn->readahead)) != NULL) { - FAR struct iob_s *tmp; - uint16_t offset; - uint8_t addrsize; - DEBUGASSERT(iob->io_pktlen > 0); - /* Transfer that buffered data from the I/O buffer chain into - * the user buffer. - */ - - /* First get the size of the address */ - - recvlen = iob_copyout(&addrsize, iob, sizeof(uint8_t), 0); - if (recvlen != sizeof(uint8_t)) - { - ret = -EIO; - goto out; - } - - offset = sizeof(uint8_t); - - if (addrsize > sizeof(struct sockaddr_in)) - { - ret = -EINVAL; - goto out; - } - /* Then get address */ - if (from == NULL) + if (from != NULL) { - from = &bitbucket; + memcpy(from, iob->io_data, sizeof(struct sockaddr_in)); } - recvlen = iob_copyout((FAR uint8_t *)from, iob, addrsize, offset); - if (recvlen != addrsize) - { - ret = -EIO; - goto out; - } + /* Copy to user */ - if (fromlen != NULL) - { - *fromlen = addrsize; - } - - offset += addrsize; - - /* And finally, get the buffered data */ - - ret = (ssize_t)iob_copyout(buf, iob, buflen, offset); + ret = (ssize_t)iob_copyout(buf, iob, buflen, 0); ninfo("Received %ld bytes (of %u)\n", (long)ret, iob->io_pktlen); -out: /* Remove the I/O buffer chain from the head of the read-ahead * buffer queue. */ - tmp = iob_remove_queue(&conn->readahead); - DEBUGASSERT(tmp == iob); - UNUSED(tmp); + iob_remove_queue(&conn->readahead); /* And free the I/O buffer chain */ diff --git a/net/icmp/icmp_reply.c b/net/icmp/icmp_reply.c index 82b758339b..ad9aed2963 100644 --- a/net/icmp/icmp_reply.c +++ b/net/icmp/icmp_reply.c @@ -91,7 +91,7 @@ void icmp_reply(FAR struct net_driver_s *dev, int type, int code) { int ipicmplen = IPv4_HDRLEN + sizeof(struct icmp_hdr_s); FAR struct ipv4_hdr_s *ipv4 = IPv4BUF; - FAR struct icmp_hdr_s *icmp = (FAR struct icmp_hdr_s *)(ipv4 + 1); + FAR struct icmp_hdr_s *icmp; uint16_t datalen; #ifdef CONFIG_NET_BROADCAST const in_addr_t bcast = INADDR_BROADCAST; @@ -119,20 +119,75 @@ void icmp_reply(FAR struct net_driver_s *dev, int type, int code) if (datalen > ICMP_MAXMSGLEN - ipicmplen) { datalen = ICMP_MAXMSGLEN - ipicmplen; + iob_trimtail(dev->d_iob, dev->d_iob->io_pktlen - datalen); + } + + /* Save the original datagram */ + + if (CONFIG_IOB_BUFSIZE >= datalen + ipicmplen + + CONFIG_NET_LL_GUARDSIZE) + { + /* Reuse current iob */ + + memmove((FAR char *)ipv4 + ipicmplen, ipv4, datalen); + + /* Skip icmp header from iob */ + + iob_update_pktlen(dev->d_iob, datalen + ipicmplen); + } + else + { + FAR struct iob_s *iob; + + /* Save the original datagram to iob chain */ + + iob = dev->d_iob; + dev->d_iob = NULL; + + /* Re-prepare device buffer */ + + if (netdev_iob_prepare(dev, false, 0) != OK) + { + dev->d_len = 0; + dev->d_iob = iob; + netdev_iob_release(dev); + return; + } + + /* Copy ipv4 header to device buffer */ + + if (iob_trycopyin(dev->d_iob, (FAR void *)ipv4, + IPv4_HDRLEN, 0, false) != IPv4_HDRLEN) + { + dev->d_len = 0; + netdev_iob_release(dev); + iob_free_chain(iob); + return; + } + + /* Skip icmp header from iob */ + + iob_update_pktlen(dev->d_iob, dev->d_iob->io_pktlen + + sizeof(struct icmp_hdr_s)); + + /* Concat new icmp packet before original datagram */ + + iob_concat(dev->d_iob, iob); + + /* IPv4 header to new iob */ + + ipv4 = IPBUF(0); } dev->d_len = ipicmplen + datalen; - /* Copy fields from original packet */ - - memmove(icmp + 1, ipv4, datalen); - ipv4_build_header(IPv4BUF, dev->d_len, IP_PROTO_ICMP, &dev->d_ipaddr, (FAR in_addr_t *)ipv4->srcipaddr, IP_TTL_DEFAULT, NULL); /* Initialize the ICMP header */ + icmp = (FAR struct icmp_hdr_s *)(ipv4 + 1); icmp->type = type; icmp->icode = code; icmp->data[0] = 0; @@ -141,7 +196,7 @@ void icmp_reply(FAR struct net_driver_s *dev, int type, int code) /* Calculate the ICMP checksum. */ icmp->icmpchksum = 0; - icmp->icmpchksum = ~icmp_chksum(dev, datalen + sizeof(*icmp)); + icmp->icmpchksum = ~icmp_chksum_iob(dev->d_iob); if (icmp->icmpchksum == 0) { icmp->icmpchksum = 0xffff; diff --git a/net/icmp/icmp_sendmsg.c b/net/icmp/icmp_sendmsg.c index f3ddcd43cd..7e50f92774 100644 --- a/net/icmp/icmp_sendmsg.c +++ b/net/icmp/icmp_sendmsg.c @@ -114,7 +114,11 @@ static void sendto_request(FAR struct net_driver_s *dev, /* Copy the ICMP header and payload into place after the IPv4 header */ icmp = IPBUF(IPv4_HDRLEN); - memcpy(icmp, pstate->snd_buf, pstate->snd_buflen); + + iob_update_pktlen(dev->d_iob, IPv4_HDRLEN); + + iob_copyin(dev->d_iob, pstate->snd_buf, + pstate->snd_buflen, IPv4_HDRLEN, false); /* Initialize the IP header. */ @@ -125,7 +129,7 @@ static void sendto_request(FAR struct net_driver_s *dev, /* Calculate the ICMP checksum. */ icmp->icmpchksum = 0; - icmp->icmpchksum = ~(icmp_chksum(dev, pstate->snd_buflen)); + icmp->icmpchksum = ~icmp_chksum_iob(dev->d_iob); if (icmp->icmpchksum == 0) { icmp->icmpchksum = 0xffff; diff --git a/net/icmpv6/icmpv6_advertise.c b/net/icmpv6/icmpv6_advertise.c index 1ca008eacf..decce33189 100644 --- a/net/icmpv6/icmpv6_advertise.c +++ b/net/icmpv6/icmpv6_advertise.c @@ -103,6 +103,10 @@ void icmpv6_advertise(FAR struct net_driver_s *dev, memcpy(adv->tgtlladdr, &dev->d_mac, lladdrsize); + /* Update device buffer length */ + + iob_update_pktlen(dev->d_iob, IPv6_HDRLEN + l3size); + /* Calculate the checksum over both the ICMP header and payload */ adv->chksum = 0; diff --git a/net/icmpv6/icmpv6_input.c b/net/icmpv6/icmpv6_input.c index 24d799ad3a..50ed37d853 100644 --- a/net/icmpv6/icmpv6_input.c +++ b/net/icmpv6/icmpv6_input.c @@ -87,82 +87,32 @@ static uint16_t icmpv6_datahandler(FAR struct net_driver_s *dev, unsigned int iplen) { FAR struct ipv6_hdr_s *ipv6; - FAR struct icmpv6_hdr_s *icmpv6; - FAR struct iob_s *iob; struct sockaddr_in6 inaddr; - uint16_t offset; + FAR struct iob_s *iob; uint16_t buflen; - uint8_t addrsize; int ret; - /* Try to allocate on I/O buffer to start the chain without waiting (and - * throttling as necessary). If we would have to wait, then drop the - * packet. - */ - - iob = iob_tryalloc(true); - if (iob == NULL) - { - nerr("ERROR: Failed to create new I/O buffer chain\n"); - goto drop; - } - /* Put the IPv6 address at the beginning of the read-ahead buffer */ + iob = dev->d_iob; ipv6 = IPv6BUF; inaddr.sin6_family = AF_INET6; inaddr.sin6_port = 0; net_ipv6addr_copy(inaddr.sin6_addr.s6_addr16, ipv6->srcipaddr); - /* Copy the src address info into the I/O buffer chain. We will not wait - * for an I/O buffer to become available in this context. It there is - * any failure to allocated, the entire I/O buffer chain will be discarded. + /* Copy the src address info into the front of I/O buffer chain which + * overwrites the contents of the packet header field. */ - addrsize = sizeof(struct sockaddr_in6); - ret = iob_trycopyin(iob, &addrsize, sizeof(uint8_t), 0, true); - if (ret < 0) - { - /* On a failure, iob_trycopyin return a negated error value but does - * not free any I/O buffers. - */ - - nerr("ERROR: Failed to length to the I/O buffer chain: %d\n", ret); - goto drop_with_chain; - } - - offset = sizeof(uint8_t); - - ret = iob_trycopyin(iob, (FAR const uint8_t *)&inaddr, - sizeof(struct sockaddr_in6), offset, true); - if (ret < 0) - { - /* On a failure, iob_trycopyin return a negated error value but does - * not free any I/O buffers. - */ - - nerr("ERROR: Failed to source address to the I/O buffer chain: %d\n", - ret); - goto drop_with_chain; - } - - offset += sizeof(struct sockaddr_in6); + memcpy(iob->io_data, &inaddr, sizeof(struct sockaddr_in6)); /* Copy the new ICMPv6 reply into the I/O buffer chain (without waiting) */ buflen = ICMPv6SIZE; - icmpv6 = IPBUF(iplen); - ret = iob_trycopyin(iob, (FAR uint8_t *)ICMPv6REPLY, buflen, offset, true); - if (ret < 0) - { - /* On a failure, iob_copyin return a negated error value but does - * not free any I/O buffers. - */ + /* Trim l3 header */ - nerr("ERROR: Failed to add data to the I/O buffer chain: %d\n", ret); - goto drop_with_chain; - } + iob = iob_trimhead(iob, iplen); /* Add the new I/O buffer chain to the tail of the read-ahead queue (again * without waiting). @@ -172,19 +122,17 @@ static uint16_t icmpv6_datahandler(FAR struct net_driver_s *dev, if (ret < 0) { nerr("ERROR: Failed to queue the I/O buffer chain: %d\n", ret); - goto drop_with_chain; + iob_free_chain(iob); + } + else + { + ninfo("Buffered %d bytes\n", buflen); } - ninfo("Buffered %d bytes\n", buflen + addrsize + 1); - dev->d_len = 0; + /* Device buffer must be enqueue or freed, clear the handle */ + + netdev_iob_clear(dev); return buflen; - -drop_with_chain: - iob_free_chain(iob); - -drop: - dev->d_len = 0; - return 0; } #endif diff --git a/net/icmpv6/icmpv6_radvertise.c b/net/icmpv6/icmpv6_radvertise.c index e818229ca0..0281472687 100644 --- a/net/icmpv6/icmpv6_radvertise.c +++ b/net/icmpv6/icmpv6_radvertise.c @@ -193,6 +193,10 @@ void icmpv6_radvertise(FAR struct net_driver_s *dev) ipv6addr_mask(prefix->prefix, dev->d_ipv6addr, dev->d_ipv6netmask); #endif /* CONFIG_NET_ICMPv6_ROUTER_MANUAL */ + /* Update device buffer length */ + + iob_update_pktlen(dev->d_iob, IPv6_HDRLEN + l3size); + /* Calculate the checksum over both the ICMP header and payload */ adv->chksum = 0; diff --git a/net/icmpv6/icmpv6_recvmsg.c b/net/icmpv6/icmpv6_recvmsg.c index 9a805586ad..98e1649580 100644 --- a/net/icmpv6/icmpv6_recvmsg.c +++ b/net/icmpv6/icmpv6_recvmsg.c @@ -232,10 +232,8 @@ static inline ssize_t icmpv6_readahead(FAR struct icmpv6_conn_s *conn, FAR struct sockaddr_in6 *from, FAR socklen_t *fromlen) { - FAR struct sockaddr_in6 bitbucket; FAR struct iob_s *iob; ssize_t ret = -ENODATA; - int recvlen; /* Check there is any ICMPv6 replies already buffered in a read-ahead * buffer. @@ -243,68 +241,26 @@ static inline ssize_t icmpv6_readahead(FAR struct icmpv6_conn_s *conn, if ((iob = iob_peek_queue(&conn->readahead)) != NULL) { - FAR struct iob_s *tmp; - uint16_t offset; - uint8_t addrsize; - DEBUGASSERT(iob->io_pktlen > 0); - /* Transfer that buffered data from the I/O buffer chain into - * the user buffer. - */ - - /* First get the size of the address */ - - recvlen = iob_copyout(&addrsize, iob, sizeof(uint8_t), 0); - if (recvlen != sizeof(uint8_t)) - { - ret = -EIO; - goto out; - } - - offset = sizeof(uint8_t); - - if (addrsize > sizeof(struct sockaddr_in6)) - { - ret = -EINVAL; - goto out; - } - /* Then get address */ - if (from == NULL) + if (from != NULL) { - from = &bitbucket; + memcpy(from, iob->io_data, sizeof(struct sockaddr_in6)); } - recvlen = iob_copyout((FAR uint8_t *)from, iob, addrsize, offset); - if (recvlen != addrsize) - { - ret = -EIO; - goto out; - } + /* Copy to user */ - if (fromlen != NULL) - { - *fromlen = addrsize; - } - - offset += addrsize; - - /* And finally, get the buffered data */ - - ret = (ssize_t)iob_copyout(buf, iob, buflen, offset); + ret = iob_copyout(buf, iob, buflen, 0); ninfo("Received %ld bytes (of %u)\n", (long)ret, iob->io_pktlen); -out: /* Remove the I/O buffer chain from the head of the read-ahead * buffer queue. */ - tmp = iob_remove_queue(&conn->readahead); - DEBUGASSERT(tmp == iob); - UNUSED(tmp); + iob_remove_queue(&conn->readahead); /* And free the I/O buffer chain */ diff --git a/net/icmpv6/icmpv6_reply.c b/net/icmpv6/icmpv6_reply.c index addc3d086f..e67f303c72 100644 --- a/net/icmpv6/icmpv6_reply.c +++ b/net/icmpv6/icmpv6_reply.c @@ -86,7 +86,7 @@ void icmpv6_reply(FAR struct net_driver_s *dev, int type, int code, int data) { int ipicmplen = IPv6_HDRLEN + sizeof(struct icmpv6_hdr_s); FAR struct ipv6_hdr_s *ipv6 = IPv6BUF; - FAR struct icmpv6_hdr_s *icmpv6 = (FAR struct icmpv6_hdr_s *)(ipv6 + 1); + FAR struct icmpv6_hdr_s *icmpv6; uint16_t datalen; if (net_ipv6addr_cmp(ipv6->destipaddr, g_ipv6_unspecaddr) @@ -108,19 +108,74 @@ void icmpv6_reply(FAR struct net_driver_s *dev, int type, int code, int data) if (datalen > ICMPv6_MINMTULEN - ipicmplen) { datalen = ICMPv6_MINMTULEN - ipicmplen; + iob_trimtail(dev->d_iob, dev->d_iob->io_pktlen - datalen); + } + + /* Save the original datagram */ + + if (CONFIG_IOB_BUFSIZE >= datalen + ipicmplen + + CONFIG_NET_LL_GUARDSIZE) + { + /* Reuse current iob */ + + memmove((FAR char *)ipv6 + ipicmplen, ipv6, datalen); + + /* Skip icmp header from iob */ + + iob_update_pktlen(dev->d_iob, datalen + ipicmplen); + } + else + { + FAR struct iob_s *iob; + + /* Save the original datagram to iob chain */ + + iob = dev->d_iob; + dev->d_iob = NULL; + + /* Re-prepare device buffer */ + + if (netdev_iob_prepare(dev, false, 0) != OK) + { + dev->d_len = 0; + dev->d_iob = iob; + netdev_iob_release(dev); + return; + } + + /* Copy ipv4 header to device buffer */ + + if (iob_trycopyin(dev->d_iob, (FAR void *)ipv6, + IPv6_HDRLEN, 0, false) != IPv6_HDRLEN) + { + dev->d_len = 0; + netdev_iob_release(dev); + iob_free_chain(iob); + return; + } + + /* Skip icmp header from iob */ + + iob_update_pktlen(dev->d_iob, dev->d_iob->io_pktlen + + sizeof(struct icmpv6_hdr_s)); + + /* Concat new icmp packet before original datagram */ + + iob_concat(dev->d_iob, iob); + + /* IPv6 header to new iob */ + + ipv6 = IPBUF(0); } dev->d_len = ipicmplen + datalen; - /* Copy fields from original packet */ - - memmove(icmpv6 + 1, ipv6, datalen); - ipv6_build_header(IPv6BUF, dev->d_len - IPv6_HDRLEN, IP_PROTO_ICMP6, dev->d_ipv6addr, ipv6->srcipaddr, 255); /* Initialize the ICMPv6 header */ + icmpv6 = (FAR struct icmpv6_hdr_s *)(ipv6 + 1); icmpv6->type = type; icmpv6->code = code; icmpv6->data[0] = htons(data >> 16); diff --git a/net/icmpv6/icmpv6_rsolicit.c b/net/icmpv6/icmpv6_rsolicit.c index 253d3cb0a9..117ebbd02e 100644 --- a/net/icmpv6/icmpv6_rsolicit.c +++ b/net/icmpv6/icmpv6_rsolicit.c @@ -97,6 +97,10 @@ void icmpv6_rsolicit(FAR struct net_driver_s *dev) memcpy(sol->srclladdr, &dev->d_mac, lladdrsize); + /* Update device buffer length */ + + iob_update_pktlen(dev->d_iob, IPv6_HDRLEN + l3size); + /* Calculate the checksum over both the ICMP header and payload */ sol->chksum = 0; diff --git a/net/icmpv6/icmpv6_sendmsg.c b/net/icmpv6/icmpv6_sendmsg.c index 12336e1ef8..70bd8eefa1 100644 --- a/net/icmpv6/icmpv6_sendmsg.c +++ b/net/icmpv6/icmpv6_sendmsg.c @@ -116,7 +116,11 @@ static void sendto_request(FAR struct net_driver_s *dev, /* Copy the ICMPv6 request and payload into place after the IPv6 header */ icmpv6 = IPBUF(IPv6_HDRLEN); - memcpy(icmpv6, pstate->snd_buf, pstate->snd_buflen); + + iob_update_pktlen(dev->d_iob, IPv6_HDRLEN); + + iob_copyin(dev->d_iob, pstate->snd_buf, + pstate->snd_buflen, IPv6_HDRLEN, false); /* Calculate the ICMPv6 checksum over the ICMPv6 header and payload. */ diff --git a/net/icmpv6/icmpv6_solicit.c b/net/icmpv6/icmpv6_solicit.c index e4c22445fa..3fd2b7a463 100644 --- a/net/icmpv6/icmpv6_solicit.c +++ b/net/icmpv6/icmpv6_solicit.c @@ -120,6 +120,10 @@ void icmpv6_solicit(FAR struct net_driver_s *dev, memcpy(sol->srclladdr, &dev->d_mac, lladdrsize); + /* Update device buffer length */ + + iob_update_pktlen(dev->d_iob, IPv6_HDRLEN + l3size); + /* Calculate the checksum over both the ICMP header and payload */ sol->chksum = 0; diff --git a/net/igmp/igmp_send.c b/net/igmp/igmp_send.c index 1146e81ea9..c15a695f77 100644 --- a/net/igmp/igmp_send.c +++ b/net/igmp/igmp_send.c @@ -120,6 +120,10 @@ void igmp_send(FAR struct net_driver_s *dev, FAR struct igmp_group_s *group, dev->d_len = iphdrlen + IGMP_HDRLEN; + /* Update device buffer length */ + + iob_update_pktlen(dev->d_iob, dev->d_len); + /* The total size of the data is the size of the IGMP header */ dev->d_sndlen = IGMP_HDRLEN; diff --git a/net/ipforward/ipv4_forward.c b/net/ipforward/ipv4_forward.c index 3ad5114459..b7e7e14b16 100644 --- a/net/ipforward/ipv4_forward.c +++ b/net/ipforward/ipv4_forward.c @@ -265,35 +265,9 @@ static int ipv4_dev_forward(FAR struct net_driver_s *dev, } #endif - /* Try to allocate the head of an IOB chain. If this fails, the - * packet will be dropped; we are not operating in a context - * where waiting for an IOB is a good idea - */ + /* Relay the device buffer */ - fwd->f_iob = iob_tryalloc(false); - if (fwd->f_iob == NULL) - { - nwarn("WARNING: iob_tryalloc() failed\n"); - ret = -ENOMEM; - goto errout_with_fwd; - } - - /* Copy the L2/L3 headers plus any following payload into an IOB chain. - * iob_trycopin() will not wait, but will fail there are no available - * IOBs. - * - * REVISIT: Consider an alternative design that does not require data - * copying. This would require a pool of d_buf's that are managed by - * the network rather than the network device. - */ - - ret = iob_trycopyin(fwd->f_iob, (FAR const uint8_t *)ipv4, - dev->d_len, 0, false); - if (ret < 0) - { - nwarn("WARNING: iob_trycopyin() failed: %d\n", ret); - goto errout_with_iobchain; - } + fwd->f_iob = dev->d_iob; /* Decrement the TTL in the copy of the IPv4 header (retaining the * original TTL in the source to handle the broadcast case). If the @@ -305,7 +279,7 @@ static int ipv4_dev_forward(FAR struct net_driver_s *dev, { nwarn("WARNING: Hop limit exceeded... Dropping!\n"); ret = -EMULTIHOP; - goto errout_with_iobchain; + goto errout_with_fwd; } #ifdef CONFIG_NET_NAT @@ -317,7 +291,7 @@ static int ipv4_dev_forward(FAR struct net_driver_s *dev, if (ret < 0) { nwarn("WARNING: Performing NAT outbound failed, dropping!\n"); - goto errout_with_iobchain; + goto errout_with_fwd; } #endif @@ -326,16 +300,10 @@ static int ipv4_dev_forward(FAR struct net_driver_s *dev, ret = ipfwd_forward(fwd); if (ret >= 0) { - dev->d_len = 0; + netdev_iob_clear(dev); return OK; } -errout_with_iobchain: - if (fwd != NULL && fwd->f_iob != NULL) - { - iob_free_chain(fwd->f_iob); - } - errout_with_fwd: if (fwd != NULL) { diff --git a/net/ipforward/ipv6_forward.c b/net/ipforward/ipv6_forward.c index 035cf4e8ff..6e6fdb0244 100644 --- a/net/ipforward/ipv6_forward.c +++ b/net/ipforward/ipv6_forward.c @@ -399,35 +399,9 @@ static int ipv6_dev_forward(FAR struct net_driver_s *dev, } #endif - /* Try to allocate the head of an IOB chain. If this fails, the - * packet will be dropped; we are not operating in a context where - * waiting for an IOB is a good idea - */ + /* Relay the device buffer */ - fwd->f_iob = iob_tryalloc(false); - if (fwd->f_iob == NULL) - { - nwarn("WARNING: iob_tryalloc() failed\n"); - ret = -ENOMEM; - goto errout_with_fwd; - } - - /* Copy the L2/L3 headers plus any following payload into an IOB - * chain. iob_trycopin() will not wait, but will fail there are no - * available IOBs. - * - * REVISIT: Consider an alternative design that does not require data - * copying. This would require a pool of d_buf's that are managed by - * the network rather than the network device. - */ - - ret = iob_trycopyin(fwd->f_iob, (FAR const uint8_t *)ipv6, - dev->d_len, 0, false); - if (ret < 0) - { - nwarn("WARNING: iob_trycopyin() failed: %d\n", ret); - goto errout_with_iobchain; - } + fwd->f_iob = dev->d_iob; /* Decrement the TTL in the copy of the IPv6 header (retaining the * original TTL in the sourcee to handle the broadcast case). If the @@ -439,7 +413,7 @@ static int ipv6_dev_forward(FAR struct net_driver_s *dev, { nwarn("WARNING: Hop limit exceeded... Dropping!\n"); ret = -EMULTIHOP; - goto errout_with_iobchain; + goto errout_with_fwd; } /* Then set up to forward the packet according to the protocol. */ @@ -447,17 +421,11 @@ static int ipv6_dev_forward(FAR struct net_driver_s *dev, ret = ipfwd_forward(fwd); if (ret >= 0) { - dev->d_len = 0; + netdev_iob_clear(dev); return OK; } } -errout_with_iobchain: - if (fwd != NULL && fwd->f_iob != NULL) - { - iob_free_chain(fwd->f_iob); - } - errout_with_fwd: if (fwd != NULL) { diff --git a/net/mld/mld_send.c b/net/mld/mld_send.c index 60903686c8..30de73e732 100644 --- a/net/mld/mld_send.c +++ b/net/mld/mld_send.c @@ -159,6 +159,10 @@ void mld_send(FAR struct net_driver_s *dev, FAR struct mld_group_s *group, dev->d_sndlen = RASIZE + mldsize; + /* Update device buffer length */ + + iob_update_pktlen(dev->d_iob, dev->d_len); + /* Select the IPv6 destination address. * This varies with the type of message being sent: * diff --git a/net/neighbor/neighbor_ethernet_out.c b/net/neighbor/neighbor_ethernet_out.c index 496c892778..f8246498d1 100644 --- a/net/neighbor/neighbor_ethernet_out.c +++ b/net/neighbor/neighbor_ethernet_out.c @@ -196,6 +196,10 @@ void neighbor_ethernet_out(FAR struct net_driver_s *dev) memcpy(eth->src, dev->d_mac.ether.ether_addr_octet, ETHER_ADDR_LEN); eth->type = HTONS(ETHTYPE_IP6); + /* Update device buffer length */ + + iob_update_pktlen(dev->d_iob, dev->d_len); + /* Add the size of the layer layer header to the total size of the * outgoing packet. */ diff --git a/net/netdev/Make.defs b/net/netdev/Make.defs index 31c3c786d5..ace44637da 100644 --- a/net/netdev/Make.defs +++ b/net/netdev/Make.defs @@ -26,6 +26,10 @@ NETDEV_CSRCS += netdev_count.c netdev_ifconf.c netdev_foreach.c NETDEV_CSRCS += netdev_unregister.c netdev_carrier.c netdev_default.c NETDEV_CSRCS += netdev_verify.c netdev_lladdrsize.c +ifeq ($(CONFIG_MM_IOB),y) +NETDEV_CSRCS += netdev_input.c netdev_iob.c +endif + ifeq ($(CONFIG_NETDOWN_NOTIFIER),y) SOCK_CSRCS += netdown_notifier.c endif diff --git a/net/netdev/netdev.h b/net/netdev/netdev.h index 34ba13d6c5..ba5e393a3c 100644 --- a/net/netdev/netdev.h +++ b/net/netdev/netdev.h @@ -31,6 +31,7 @@ #include #include +#include #ifdef CONFIG_NETDOWN_NOTIFIER # include diff --git a/net/netdev/netdev_input.c b/net/netdev/netdev_input.c new file mode 100644 index 0000000000..a2669a4c98 --- /dev/null +++ b/net/netdev/netdev_input.c @@ -0,0 +1,117 @@ +/**************************************************************************** + * net/netdev/netdev_input.c + * + * 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 + +#include + +#include "utils/utils.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: netdev_input + * + * Description: + * This function will copy the flat buffer that does not support + * Scatter/gather to the iob vector buffer. + * + * Compatible with all old flat buffer NICs: + * + * [tcp|udp|icmp|...]ipv[4|6]_data_handler() + * | (iob_concat/append to readahead) + * | + * pkt/ipv[4/6]_in()/... + * | + * | + * netdev_input() // new interface, Scatter/gather flat/iobs + * | + * | + * pkt/ipv[4|6]_input()/... + * | + * | + * NICs io vector receive(Orignal flat buffer) + * + * Input Parameters: + * NULL + * + * Returned Value: + * Pointer to default network driver on success; null on failure + * + ****************************************************************************/ + +int netdev_input(FAR struct net_driver_s *dev, + devif_poll_callback_t callback, bool reply) +{ + uint16_t llhdrlen = NET_LL_HDRLEN(dev); + unsigned int offset = CONFIG_NET_LL_GUARDSIZE - llhdrlen; + FAR uint8_t *buf = dev->d_buf; + unsigned int l3l4len; + int ret; + + /* Prepare iob buffer */ + + ret = netdev_iob_prepare(dev, false, 0); + if (ret != OK) + { + return ret; + } + + /* Copy l2 header to gruard area */ + + memcpy(dev->d_iob->io_data + offset, buf, llhdrlen); + + /* Copy l3/l4 data to iob entry */ + + l3l4len = dev->d_len - llhdrlen; + + ret = iob_trycopyin(dev->d_iob, buf + llhdrlen, + l3l4len, 0, false); + if (ret == l3l4len) + { + /* Update device buffer to l2 start */ + + dev->d_buf = dev->d_iob->io_data + offset; + + iob_update_pktlen(dev->d_iob, l3l4len); + + ret = callback(dev); + if (dev->d_iob != NULL && reply) + { + if (ret == OK && dev->d_len > 0) + { + iob_copyout(buf + llhdrlen, dev->d_iob, dev->d_len, 0); + memcpy(buf, dev->d_iob->io_data + offset, llhdrlen); + } + } + } + + netdev_iob_release(dev); + + dev->d_buf = buf; + + return ret; +} diff --git a/net/netdev/netdev_iob.c b/net/netdev/netdev_iob.c new file mode 100644 index 0000000000..1ed32a0d2f --- /dev/null +++ b/net/netdev/netdev_iob.c @@ -0,0 +1,128 @@ +/**************************************************************************** + * net/netdev/netdev_iob.c + * + * 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 + +#include +#include + +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: netdev_iob_prepare + * + * Description: + * Prepare data buffer for a given NIC + * The iob offset will be updated to l2 gruard size by default: + * ---------------------------------------------------------------- + * | iob entry | + * ---------------------------------------------------------------| + * |<--- CONFIG_NET_LL_GUARDSIZE -->|<--- io_len/io_pktlen(0) --->| + * ---------------------------------------------------------------| + * + * Assumptions: + * The caller has locked the network. + * + * Returned Value: + * A non-zero copy is returned on success. + * + ****************************************************************************/ + +int netdev_iob_prepare(FAR struct net_driver_s *dev, bool throttled, + unsigned int timeout) +{ + /* Prepare iob buffer */ + + if (dev->d_iob == NULL) + { + dev->d_iob = net_iobtimedalloc(false, timeout); + if (dev->d_iob == NULL && throttled) + { + dev->d_iob = net_iobtimedalloc(true, timeout); + } + } + + if (dev->d_iob == NULL) + { + return -ENOMEM; + } + + /* Set the device buffer to l2 */ + + dev->d_buf = &dev->d_iob->io_data[CONFIG_NET_LL_GUARDSIZE - + NET_LL_HDRLEN(dev)]; + + /* Update l2 gruard size */ + + iob_reserve(dev->d_iob, CONFIG_NET_LL_GUARDSIZE); + + return OK; +} + +/**************************************************************************** + * Name: netdev_iob_clear + * + * Description: + * Clean up buffer resources for a given NIC + * + * Assumptions: + * The caller has locked the network and dev->d_iob has been + * released or taken away. + * + ****************************************************************************/ + +void netdev_iob_clear(FAR struct net_driver_s *dev) +{ + /* Clear the device buffer */ + + dev->d_iob = NULL; + dev->d_buf = NULL; + dev->d_len = 0; +} + +/**************************************************************************** + * Name: netdev_iob_release + * + * Description: + * Release buffer resources for a given NIC + * + * Assumptions: + * The caller has locked the network. + * + ****************************************************************************/ + +void netdev_iob_release(FAR struct net_driver_s *dev) +{ + /* Release device buffer */ + + if (dev->d_iob != NULL) + { + iob_free_chain(dev->d_iob); + dev->d_iob = NULL; + } +} diff --git a/net/pkt/pkt_input.c b/net/pkt/pkt_input.c index d84946be51..f825360a6a 100644 --- a/net/pkt/pkt_input.c +++ b/net/pkt/pkt_input.c @@ -36,21 +36,23 @@ #include "pkt/pkt.h" /**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -#define PKTBUF ((FAR struct eth_hdr_s *)dev->d_buf) - -/**************************************************************************** - * Public Functions + * Private Functions ****************************************************************************/ /**************************************************************************** - * Name: pkt_input + * Name: pkt_in * * Description: * Handle incoming packet input * + * This is the iob buffer version of pkt_input(), + * this function will support send/receive iob vectors directly between + * the driver and l3/l4 stack to avoid unnecessary memory copies, + * especially on hardware that supports Scatter/gather, which can + * greatly improve performance + * this function will uses d_iob as packets input which used by some + * NICs such as celluler net driver. + * * Input Parameters: * dev - The device driver structure containing the received packet * @@ -65,10 +67,10 @@ * ****************************************************************************/ -int pkt_input(struct net_driver_s *dev) +static int pkt_in(FAR struct net_driver_s *dev) { FAR struct pkt_conn_s *conn; - FAR struct eth_hdr_s *pbuf = PKTBUF; + FAR struct eth_hdr_s *pbuf = ETHBUF; int ret = OK; conn = pkt_active(pbuf); @@ -109,4 +111,38 @@ int pkt_input(struct net_driver_s *dev) return ret; } +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pkt_input + * + * Description: + * Handle incoming packet input + * + * Input Parameters: + * dev - The device driver structure containing the received packet + * + * Returned Value: + * OK The packet has been processed and can be deleted + * -EAGAIN There is a matching connection, but could not dispatch the packet + * yet. Useful when a packet arrives before a recv call is in + * place. + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +int pkt_input(FAR struct net_driver_s *dev) +{ + if (dev->d_iob != NULL) + { + return pkt_in(dev); + } + + return netdev_input(dev, pkt_in, false); +} + #endif /* CONFIG_NET && CONFIG_NET_PKT */ diff --git a/net/pkt/pkt_recvmsg.c b/net/pkt/pkt_recvmsg.c index d0e4999586..aa989da875 100644 --- a/net/pkt/pkt_recvmsg.c +++ b/net/pkt/pkt_recvmsg.c @@ -115,6 +115,7 @@ static inline void pkt_add_recvlen(FAR struct pkt_recvfrom_s *pstate, static void pkt_recvfrom_newdata(FAR struct net_driver_s *dev, FAR struct pkt_recvfrom_s *pstate) { + unsigned int offset; size_t recvlen; if (dev->d_len > pstate->pr_buflen) @@ -128,7 +129,10 @@ static void pkt_recvfrom_newdata(FAR struct net_driver_s *dev, /* Copy the new packet data into the user buffer */ - memcpy(pstate->pr_buffer, dev->d_buf, recvlen); + offset = (dev->d_appdata - dev->d_iob->io_data) - dev->d_iob->io_offset; + + recvlen = iob_copyout(pstate->pr_buffer, dev->d_iob, recvlen, offset); + ninfo("Received %d bytes (of %d)\n", (int)recvlen, (int)dev->d_len); /* Update the accumulated size of the data read */ diff --git a/net/tcp/tcp.h b/net/tcp/tcp.h index a7af8d3cec..73858fea9b 100644 --- a/net/tcp/tcp.h +++ b/net/tcp/tcp.h @@ -35,6 +35,7 @@ #include #include #include +#include #include #ifdef CONFIG_NET_TCP @@ -1250,8 +1251,9 @@ uint16_t tcp_callback(FAR struct net_driver_s *dev, * ****************************************************************************/ -uint16_t tcp_datahandler(FAR struct tcp_conn_s *conn, FAR uint8_t *buffer, - uint16_t nbytes); +uint16_t tcp_datahandler(FAR struct net_driver_s *dev, + FAR struct tcp_conn_s *conn, + uint16_t offset); /**************************************************************************** * Name: tcp_backlogcreate @@ -2044,6 +2046,22 @@ int tcp_ioctl(FAR struct tcp_conn_s *conn, int cmd, unsigned long arg); void tcp_sendbuffer_notify(FAR struct tcp_conn_s *conn); #endif /* CONFIG_NET_SEND_BUFSIZE */ +/**************************************************************************** + * Name: tcpip_hdrsize + * + * Description: + * Get the total size of L3 and L4 TCP header + * + * Input Parameters: + * conn The connection structure associated with the socket + * + * Returned Value: + * the total size of L3 and L4 TCP header + * + ****************************************************************************/ + +uint16_t tcpip_hdrsize(FAR struct tcp_conn_s *conn); + #ifdef __cplusplus } #endif diff --git a/net/tcp/tcp_callback.c b/net/tcp/tcp_callback.c index 4855c821c0..6d27baa457 100644 --- a/net/tcp/tcp_callback.c +++ b/net/tcp/tcp_callback.c @@ -61,13 +61,13 @@ static inline uint16_t tcp_data_event(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn, uint16_t flags) { - uint16_t ret; + uint16_t recvlen; /* Assume that we will ACK the data. The data will be ACKed if it is * placed in the read-ahead buffer -OR- if it zero length */ - ret = (flags & ~TCP_NEWDATA) | TCP_SNDACK; + flags = (flags & ~TCP_NEWDATA) | TCP_SNDACK; /* Is there new data? With non-zero length? (Certain connection events * can have zero-length with TCP_NEWDATA set just to cause an ACK). @@ -75,46 +75,25 @@ tcp_data_event(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn, if (dev->d_len > 0) { - uint8_t *buffer = dev->d_appdata; - int buflen = dev->d_len; - uint16_t recvlen; - ninfo("No listener on connection\n"); /* Save as the packet data as in the read-ahead buffer. NOTE that * partial packets will not be buffered. */ - recvlen = tcp_datahandler(conn, buffer, buflen); - if (recvlen < buflen) - { - /* There is no handler to receive new data and there are no free - * read-ahead buffers to retain the data -- drop the packet. - */ - - ninfo("Dropped %d/%d bytes\n", buflen - recvlen, buflen); - -#ifdef CONFIG_NET_STATISTICS - g_netstats.tcp.drop++; -#endif - /* Clear the TCP_SNDACK bit so that no ACK will be sent. - * Clear the TCP_CLOSE because we effectively dropped - * the FIN as well. - * - * Revisit: It might make more sense to send a dup ack - * to give a hint to the peer. - */ - - ret &= ~(TCP_SNDACK | TCP_CLOSE); - } + recvlen = tcp_datahandler(dev, conn, + (dev->d_appdata - dev->d_iob->io_data) - + dev->d_iob->io_offset); net_incr32(conn->rcvseq, recvlen); + + netdev_iob_clear(dev); } /* In any event, the new data has now been handled */ dev->d_len = 0; - return ret; + return flags; } /**************************************************************************** @@ -139,6 +118,13 @@ uint16_t tcp_callback(FAR struct net_driver_s *dev, uint16_t orig = flags; #endif + /* Prepare device buffer */ + + if (dev->d_iob == NULL && netdev_iob_prepare(dev, true, 0) != OK) + { + return 0; + } + /* Preserve the TCP_ACKDATA, TCP_CLOSE, and TCP_ABORT in the response. * These is needed by the network to handle responses and buffer state. * The TCP_NEWDATA indication will trigger the ACK response, but must be @@ -202,6 +188,13 @@ uint16_t tcp_callback(FAR struct net_driver_s *dev, } #endif + /* Re-prepare the device buffer if d_iob is consumed by the stack */ + + if (dev->d_iob == NULL) + { + netdev_iob_prepare(dev, true, 0); + } + return flags; } @@ -231,84 +224,41 @@ uint16_t tcp_callback(FAR struct net_driver_s *dev, * ****************************************************************************/ -uint16_t tcp_datahandler(FAR struct tcp_conn_s *conn, FAR uint8_t *buffer, - uint16_t buflen) +uint16_t tcp_datahandler(FAR struct net_driver_s *dev, + FAR struct tcp_conn_s *conn, + uint16_t offset) { - FAR struct iob_s *iob; - uint16_t copied = 0; - int ret; - unsigned int i; + FAR struct iob_s *iob = dev->d_iob; + uint16_t buflen; - /* Try to allocate I/O buffers and copy the data into them - * without waiting (and throttling as necessary). - */ - - iob = conn->readahead; - for (i = 0; i < 2; i++) + if (offset > 0) { - bool throttled = i == 0; /* try throttled=true first */ + /* Remove 'bufoff' bytes from the beginning of the input I/O chain */ - if (!throttled) - { -#if CONFIG_IOB_THROTTLE > 0 - if (conn->readahead != NULL) - { - ninfo("Do not use throttled=false because of " - "non-empty readahead\n"); - break; - } -#else - break; -#endif - } - - if (iob == NULL) - { - iob = iob_tryalloc(throttled); - if (iob == NULL) - { - continue; - } - - iob->io_pktlen = 0; - } - - if (iob != NULL) - { - uint32_t olen = iob->io_pktlen; - - ret = iob_trycopyin(iob, buffer + copied, buflen - copied, - olen, throttled); - copied += iob->io_pktlen - olen; - if (ret < 0) - { - /* On a failure, iob_copyin return a negated error value but - * does not free any I/O buffers. - */ - - continue; - } - } - - break; + iob = iob_trimhead(iob, offset); } - DEBUGASSERT(conn->readahead == iob || conn->readahead == NULL); - if (iob == NULL) + /* Trim tail if l3/l4 header has been removed */ + + if (dev->d_len < iob->io_pktlen) { - nerr("ERROR: Failed to create new I/O buffer chain\n"); - DEBUGASSERT(copied == 0); - return 0; + iob = iob_trimtail(iob, iob->io_pktlen - dev->d_len); } - if (copied == 0) + buflen = iob->io_pktlen; + + /* Concat the iob to readahead */ + + if (conn->readahead == NULL) { - nerr("ERROR: Failed to append new I/O buffer\n"); - DEBUGASSERT(conn->readahead == iob); - return 0; + conn->readahead = iob; + } + else + { + iob_concat(conn->readahead, iob); } - conn->readahead = iob; + netdev_iob_clear(dev); #ifdef CONFIG_NET_TCP_NOTIFIER /* Provide notification(s) that additional TCP read-ahead data is @@ -318,8 +268,8 @@ uint16_t tcp_datahandler(FAR struct tcp_conn_s *conn, FAR uint8_t *buffer, tcp_readahead_signal(conn); #endif - ninfo("Buffered %" PRIu16 " bytes\n", copied); - return copied; + ninfo("Buffered %" PRIu16 " bytes\n", buflen); + return buflen; } #endif /* NET_TCP_HAVE_STACK */ diff --git a/net/tcp/tcp_recvfrom.c b/net/tcp/tcp_recvfrom.c index 64e42394fe..cc79e5b5dd 100644 --- a/net/tcp/tcp_recvfrom.c +++ b/net/tcp/tcp_recvfrom.c @@ -113,6 +113,7 @@ static inline void tcp_update_recvlen(FAR struct tcp_recvfrom_s *pstate, static size_t tcp_recvfrom_newdata(FAR struct net_driver_s *dev, FAR struct tcp_recvfrom_s *pstate) { + unsigned int offset; size_t recvlen; /* Get the length of the data to return */ @@ -128,7 +129,14 @@ static size_t tcp_recvfrom_newdata(FAR struct net_driver_s *dev, /* Copy the new appdata into the user buffer */ - memcpy(pstate->ir_buffer, dev->d_appdata, recvlen); + offset = (dev->d_appdata - dev->d_iob->io_data) - dev->d_iob->io_offset; + + recvlen = iob_copyout(pstate->ir_buffer, dev->d_iob, recvlen, offset); + + /* Trim the copied buffers */ + + dev->d_iob = iob_trimhead(dev->d_iob, recvlen + offset); + ninfo("Received %d bytes (of %d)\n", (int)recvlen, (int)dev->d_len); /* Update the accumulated size of the data read */ @@ -172,11 +180,10 @@ static inline uint16_t tcp_newdata(FAR struct net_driver_s *dev, if (recvlen < dev->d_len) { - FAR uint8_t *buffer = (FAR uint8_t *)dev->d_appdata + recvlen; - uint16_t buflen = dev->d_len - recvlen; uint16_t nsaved; + uint16_t buflen = dev->d_len - recvlen; - nsaved = tcp_datahandler(conn, buffer, buflen); + nsaved = tcp_datahandler(dev, conn, 0); if (nsaved < buflen) { nwarn("WARNING: packet data not fully saved " @@ -189,6 +196,10 @@ static inline uint16_t tcp_newdata(FAR struct net_driver_s *dev, recvlen += nsaved; } + else + { + netdev_iob_release(dev); + } if (recvlen < dev->d_len) { @@ -379,16 +390,16 @@ static uint16_t tcp_recvhandler(FAR struct net_driver_s *dev, if ((flags & TCP_NEWDATA) != 0) { + /* Save the sender's address in the caller's 'from' location */ + + tcp_sender(dev, pstate); + /* Copy the data from the packet (saving any unused bytes from the * packet in the read-ahead buffer). */ flags = tcp_newdata(dev, pstate, flags); - /* Save the sender's address in the caller's 'from' location */ - - tcp_sender(dev, pstate); - /* Indicate that the data has been consumed and that an ACK * should be sent. */ diff --git a/net/tcp/tcp_send.c b/net/tcp/tcp_send.c index 6b1da6190e..2d8f2f297b 100644 --- a/net/tcp/tcp_send.c +++ b/net/tcp/tcp_send.c @@ -168,6 +168,10 @@ static void tcp_sendcommon(FAR struct net_driver_s *dev, tcp->urgp[0] = 0; tcp->urgp[1] = 0; + /* Update device buffer length before setup the IP header */ + + iob_update_pktlen(dev->d_iob, dev->d_len); + /* Calculate chk & build L3 header */ #ifdef CONFIG_NET_IPv6 @@ -263,8 +267,14 @@ static void tcp_sendcommon(FAR struct net_driver_s *dev, void tcp_send(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn, uint16_t flags, uint16_t len) { - FAR struct tcp_hdr_s *tcp = tcp_header(dev); + FAR struct tcp_hdr_s *tcp; + if (dev->d_iob == NULL) + { + return; + } + + tcp = tcp_header(dev); tcp->flags = flags; dev->d_len = len; tcp->tcpoffset = (TCP_HDRLEN / 4) << 4; @@ -290,18 +300,25 @@ void tcp_send(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn, void tcp_reset(FAR struct net_driver_s *dev) { - FAR struct tcp_hdr_s *tcp = tcp_header(dev); + FAR struct tcp_hdr_s *tcp; uint32_t ackno; uint16_t tmp16; uint16_t acklen = 0; uint8_t seqbyte; + if (dev->d_iob == NULL) + { + return; + } + #ifdef CONFIG_NET_STATISTICS g_netstats.tcp.rst++; #endif /* TCP setup */ + tcp = tcp_header(dev); + if ((tcp->flags & TCP_SYN) != 0 || (tcp->flags & TCP_FIN) != 0) { acklen++; @@ -313,7 +330,12 @@ void tcp_reset(FAR struct net_driver_s *dev) #endif { FAR struct ipv6_hdr_s *ip = IPv6BUF; + acklen += (ip->len[0] << 8 | ip->len[1]); + + /* Set the packet length to the size of the IPv6 + TCP headers */ + + dev->d_len = IPv6TCP_HDRLEN; } #endif /* CONFIG_NET_IPv6 */ @@ -323,7 +345,12 @@ void tcp_reset(FAR struct net_driver_s *dev) #endif { FAR struct ipv4_hdr_s *ip = IPv4BUF; + acklen += (ip->len[0] << 8) + ip->len[1] - (ip->vhl & 0x0f) * 4; + + /* Set the packet length to the size of the IPv4 + TCP headers */ + + dev->d_len = IPv4TCP_HDRLEN; } #endif /* CONFIG_NET_IPv4 */ @@ -373,6 +400,10 @@ void tcp_reset(FAR struct net_driver_s *dev) tcp->urgp[0] = 0; tcp->urgp[0] = 0; + /* Update device buffer length before setup the IP header */ + + iob_update_pktlen(dev->d_iob, dev->d_len); + /* Calculate chk & build L3 header */ #ifdef CONFIG_NET_IPv6 @@ -382,10 +413,6 @@ void tcp_reset(FAR struct net_driver_s *dev) { FAR struct ipv6_hdr_s *ipv6 = IPv6BUF; - /* Set the packet length to the size of the IPv6 + TCP headers */ - - dev->d_len = IPv6TCP_HDRLEN; - ipv6_build_header(ipv6, dev->d_len - IPv6_HDRLEN, IP_PROTO_TCP, dev->d_ipv6addr, ipv6->srcipaddr, IP_TTL_DEFAULT); @@ -402,10 +429,6 @@ void tcp_reset(FAR struct net_driver_s *dev) { FAR struct ipv4_hdr_s *ipv4 = IPv4BUF; - /* Set the packet length to the size of the IPv4 + TCP headers */ - - dev->d_len = IPv4TCP_HDRLEN; - ipv4_build_header(IPv4BUF, dev->d_len, IP_PROTO_TCP, &dev->d_ipaddr, (FAR in_addr_t *)ipv4->srcipaddr, IP_TTL_DEFAULT, NULL); @@ -485,6 +508,11 @@ void tcp_synack(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn, uint16_t tcp_mss; int16_t optlen = 0; + if (dev->d_iob == NULL) + { + return; + } + /* Get values that vary with the underlying IP domain */ #ifdef CONFIG_NET_IPv6 @@ -596,4 +624,44 @@ void tcp_send_txnotify(FAR struct socket *psock, #endif /* CONFIG_NET_IPv6 */ } +/**************************************************************************** + * Name: tcpip_hdrsize + * + * Description: + * Get the total size of L3 and L4 TCP header + * + * Input Parameters: + * conn The connection structure associated with the socket + * + * Returned Value: + * the total size of L3 and L4 TCP header + * + ****************************************************************************/ + +uint16_t tcpip_hdrsize(FAR struct tcp_conn_s *conn) +{ + uint16_t hdrsize = sizeof(struct tcp_hdr_s); + +#if defined(CONFIG_NET_IPv4) && defined(CONFIG_NET_IPv6) + if (conn->domain == PF_INET) + { + /* Select the IPv4 domain */ + + return sizeof(struct ipv4_hdr_s) + hdrsize; + } + else /* if (domain == PF_INET6) */ + { + /* Select the IPv6 domain */ + + return sizeof(struct ipv6_hdr_s) + hdrsize; + } +#elif defined(CONFIG_NET_IPv4) + ((void)conn); + return sizeof(struct ipv4_hdr_s) + hdrsize; +#elif defined(CONFIG_NET_IPv6) + ((void)conn); + return sizeof(struct ipv6_hdr_s) + hdrsize; +#endif +} + #endif /* CONFIG_NET && CONFIG_NET_TCP */ diff --git a/net/tcp/tcp_send_buffered.c b/net/tcp/tcp_send_buffered.c index 2387da4a24..6925c25c1d 100644 --- a/net/tcp/tcp_send_buffered.c +++ b/net/tcp/tcp_send_buffered.c @@ -596,7 +596,8 @@ static uint16_t psock_send_eventhandler(FAR struct net_driver_s *dev, * happen until the polling cycle completes). */ - devif_iob_send(dev, TCP_WBIOB(wrb), sndlen, 0); + devif_iob_send(dev, TCP_WBIOB(wrb), sndlen, + 0, tcpip_hdrsize(conn)); /* Reset the retransmission timer. */ @@ -882,7 +883,8 @@ static uint16_t psock_send_eventhandler(FAR struct net_driver_s *dev, * won't actually happen until the polling cycle completes). */ - devif_iob_send(dev, TCP_WBIOB(wrb), sndlen, TCP_WBSENT(wrb)); + devif_iob_send(dev, TCP_WBIOB(wrb), sndlen, + TCP_WBSENT(wrb), tcpip_hdrsize(conn)); /* Remember how much data we send out now so that we know * when everything has been acknowledged. Just increment diff --git a/net/tcp/tcp_send_unbuffered.c b/net/tcp/tcp_send_unbuffered.c index efbd7f6c5a..8ddcee4404 100644 --- a/net/tcp/tcp_send_unbuffered.c +++ b/net/tcp/tcp_send_unbuffered.c @@ -326,9 +326,8 @@ static uint16_t tcpsend_eventhandler(FAR struct net_driver_s *dev, * happen until the polling cycle completes). */ - devif_send(dev, - &pstate->snd_buffer[pstate->snd_acked], - sndlen); + devif_send(dev, &pstate->snd_buffer[pstate->snd_acked], + sndlen, tcpip_hdrsize(conn)); /* Continue waiting */ @@ -409,7 +408,8 @@ static uint16_t tcpsend_eventhandler(FAR struct net_driver_s *dev, * happen until the polling cycle completes). */ - devif_send(dev, &pstate->snd_buffer[pstate->snd_sent], sndlen); + devif_send(dev, &pstate->snd_buffer[pstate->snd_sent], + sndlen, tcpip_hdrsize(conn)); /* Update the amount of data sent (but not necessarily ACKed) */ diff --git a/net/udp/udp.h b/net/udp/udp.h index 4d7444a1fe..37737b5eee 100644 --- a/net/udp/udp.h +++ b/net/udp/udp.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #ifdef CONFIG_NET_UDP_NOTIFIER @@ -894,7 +895,7 @@ void udp_readahead_signal(FAR struct udp_conn_s *conn); * When write buffer becomes empty, *all* of the workers waiting * for that event data will be executed. If there are multiple workers * waiting for read-ahead data then only the first to execute will get the - * data. Others will need to call tcp_writebuffer_notifier_setup() once + * data. Others will need to call udp_writebuffer_notifier_setup() once * again. * * Input Parameters: @@ -964,6 +965,22 @@ int udp_ioctl(FAR struct udp_conn_s *conn, int cmd, unsigned long arg); void udp_sendbuffer_notify(FAR struct udp_conn_s *conn); #endif /* CONFIG_NET_SEND_BUFSIZE */ +/**************************************************************************** + * Name: udpip_hdrsize + * + * Description: + * Get the total size of L3 and L4 UDP header + * + * Input Parameters: + * conn The connection structure associated with the socket + * + * Returned Value: + * the total size of L3 and L4 TCP header + * + ****************************************************************************/ + +uint16_t udpip_hdrsize(FAR struct udp_conn_s *conn); + #undef EXTERN #ifdef __cplusplus } diff --git a/net/udp/udp_callback.c b/net/udp/udp_callback.c index a83b96772b..0b13b3f757 100644 --- a/net/udp/udp_callback.c +++ b/net/udp/udp_callback.c @@ -70,8 +70,8 @@ static uint16_t udp_datahandler(FAR struct net_driver_s *dev, }; #endif - FAR void *src_addr; uint8_t src_addr_size; + FAR void *src_addr; uint8_t offset = 0; #if CONFIG_NET_RECV_BUFSIZE > 0 @@ -82,16 +82,7 @@ static uint16_t udp_datahandler(FAR struct net_driver_s *dev, } #endif - /* Allocate on I/O buffer to start the chain (throttling as necessary). - * We will not wait for an I/O buffer to become available in this context. - */ - - iob = iob_tryalloc(true); - if (iob == NULL) - { - nerr("ERROR: Failed to create new I/O buffer chain\n"); - return 0; - } + iob = dev->d_iob; #ifdef CONFIG_NET_IPv6 #ifdef CONFIG_NET_IPv4 @@ -157,71 +148,18 @@ static uint16_t udp_datahandler(FAR struct net_driver_s *dev, } #endif /* CONFIG_NET_IPv4 */ - /* Copy the src address info into the I/O buffer chain. We will not wait - * for an I/O buffer to become available in this context. It there is - * any failure to allocated, the entire I/O buffer chain will be discarded. - */ - - ret = iob_trycopyin(iob, (FAR const uint8_t *)&src_addr_size, - sizeof(uint8_t), offset, true); - offset += sizeof(uint8_t); - if (ret < 0) - { - /* On a failure, iob_trycopyin return a negated error value but does - * not free any I/O buffers. - */ - - nerr("ERROR: Failed to add data to the I/O buffer chain: %d\n", ret); - iob_free_chain(iob); - return 0; - } - - ret = iob_trycopyin(iob, (FAR const uint8_t *)src_addr, src_addr_size, - offset, true); - offset += src_addr_size; - if (ret < 0) - { - /* On a failure, iob_trycopyin return a negated error value but does - * not free any I/O buffers. - */ - - nerr("ERROR: Failed to add data to the I/O buffer chain: %d\n", ret); - iob_free_chain(iob); - return 0; - } + /* Override the address info begin of io_data */ #ifdef CONFIG_NETDEV_IFINDEX - ret = iob_trycopyin(iob, &dev->d_ifindex, sizeof(uint8_t), offset, true); - offset += sizeof(uint8_t); - if (ret < 0) - { - /* On a failure, iob_trycopyin return a negated error value but does - * not free any I/O buffers. - */ - - nerr("ERROR: Failed to add dindex to the I/O buffer chain: %d\n", ret); - iob_free_chain(iob); - return 0; - } + iob->io_data[offset++] = dev->d_ifindex; #endif + iob->io_data[offset++] = src_addr_size; + memcpy(&iob->io_data[offset], src_addr, src_addr_size); - if (buflen > 0) - { - /* Copy the new appdata into the I/O buffer chain */ + /* Trim l3/l4 offset */ - ret = iob_trycopyin(iob, buffer, buflen, offset, true); - if (ret < 0) - { - /* On a failure, iob_trycopyin return a negated error value but - * does not free any I/O buffers. - */ - - nerr("ERROR: Failed to add data to the I/O buffer chain: %d\n", - ret); - iob_free_chain(iob); - return 0; - } - } + iob = iob_trimhead(iob, (dev->d_appdata - iob->io_data) - + iob->io_offset); /* Add the new I/O buffer chain to the tail of the read-ahead queue */ @@ -229,19 +167,24 @@ static uint16_t udp_datahandler(FAR struct net_driver_s *dev, if (ret < 0) { nerr("ERROR: Failed to queue the I/O buffer chain: %d\n", ret); + iob_free_chain(iob); - return 0; + buflen = 0; } - #ifdef CONFIG_NET_UDP_NOTIFIER - /* Provided notification(s) that additional UDP read-ahead data is - * available. - */ + else + { + ninfo("Buffered %d bytes\n", buflen); - udp_readahead_signal(conn); + /* Provided notification(s) that additional UDP read-ahead data is + * available. + */ + + udp_readahead_signal(conn); + } #endif - ninfo("Buffered %d bytes\n", buflen); + netdev_iob_clear(dev); return buflen; } diff --git a/net/udp/udp_recvfrom.c b/net/udp/udp_recvfrom.c index dbd639cad8..972dc43940 100644 --- a/net/udp/udp_recvfrom.c +++ b/net/udp/udp_recvfrom.c @@ -126,19 +126,19 @@ out: * Copy the read data from the packet * * Input Parameters: - * dev The structure of the network driver that generated the event. + * dev The structure of the network driver that generated the event * pstate recvfrom state structure * * Returned Value: - * The number of bytes taken from the packet. + * None. * * Assumptions: * The network is locked. * ****************************************************************************/ -static size_t udp_recvfrom_newdata(FAR struct net_driver_s *dev, - FAR struct udp_recvfrom_s *pstate) +static inline size_t udp_recvfrom_newdata(FAR struct net_driver_s *dev, + FAR struct udp_recvfrom_s *pstate) { size_t recvlen; @@ -155,50 +155,23 @@ static size_t udp_recvfrom_newdata(FAR struct net_driver_s *dev, /* Copy the new appdata into the user buffer */ - memcpy(pstate->ir_msg->msg_iov->iov_base, dev->d_appdata, recvlen); - ninfo("Received %zu bytes (of %" PRIu16 ")\n", recvlen, dev->d_len); + recvlen = iob_copyout(pstate->ir_msg->msg_iov->iov_base, dev->d_iob, + recvlen, dev->d_appdata - dev->d_iob->io_data - + dev->d_iob->io_offset); /* Update the size of the data read */ pstate->ir_recvlen = recvlen; - return recvlen; -} - -/**************************************************************************** - * Name: udp_newdata - * - * Description: - * Copy the read data from the packet - * - * Input Parameters: - * dev The structure of the network driver that generated the event - * pstate recvfrom state structure - * - * Returned Value: - * None. - * - * Assumptions: - * The network is locked. - * - ****************************************************************************/ - -static inline void udp_newdata(FAR struct net_driver_s *dev, - FAR struct udp_recvfrom_s *pstate) -{ - /* Take as much data from the packet as we can */ - - udp_recvfrom_newdata(dev, pstate); - - /* Indicate no data in the buffer */ dev->d_len = 0; + + return recvlen; } static inline void udp_readahead(struct udp_recvfrom_s *pstate) { FAR struct udp_conn_s *conn = pstate->ir_conn; FAR struct iob_s *iob; - int recvlen; /* Check there is any UDP datagram already buffered in a read-ahead * buffer. @@ -208,44 +181,34 @@ static inline void udp_readahead(struct udp_recvfrom_s *pstate) if ((iob = iob_peek_queue(&conn->readahead)) != NULL) { - FAR struct iob_s *tmp; -#ifdef CONFIG_NET_IPv6 - uint8_t srcaddr[sizeof(struct sockaddr_in6)]; -#else - uint8_t srcaddr[sizeof(struct sockaddr_in)]; -#endif + int recvlen = pstate->ir_msg->msg_iov->iov_len; uint8_t src_addr_size; - uint8_t offset = 0; - uint8_t ifindex = 0; + uint8_t offset = 0; + FAR void *srcaddr; + uint8_t ifindex; DEBUGASSERT(iob->io_pktlen > 0); - /* Transfer that buffered data from the I/O buffer chain into - * the user buffer. - */ - - recvlen = iob_copyout(&src_addr_size, iob, sizeof(uint8_t), offset); - offset += sizeof(uint8_t); - if (recvlen != sizeof(uint8_t)) - { - goto out; - } - - recvlen = iob_copyout(srcaddr, iob, src_addr_size, offset); - offset += src_addr_size; - if (recvlen != src_addr_size) - { - goto out; - } + /* Unflatten saved connection information */ #ifdef CONFIG_NETDEV_IFINDEX - recvlen = iob_copyout(&ifindex, iob, sizeof(uint8_t), offset); - offset += sizeof(uint8_t); - if (recvlen != sizeof(uint8_t)) - { - goto out; - } + ifindex = iob->io_data[offset++]; +#else + ifindex = 0; #endif + src_addr_size = iob->io_data[offset++]; + srcaddr = &iob->io_data[offset]; + + /* Copy to user */ + + recvlen = iob_copyout(pstate->ir_msg->msg_iov->iov_base, + iob, recvlen, 0); + + /* Update the accumulated size of the data read */ + + pstate->ir_recvlen = recvlen; + + ninfo("Received %d bytes (of %d)\n", recvlen, iob->io_pktlen); if (pstate->ir_msg->msg_name) { @@ -257,33 +220,13 @@ static inline void udp_readahead(struct udp_recvfrom_s *pstate) pstate->ir_msg->msg_namelen); } - if (pstate->ir_msg->msg_iov->iov_len > 0) - { - recvlen = iob_copyout(pstate->ir_msg->msg_iov->iov_base, - iob, pstate->ir_msg->msg_iov->iov_len, - offset); - - ninfo("Received %d bytes (of %d)\n", recvlen, iob->io_pktlen); - - /* Update the accumulated size of the data read */ - - pstate->ir_recvlen = recvlen; - } - else - { - pstate->ir_recvlen = 0; - } - udp_recvpktinfo(pstate, srcaddr, ifindex); -out: /* Remove the I/O buffer chain from the head of the read-ahead * buffer queue. */ - tmp = iob_remove_queue(&conn->readahead); - DEBUGASSERT(tmp == iob); - UNUSED(tmp); + iob_remove_queue(&conn->readahead); /* And free the I/O buffer chain */ @@ -481,18 +424,18 @@ static uint16_t udp_eventhandler(FAR struct net_driver_s *dev, else if ((flags & UDP_NEWDATA) != 0) { + /* Save the sender's address in the caller's 'from' location */ + + udp_sender(dev, pstate); + /* Copy the data from the packet */ - udp_newdata(dev, pstate); + udp_recvfrom_newdata(dev, pstate); /* We are finished. */ ninfo("UDP done\n"); - /* Save the sender's address in the caller's 'from' location */ - - udp_sender(dev, pstate); - /* Don't allow any further UDP call backs. */ udp_terminate(pstate, OK); @@ -500,6 +443,12 @@ static uint16_t udp_eventhandler(FAR struct net_driver_s *dev, /* Indicate that the data has been consumed */ flags &= ~UDP_NEWDATA; + + /* Indicate no data in the buffer */ + + netdev_iob_release(dev); + + dev->d_buf = NULL; } } diff --git a/net/udp/udp_send.c b/net/udp/udp_send.c index 7298e1e174..7c08b89ec9 100644 --- a/net/udp/udp_send.c +++ b/net/udp/udp_send.c @@ -172,6 +172,10 @@ void udp_send(FAR struct net_driver_s *dev, FAR struct udp_conn_s *conn) udp->udplen = HTONS(dev->d_sndlen + UDP_HDRLEN); udp->udpchksum = 0; + /* Update the device buffer length */ + + iob_update_pktlen(dev->d_iob, dev->d_len); + #ifdef CONFIG_NET_UDP_CHECKSUMS /* Calculate UDP checksum. */ @@ -208,4 +212,46 @@ void udp_send(FAR struct net_driver_s *dev, FAR struct udp_conn_s *conn) #endif } } + +/**************************************************************************** + * Name: udpip_hdrsize + * + * Description: + * Get the total size of L3 and L4 UDP header + * + * Input Parameters: + * conn The connection structure associated with the socket + * + * Returned Value: + * the total size of L3 and L4 TCP header + * + ****************************************************************************/ + +uint16_t udpip_hdrsize(FAR struct udp_conn_s *conn) +{ + uint16_t hdrsize = sizeof(struct udp_hdr_s); + +#if defined(CONFIG_NET_IPv4) && defined(CONFIG_NET_IPv6) + /* Which domain the socket used */ + + if (conn->domain == PF_INET) + { + /* Select the IPv4 domain */ + + return sizeof(struct ipv4_hdr_s) + hdrsize; + } + else /* if (domain == PF_INET6) */ + { + /* Select the IPv6 domain */ + + return sizeof(struct ipv6_hdr_s) + hdrsize; + } +#elif defined(CONFIG_NET_IPv4) + ((void)conn); + return sizeof(struct ipv4_hdr_s) + hdrsize; +#elif defined(CONFIG_NET_IPv6) + ((void)conn); + return sizeof(struct ipv6_hdr_s) + hdrsize; +#endif +} #endif /* CONFIG_NET && CONFIG_NET_UDP */ diff --git a/net/udp/udp_sendto_buffered.c b/net/udp/udp_sendto_buffered.c index 0f48147b50..1fed274f79 100644 --- a/net/udp/udp_sendto_buffered.c +++ b/net/udp/udp_sendto_buffered.c @@ -152,6 +152,12 @@ static void sendto_writebuffer_release(FAR struct udp_conn_s *conn) wrb = (FAR struct udp_wrbuffer_s *)sq_remfirst(&conn->write_q); DEBUGASSERT(wrb != NULL); + /* Do not need to release wb_iob, the life cycle of wb_iob is + * handed over to the network device + */ + + wrb->wb_iob = NULL; + udp_wrbuffer_release(wrb); /* Set up for the next packet transfer by setting the connection @@ -399,6 +405,7 @@ static uint16_t sendto_eventhandler(FAR struct net_driver_s *dev, if (dev->d_sndlen <= 0 && (flags & UDP_NEWDATA) == 0 && (flags & UDP_POLL) != 0 && !sq_empty(&conn->write_q)) { + uint16_t udpiplen = udpip_hdrsize(conn); FAR struct udp_wrbuffer_s *wrb; size_t sndlen; @@ -424,7 +431,7 @@ static uint16_t sendto_eventhandler(FAR struct net_driver_s *dev, * window size. */ - sndlen = wrb->wb_iob->io_pktlen; + sndlen = wrb->wb_iob->io_pktlen - udpiplen; ninfo("wrb=%p sndlen=%zu\n", wrb, sndlen); #ifdef NEED_IPDOMAIN_SUPPORT @@ -436,11 +443,16 @@ static uint16_t sendto_eventhandler(FAR struct net_driver_s *dev, sendto_ipselect(dev, conn); #endif + + /* Release current device buffer and bypass the iob to l2 driver */ + + netdev_iob_release(dev); + /* Then set-up to send that amount of data with the offset * corresponding to the size of the IP-dependent address structure. */ - devif_iob_send(dev, wrb->wb_iob, sndlen, 0); + devif_iob_send(dev, wrb->wb_iob, sndlen, 0, udpiplen); /* Free the write buffer at the head of the queue and attempt to * setup the next transfer. @@ -496,6 +508,7 @@ ssize_t psock_udp_sendto(FAR struct socket *psock, FAR const void *buf, FAR struct udp_wrbuffer_s *wrb; FAR struct udp_conn_s *conn; unsigned int timeout; + uint16_t udpiplen; bool nonblock; bool empty; int ret = OK; @@ -737,6 +750,13 @@ ssize_t psock_udp_sendto(FAR struct socket *psock, FAR const void *buf, memcpy(&wrb->wb_dest, to, tolen); } + /* Skip l2/l3/l4 offset before copy */ + + udpiplen = udpip_hdrsize(conn); + + iob_reserve(wrb->wb_iob, CONFIG_NET_LL_GUARDSIZE); + iob_update_pktlen(wrb->wb_iob, udpiplen); + /* Copy the user data into the write buffer. We cannot wait for * buffer space if the socket was opened non-blocking. */ @@ -744,7 +764,7 @@ ssize_t psock_udp_sendto(FAR struct socket *psock, FAR const void *buf, if (nonblock) { ret = iob_trycopyin(wrb->wb_iob, (FAR uint8_t *)buf, - len, 0, false); + len, udpiplen, false); } else { @@ -757,7 +777,8 @@ ssize_t psock_udp_sendto(FAR struct socket *psock, FAR const void *buf, */ blresult = net_breaklock(&count); - ret = iob_copyin(wrb->wb_iob, (FAR uint8_t *)buf, len, 0, false); + ret = iob_copyin(wrb->wb_iob, (FAR uint8_t *)buf, + len, udpiplen, false); if (blresult >= 0) { net_restorelock(count); diff --git a/net/udp/udp_sendto_unbuffered.c b/net/udp/udp_sendto_unbuffered.c index b4bf0da969..0ab154189e 100644 --- a/net/udp/udp_sendto_unbuffered.c +++ b/net/udp/udp_sendto_unbuffered.c @@ -62,9 +62,7 @@ struct sendto_s { -#ifdef NEED_IPDOMAIN_SUPPORT FAR struct udp_conn_s *st_conn; /* The UDP connection of interest */ -#endif FAR struct devif_callback_s *st_cb; /* Reference to callback instance */ FAR struct net_driver_s *st_dev; /* Driver that will perform the transmission */ sem_t st_sem; /* Semaphore signals sendto completion */ @@ -206,7 +204,8 @@ static uint16_t sendto_eventhandler(FAR struct net_driver_s *dev, /* Copy the user data into d_appdata and send it */ - devif_send(dev, pstate->st_buffer, pstate->st_buflen); + devif_send(dev, pstate->st_buffer, + pstate->st_buflen, udpip_hdrsize(pstate->st_conn)); pstate->st_sndlen = pstate->st_buflen; } @@ -400,13 +399,11 @@ ssize_t psock_udp_sendto(FAR struct socket *psock, FAR const void *buf, state.st_buflen = len; state.st_buffer = buf; -#ifdef NEED_IPDOMAIN_SUPPORT /* Save the reference to the conn structure if it will be needed for * asynchronous processing. */ state.st_conn = conn; -#endif /* Check if the socket is connected */ diff --git a/net/utils/net_chksum.c b/net/utils/net_chksum.c index 311dc7e665..fb4933a001 100644 --- a/net/utils/net_chksum.c +++ b/net/utils/net_chksum.c @@ -90,6 +90,51 @@ uint16_t chksum(uint16_t sum, FAR const uint8_t *data, uint16_t len) } #endif /* CONFIG_NET_ARCH_CHKSUM */ +/**************************************************************************** + * Name: chksum_iob + * + * Description: + * Calculate the Internet checksum over an iob chain buffer. + * + * Input Parameters: + * sum - Partial calculations carried over from a previous call to + * chksum(). This should be zero on the first time that check + * sum is called. + * iob - An iob chain buffer over which the checksum is to be computed. + * offset - Specifies the byte offset of the start of valid data. + * + * Returned Value: + * The updated checksum value. + * + ****************************************************************************/ + +#ifdef CONFIG_MM_IOB +uint16_t chksum_iob(uint16_t sum, FAR struct iob_s *iob, uint16_t offset) +{ + /* Skip to the I/O buffer containing the data offset */ + + while (iob != NULL && offset > iob->io_len) + { + offset -= iob->io_len; + iob = iob->io_flink; + } + + /* If the link pointer is not empty, loop to walk through all I/O buffer + * and accumulate the sum + */ + + while (iob != NULL) + { + sum = chksum(sum, iob->io_data + iob->io_offset + offset, + iob->io_len - offset); + iob = iob->io_flink; + offset = 0; + } + + return sum; +} +#endif /* CONFIG_MM_IOB */ + /**************************************************************************** * Name: net_chksum * @@ -123,6 +168,39 @@ uint16_t net_chksum(FAR uint16_t *data, uint16_t len) } #endif /* CONFIG_NET_ARCH_CHKSUM */ +/**************************************************************************** + * Name: net_chksum_iob + * + * Description: + * Calculate the Internet checksum over an iob chain buffer. + * + * The Internet checksum is the one's complement of the one's complement + * sum of all 16-bit words in the buffer. + * + * See RFC1071. + * + * If CONFIG_NET_ARCH_CHKSUM is defined, then this function must be + * provided by architecture-specific logic. + * + * Input Parameters: + * sum - Partial calculations carried over from a previous call to + * chksum(). This should be zero on the first time that check + * sum is called. + * iob - An iob chain buffer over which the checksum is to be computed. + * offset - Specifies the byte offset of the start of valid data. + * + * Returned Value: + * The Internet checksum of the given iob chain buffer. + * + ****************************************************************************/ + +#ifdef CONFIG_MM_IOB +uint16_t net_chksum_iob(uint16_t sum, FAR struct iob_s *iob, uint16_t offset) +{ + return HTONS(chksum_iob(sum, iob, offset)); +} +#endif /* CONFIG_MM_IOB */ + /**************************************************************************** * Name: net_chksum_adjust * diff --git a/net/utils/net_icmpchksum.c b/net/utils/net_icmpchksum.c index 8a8d42c781..9645e30dc8 100644 --- a/net/utils/net_icmpchksum.c +++ b/net/utils/net_icmpchksum.c @@ -61,6 +61,22 @@ uint16_t icmp_chksum(FAR struct net_driver_s *dev, int len) } #endif /* CONFIG_NET_ICMP */ +/**************************************************************************** + * Name: icmp_chksum_iob + * + * Description: + * Calculate the checksum of the ICMP message, the input can be an I/O + * buffer chain + * + ****************************************************************************/ + +#if defined(CONFIG_NET_ICMP) && defined(CONFIG_MM_IOB) +uint16_t icmp_chksum_iob(FAR struct iob_s *iob) +{ + return net_chksum_iob(0, iob, 0); +} +#endif /* CONFIG_NET_ICMP */ + /**************************************************************************** * Name: icmpv6_chksum * diff --git a/net/utils/net_ipchksum.c b/net/utils/net_ipchksum.c index 1da647f719..e0d90bff1a 100644 --- a/net/utils/net_ipchksum.c +++ b/net/utils/net_ipchksum.c @@ -93,7 +93,17 @@ uint16_t ipv4_upperlayer_chksum(FAR struct net_driver_s *dev, uint8_t proto) /* Sum IP payload data. */ - sum = chksum(sum, IPBUF(iphdrlen), upperlen); +#ifdef CONFIG_MM_IOB + if (dev->d_iob != NULL) + { + sum = chksum_iob(sum, dev->d_iob, iphdrlen); + } + else +#endif + { + sum = chksum(sum, IPBUF(iphdrlen), upperlen); + } + return (sum == 0) ? 0xffff : HTONS(sum); } #endif /* CONFIG_NET_ARCH_CHKSUM */ @@ -160,7 +170,17 @@ uint16_t ipv6_upperlayer_chksum(FAR struct net_driver_s *dev, /* Sum IP payload data. */ - sum = chksum(sum, IPBUF(iplen), upperlen); +#ifdef CONFIG_MM_IOB + if (dev->d_iob != NULL) + { + sum = chksum_iob(sum, dev->d_iob, iplen); + } + else +#endif + { + sum = chksum(sum, IPBUF(iplen), upperlen); + } + return (sum == 0) ? 0xffff : HTONS(sum); } #endif /* CONFIG_NET_ARCH_CHKSUM */ diff --git a/net/utils/utils.h b/net/utils/utils.h index 83291ecaf7..4bf0e343bf 100644 --- a/net/utils/utils.h +++ b/net/utils/utils.h @@ -286,6 +286,19 @@ uint16_t udp_ipv6_chksum(FAR struct net_driver_s *dev); uint16_t icmp_chksum(FAR struct net_driver_s *dev, int len); #endif +/**************************************************************************** + * Name: icmp_chksum_iob + * + * Description: + * Calculate the checksum of the ICMP message, the input can be an I/O + * buffer chain + * + ****************************************************************************/ + +#if defined(CONFIG_NET_ICMP) && defined(CONFIG_MM_IOB) +uint16_t icmp_chksum_iob(FAR struct iob_s *iob); +#endif /* CONFIG_NET_ICMP && CONFIG_MM_IOB */ + /**************************************************************************** * Name: icmpv6_chksum *