From 2c7123197ca88ffb07aa17814174c34569fe04bd Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Wed, 28 Jan 2015 13:41:24 -0600 Subject: [PATCH] Networking: Move UDP-specifc parts of sendto() out of socket/sendto.c and into udp/udp_sendto.c. Hook in Unix domain sokcet sendto() logic (still just a stub for the moment) --- net/local/local.h | 16 +- net/local/local_sendto.c | 14 +- net/socket/sendto.c | 424 ++-------------------------------- net/udp/Make.defs | 6 + net/udp/udp.h | 40 +++- net/udp/udp_sendto.c | 475 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 558 insertions(+), 417 deletions(-) create mode 100644 net/udp/udp_sendto.c diff --git a/net/local/local.h b/net/local/local.h index 23a4ed8fe1..668bca66b1 100644 --- a/net/local/local.h +++ b/net/local/local.h @@ -346,23 +346,27 @@ ssize_t psock_local_send(FAR struct socket *psock, FAR const void *buf, size_t len, int flags); /**************************************************************************** - * Function: psock_sendto + * Function: psock_local_sendto * * Description: - * Send a local packet as a datagram. + * This function implements the Unix domain-specific logic of the + * standard sendto() socket operation. * - * Parameters: + * Input Parameters: * psock A pointer to a NuttX-specific, internal socket structure * buf Data to send * len Length of data to send - * flags Send flags (ignored for now) + * flags Send flags * to Address of recipient * tolen The length of the address structure * + * NOTE: All input parameters were verified by sendto() before this + * function was called. + * * Returned Value: * On success, returns the number of characters sent. On error, - * -1 is returned, and errno is set appropriately (see sendto() for the - * list of errno numbers). + * a negated errno value is returned. See the description in + * net/socket/sendto.c for the list of appropriate return value. * ****************************************************************************/ diff --git a/net/local/local_sendto.c b/net/local/local_sendto.c index a44333d6f0..625ccb8be8 100644 --- a/net/local/local_sendto.c +++ b/net/local/local_sendto.c @@ -53,12 +53,13 @@ ****************************************************************************/ /**************************************************************************** - * Function: psock_sendto + * Function: psock_local_sendto * * Description: - * Send a local packet as a datagram. + * This function implements the Unix domain-specific logic of the + * standard sendto() socket operation. * - * Parameters: + * Input Parameters: * psock A pointer to a NuttX-specific, internal socket structure * buf Data to send * len Length of data to send @@ -66,10 +67,13 @@ * to Address of recipient * tolen The length of the address structure * + * NOTE: All input parameters were verified by sendto() before this + * function was called. + * * Returned Value: * On success, returns the number of characters sent. On error, - * -1 is returned, and errno is set appropriately (see sendto() for the - * list of errno numbers). + * a negated errno value is returned. See the description in + * net/socket/sendto.c for the list of appropriate return value. * ****************************************************************************/ diff --git a/net/socket/sendto.c b/net/socket/sendto.c index 85ce83f31d..a0350f11a5 100644 --- a/net/socket/sendto.c +++ b/net/socket/sendto.c @@ -1,7 +1,7 @@ /**************************************************************************** * net/socket/sendto.c * - * Copyright (C) 2007-2009, 2011-2014 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2009, 2011-2015 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -43,314 +43,15 @@ #include #include #include -#include #include #include -#include -#include #include -#include -#include -#include "netdev/netdev.h" -#include "devif/devif.h" -#include "arp/arp.h" #include "udp/udp.h" +#include "local/local.h" #include "socket/socket.h" -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ -/* If both IPv4 and IPv6 support are both enabled, then we will need to build - * in some additional domain selection support. - */ - -#if defined(CONFIG_NET_IPv4) && defined(CONFIG_NET_IPv6) -# define NEED_IPDOMAIN_SUPPORT 1 -#endif - -/* Timeouts on sendto() do not make sense. Each polling cycle from the - * driver is an opportunity to send a packet. If the driver is not polling, - * then the network is not up (and there are no polling cycles to drive - * the timeout). - * - * There is a remote possibility that if there is a lot of other network - * traffic that a UDP sendto could get delayed, but I would not expect this - * generate a timeout. - */ - -#undef CONFIG_NET_SENDTO_TIMEOUT - -/* If supported, the sendto timeout function would depend on socket options - * and a system clock. - */ - -#ifndef CONFIG_NET_SOCKOPTS -# undef CONFIG_NET_SENDTO_TIMEOUT -#endif - -/**************************************************************************** - * Private Types - ****************************************************************************/ - -struct sendto_s -{ -#if defined(CONFIG_NET_SENDTO_TIMEOUT) || defined(NEED_IPDOMAIN_SUPPORT) - FAR struct socket *st_sock; /* Points to the parent socket structure */ -#endif -#ifdef CONFIG_NET_SENDTO_TIMEOUT - uint32_t st_time; /* Last send time for determining timeout */ -#endif - FAR struct devif_callback_s *st_cb; /* Reference to callback instance */ - sem_t st_sem; /* Semaphore signals sendto completion */ - uint16_t st_buflen; /* Length of send buffer (error if <0) */ - const char *st_buffer; /* Pointer to send buffer */ - int st_sndlen; /* Result of the send (length sent or negated errno) */ -}; - -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -/**************************************************************************** - * Function: send_timeout - * - * Description: - * Check for send timeout. - * - * Parameters: - * pstate - sendto state structure - * - * Returned Value: - * TRUE:timeout FALSE:no timeout - * - * Assumptions: - * Running at the interrupt level - * - ****************************************************************************/ - -#ifdef CONFIG_NET_SENDTO_TIMEOUT -static inline int send_timeout(FAR struct sendto_s *pstate) -{ - FAR struct socket *psock; - - /* Check for a timeout configured via setsockopts(SO_SNDTIMEO). - * If none... we well let the send wait forever. - */ - - psock = pstate->st_sock; - if (psock && psock->s_sndtimeo != 0) - { - /* Check if the configured timeout has elapsed */ - - return net_timeo(pstate->st_time, psock->s_sndtimeo); - } - - /* No timeout */ - - return FALSE; -} -#endif /* CONFIG_NET_SENDTO_TIMEOUT */ - -/**************************************************************************** - * Function: sendto_ipselect - * - * Description: - * If both IPv4 and IPv6 support are enabled, then we will need to select - * which one to use when generating the outgoing packet. If only one - * domain is selected, then the setup is already in place and we need do - * nothing. - * - * Parameters: - * dev - The structure of the network driver that caused the interrupt - * pstate - sendto state structure - * - * Returned Value: - * None - * - * Assumptions: - * Running at the interrupt level - * - ****************************************************************************/ - -#ifdef NEED_IPDOMAIN_SUPPORT -static inline void sendto_ipselect(FAR struct net_driver_s *dev, - FAR struct sendto_s *pstate) -{ - FAR struct socket *psock = pstate->st_sock; - DEBUGASSERT(psock); - - /* Which domain the the socket support */ - - if (psock->s_domain == PF_INET) - { - /* Select the IPv4 domain */ - - udp_ipv4_select(dev); - } - else /* if (psock->s_domain == PF_INET6) */ - { - /* Select the IPv6 domain */ - - DEBUGASSERT(psock->s_domain == PF_INET6); - udp_ipv4_select(dev); - } -} -#endif - -/**************************************************************************** - * Function: sendto_interrupt - * - * Description: - * This function is called from the interrupt level to perform the actual - * send operation when polled by the lower, device interfacing layer. - * - * Parameters: - * dev The structure of the network driver that caused the interrupt - * conn An instance of the UDP connection structure cast to void * - * pvpriv An instance of struct sendto_s cast to void* - * flags Set of events describing why the callback was invoked - * - * Returned Value: - * Modified value of the input flags - * - * Assumptions: - * Running at the interrupt level - * - ****************************************************************************/ - -#ifdef CONFIG_NET_UDP -static uint16_t sendto_interrupt(FAR struct net_driver_s *dev, FAR void *conn, - FAR void *pvpriv, uint16_t flags) -{ - FAR struct sendto_s *pstate = (FAR struct sendto_s *)pvpriv; - - nllvdbg("flags: %04x\n", flags); - if (pstate) - { - /* Check if the outgoing packet is available. It may have been claimed - * by a sendto interrupt serving a different thread -OR- if the output - * buffer currently contains unprocessed incoming data. In these cases - * we will just have to wait for the next polling cycle. - */ - - if (dev->d_sndlen > 0 || (flags & UDP_NEWDATA) != 0) - { - /* Another thread has beat us sending data or the buffer is busy, - * Check for a timeout. If not timed out, wait for the next - * polling cycle and check again. - */ - -#ifdef CONFIG_NET_SENDTO_TIMEOUT - if (send_timeout(pstate)) - { - /* Yes.. report the timeout */ - - nlldbg("SEND timeout\n"); - pstate->st_sndlen = -ETIMEDOUT; - } - else -#endif /* CONFIG_NET_SENDTO_TIMEOUT */ - { - /* No timeout. Just wait for the next polling cycle */ - - return flags; - } - } - - /* It looks like we are good to send the data */ - - else - { -#ifdef NEED_IPDOMAIN_SUPPORT - /* If both IPv4 and IPv6 support are enabled, then we will need to - * select which one to use when generating the outgoing packet. - * If only one domain is selected, then the setup is already in - * place and we need do nothing. - */ - - sendto_ipselect(dev, pstate); -#endif - - /* Copy the user data into d_appdata and send it */ - - devif_send(dev, pstate->st_buffer, pstate->st_buflen); - pstate->st_sndlen = pstate->st_buflen; - } - - /* Don't allow any further call backs. */ - - pstate->st_cb->flags = 0; - pstate->st_cb->priv = NULL; - pstate->st_cb->event = NULL; - - /* Wake up the waiting thread */ - - sem_post(&pstate->st_sem); - } - - return flags; -} -#endif - -/**************************************************************************** - * Function: sendto_txnotify - * - * Description: - * Notify the appropriate device driver that we are have data ready to - * be send (UDP) - * - * Parameters: - * psock - Socket state structure - * conn - The UDP connection structure - * - * Returned Value: - * None - * - ****************************************************************************/ - -#ifdef CONFIG_NET_UDP -static inline void sendto_txnotify(FAR struct socket *psock, - FAR struct udp_conn_s *conn) -{ -#ifdef CONFIG_NET_IPv4 -#ifdef CONFIG_NET_IPv6 - /* If both IPv4 and IPv6 support are enabled, then we will need to select - * the device driver using the appropriate IP domain. - */ - - if (psock->s_domain == PF_INET) -#endif - { - /* Notify the device driver that send data is available */ - -#ifdef CONFIG_NET_MULTILINK - netdev_ipv4_txnotify(conn->u.ipv4.laddr, conn->u.ipv4.raddr); -#else - netdev_ipv4_txnotify(conn->u.ipv4.raddr); -#endif - } -#endif /* CONFIG_NET_IPv4 */ - -#ifdef CONFIG_NET_IPv6 -#ifdef CONFIG_NET_IPv4 - else /* if (psock->s_domain == PF_INET6) */ -#endif /* CONFIG_NET_IPv4 */ - { - /* Notify the device driver that send data is available */ - - DEBUGASSERT(psock->s_domain == PF_INET6); -#ifdef CONFIG_NET_MULTILINK - netdev_ipv6_txnotify(conn->u.ipv6.laddr, conn->u.ipv6.raddr); -#else - netdev_ipv6_txnotify(conn->u.ipv6.raddr); -#endif - } -#endif /* CONFIG_NET_IPv6 */ -} -#endif /* CONFIG_NET_UDP */ - /**************************************************************************** * Public Functions ****************************************************************************/ @@ -424,16 +125,10 @@ ssize_t psock_sendto(FAR struct socket *psock, FAR const void *buf, size_t len, int flags, FAR const struct sockaddr *to, socklen_t tolen) { -#ifdef CONFIG_NET_UDP - FAR struct udp_conn_s *conn; -#ifdef CONFIG_NET_ARP_SEND - FAR const struct sockaddr_in *into; -#endif - struct sendto_s state; - net_lock_t save; + socklen_t minlen; +#if defined(CONFIG_NET_UDP) || defined(CONFIG_NET_LOCAL) int ret; #endif - socklen_t minlen; int err; /* If to is NULL or tolen is zero, then this function is same as send (for @@ -504,113 +199,42 @@ ssize_t psock_sendto(FAR struct socket *psock, FAR const void *buf, goto errout; } - /* Make sure that the IP address mapping is in the ARP table */ - -#ifdef CONFIG_NET_ARP_SEND - into = (FAR const struct sockaddr_in *)to; - ret = arp_send(into->sin_addr.s_addr); - if (ret < 0) - { - ndbg("ERROR: Not reachable\n"); - err = ENETUNREACH; - goto errout; - } -#endif +#if defined(CONFIG_NET_UDP) || defined(CONFIG_NET_LOCAL) + /* Now handle the sendto() operation according to the socket domain, + * currently either IP or Unix domains. + */ #ifdef CONFIG_NET_LOCAL - /* Perform the Unix domain sendto operation */ -# warning Missing logic +#ifdef CONFIG_NET_UDP + if (psock->s_domain == PF_LOCAL) #endif + { + ret = psock_local_sendto(psock, buf, len, flags, to, tolen); + } +#endif /* CONFIG_NET_LOCAL */ #ifdef CONFIG_NET_UDP - /* Perform the UDP sendto operation */ - /* Set the socket state to sending */ - - psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_SEND); - - /* Initialize the state structure. This is done with interrupts - * disabled because we don't want anything to happen until we - * are ready. - */ - - save = net_lock(); - memset(&state, 0, sizeof(struct sendto_s)); - sem_init(&state.st_sem, 0, 0); - state.st_buflen = len; - state.st_buffer = buf; - -#if defined(CONFIG_NET_SENDTO_TIMEOUT) || defined(NEED_IPDOMAIN_SUPPORT) - /* Save the reference to the socket structure if it will be needed for - * asynchronous processing. - */ - - state.st_sock = psock; +#ifdef CONFIG_NET_LOCAL + else #endif + { + ret = psock_udp_sendto(psock, buf, len, flags, to, tolen); + } +#endif /* CONFIG_NET_UDP */ -#ifdef CONFIG_NET_SENDTO_TIMEOUT - /* Set the initial time for calculating timeouts */ + /* Check if the domain-specific sendto() logic failed */ - state.st_time = clock_systimer(); -#endif - - /* Setup the UDP socket */ - - conn = (FAR struct udp_conn_s *)psock->s_conn; - ret = udp_connect(conn, to); if (ret < 0) { - net_unlock(save); + ndbg("ERROR: Domain sendto() failed: %d\n", ret); err = -ret; goto errout; } - /* Set up the callback in the connection */ - - state.st_cb = udp_callback_alloc(conn); - if (state.st_cb) - { - state.st_cb->flags = UDP_POLL; - state.st_cb->priv = (void*)&state; - state.st_cb->event = sendto_interrupt; - - /* Notify the device driver of the availability of TX data */ - - sendto_txnotify(psock, conn); - - /* Wait for either the receive to complete or for an error/timeout to occur. - * NOTES: (1) net_lockedwait will also terminate if a signal is received, (2) - * interrupts may be disabled! They will be re-enabled while the task sleeps - * and automatically re-enabled when the task restarts. - */ - - net_lockedwait(&state.st_sem); - - /* Make sure that no further interrupts are processed */ - - udp_callback_free(conn, state.st_cb); - } - - net_unlock(save); - sem_destroy(&state.st_sem); - - /* Set the socket state to idle */ - - psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_IDLE); - - /* Check for errors */ - - if (state.st_sndlen < 0) - { - err = -state.st_sndlen; - goto errout; - } - - /* Success */ - - return state.st_sndlen; + return OK; #else err = ENOSYS; -#endif /* CONFIG_NET_UDP */ +#endif /* CONFIG_NET_UDP || CONFIG_NET_LOCAL */ errout: set_errno(err); diff --git a/net/udp/Make.defs b/net/udp/Make.defs index e0714ce30d..9cbe77b3df 100644 --- a/net/udp/Make.defs +++ b/net/udp/Make.defs @@ -37,6 +37,12 @@ ifeq ($(CONFIG_NET_UDP),y) +# Socket layer + +NET_CSRCS += udp_sendto.c + +# Tranport layer + NET_CSRCS += udp_conn.c udp_poll.c udp_send.c udp_input.c udp_callback.c NET_CSRCS += udp_ipselect.c diff --git a/net/udp/udp.h b/net/udp/udp.h index f20ebf377d..305f28b37d 100644 --- a/net/udp/udp.h +++ b/net/udp/udp.h @@ -43,6 +43,9 @@ #include #include +#include + +#include #ifdef CONFIG_NET_UDP @@ -95,9 +98,10 @@ extern "C" * Public Function Prototypes ****************************************************************************/ +struct sockaddr; /* Forward reference */ +struct socket; /* Forward reference */ struct net_driver_s; /* Forward reference */ -/* Defined in udp_conn.c ****************************************************/ /**************************************************************************** * Name: udp_initialize * @@ -197,7 +201,6 @@ int udp_bind(FAR struct udp_conn_s *conn, FAR const struct sockaddr *addr); int udp_connect(FAR struct udp_conn_s *conn, FAR const struct sockaddr *addr); -/* Defined in udp_ipselect.c ************************************************/ /**************************************************************************** * Function: udp_ipv4_select * @@ -222,7 +225,6 @@ void udp_ipv4_select(FAR struct net_driver_s *dev); void udp_ipv6_select(FAR struct net_driver_s *dev); #endif -/* Defined in udp_poll.c ****************************************************/ /**************************************************************************** * Name: udp_poll * @@ -243,7 +245,6 @@ void udp_ipv6_select(FAR struct net_driver_s *dev); void udp_poll(FAR struct net_driver_s *dev, FAR struct udp_conn_s *conn); -/* Defined in udp_send.c ****************************************************/ /**************************************************************************** * Name: udp_send * @@ -264,7 +265,6 @@ void udp_poll(FAR struct net_driver_s *dev, FAR struct udp_conn_s *conn); void udp_send(FAR struct net_driver_s *dev, FAR struct udp_conn_s *conn); -/* Defined in udp_input.c ***************************************************/ /**************************************************************************** * Name: udp_ipv4_input * @@ -311,7 +311,6 @@ int udp_ipv4_input(FAR struct net_driver_s *dev); int udp_ipv6_input(FAR struct net_driver_s *dev); #endif -/* Defined in udp_callback.c ************************************************/ /**************************************************************************** * Function: udp_callback * @@ -329,6 +328,35 @@ int udp_ipv6_input(FAR struct net_driver_s *dev); uint16_t udp_callback(FAR struct net_driver_s *dev, FAR struct udp_conn_s *conn, uint16_t flags); +/**************************************************************************** + * Function: psock_udp_sendto + * + * Description: + * This function implements the UDP-specific logic of the standard + * sendto() socket operation. + * + * Input Parameters: + * psock A pointer to a NuttX-specific, internal socket structure + * buf Data to send + * len Length of data to send + * flags Send flags + * to Address of recipient + * tolen The length of the address structure + * + * NOTE: All input parameters were verified by sendto() before this + * function was called. + * + * Returned Value: + * On success, returns the number of characters sent. On error, + * a negated errno value is returned. See the description in + * net/socket/sendto.c for the list of appropriate return value. + * + ****************************************************************************/ + +ssize_t psock_udp_sendto(FAR struct socket *psock, FAR const void *buf, + size_t len, int flags, FAR const struct sockaddr *to, + socklen_t tolen); + #undef EXTERN #ifdef __cplusplus } diff --git a/net/udp/udp_sendto.c b/net/udp/udp_sendto.c new file mode 100644 index 0000000000..c3a53cf8bc --- /dev/null +++ b/net/udp/udp_sendto.c @@ -0,0 +1,475 @@ +/**************************************************************************** + * net/udp/udp_sendto.c + * + * Copyright (C) 2007-2009, 2011-2015 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#ifdef CONFIG_NET_UDP + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "netdev/netdev.h" +#include "devif/devif.h" +#include "arp/arp.h" +#include "socket/socket.h" +#include "udp/udp.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* If both IPv4 and IPv6 support are both enabled, then we will need to build + * in some additional domain selection support. + */ + +#if defined(CONFIG_NET_IPv4) && defined(CONFIG_NET_IPv6) +# define NEED_IPDOMAIN_SUPPORT 1 +#endif + +/* Timeouts on sendto() do not make sense. Each polling cycle from the + * driver is an opportunity to send a packet. If the driver is not polling, + * then the network is not up (and there are no polling cycles to drive + * the timeout). + * + * There is a remote possibility that if there is a lot of other network + * traffic that a UDP sendto could get delayed, but I would not expect this + * generate a timeout. + */ + +#undef CONFIG_NET_SENDTO_TIMEOUT + +/* If supported, the sendto timeout function would depend on socket options + * and a system clock. + */ + +#ifndef CONFIG_NET_SOCKOPTS +# undef CONFIG_NET_SENDTO_TIMEOUT +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct sendto_s +{ +#if defined(CONFIG_NET_SENDTO_TIMEOUT) || defined(NEED_IPDOMAIN_SUPPORT) + FAR struct socket *st_sock; /* Points to the parent socket structure */ +#endif +#ifdef CONFIG_NET_SENDTO_TIMEOUT + uint32_t st_time; /* Last send time for determining timeout */ +#endif + FAR struct devif_callback_s *st_cb; /* Reference to callback instance */ + sem_t st_sem; /* Semaphore signals sendto completion */ + uint16_t st_buflen; /* Length of send buffer (error if <0) */ + const char *st_buffer; /* Pointer to send buffer */ + int st_sndlen; /* Result of the send (length sent or negated errno) */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Function: send_timeout + * + * Description: + * Check for send timeout. + * + * Parameters: + * pstate - sendto state structure + * + * Returned Value: + * TRUE:timeout FALSE:no timeout + * + * Assumptions: + * Running at the interrupt level + * + ****************************************************************************/ + +#ifdef CONFIG_NET_SENDTO_TIMEOUT +static inline int send_timeout(FAR struct sendto_s *pstate) +{ + FAR struct socket *psock; + + /* Check for a timeout configured via setsockopts(SO_SNDTIMEO). + * If none... we well let the send wait forever. + */ + + psock = pstate->st_sock; + if (psock && psock->s_sndtimeo != 0) + { + /* Check if the configured timeout has elapsed */ + + return net_timeo(pstate->st_time, psock->s_sndtimeo); + } + + /* No timeout */ + + return FALSE; +} +#endif /* CONFIG_NET_SENDTO_TIMEOUT */ + +/**************************************************************************** + * Function: sendto_ipselect + * + * Description: + * If both IPv4 and IPv6 support are enabled, then we will need to select + * which one to use when generating the outgoing packet. If only one + * domain is selected, then the setup is already in place and we need do + * nothing. + * + * Parameters: + * dev - The structure of the network driver that caused the interrupt + * pstate - sendto state structure + * + * Returned Value: + * None + * + * Assumptions: + * Running at the interrupt level + * + ****************************************************************************/ + +#ifdef NEED_IPDOMAIN_SUPPORT +static inline void sendto_ipselect(FAR struct net_driver_s *dev, + FAR struct sendto_s *pstate) +{ + FAR struct socket *psock = pstate->st_sock; + DEBUGASSERT(psock); + + /* Which domain the the socket support */ + + if (psock->s_domain == PF_INET) + { + /* Select the IPv4 domain */ + + udp_ipv4_select(dev); + } + else /* if (psock->s_domain == PF_INET6) */ + { + /* Select the IPv6 domain */ + + DEBUGASSERT(psock->s_domain == PF_INET6); + udp_ipv4_select(dev); + } +} +#endif + +/**************************************************************************** + * Function: sendto_interrupt + * + * Description: + * This function is called from the interrupt level to perform the actual + * send operation when polled by the lower, device interfacing layer. + * + * Parameters: + * dev The structure of the network driver that caused the interrupt + * conn An instance of the UDP connection structure cast to void * + * pvpriv An instance of struct sendto_s cast to void* + * flags Set of events describing why the callback was invoked + * + * Returned Value: + * Modified value of the input flags + * + * Assumptions: + * Running at the interrupt level + * + ****************************************************************************/ + +static uint16_t sendto_interrupt(FAR struct net_driver_s *dev, FAR void *conn, + FAR void *pvpriv, uint16_t flags) +{ + FAR struct sendto_s *pstate = (FAR struct sendto_s *)pvpriv; + + nllvdbg("flags: %04x\n", flags); + if (pstate) + { + /* Check if the outgoing packet is available. It may have been claimed + * by a sendto interrupt serving a different thread -OR- if the output + * buffer currently contains unprocessed incoming data. In these cases + * we will just have to wait for the next polling cycle. + */ + + if (dev->d_sndlen > 0 || (flags & UDP_NEWDATA) != 0) + { + /* Another thread has beat us sending data or the buffer is busy, + * Check for a timeout. If not timed out, wait for the next + * polling cycle and check again. + */ + +#ifdef CONFIG_NET_SENDTO_TIMEOUT + if (send_timeout(pstate)) + { + /* Yes.. report the timeout */ + + nlldbg("SEND timeout\n"); + pstate->st_sndlen = -ETIMEDOUT; + } + else +#endif /* CONFIG_NET_SENDTO_TIMEOUT */ + { + /* No timeout. Just wait for the next polling cycle */ + + return flags; + } + } + + /* It looks like we are good to send the data */ + + else + { +#ifdef NEED_IPDOMAIN_SUPPORT + /* If both IPv4 and IPv6 support are enabled, then we will need to + * select which one to use when generating the outgoing packet. + * If only one domain is selected, then the setup is already in + * place and we need do nothing. + */ + + sendto_ipselect(dev, pstate); +#endif + + /* Copy the user data into d_appdata and send it */ + + devif_send(dev, pstate->st_buffer, pstate->st_buflen); + pstate->st_sndlen = pstate->st_buflen; + } + + /* Don't allow any further call backs. */ + + pstate->st_cb->flags = 0; + pstate->st_cb->priv = NULL; + pstate->st_cb->event = NULL; + + /* Wake up the waiting thread */ + + sem_post(&pstate->st_sem); + } + + return flags; +} + +/**************************************************************************** + * Function: sendto_txnotify + * + * Description: + * Notify the appropriate device driver that we are have data ready to + * be send (UDP) + * + * Parameters: + * psock - Socket state structure + * conn - The UDP connection structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void sendto_txnotify(FAR struct socket *psock, + FAR struct udp_conn_s *conn) +{ +#ifdef CONFIG_NET_IPv4 +#ifdef CONFIG_NET_IPv6 + /* If both IPv4 and IPv6 support are enabled, then we will need to select + * the device driver using the appropriate IP domain. + */ + + if (psock->s_domain == PF_INET) +#endif + { + /* Notify the device driver that send data is available */ + +#ifdef CONFIG_NET_MULTILINK + netdev_ipv4_txnotify(conn->u.ipv4.laddr, conn->u.ipv4.raddr); +#else + netdev_ipv4_txnotify(conn->u.ipv4.raddr); +#endif + } +#endif /* CONFIG_NET_IPv4 */ + +#ifdef CONFIG_NET_IPv6 +#ifdef CONFIG_NET_IPv4 + else /* if (psock->s_domain == PF_INET6) */ +#endif /* CONFIG_NET_IPv4 */ + { + /* Notify the device driver that send data is available */ + + DEBUGASSERT(psock->s_domain == PF_INET6); +#ifdef CONFIG_NET_MULTILINK + netdev_ipv6_txnotify(conn->u.ipv6.laddr, conn->u.ipv6.raddr); +#else + netdev_ipv6_txnotify(conn->u.ipv6.raddr); +#endif + } +#endif /* CONFIG_NET_IPv6 */ +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Function: psock_udp_sendto + * + * Description: + * This function implements the UDP-specific logic of the standard + * sendto() socket operation. + * + * Input Parameters: + * psock A pointer to a NuttX-specific, internal socket structure + * buf Data to send + * len Length of data to send + * flags Send flags + * to Address of recipient + * tolen The length of the address structure + * + * NOTE: All input parameters were verified by sendto() before this + * function was called. + * + * Returned Value: + * On success, returns the number of characters sent. On error, + * a negated errno value is returned. See the description in + * net/socket/sendto.c for the list of appropriate return value. + * + ****************************************************************************/ + +ssize_t psock_udp_sendto(FAR struct socket *psock, FAR const void *buf, + size_t len, int flags, FAR const struct sockaddr *to, + socklen_t tolen) +{ + FAR struct udp_conn_s *conn; + FAR const struct sockaddr_in *into; + struct sendto_s state; + net_lock_t save; + int ret; + + /* Make sure that the IP address mapping is in the ARP table */ + +#ifdef CONFIG_NET_ARP_SEND + into = (FAR const struct sockaddr_in *)to; + ret = arp_send(into->sin_addr.s_addr); + if (ret < 0) + { + ndbg("ERROR: Not reachable\n"); + return -ENETUNREACH; + } +#endif + + /* Set the socket state to sending */ + + psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_SEND); + + /* Initialize the state structure. This is done with interrupts + * disabled because we don't want anything to happen until we + * are ready. + */ + + save = net_lock(); + memset(&state, 0, sizeof(struct sendto_s)); + sem_init(&state.st_sem, 0, 0); + state.st_buflen = len; + state.st_buffer = buf; + +#if defined(CONFIG_NET_SENDTO_TIMEOUT) || defined(NEED_IPDOMAIN_SUPPORT) + /* Save the reference to the socket structure if it will be needed for + * asynchronous processing. + */ + + state.st_sock = psock; +#endif + +#ifdef CONFIG_NET_SENDTO_TIMEOUT + /* Set the initial time for calculating timeouts */ + + state.st_time = clock_systimer(); +#endif + + /* Setup the UDP socket */ + + conn = (FAR struct udp_conn_s *)psock->s_conn; + ret = udp_connect(conn, to); + if (ret < 0) + { + net_unlock(save); + return ret; + } + + /* Set up the callback in the connection */ + + state.st_cb = udp_callback_alloc(conn); + if (state.st_cb) + { + state.st_cb->flags = UDP_POLL; + state.st_cb->priv = (void*)&state; + state.st_cb->event = sendto_interrupt; + + /* Notify the device driver of the availability of TX data */ + + sendto_txnotify(psock, conn); + + /* Wait for either the receive to complete or for an error/timeout to occur. + * NOTES: (1) net_lockedwait will also terminate if a signal is received, (2) + * interrupts may be disabled! They will be re-enabled while the task sleeps + * and automatically re-enabled when the task restarts. + */ + + net_lockedwait(&state.st_sem); + + /* Make sure that no further interrupts are processed */ + + udp_callback_free(conn, state.st_cb); + } + + net_unlock(save); + sem_destroy(&state.st_sem); + + /* Set the socket state to idle */ + + psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_IDLE); + + /* Return the result of the sendto() operation */ + + return state.st_sndlen; +} + +#endif /* CONFIG_NET_UDP */