diff options
author | Gregory Nutt <gnutt@nuttx.org> | 2014-01-13 11:58:45 -0600 |
---|---|---|
committer | Gregory Nutt <gnutt@nuttx.org> | 2014-01-13 11:58:45 -0600 |
commit | 45e0b49b1a2808c4ce4c289fbbc8ee284b293917 (patch) | |
tree | 838202f92736ac1b5cc62a0e7d73a7cc3c86ae22 | |
parent | df4d1ee71b6dd23755e23b95dc88898f0635816c (diff) | |
download | nuttx-45e0b49b1a2808c4ce4c289fbbc8ee284b293917.tar.gz nuttx-45e0b49b1a2808c4ce4c289fbbc8ee284b293917.tar.bz2 nuttx-45e0b49b1a2808c4ce4c289fbbc8ee284b293917.zip |
Add support for the SO_LINGER socket option. Based on logic from Jason Jiang. Utestested on initial commit
-rw-r--r-- | nuttx/ChangeLog | 5 | ||||
-rw-r--r-- | nuttx/configs/viewtool-stm32f107/netnsh/defconfig | 12 | ||||
-rw-r--r-- | nuttx/include/nuttx/net/net.h | 5 | ||||
-rw-r--r-- | nuttx/include/sys/socket.h | 45 | ||||
-rw-r--r-- | nuttx/net/Kconfig | 7 | ||||
-rw-r--r-- | nuttx/net/net_close.c | 253 | ||||
-rw-r--r-- | nuttx/net/setsockopt.c | 50 |
7 files changed, 333 insertions, 44 deletions
diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog index 7c4861e0e..776a642a5 100644 --- a/nuttx/ChangeLog +++ b/nuttx/ChangeLog @@ -6415,3 +6415,8 @@ * binfmt/binfmt_loadmodule.c: Fix a memory leak (2013-1-11). * configs/stm3vldiscovery: Support for the STM32VL-Discovery board. Contributed by Alan Carvalho de Assis (2014-1-12). + * net/net_close.c, net/Kconfig, include/nuttx/net/net.h, and + include/sys/socket.h: Add support for the SO_LINGER socket option. + Extended from logic provided by Jason Jiang. Enabled with + CONFIG_NET_SOLINGER. At this point, it has only been verified that + the changes does not seem to do any harm (2014-1-13). diff --git a/nuttx/configs/viewtool-stm32f107/netnsh/defconfig b/nuttx/configs/viewtool-stm32f107/netnsh/defconfig index 21343d272..d9b31ebf9 100644 --- a/nuttx/configs/viewtool-stm32f107/netnsh/defconfig +++ b/nuttx/configs/viewtool-stm32f107/netnsh/defconfig @@ -212,6 +212,8 @@ CONFIG_STM32_CONNECTIVITYLINE=y # # STM32 Peripheral Support # +# CONFIG_STM32_HAVE_USBDEV is not set +CONFIG_STM32_HAVE_OTGFS=y # CONFIG_STM32_ADC1 is not set # CONFIG_STM32_ADC2 is not set # CONFIG_STM32_ADC3 is not set @@ -225,6 +227,7 @@ CONFIG_STM32_CONNECTIVITYLINE=y CONFIG_STM32_ETHMAC=y # CONFIG_STM32_I2C1 is not set # CONFIG_STM32_I2C2 is not set +# CONFIG_STM32_OTGFS is not set CONFIG_STM32_PWR=y # CONFIG_STM32_SPI1 is not set # CONFIG_STM32_SPI2 is not set @@ -242,7 +245,6 @@ CONFIG_STM32_USART1=y # CONFIG_STM32_USART3 is not set # CONFIG_STM32_UART4 is not set # CONFIG_STM32_UART5 is not set -# CONFIG_STM32_USB is not set # CONFIG_STM32_IWDG is not set # CONFIG_STM32_WWDG is not set @@ -538,6 +540,7 @@ CONFIG_NET_MULTIBUFFER=y CONFIG_NSOCKET_DESCRIPTORS=10 CONFIG_NET_NACTIVESOCKETS=16 CONFIG_NET_SOCKOPTS=y +# CONFIG_NET_SOLINGER is not set CONFIG_NET_BUFSIZE=650 # CONFIG_NET_TCPURGDATA is not set @@ -560,7 +563,7 @@ CONFIG_NET_TCP_RECVDELAY=0 CONFIG_NET_UDP=y # CONFIG_NET_UDP_CHECKSUMS is not set CONFIG_NET_UDP_CONNS=8 -# CONFIG_NET_BROADCAST is not set +CONFIG_NET_BROADCAST=y # CONFIG_NET_RXAVAIL is not set CONFIG_NET_ICMP=y CONFIG_NET_ICMP_PING=y @@ -842,7 +845,10 @@ CONFIG_NSH_CODECS_BUFSIZE=128 # CONFIG_NSH_CMDOPT_HEXDUMP is not set CONFIG_NSH_FILEIOSIZE=1024 CONFIG_NSH_LINELEN=80 +# CONFIG_NSH_DISABLE_SEMICOLON is not set +# CONFIG_NSH_CMDPARMS is not set CONFIG_NSH_MAXARGUMENTS=6 +# CONFIG_NSH_ARGCAT is not set CONFIG_NSH_NESTDEPTH=3 # CONFIG_NSH_DISABLESCRIPT is not set # CONFIG_NSH_DISABLEBG is not set @@ -861,9 +867,11 @@ CONFIG_NSH_TELNETD_CLIENTPRIO=100 CONFIG_NSH_TELNETD_CLIENTSTACKSIZE=2048 CONFIG_NSH_IOBUFFER_SIZE=512 # CONFIG_NSH_TELNET_LOGIN is not set +# CONFIG_NSH_DHCPC is not set CONFIG_NSH_IPADDR=0x0a000002 CONFIG_NSH_DRIPADDR=0x0a000001 CONFIG_NSH_NETMASK=0xffffff00 +# CONFIG_NSH_DNS is not set CONFIG_NSH_NOMAC=y CONFIG_NSH_MAX_ROUNDTRIP=20 diff --git a/nuttx/include/nuttx/net/net.h b/nuttx/include/nuttx/net/net.h index 402be5d5f..d434a89b1 100644 --- a/nuttx/include/nuttx/net/net.h +++ b/nuttx/include/nuttx/net/net.h @@ -1,7 +1,7 @@ /**************************************************************************** * include/nuttx/net/net.h * - * Copyright (C) 2007, 2009-2013 Gregory Nutt. All rights reserved. + * Copyright (C) 2007, 2009-2014 Gregory Nutt. All rights reserved. * Author: Gregory Nutt <gnutt@nuttx.org> * * Redistribution and use in source and binary forms, with or without @@ -96,6 +96,9 @@ struct socket socktimeo_t s_rcvtimeo; /* Receive timeout value (in deciseconds) */ socktimeo_t s_sndtimeo; /* Send timeout value (in deciseconds) */ #endif +#ifdef CONFIG_NET_SOLINGER + socktimeo_t s_linger; /* Linger timeout value (in deciseconds) */ +#endif #endif FAR void *s_conn; /* Connection: struct uip_conn or uip_udp_conn */ }; diff --git a/nuttx/include/sys/socket.h b/nuttx/include/sys/socket.h index 89ade941f..5830490ae 100644 --- a/nuttx/include/sys/socket.h +++ b/nuttx/include/sys/socket.h @@ -191,6 +191,14 @@ struct sockaddr char sa_data[14]; /* 14-bytes of address data */ }; +/* Used with the SO_LINGER socket option */ + +struct linger +{ + int l_onoff; /* Indicates whether linger option is enabled. */ + int l_linger; /* Linger time, in seconds. */ +}; + /**************************************************************************** * Public Function Prototypes ****************************************************************************/ @@ -198,33 +206,34 @@ struct sockaddr #undef EXTERN #if defined(__cplusplus) #define EXTERN extern "C" -extern "C" { +extern "C" +{ #else #define EXTERN extern #endif -EXTERN int socket(int domain, int type, int protocol); -EXTERN int bind(int sockfd, FAR const struct sockaddr *addr, socklen_t addrlen); -EXTERN int connect(int sockfd, FAR const struct sockaddr *addr, socklen_t addrlen); +int socket(int domain, int type, int protocol); +int bind(int sockfd, FAR const struct sockaddr *addr, socklen_t addrlen); +int connect(int sockfd, FAR const struct sockaddr *addr, socklen_t addrlen); -EXTERN int listen(int sockfd, int backlog); -EXTERN int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); +int listen(int sockfd, int backlog); +int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); -EXTERN ssize_t send(int sockfd, FAR const void *buf, size_t len, int flags); -EXTERN ssize_t sendto(int sockfd, FAR const void *buf, size_t len, int flags, - FAR const struct sockaddr *to, socklen_t tolen); +ssize_t send(int sockfd, FAR const void *buf, size_t len, int flags); +ssize_t sendto(int sockfd, FAR const void *buf, size_t len, int flags, + FAR const struct sockaddr *to, socklen_t tolen); -EXTERN ssize_t recv(int sockfd, FAR void *buf, size_t len, int flags); -EXTERN ssize_t recvfrom(int sockfd, FAR void *buf, size_t len, int flags, - FAR struct sockaddr *from, FAR socklen_t *fromlen); +ssize_t recv(int sockfd, FAR void *buf, size_t len, int flags); +ssize_t recvfrom(int sockfd, FAR void *buf, size_t len, int flags, + FAR struct sockaddr *from, FAR socklen_t *fromlen); -EXTERN int setsockopt(int sockfd, int level, int option, - FAR const void *value, socklen_t value_len); -EXTERN int getsockopt(int sockfd, int level, int option, - FAR void *value, FAR socklen_t *value_len); +int setsockopt(int sockfd, int level, int option, + FAR const void *value, socklen_t value_len); +int getsockopt(int sockfd, int level, int option, + FAR void *value, FAR socklen_t *value_len); -EXTERN int getsockname(int sockfd, FAR struct sockaddr *addr, - FAR socklen_t *addrlen); +int getsockname(int sockfd, FAR struct sockaddr *addr, + FAR socklen_t *addrlen); #undef EXTERN #if defined(__cplusplus) diff --git a/nuttx/net/Kconfig b/nuttx/net/Kconfig index 1a1d1e8e6..6bd92b118 100644 --- a/nuttx/net/Kconfig +++ b/nuttx/net/Kconfig @@ -72,6 +72,13 @@ config NET_SOCKOPTS ---help--- Enable or disable support for socket options +config NET_SOLINGER + bool "SO_LINGER socket option" + default n + depends on NET_SOCKOPTS && !DISABLE_CLOCK + ---help--- + Enable or disable support for the SO_LINGER socket option. + config NET_BUFSIZE int "Network packet size" default 562 if !NET_SLIP diff --git a/nuttx/net/net_close.c b/nuttx/net/net_close.c index 0bd5960ef..8aa775aba 100644 --- a/nuttx/net/net_close.c +++ b/nuttx/net/net_close.c @@ -1,7 +1,7 @@ /**************************************************************************** * net/net_close.c * - * Copyright (C) 2007-2013 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2014 Gregory Nutt. All rights reserved. * Author: Gregory Nutt <gnutt@nuttx.org> * * Redistribution and use in source and binary forms, with or without @@ -43,24 +43,94 @@ #include <sys/types.h> #include <sys/socket.h> #include <stdint.h> +#include <stdbool.h> #include <errno.h> #include <debug.h> #include <arch/irq.h> #include <nuttx/net/uip/uip-arch.h> +#ifdef CONFIG_NET_SOLINGER +# include <nuttx/clock.h> +#endif + #include "net_internal.h" #include "uip/uip_internal.h" /**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** * Private Types ****************************************************************************/ +#ifdef CONFIG_NET_TCP +struct tcp_close_s +{ + FAR struct uip_callback_s *cl_cb; /* Reference to TCP callback instance */ +#ifdef CONFIG_NET_SOLINGER + FAR struct socket *cl_psock; /* Reference to the TCP socket */ + sem_t cl_sem; /* Signals disconnect completion */ + int cl_result; /* The result of the close */ +#ifndef CONFIG_DISABLE_CLOCK + uint32_t cl_start; /* Time close started (in ticks) */ +#endif +#endif +}; +#endif + /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** + * Function: close_timeout + * + * Description: + * Check for a timeout on a lingering close. + * + * Parameters: + * pstate send state structure + * + * Returned Value: + * TRUE:timeout FALSE:no timeout + * + * Assumptions: + * Running at the interrupt level + * + ****************************************************************************/ + +#if defined(CONFIG_NET_TCP) && defined(CONFIG_NET_SOLINGER) && \ + !defined(CONFIG_DISABLE_CLOCK) +static inline int close_timeout(FAR struct tcp_close_s *pstate) +{ + FAR struct socket *psock = 0; + + /* Make sure that we are performing a lingering close */ + + if (pstate) + { + /* Yes Check for a timeout configured via setsockopts(SO_LINGER). + * If none... we well let the send wait forever. + */ + + psock = pstate->cl_psock; + if (psock && psock->s_linger != 0) + { + /* Check if the configured timeout has elapsed */ + + return net_timeo(pstate->cl_start, psock->s_linger); + } + } + + /* No timeout */ + + return FALSE; +} +#endif /* CONFIG_NET_SOCKOPTS && CONFIG_NET_SOLINGER && !CONFIG_DISABLE_CLOCK */ + +/**************************************************************************** * Function: netclose_interrupt * * Description: @@ -82,7 +152,10 @@ static uint16_t netclose_interrupt(FAR struct uip_driver_s *dev, FAR void *pvconn, FAR void *pvpriv, uint16_t flags) { - FAR struct uip_conn *conn = (FAR struct uip_conn*)pvconn; +#ifdef CONFIG_NET_SOLINGER + FAR struct tcp_close_s *pstate = (struct tcp_close_s *)pvpriv; +#endif + FAR struct uip_conn *conn = (FAR struct uip_conn *)pvconn; DEBUGASSERT(conn != NULL); @@ -93,16 +166,64 @@ static uint16_t netclose_interrupt(FAR struct uip_driver_s *dev, * UIP_TIMEDOUT: The remote did not respond, the connection timed out */ - if ((flags & (UIP_CLOSE|UIP_ABORT|UIP_TIMEDOUT)) != 0) + if ((flags & (UIP_CLOSE | UIP_ABORT | UIP_TIMEDOUT)) != 0) { - /* Free connection resources */ + /* The disconnection is complete */ - uip_tcpfree(conn); +#ifdef CONFIG_NET_SOLINGER + /* pstate non-NULL means that we are performing a LINGERing close.*/ + + if (pstate) + { + pstate->cl_result = OK; + goto end_wait; + } - /* Stop further callbacks */ + /* Otherwise, nothing is waiting on the close event and we can perform + * the completion actions here. + */ + + else +#endif + { + /* Free connection resources */ + + uip_tcpfree(conn); + + /* Stop further callbacks */ + + flags = 0; + } + } + +#ifdef CONFIG_NET_SOLINGER +#ifndef CONFIG_DISABLE_CLOCK + /* Check for a timeout. */ + + else if (pstate && close_timeout(pstate)) + { + /* Yes.. report the timeout */ - flags = 0; + nlldbg("CLOSE timeout\n"); + pstate->cl_result = -ETIMEDOUT; + goto end_wait; } +#endif /* CONFIG_DISABLE_CLOCK */ + + /* Check if all outstanding bytes have been ACKed */ + + else if (pstate && conn->unacked != 0) + { + /* No... we are still waiting for ACKs. Drop any received data, but + * do not yet report UIP_CLOSE in the response. + */ + + dev->d_len = 0; + flags = (flags & ~UIP_NEWDATA); + } + +#endif /* CONFIG_NET_SOLINGER */ + else { /* Drop data received in this state and make sure that UIP_CLOSE @@ -114,6 +235,17 @@ static uint16_t netclose_interrupt(FAR struct uip_driver_s *dev, } return flags; + +#ifdef CONFIG_NET_SOLINGER +end_wait: + pstate->cl_cb->flags = 0; + pstate->cl_cb->priv = NULL; + pstate->cl_cb->event = NULL; + sem_post(&pstate->cl_sem); + + nllvdbg("Resuming\n"); + return 0; +#endif } #endif /* CONFIG_NET_TCP */ @@ -135,11 +267,15 @@ static uint16_t netclose_interrupt(FAR struct uip_driver_s *dev, ****************************************************************************/ #ifdef CONFIG_NET_TCP -static inline void netclose_disconnect(FAR struct socket *psock) +static inline int netclose_disconnect(FAR struct socket *psock) { + struct tcp_close_s state; FAR struct uip_conn *conn; - FAR struct uip_callback_s *cb; uip_lock_t flags; +#ifdef CONFIG_NET_SOLINGER + bool linger; +#endif + int ret = OK; /* Interrupts are disabled here to avoid race conditions */ @@ -153,16 +289,82 @@ static inline void netclose_disconnect(FAR struct socket *psock) /* Check for the case where the host beat us and disconnected first */ if (conn->tcpstateflags == UIP_ESTABLISHED && - (cb = uip_tcpcallbackalloc(conn)) != NULL) + (state.cl_cb = uip_tcpcallbackalloc(conn)) != NULL) { /* Set up to receive TCP data event callbacks */ - cb->flags = UIP_NEWDATA|UIP_POLL|UIP_CLOSE|UIP_ABORT|UIP_TIMEDOUT; - cb->event = netclose_interrupt; + state.cl_cb->flags = (UIP_NEWDATA | UIP_POLL | UIP_CLOSE | UIP_ABORT | \ + UIP_TIMEDOUT); + state.cl_cb->event = netclose_interrupt; + +#ifdef CONFIG_NET_SOLINGER + /* Check for a lingering close */ + + linger = _SO_GETOPT(psock->s_options, SO_LINGER); + + /* Has a lingering close been requested */ + + if (linger) + { + /* A non-NULL value of the priv field means that lingering is + * enabled. + */ + + state.cl_cb->priv = (void*)&state; + + /* Set up for the lingering wait */ + + state.cl_psock = psock; + state.cl_result = -EBUSY; + sem_init(&state.cl_sem, 0, 0); + +#ifndef CONFIG_DISABLE_CLOCK + /* Record the time that we started the wait (in ticks) */ + + state.cl_start = clock_systimer(); +#endif + } + else +#endif /* CONFIG_NET_SOLINGER */ + + { + /* We will close immediately. The NULL priv field signals this */ + + state.cl_cb->priv = NULL; - /* Notify the device driver of the availaibilty of TX data */ + /* No further references on the connection */ + + conn->crefs = 0; + } + + /* Notify the device driver of the availability of TX data */ netdev_txnotify(conn->ripaddr); + +#ifdef CONFIG_NET_SOLINGER + /* Wait only if we are lingering */ + + if (linger) + { + /* Wait for the disconnect event */ + + (void)uip_lockedwait(&state.cl_sem); + + /* We are now disconnected */ + + sem_destroy(&state.cl_sem); + uip_tcpcallbackfree(conn, state.cl_cb); + + /* Free the connection */ + + conn->crefs = 0; /* No more references on the connection */ + uip_tcpfree(conn); /* Free uIP resources */ + + /* Get the result of the close */ + + ret = state.cl_result; + } +#endif /* CONFIG_NET_SOLINGER */ } else { @@ -170,8 +372,9 @@ static inline void netclose_disconnect(FAR struct socket *psock) } uip_unlock(flags); + return ret; } -#endif +#endif /* CONFIG_NET_TCP */ /**************************************************************************** * Public Functions @@ -228,9 +431,16 @@ int psock_close(FAR struct socket *psock) { /* Yes... then perform the disconnection now */ - uip_unlisten(conn); /* No longer accepting connections */ - conn->crefs = 0; /* No more references on the connection */ - netclose_disconnect(psock); /* Break any current connections */ + uip_unlisten(conn); /* No longer accepting connections */ + err = netclose_disconnect(psock); /* Break any current connections */ + if (err < 0) + { + /* This would normally occur only if there is a timeout + * from a lingering close. + */ + + goto errout_with_psock; + } } else { @@ -248,7 +458,7 @@ int psock_close(FAR struct socket *psock) struct uip_udp_conn *conn = psock->s_conn; /* Is this the last reference to the connection structure (there - * could be more if the socket was dup'ed. + * could be more if the socket was dup'ed). */ if (conn->crefs <= 1) @@ -279,8 +489,13 @@ int psock_close(FAR struct socket *psock) sock_release(psock); return OK; +#ifdef CONFIG_NET_TCP +errout_with_psock: + sock_release(psock); +#endif + errout: - errno = err; + set_errno(err); return ERROR; } diff --git a/nuttx/net/setsockopt.c b/nuttx/net/setsockopt.c index 7b65a7bf7..6fb584558 100644 --- a/nuttx/net/setsockopt.c +++ b/nuttx/net/setsockopt.c @@ -1,7 +1,8 @@ /**************************************************************************** * net/setsockopt.c * - * Copyright (C) 2007, 2008, 2011-2012 Gregory Nutt. All rights reserved. + * Copyright (C) 2007, 2008, 2011-2012, 2014 Gregory Nutt. All rights + * reserved. * Author: Gregory Nutt <gnutt@nuttx.org> * * Redistribution and use in source and binary forms, with or without @@ -112,9 +113,10 @@ int psock_setsockopt(FAR struct socket *psock, int level, int option, } /* Process the option */ + switch (option) { - /* The following options take a point to an integer boolean value. + /* The following options take a pointer to an integer boolean value. * We will blindly set the bit here although the implementation * is outside of the scope of setsockopt. */ @@ -159,15 +161,16 @@ int psock_setsockopt(FAR struct socket *psock, int level, int option, { _SO_CLROPT(psock->s_options, option); } + uip_unlock(flags); } break; /* The following are valid only if the OS CLOCK feature is enabled */ +#ifndef CONFIG_DISABLE_CLOCK case SO_RCVTIMEO: case SO_SNDTIMEO: -#ifndef CONFIG_DISABLE_CLOCK { socktimeo_t timeo; @@ -208,9 +211,48 @@ int psock_setsockopt(FAR struct socket *psock, int level, int option, break; #endif +#ifdef CONFIG_NET_SOLINGER + case SO_LINGER: + { + FAR struct linger *setting; + + /* Verify that option is at least the size of an 'struct linger'. */ + + if (value_len < sizeof(FAR struct linger)) + { + err = EINVAL; + goto errout; + } + + /* Get the value. Is the option being set or cleared? */ + + setting = (FAR struct linger *)value; + + /* Disable interrupts so that there is no conflict with interrupt + * level access to options. + */ + + flags = uip_lock(); + + /* Set or clear the linger option bit and linger time (in deciseconds) */ + + if (setting->l_onoff) + { + _SO_SETOPT(psock->s_options, option); + psock->s_linger = 10 * setting->l_linger; + } + else + { + _SO_CLROPT(psock->s_options, option); + psock->s_linger = 0; + } + + uip_unlock(flags); + } + break; +#endif /* The following are not yet implemented */ - case SO_LINGER: case SO_SNDBUF: /* Sets send buffer size */ case SO_RCVBUF: /* Sets receive buffer size */ case SO_RCVLOWAT: /* Sets the minimum number of bytes to input */ |