UDP Networking: Add support for device event notification for UDP transfers.

This commit is contained in:
Gregory Nutt 2015-05-29 10:21:06 -06:00
parent e28a8b1416
commit e672bcebfd
5 changed files with 187 additions and 86 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

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

View File

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