diff --git a/net/socket/recvfrom.c b/net/socket/recvfrom.c index 0f7c3dd1bef..9b893e0f9b2 100644 --- a/net/socket/recvfrom.c +++ b/net/socket/recvfrom.c @@ -1006,7 +1006,41 @@ static inline void recvfrom_udpsender(struct net_driver_s *dev, struct recvfrom_ #endif /* CONFIG_NET_UDP */ /**************************************************************************** - * Function: recvfrom_udpinterrupt + * Function: recvfrom_udp_terminate + * + * Description: + * Terminate the UDP transfer. + * + * Parameters: + * conn The connection structure associated with the socket + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_NET_UDP +static void recvfrom_udp_terminate(FAR struct recvfrom_s *pstate, int result) +{ + /* Don't allow any further UDP call backs. */ + + pstate->rf_cb->flags = 0; + pstate->rf_cb->priv = NULL; + pstate->rf_cb->event = NULL; + + /* Save the result of the transfer */ + + pstate->rf_result = result; + + /* Wake up the waiting thread, returning the number of bytes + * actually read. + */ + + sem_post(&pstate->rf_sem); +} + +/**************************************************************************** + * Function: recvfrom_udp_interrupt * * Description: * This function is called from the interrupt level to perform the actual @@ -1026,10 +1060,11 @@ static inline void recvfrom_udpsender(struct net_driver_s *dev, struct recvfrom_ ****************************************************************************/ #ifdef CONFIG_NET_UDP -static uint16_t recvfrom_udpinterrupt(struct net_driver_s *dev, void *pvconn, - void *pvpriv, uint16_t flags) +static uint16_t recvfrom_udp_interrupt(FAR struct net_driver_s *dev, + FAR void *pvconn, FAR void *pvpriv, + uint16_t flags) { - struct recvfrom_s *pstate = (struct recvfrom_s *)pvpriv; + FAR struct recvfrom_s *pstate = (FAR struct recvfrom_s *)pvpriv; nllvdbg("flags: %04x\n", flags); @@ -1037,9 +1072,21 @@ static uint16_t recvfrom_udpinterrupt(struct net_driver_s *dev, void *pvconn, if (pstate) { + /* If the network device has gone down, then we will have terminate + * the wait now with an error. + */ + + if ((flags & NETDEV_DOWN) != 0) + { + /* Terminate the transfer with an error. */ + + nlldbg("ERROR: Network is down\n"); + recvfrom_udp_terminate(pstate, -ENETUNREACH); + } + /* If new data is available, then complete the read action. */ - if ((flags & UDP_NEWDATA) != 0) + else if ((flags & UDP_NEWDATA) != 0) { /* Copy the data from the packet */ @@ -1049,25 +1096,17 @@ static uint16_t recvfrom_udpinterrupt(struct net_driver_s *dev, void *pvconn, nllvdbg("UDP done\n"); - /* Don't allow any further UDP call backs. */ - - pstate->rf_cb->flags = 0; - pstate->rf_cb->priv = NULL; - pstate->rf_cb->event = NULL; - /* Save the sender's address in the caller's 'from' location */ recvfrom_udpsender(dev, pstate); + /* Don't allow any further UDP call backs. */ + + recvfrom_udp_terminate(pstate, OK); + /* Indicate that the data has been consumed */ flags &= ~UDP_NEWDATA; - - /* Wake up the waiting thread, returning the number of bytes - * actually read. - */ - - sem_post(&pstate->rf_sem); } #ifdef CONFIG_NET_SOCKOPTS @@ -1081,21 +1120,11 @@ static uint16_t recvfrom_udpinterrupt(struct net_driver_s *dev, void *pvconn, * callbacks */ - nllvdbg("UDP timeout\n"); + nllvdbg("ERROR: UDP timeout\n"); - /* Stop further callbacks */ + /* Terminate the transfer with an -EAGAIN error */ - pstate->rf_cb->flags = 0; - pstate->rf_cb->priv = NULL; - pstate->rf_cb->event = NULL; - - /* Report a timeout error */ - - pstate->rf_result = -EAGAIN; - - /* Wake up the waiting thread */ - - sem_post(&pstate->rf_sem); + recvfrom_udp_terminate(pstate, -EAGAIN); } #endif /* CONFIG_NET_SOCKOPTS */ } @@ -1401,6 +1430,7 @@ static ssize_t udp_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, FAR struct sockaddr *from, FAR socklen_t *fromlen) { FAR struct udp_conn_s *conn = (FAR struct udp_conn_s *)psock->s_conn; + FAR struct net_driver_s *dev; struct recvfrom_s state; net_lock_t save; int ret; @@ -1464,40 +1494,48 @@ static ssize_t udp_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, else if (state.rf_recvlen == 0) #endif - { - /* Set up the callback in the connection */ - - state.rf_cb = udp_callback_alloc(conn); - if (state.rf_cb) { - /* Set up the callback in the connection */ - - state.rf_cb->flags = (UDP_NEWDATA | UDP_POLL); - state.rf_cb->priv = (void*)&state; - state.rf_cb->event = recvfrom_udpinterrupt; - - /* Notify the device driver of the receive call */ - - recvfrom_udp_rxnotify(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 are disabled! They will be re-enabled while the task sleeps - * and automatically re-enabled when the task restarts. + /* Get the device that will handle the packet transfers. This may be + * NULL if the UDP socket is bound to INADDR_ANY. In that case, no + * NETDEV_DOWN notifications will be received. */ - ret = net_lockedwait(&state. rf_sem); + dev = udp_find_laddr_device(conn); - /* Make sure that no further interrupts are processed */ + /* Set up the callback in the connection */ - udp_callback_free(conn, state.rf_cb); - ret = recvfrom_result(ret, &state); + state.rf_cb = udp_callback_alloc(dev, conn); + if (state.rf_cb) + { + /* Set up the callback in the connection */ + + state.rf_cb->flags = (UDP_NEWDATA | UDP_POLL | NETDEV_DOWN); + state.rf_cb->priv = (void*)&state; + state.rf_cb->event = recvfrom_udp_interrupt; + + /* Notify the device driver of the receive call */ + + recvfrom_udp_rxnotify(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 are disabled! They will be + * re-enabled while the task sleeps and automatically re-enabled + * when the task restarts. + */ + + ret = net_lockedwait(&state. rf_sem); + + /* Make sure that no further interrupts are processed */ + + udp_callback_free(dev, conn, state.rf_cb); + ret = recvfrom_result(ret, &state); + } + else + { + ret = -EBUSY; + } } - else - { - ret = -EBUSY; - } - } errout_with_state: net_unlock(save); diff --git a/net/tcp/tcp_netpoll.c b/net/tcp/tcp_netpoll.c index 58553d9cc4f..cfa9a2b2576 100644 --- a/net/tcp/tcp_netpoll.c +++ b/net/tcp/tcp_netpoll.c @@ -119,7 +119,7 @@ static uint16_t tcp_poll_interrupt(FAR struct net_driver_s *dev, FAR void *conn, /* Check for a loss of connection events. */ - if ((flags & (TCP_CLOSE | TCP_ABORT | TCP_TIMEDOUT)) != 0) + if ((flags & (TCP_CLOSE | TCP_ABORT | TCP_TIMEDOUT | NETDEV_DOWN)) != 0) { /* Marki that the connection has been lost */ @@ -209,7 +209,7 @@ int tcp_pollsetup(FAR struct socket *psock, FAR struct pollfd *fds) */ cb->flags = (TCP_NEWDATA | TCP_BACKLOG | TCP_POLL | TCP_CLOSE | - TCP_ABORT | TCP_TIMEDOUT); + TCP_ABORT | TCP_TIMEDOUT | NETDEV_DOWN); cb->priv = (FAR void *)info; cb->event = tcp_poll_interrupt; diff --git a/net/udp/udp.h b/net/udp/udp.h index f09fcca7054..41c582a63b6 100644 --- a/net/udp/udp.h +++ b/net/udp/udp.h @@ -65,8 +65,10 @@ /* Allocate a new UDP data callback */ -#define udp_callback_alloc(conn) devif_callback_alloc(NULL, &conn->list) -#define udp_callback_free(conn,cb) devif_callback_free(NULL, cb, &conn->list) +#define udp_callback_alloc(dev, conn) \ + devif_callback_alloc(dev, &conn->list) +#define udp_callback_free(dev, conn,cb) \ + devif_callback_free(dev, cb, &conn->list) /**************************************************************************** * Public Type Definitions diff --git a/net/udp/udp_netpoll.c b/net/udp/udp_netpoll.c index 0ab9a5f90ea..33e8e50aaf1 100644 --- a/net/udp/udp_netpoll.c +++ b/net/udp/udp_netpoll.c @@ -59,6 +59,7 @@ struct udp_poll_s { FAR struct socket *psock; /* Needed to handle loss of connection */ + FAR struct net_driver_s *dev; /* Needed to free the callback structure */ struct pollfd *fds; /* Needed to handle poll events */ FAR struct devif_callback_s *cb; /* Needed to teardown the poll */ }; @@ -104,18 +105,25 @@ static uint16_t udp_poll_interrupt(FAR struct net_driver_s *dev, FAR void *conn, /* Check for data or connection availability events. */ - if ((flags & (UDP_NEWDATA)) != 0) + if ((flags & UDP_NEWDATA) != 0) { eventset |= (POLLIN & info->fds->events); } - /* A poll is a sign that we are free to send data. */ + /* poll is a sign that we are free to send data. */ if ((flags & UDP_POLL) != 0) { eventset |= (POLLOUT & info->fds->events); } + /* Check for loss of connection events. */ + + if ((flags & NETDEV_DOWN) != 0) + { + eventset |= ((PULLHUP | POLLERR) & info->fds->events); + } + /* Awaken the caller of poll() is requested event occurred. */ if (eventset) @@ -177,6 +185,13 @@ int udp_pollsetup(FAR struct socket *psock, FAR struct pollfd *fds) flags = net_lock(); + /* Get the device that will provide the provide the NETDEV_DOWN event. + * NOTE: in the event that the local socket is bound to INADDR_ANY, the + * dev value will be zero and there will be no NETDEV_DOWN notifications. + */ + + info->dev = udp_find_laddr_device(conn); + /* Setup the UDP remote connection */ ret = udp_connect(conn, NULL); @@ -187,7 +202,7 @@ int udp_pollsetup(FAR struct socket *psock, FAR struct pollfd *fds) /* Allocate a TCP/IP callback structure */ - cb = udp_callback_alloc(conn); + cb = udp_callback_alloc(info->dev, conn); if (!cb) { ret = -EBUSY; @@ -205,20 +220,30 @@ int udp_pollsetup(FAR struct socket *psock, FAR struct pollfd *fds) * callback processing. */ - cb->flags = (0); + cb->flags = 0; cb->priv = (FAR void *)info; cb->event = udp_poll_interrupt; - if (info->fds->events & POLLOUT) - cb->flags |= UDP_POLL; - if (info->fds->events & POLLIN) - cb->flags |= UDP_NEWDATA; + if ((info->fds->events & POLLOUT) != 0) + { + cb->flags |= UDP_POLL; + } + + if ((info->fds->events & POLLIN) != 0) + { + cb->flags |= UDP_NEWDATA; + } + + if ((info->fds->events & (POLLHUP | POLLERR)) != 0) + { + cb->flags |= NETDEV_DOWN; + } /* Save the reference in the poll info structure as fds private as well - * for use durring poll teardown as well. + * for use during poll teardown as well. */ - fds->priv = (FAR void *)info; + fds->priv = (FAR void *)info; /* Check for read data availability now */ @@ -286,7 +311,7 @@ int udp_pollteardown(FAR struct socket *psock, FAR struct pollfd *fds) /* Release the callback */ flags = net_lock(); - udp_callback_free(conn, info->cb); + udp_callback_free(info->dev, conn, info->cb); net_unlock(flags); /* Release the poll/select data slot */ diff --git a/net/udp/udp_sendto.c b/net/udp/udp_sendto.c index e109c220856..cba34bbddff 100644 --- a/net/udp/udp_sendto.c +++ b/net/udp/udp_sendto.c @@ -227,13 +227,25 @@ static uint16_t sendto_interrupt(FAR struct net_driver_s *dev, FAR void *conn, nllvdbg("flags: %04x\n", flags); if (pstate) { + /* If the network device has gone down, then we will have terminate + * the wait now with an error. + */ + + if ((flags & NETDEV_DOWN) != 0) + { + /* Terminate the transfer with an error. */ + + nlldbg("ERROR: Network is down\n"); + pstate->st_sndlen = -ENETUNREACH; + } + /* 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) + else 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 @@ -245,7 +257,7 @@ static uint16_t sendto_interrupt(FAR struct net_driver_s *dev, FAR void *conn, { /* Yes.. report the timeout */ - nlldbg("SEND timeout\n"); + nlldbg("ERROR: SEND timeout\n"); pstate->st_sndlen = -ETIMEDOUT; } else @@ -380,6 +392,7 @@ ssize_t psock_udp_sendto(FAR struct socket *psock, FAR const void *buf, socklen_t tolen) { FAR struct udp_conn_s *conn; + FAR struct net_driver_s *dev; struct sendto_s state; net_lock_t save; int ret; @@ -392,6 +405,11 @@ ssize_t psock_udp_sendto(FAR struct socket *psock, FAR const void *buf, { FAR const struct sockaddr_in *into; + /* Get the device that will service this transfer. This may be the + * same device that the + +conn->u.ipv4.raddr + /* Make sure that the IP address mapping is in the ARP table */ into = (FAR const struct sockaddr_in *)to; @@ -399,7 +417,6 @@ ssize_t psock_udp_sendto(FAR struct socket *psock, FAR const void *buf, } #endif /* CONFIG_NET_ARP_SEND */ - #ifdef CONFIG_NET_ICMPv6_NEIGHBOR #ifdef CONFIG_NET_ARP_SEND else @@ -452,7 +469,9 @@ ssize_t psock_udp_sendto(FAR struct socket *psock, FAR const void *buf, state.st_time = clock_systimer(); #endif - /* Setup the UDP socket */ + /* Setup the UDP socket. udp_connect will set the remote address in the + * connection structure. + */ conn = (FAR struct udp_conn_s *)psock->s_conn; DEBUGASSERT(conn); @@ -460,16 +479,26 @@ ssize_t psock_udp_sendto(FAR struct socket *psock, FAR const void *buf, ret = udp_connect(conn, to); if (ret < 0) { - net_unlock(save); - return ret; + goto errout_with_lock; } + /* Get the device that will handle the remote packet transfers. This + * should never be NULL. + */ + + dev = udp_find_raddr_device(conn); + if (dev == NULL) + { + ret = -ENETUNREACH; + goto errout_with_lock; + } + /* Set up the callback in the connection */ - state.st_cb = udp_callback_alloc(conn); + state.st_cb = udp_callback_alloc(dev, conn); if (state.st_cb) { - state.st_cb->flags = UDP_POLL; + state.st_cb->flags = (UDP_POLL | NETDEV_DOWN); state.st_cb->priv = (void*)&state; state.st_cb->event = sendto_interrupt; @@ -487,19 +516,26 @@ ssize_t psock_udp_sendto(FAR struct socket *psock, FAR const void *buf, /* Make sure that no further interrupts are processed */ - udp_callback_free(conn, state.st_cb); + udp_callback_free(dev, conn, state.st_cb); } - net_unlock(save); + /* The result of the sendto operation is the number of bytes transferred */ + + ret = state.st_sndlen; + +errout_with_lock: + /* Release the semaphore */ + sem_destroy(&state.st_sem); - /* Set the socket state to idle */ + /* Set the socket state back to idle */ psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_IDLE); - /* Return the result of the sendto() operation */ + /* Unlock the network and return the result of the sendto() operation */ - return state.st_sndlen; + net_unlock(save); + return ret; } #endif /* CONFIG_NET_UDP */