diff --git a/net/netlink/netlink.h b/net/netlink/netlink.h index effaf430321..0686a5ba23c 100644 --- a/net/netlink/netlink.h +++ b/net/netlink/netlink.h @@ -212,6 +212,49 @@ FAR struct netlink_response_s * FAR struct netlink_response_s * netlink_get_response(FAR struct socket *psock); +/**************************************************************************** + * Name: netlink_check_response + * + * Description: + * Return true is a response is pending now. + * + * Returned Value: + * True: A response is available; False; No response is available. + * + ****************************************************************************/ + +bool netlink_check_response(FAR struct socket *psock); + +/**************************************************************************** + * Name: netlink_notify_response + * + * Description: + * Notify a thread until a response be available. The thread will be + * notified via CONFIG_NETLINK_SIGNAL when the response becomes available. + * + * Returned Value: + * Zero (OK) is returned if the response is already available. Not signal + * will be sent. + * One is returned if the notification was successfully setup. + * A negated errno value is returned on any failure. + * + ****************************************************************************/ + +int netlink_notify_response(FAR struct socket *psock); + +/**************************************************************************** + * Name: netlink_notify_cancel + * + * Description: + * Cancel a notification previously created with netlink_notify_response(). + * + * Returned Value: + * Zero (OK) is always returned. + * + ****************************************************************************/ + +int netlink_notify_cancel(FAR struct socket *psock); + /**************************************************************************** * Name: netlink_route_sendto() * diff --git a/net/netlink/netlink_conn.c b/net/netlink/netlink_conn.c index 0acc0a584d6..7b8cd369c97 100644 --- a/net/netlink/netlink_conn.c +++ b/net/netlink/netlink_conn.c @@ -143,6 +143,8 @@ static void netlink_notify_waiters(FAR struct netlink_conn_s *conn) nerr("ERROR: nxsig_kill() failed: %d\n", ret); UNUSED(ret); } + + conn->waiter[i] = NETLINK_NO_WAITER; } } @@ -178,6 +180,34 @@ static int netlink_add_waiter(FAR struct netlink_conn_s *conn) return -ENOSPC; } +/**************************************************************************** + * Name: netlink_remove_waiter + * + * Description: + * Remove a waiter to the list of waiters. + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +static int netlink_remove_waiter(FAR struct netlink_conn_s *conn, + pid_t waiter) +{ + int i; + + for (i = 0; i < CONFIG_NETLINK_MAXPENDING; i++) + { + if (conn->waiter[i] == waiter) + { + conn->waiter[i] = NETLINK_NO_WAITER; + break; + } + } + + return OK; +} + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -484,7 +514,7 @@ FAR struct netlink_response_s * ret = sigwaitinfo(&set, &info); if (ret < 0) { - nerr("ERROR: sigwaitinfo failed: %d\n", ret); + nerr("ERROR: sigwaitinfo() failed: %d\n", ret); } /* Restore the network lock */ @@ -496,4 +526,116 @@ FAR struct netlink_response_s * return resp; } +/**************************************************************************** + * Name: netlink_check_response + * + * Description: + * Return true is a response is pending now. + * + * Returned Value: + * True: A response is available; False; No response is available. + * + ****************************************************************************/ + +bool netlink_check_response(FAR struct socket *psock) +{ + FAR struct netlink_conn_s *conn; + + DEBUGASSERT(psock != NULL && psock->s_conn != NULL); + conn = (FAR struct netlink_conn_s *)psock->s_conn; + + /* Check if the response is available. It is not necessary to lock the + * network because the sq_peek() is an atomic operation. + */ + + return (sq_peek(&conn->resplist) != NULL); +} + +/**************************************************************************** + * Name: netlink_notify_response + * + * Description: + * Notify a thread until a response be available. The thread will be + * notified via CONFIG_NETLINK_SIGNAL when the response becomes available. + * + * Returned Value: + * Zero (OK) is returned if the response is already available. Not signal + * will be sent. + * One is returned if the notification was successfully setup. + * A negated errno value is returned on any failure. + * + ****************************************************************************/ + +int netlink_notify_response(FAR struct socket *psock) +{ + FAR struct netlink_conn_s *conn; + FAR struct siginfo info; + sigset_t set; + int ret = 0; + + DEBUGASSERT(psock != NULL && psock->s_conn != NULL); + conn = (FAR struct netlink_conn_s *)psock->s_conn; + + /* Check if the response is available */ + + net_lock(); + if (((FAR struct netlink_response_s *)sq_peek(&conn->resplist)) == NULL) + { + /* No.. Add this task as a waiter */ + + ret = netlink_add_waiter(conn); + if (ret < 0) + { + nerr("ERROR: netlink_add_waiter failed: %d\n", ret); + } + else + { + /* Set up to signal when a response is available */ + + sigemptyset(&set); + sigaddset(&set, CONFIG_NETLINK_SIGNAL); + ret = sigwaitinfo(&set, &info); + if (ret < 0) + { + nerr("ERROR: sigwaitinfo() failed: %d\n", ret); + } + else + { + ret = 1; + } + } + } + + net_unlock(); + return ret; +} + +/**************************************************************************** + * Name: netlink_notify_cancel + * + * Description: + * Cancel a notification previously created with netlink_notify_response(). + * + * Returned Value: + * Zero (OK) is always returned. + * + ****************************************************************************/ + +int netlink_notify_cancel(FAR struct socket *psock) +{ + FAR struct netlink_conn_s *conn; + + DEBUGASSERT(psock != NULL && psock->s_conn != NULL); + conn = (FAR struct netlink_conn_s *)psock->s_conn; + + /* Remove this thread as waiter for response notifications for this + * socket. + */ + + net_lock(); + (void)netlink_remove_waiter(conn, getpid()); + net_unlock(); + return OK; +} + #endif /* CONFIG_NET_NETLINK */ diff --git a/net/netlink/netlink_sockif.c b/net/netlink/netlink_sockif.c index f34f659d915..6934d3b47f3 100644 --- a/net/netlink/netlink_sockif.c +++ b/net/netlink/netlink_sockif.c @@ -43,10 +43,12 @@ #include #include #include +#include #include #include #include +#include #include #include "netlink/netlink.h" @@ -489,6 +491,12 @@ static int netlink_accept(FAR struct socket *psock, FAR struct sockaddr *addr, * The standard poll() operation redirects operations on socket descriptors * to this function. * + * POLLUP: Will never be reported + * POLLERR: Reported in the event of any failure. + * POLLOUT: Always reported if requested. + * POLLIN: Reported if requested but only when pending response data is + * available + * * Input Parameters: * psock - An instance of the internal socket structure. * fds - The structure describing the events to be monitored. @@ -502,8 +510,74 @@ static int netlink_accept(FAR struct socket *psock, FAR struct sockaddr *addr, static int netlink_poll(FAR struct socket *psock, FAR struct pollfd *fds, bool setup) { -#warning Missing logic for NETLINK poll - return -EOPNOTSUPP; + int ret; + + /* Check if we are setting up or tearing down the poll */ + + if (setup) + { + /* If POLLOUT is selected, return immediately (maybe) */ + + pollevent_t revents = POLLOUT; + + /* If POLLIN is selected and a response is available, return + * immediately (maybe). + */ + + net_lock(); + if (netlink_check_response(psock)) + { + revents |= POLLIN; + } + + /* But return ONLY if POLLIN and/or POLLIN are included in the + * requested event set. + */ + + revents &= fds->events; + if (revents != 0) + { + fds->revents = revents; + nxsem_post(fds->sem); + net_unlock(); + return OK; + } + + /* Set up to be notified by signal when a response is available if + * POLLIN is requested. + */ + + if ((fds->events & POLLIN) != 0) + { +#if 0 + /* Call netlink_notify_response() to receive a signal when + * a response has been queued. + * + * REVISIT: How shall we pass the FDS info to the signal + * handler? + */ + +#else +#warning Missing logic for NETLINK POLLIN + nxsem_post(fds->sem); + net_unlock(); + return -ENOSYS; +#endif + } + + /* There will not be any wakeups coming? Probably an error? */ + + net_unlock(); + ret = OK; + } + else + { + /* Cancel any response notifications */ + + ret = netlink_notify_cancel(psock); + } + + return ret; } /**************************************************************************** @@ -514,10 +588,10 @@ static int netlink_poll(FAR struct socket *psock, FAR struct pollfd *fds, * a connected state (so that the intended recipient is known). * * Input Parameters: - * psock An instance of the internal socket structure. - * buf Data to send - * len Length of data to send - * flags Send flags (ignored) + * psock - An instance of the internal socket structure. + * buf - Data to send + * len - Length of data to send + * flags - Send flags (ignored) * * Returned Value: * On success, returns the number of characters sent. On error, a negated @@ -717,7 +791,7 @@ static int netlink_close(FAR struct socket *psock) { /* Yes... inform user-space daemon of socket close. */ -#warning Missing logic +#warning Missing logic in NETLINK close() /* Free the connection structure */