UDP Networking: Add support for device event notification for UDP transfers.
This commit is contained in:
parent
e28a8b1416
commit
e672bcebfd
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user