nuttx/net/usrsock/usrsock_dev.c
2020-11-27 05:18:57 -06:00

1278 lines
31 KiB
C

/****************************************************************************
* net/usrsock/usrsock_dev.c
*
* Copyright (C) 2015-2017 Haltian Ltd. All rights reserved.
* Author: Jussi Kivilinna <jussi.kivilinna@haltian.com>
*
* 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 <nuttx/config.h>
#if defined(CONFIG_NET) && defined(CONFIG_NET_USRSOCK)
#include <sys/types.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <poll.h>
#include <errno.h>
#include <assert.h>
#include <debug.h>
#include <arch/irq.h>
#include <nuttx/random.h>
#include <nuttx/fs/fs.h>
#include <nuttx/semaphore.h>
#include <nuttx/net/net.h>
#include <nuttx/net/usrsock.h>
#include "devif/devif.h"
#include "usrsock/usrsock.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#ifndef CONFIG_NET_USRSOCKDEV_NPOLLWAITERS
# define CONFIG_NET_USRSOCKDEV_NPOLLWAITERS 1
#endif
/****************************************************************************
* Private Types
****************************************************************************/
struct usrsockdev_s
{
sem_t devsem; /* Lock for device node */
uint8_t ocount; /* The number of times the device has been opened */
struct
{
FAR const struct iovec *iov; /* Pending request buffers */
int iovcnt; /* Number of request buffers */
size_t pos; /* Reader position on request buffer */
sem_t sem; /* Request semaphore (only one outstanding
* request) */
sem_t acksem; /* Request acknowledgment notification */
uint8_t ack_xid; /* Exchange id for which waiting ack */
uint16_t nbusy; /* Number of requests blocked from different
* threads */
} req;
FAR struct usrsock_conn_s *datain_conn; /* Connection instance to receive
* data buffers. */
struct pollfd *pollfds[CONFIG_NET_USRSOCKDEV_NPOLLWAITERS];
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* Character driver methods */
static ssize_t usrsockdev_read(FAR struct file *filep, FAR char *buffer,
size_t len);
static ssize_t usrsockdev_write(FAR struct file *filep,
FAR const char *buffer, size_t len);
static off_t usrsockdev_seek(FAR struct file *filep, off_t offset,
int whence);
static int usrsockdev_open(FAR struct file *filep);
static int usrsockdev_close(FAR struct file *filep);
static int usrsockdev_poll(FAR struct file *filep, FAR struct pollfd *fds,
bool setup);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct file_operations g_usrsockdevops =
{
usrsockdev_open, /* open */
usrsockdev_close, /* close */
usrsockdev_read, /* read */
usrsockdev_write, /* write */
usrsockdev_seek, /* seek */
NULL, /* ioctl */
usrsockdev_poll /* poll */
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
, NULL /* unlink */
#endif
};
static struct usrsockdev_s g_usrsockdev;
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: iovec_do() - copy to/from iovec from/to buffer.
****************************************************************************/
static ssize_t iovec_do(FAR void *srcdst, size_t srcdstlen,
FAR struct iovec *iov, int iovcnt, size_t pos,
bool from_iov)
{
ssize_t total;
size_t srclen;
FAR uint8_t *ioout = srcdst;
FAR uint8_t *iovbuf;
/* Rewind to correct position. */
while (pos > 0 && iovcnt > 0)
{
if (iov->iov_len <= pos)
{
pos -= iov->iov_len;
iov++;
iovcnt--;
}
else
{
break;
}
}
if (iovcnt == 0)
{
/* Position beyond iovec. */
return -1;
}
iovbuf = iov->iov_base;
srclen = iov->iov_len;
iovbuf += pos;
srclen -= pos;
iov++;
iovcnt--;
total = 0;
while ((srclen > 0 || iovcnt > 0) && srcdstlen > 0)
{
size_t clen = srclen;
if (srclen == 0)
{
/* Skip empty iovec. */
iovbuf = iov->iov_base;
srclen = iov->iov_len;
iov++;
iovcnt--;
continue;
}
if (clen > srcdstlen)
{
clen = srcdstlen;
}
if (from_iov)
{
memmove(ioout, iovbuf, clen);
}
else
{
memmove(iovbuf, ioout, clen);
}
ioout += clen;
srcdstlen -= clen;
iovbuf += clen;
srclen -= clen;
total += clen;
if (srclen == 0)
{
if (iovcnt == 0)
{
break;
}
iovbuf = iov->iov_base;
srclen = iov->iov_len;
iov++;
iovcnt--;
}
}
return total;
}
/****************************************************************************
* Name: iovec_get() - copy from iovec to buffer.
****************************************************************************/
static ssize_t iovec_get(FAR void *dst, size_t dstlen,
FAR const struct iovec *iov, int iovcnt, size_t pos)
{
return iovec_do(dst, dstlen, (FAR struct iovec *)iov, iovcnt, pos, true);
}
/****************************************************************************
* Name: iovec_put() - copy to iovec from buffer.
****************************************************************************/
static ssize_t iovec_put(FAR struct iovec *iov, int iovcnt, size_t pos,
FAR const void *src, size_t srclen)
{
return iovec_do((FAR void *)src, srclen, iov, iovcnt, pos, false);
}
/****************************************************************************
* Name: usrsockdev_get_xid()
****************************************************************************/
static uint8_t usrsockdev_get_xid(FAR struct usrsock_conn_s *conn)
{
int conn_idx;
#if CONFIG_NET_USRSOCK_CONNS > 254
# error "CONFIG_NET_USRSOCK_CONNS too large (over 254)"
#endif
/* Each connection can one only one request/response pending. So map
* connection structure index to xid value.
*/
conn_idx = usrsock_connidx(conn);
DEBUGASSERT(1 <= conn_idx + 1);
DEBUGASSERT(conn_idx + 1 <= UINT8_MAX);
return conn_idx + 1;
}
/****************************************************************************
* Name: usrsockdev_semtake() and usrsockdev_semgive()
*
* Description:
* Take/give semaphore
*
****************************************************************************/
static int usrsockdev_semtake(FAR sem_t *sem)
{
return nxsem_wait_uninterruptible(sem);
}
static void usrsockdev_semgive(FAR sem_t *sem)
{
nxsem_post(sem);
}
/****************************************************************************
* Name: usrsockdev_is_opened
****************************************************************************/
static bool usrsockdev_is_opened(FAR struct usrsockdev_s *dev)
{
bool ret = true;
if (dev->ocount == 0)
{
ret = false; /* No usrsock daemon running. */
}
return ret;
}
/****************************************************************************
* Name: usrsockdev_pollnotify
****************************************************************************/
static void usrsockdev_pollnotify(FAR struct usrsockdev_s *dev,
pollevent_t eventset)
{
int i;
for (i = 0; i < ARRAY_SIZE(dev->pollfds); i++)
{
struct pollfd *fds = dev->pollfds[i];
if (fds)
{
fds->revents |= (fds->events & eventset);
if (fds->revents != 0)
{
ninfo("Report events: %02x\n", fds->revents);
nxsem_post(fds->sem);
}
}
}
}
/****************************************************************************
* Name: usrsockdev_read
****************************************************************************/
static ssize_t usrsockdev_read(FAR struct file *filep, FAR char *buffer,
size_t len)
{
FAR struct inode *inode = filep->f_inode;
FAR struct usrsockdev_s *dev;
int ret;
if (len == 0)
{
return 0;
}
if (buffer == NULL)
{
return -EINVAL;
}
DEBUGASSERT(inode);
dev = inode->i_private;
DEBUGASSERT(dev);
ret = usrsockdev_semtake(&dev->devsem);
if (ret < 0)
{
return ret;
}
net_lock();
/* Is request available? */
if (dev->req.iov)
{
ssize_t rlen;
/* Copy request to user-space. */
rlen = iovec_get(buffer, len, dev->req.iov, dev->req.iovcnt,
dev->req.pos);
if (rlen < 0)
{
/* Tried reading beyond buffer. */
len = 0;
}
else
{
dev->req.pos += rlen;
len = rlen;
}
}
else
{
len = 0;
}
net_unlock();
usrsockdev_semgive(&dev->devsem);
return len;
}
/****************************************************************************
* Name: usrsockdev_seek
****************************************************************************/
static off_t usrsockdev_seek(FAR struct file *filep, off_t offset,
int whence)
{
FAR struct inode *inode = filep->f_inode;
FAR struct usrsockdev_s *dev;
off_t pos;
int ret;
if (whence != SEEK_CUR && whence != SEEK_SET)
{
return -EINVAL;
}
DEBUGASSERT(inode);
dev = inode->i_private;
DEBUGASSERT(dev);
ret = usrsockdev_semtake(&dev->devsem);
if (ret < 0)
{
return ret;
}
net_lock();
/* Is request available? */
if (dev->req.iov)
{
ssize_t rlen;
if (whence == SEEK_CUR)
{
pos = dev->req.pos + offset;
}
else
{
pos = offset;
}
/* Copy request to user-space. */
rlen = iovec_get(NULL, 0, dev->req.iov, dev->req.iovcnt, pos);
if (rlen < 0)
{
/* Tried seek beyond buffer. */
pos = -EINVAL;
}
else
{
dev->req.pos = pos;
}
}
else
{
pos = 0;
}
net_unlock();
usrsockdev_semgive(&dev->devsem);
return pos;
}
/****************************************************************************
* Name: usrsockdev_handle_event
****************************************************************************/
static ssize_t usrsockdev_handle_event(FAR struct usrsockdev_s *dev,
FAR const void *buffer,
size_t len)
{
FAR const struct usrsock_message_common_s *common = buffer;
switch (common->msgid)
{
case USRSOCK_MESSAGE_SOCKET_EVENT:
{
FAR const struct usrsock_message_socket_event_s *hdr = buffer;
FAR struct usrsock_conn_s *conn;
int ret;
if (len < sizeof(*hdr))
{
nwarn("message too short, %d < %d.\n", len, sizeof(*hdr));
return -EINVAL;
}
/* Get corresponding usrsock connection. */
conn = usrsock_active(hdr->usockid);
if (!conn)
{
nwarn("no active connection for usockid=%d.\n", hdr->usockid);
return -ENOENT;
}
#ifdef CONFIG_DEV_RANDOM
/* Add randomness. */
add_sw_randomness((hdr->events << 16) - hdr->usockid);
#endif
/* Handle event. */
ret = usrsock_event(conn,
hdr->events & ~USRSOCK_EVENT_INTERNAL_MASK);
if (ret < 0)
{
return ret;
}
len = sizeof(*hdr);
}
break;
default:
nwarn("Unknown event type: %d\n", common->msgid);
return -EINVAL;
}
return len;
}
/****************************************************************************
* Name: usrsockdev_handle_response
****************************************************************************/
static ssize_t usrsockdev_handle_response(FAR struct usrsockdev_s *dev,
FAR struct usrsock_conn_s *conn,
FAR const void *buffer)
{
FAR const struct usrsock_message_req_ack_s *hdr = buffer;
if (USRSOCK_MESSAGE_REQ_IN_PROGRESS(hdr->head.flags))
{
/* In-progress response is acknowledgment that response was
* received.
*/
conn->resp.inprogress = true;
}
else
{
conn->resp.inprogress = false;
conn->resp.xid = 0;
/* Get result for common request. */
conn->resp.result = hdr->result;
/* Done with request/response. */
usrsock_event(conn, USRSOCK_EVENT_REQ_COMPLETE);
}
return sizeof(*hdr);
}
/****************************************************************************
* Name: usrsockdev_handle_datareq_response
****************************************************************************/
static ssize_t
usrsockdev_handle_datareq_response(FAR struct usrsockdev_s *dev,
FAR struct usrsock_conn_s *conn,
FAR const void *buffer)
{
FAR const struct usrsock_message_datareq_ack_s *datahdr = buffer;
FAR const struct usrsock_message_req_ack_s *hdr = &datahdr->reqack;
int num_inbufs;
int iovpos;
ssize_t ret;
if (USRSOCK_MESSAGE_REQ_IN_PROGRESS(hdr->head.flags))
{
if (datahdr->reqack.result > 0)
{
ninfo("error: request in progress, and result > 0.\n");
ret = -EINVAL;
goto unlock_out;
}
else if (datahdr->valuelen > 0)
{
ninfo("error: request in progress, and valuelen > 0.\n");
ret = -EINVAL;
goto unlock_out;
}
/* In-progress response is acknowledgment that response was
* received.
*/
conn->resp.inprogress = true;
ret = sizeof(*datahdr);
goto unlock_out;
}
conn->resp.inprogress = false;
conn->resp.xid = 0;
/* Prepare to read buffers. */
conn->resp.result = hdr->result;
conn->resp.valuelen = datahdr->valuelen;
conn->resp.valuelen_nontrunc = datahdr->valuelen_nontrunc;
if (conn->resp.result < 0)
{
/* Error, valuelen must be zero. */
if (datahdr->valuelen > 0 || datahdr->valuelen_nontrunc > 0)
{
nerr("error: response result negative, and valuelen or "
"valuelen_nontrunc non-zero.\n");
ret = -EINVAL;
goto unlock_out;
}
/* Done with request/response. */
usrsock_event(conn, USRSOCK_EVENT_REQ_COMPLETE);
ret = sizeof(*datahdr);
goto unlock_out;
}
/* Check that number of buffers match available. */
num_inbufs = (hdr->result > 0) + 1;
if (conn->resp.datain.iovcnt < num_inbufs)
{
nwarn("not enough recv buffers (need: %d, have: %d).\n", num_inbufs,
conn->resp.datain.iovcnt);
ret = -EINVAL;
goto unlock_out;
}
/* Adjust length of receiving buffers. */
conn->resp.datain.total = 0;
iovpos = 0;
/* Value buffer is always the first */
if (conn->resp.datain.iov[iovpos].iov_len < datahdr->valuelen)
{
nwarn("%dth buffer not large enough (need: %d, have: %d).\n",
iovpos,
datahdr->valuelen,
conn->resp.datain.iov[iovpos].iov_len);
ret = -EINVAL;
goto unlock_out;
}
/* Adjust read size. */
conn->resp.datain.iov[iovpos].iov_len = datahdr->valuelen;
conn->resp.datain.total += conn->resp.datain.iov[iovpos].iov_len;
iovpos++;
if (hdr->result > 0)
{
/* Value buffer is always the first */
if (conn->resp.datain.iov[iovpos].iov_len < hdr->result)
{
nwarn("%dth buffer not large enough "
"(need: %" PRId32 ", have: %d).\n",
iovpos,
hdr->result,
conn->resp.datain.iov[iovpos].iov_len);
ret = -EINVAL;
goto unlock_out;
}
/* Adjust read size. */
conn->resp.datain.iov[iovpos].iov_len = hdr->result;
conn->resp.datain.total += conn->resp.datain.iov[iovpos].iov_len;
iovpos++;
}
DEBUGASSERT(num_inbufs == iovpos);
conn->resp.datain.iovcnt = num_inbufs;
/* Next written buffers are redirected to data buffers. */
dev->datain_conn = conn;
ret = sizeof(*datahdr);
unlock_out:
return ret;
}
/****************************************************************************
* Name: usrsockdev_handle_req_response
****************************************************************************/
static ssize_t usrsockdev_handle_req_response(FAR struct usrsockdev_s *dev,
FAR const void *buffer,
size_t len)
{
FAR const struct usrsock_message_req_ack_s *hdr = buffer;
FAR struct usrsock_conn_s *conn;
unsigned int hdrlen;
ssize_t ret;
ssize_t (*handle_response)(FAR struct usrsockdev_s *dev,
FAR struct usrsock_conn_s *conn,
FAR const void *buffer);
switch (hdr->head.msgid)
{
case USRSOCK_MESSAGE_RESPONSE_ACK:
hdrlen = sizeof(struct usrsock_message_req_ack_s);
handle_response = &usrsockdev_handle_response;
break;
case USRSOCK_MESSAGE_RESPONSE_DATA_ACK:
hdrlen = sizeof(struct usrsock_message_datareq_ack_s);
handle_response = &usrsockdev_handle_datareq_response;
break;
default:
nwarn("unknown message type: %d, flags: %d, xid: %02x, "
"result: %" PRId32 "\n",
hdr->head.msgid, hdr->head.flags, hdr->xid, hdr->result);
return -EINVAL;
}
if (len < hdrlen)
{
nwarn("message too short, %d < %d.\n", len, hdrlen);
return -EINVAL;
}
net_lock();
/* Get corresponding usrsock connection for this transfer */
conn = usrsock_nextconn(NULL);
while (conn)
{
if (conn->resp.xid == hdr->xid)
break;
conn = usrsock_nextconn(conn);
}
if (!conn)
{
/* No connection waiting for this message. */
nwarn("Could find connection waiting for response with xid=%d\n",
hdr->xid);
ret = -EINVAL;
goto unlock_out;
}
if (dev->req.ack_xid == hdr->xid && dev->req.iov)
{
/* Signal that request was received and read by daemon and
* acknowledgment response was received.
*/
dev->req.iov = NULL;
nxsem_post(&dev->req.acksem);
}
ret = handle_response(dev, conn, buffer);
unlock_out:
net_unlock();
return ret;
}
/****************************************************************************
* Name: usrsockdev_handle_message
****************************************************************************/
static ssize_t usrsockdev_handle_message(FAR struct usrsockdev_s *dev,
FAR const void *buffer,
size_t len)
{
FAR const struct usrsock_message_common_s *common = buffer;
if (USRSOCK_MESSAGE_IS_EVENT(common->flags))
{
return usrsockdev_handle_event(dev, buffer, len);
}
if (USRSOCK_MESSAGE_IS_REQ_RESPONSE(common->flags))
{
return usrsockdev_handle_req_response(dev, buffer, len);
}
return -EINVAL;
}
/****************************************************************************
* Name: usrsockdev_write
****************************************************************************/
static ssize_t usrsockdev_write(FAR struct file *filep,
FAR const char *buffer, size_t len)
{
FAR struct inode *inode = filep->f_inode;
FAR struct usrsock_conn_s *conn;
FAR struct usrsockdev_s *dev;
size_t origlen = len;
ssize_t ret = 0;
if (len == 0)
{
return 0;
}
if (buffer == NULL)
{
return -EINVAL;
}
DEBUGASSERT(inode);
dev = inode->i_private;
DEBUGASSERT(dev);
ret = (ssize_t)usrsockdev_semtake(&dev->devsem);
if (ret < 0)
{
return ret;
}
if (!dev->datain_conn)
{
/* Start of message, buffer length should be at least size of common
* message header.
*/
if (len < sizeof(struct usrsock_message_common_s))
{
nwarn("message too short, %d < %d.\n", len,
sizeof(struct usrsock_message_common_s));
ret = -EINVAL;
goto errout;
}
/* Handle message. */
ret = usrsockdev_handle_message(dev, buffer, len);
if (ret >= 0)
{
buffer += ret;
len -= ret;
ret = origlen - len;
}
}
/* Data input handling. */
if (dev->datain_conn)
{
conn = dev->datain_conn;
/* Copy data from user-space. */
ret = iovec_put(conn->resp.datain.iov, conn->resp.datain.iovcnt,
conn->resp.datain.pos, buffer, len);
if (ret < 0)
{
/* Tried writing beyond buffer. */
ret = -EINVAL;
conn->resp.result = -EINVAL;
conn->resp.datain.pos =
conn->resp.datain.total;
}
else
{
conn->resp.datain.pos += ret;
buffer += ret;
len -= ret;
ret = origlen - len;
}
if (conn->resp.datain.pos == conn->resp.datain.total)
{
dev->datain_conn = NULL;
/* Done with data response. */
usrsock_event(conn, USRSOCK_EVENT_REQ_COMPLETE);
}
}
errout:
usrsockdev_semgive(&dev->devsem);
return ret;
}
/****************************************************************************
* Name: usrsockdev_open
****************************************************************************/
static int usrsockdev_open(FAR struct file *filep)
{
FAR struct inode *inode = filep->f_inode;
FAR struct usrsockdev_s *dev;
int ret;
int tmp;
DEBUGASSERT(inode);
dev = inode->i_private;
DEBUGASSERT(dev);
ret = usrsockdev_semtake(&dev->devsem);
if (ret < 0)
{
return ret;
}
ninfo("opening /dev/usrsock\n");
/* Increment the count of references to the device. */
tmp = dev->ocount + 1;
if (tmp > 1)
{
/* Only one reference is allowed. */
nwarn("failed to open\n");
ret = -EPERM;
}
else
{
dev->ocount = tmp;
ret = OK;
}
usrsockdev_semgive(&dev->devsem);
return ret;
}
/****************************************************************************
* Name: usrsockdev_close
****************************************************************************/
static int usrsockdev_close(FAR struct file *filep)
{
FAR struct inode *inode = filep->f_inode;
FAR struct usrsockdev_s *dev;
FAR struct usrsock_conn_s *conn;
int ret;
DEBUGASSERT(inode);
dev = inode->i_private;
DEBUGASSERT(dev);
ret = usrsockdev_semtake(&dev->devsem);
if (ret < 0)
{
return ret;
}
ninfo("closing /dev/usrsock\n");
/* Set active usrsock sockets to aborted state. */
conn = usrsock_nextconn(NULL);
while (conn)
{
net_lock();
conn->resp.inprogress = false;
conn->resp.xid = 0;
usrsock_event(conn, USRSOCK_EVENT_ABORT);
net_unlock();
conn = usrsock_nextconn(conn);
}
net_lock();
/* Decrement the references to the driver. */
dev->ocount--;
DEBUGASSERT(dev->ocount == 0);
ret = OK;
do
{
/* Give other threads short time window to complete recently completed
* requests.
*/
ret = net_timedwait(&dev->req.sem, 10);
if (ret < 0)
{
if (ret != -ETIMEDOUT && ret != -EINTR)
{
ninfo("net_timedwait errno: %d\n", ret);
DEBUGASSERT(false);
}
}
else
{
usrsockdev_semgive(&dev->req.sem);
}
/* Wake-up pending requests. */
if (dev->req.nbusy == 0)
{
break;
}
dev->req.iov = NULL;
nxsem_post(&dev->req.acksem);
}
while (true);
net_unlock();
/* Check if request line is active */
if (dev->req.iov != NULL)
{
dev->req.iov = NULL;
}
usrsockdev_semgive(&dev->devsem);
return ret;
}
/****************************************************************************
* Name: usrsockdev_poll
****************************************************************************/
static int usrsockdev_poll(FAR struct file *filep, FAR struct pollfd *fds,
bool setup)
{
FAR struct inode *inode = filep->f_inode;
FAR struct usrsockdev_s *dev;
pollevent_t eventset;
int ret;
int i;
DEBUGASSERT(inode);
dev = inode->i_private;
DEBUGASSERT(dev);
/* Some sanity checking */
if (!dev || !fds)
{
return -ENODEV;
}
/* Are we setting up the poll? Or tearing it down? */
ret = usrsockdev_semtake(&dev->devsem);
if (ret < 0)
{
return ret;
}
net_lock();
if (setup)
{
/* This is a request to set up the poll. Find an available
* slot for the poll structure reference
*/
for (i = 0; i < ARRAY_SIZE(dev->pollfds); i++)
{
/* Find an available slot */
if (!dev->pollfds[i])
{
/* Bind the poll structure and this slot */
dev->pollfds[i] = fds;
fds->priv = &dev->pollfds[i];
break;
}
}
if (i >= ARRAY_SIZE(dev->pollfds))
{
fds->priv = NULL;
ret = -EBUSY;
goto errout;
}
/* Should immediately notify on any of the requested events? */
eventset = 0;
/* Notify the POLLIN event if pending request. */
if (dev->req.iov != NULL &&
!(iovec_get(NULL, 0, dev->req.iov,
dev->req.iovcnt, dev->req.pos) < 0))
{
eventset |= POLLIN;
}
if (eventset)
{
usrsockdev_pollnotify(dev, eventset);
}
}
else
{
/* This is a request to tear down the poll. */
FAR struct pollfd **slot = (FAR struct pollfd **)fds->priv;
if (!slot)
{
ret = -EIO;
goto errout;
}
/* Remove all memory of the poll setup */
*slot = NULL;
fds->priv = NULL;
}
errout:
net_unlock();
usrsockdev_semgive(&dev->devsem);
return ret;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: usrsockdev_do_request
****************************************************************************/
int usrsockdev_do_request(FAR struct usrsock_conn_s *conn,
FAR struct iovec *iov, unsigned int iovcnt)
{
FAR struct usrsockdev_s *dev = conn->dev;
FAR struct usrsock_request_common_s *req_head = iov[0].iov_base;
if (!dev)
{
/* Setup conn for new usrsock device. */
dev = &g_usrsockdev;
conn->dev = dev;
}
if (!usrsockdev_is_opened(dev))
{
ninfo("usockid=%d; daemon has closed /dev/usrsock.\n", conn->usockid);
return -ENETDOWN;
}
/* Get exchange id. */
req_head->xid = usrsockdev_get_xid(conn);
/* Prepare connection for response. */
conn->resp.xid = req_head->xid;
conn->resp.result = -EACCES;
++dev->req.nbusy; /* net_lock held. */
/* Set outstanding request for daemon to handle. */
net_lockedwait_uninterruptible(&dev->req.sem);
if (usrsockdev_is_opened(dev))
{
DEBUGASSERT(dev->req.iov == NULL);
dev->req.ack_xid = req_head->xid;
dev->req.iov = iov;
dev->req.pos = 0;
dev->req.iovcnt = iovcnt;
/* Notify daemon of new request. */
usrsockdev_pollnotify(dev, POLLIN);
/* Wait ack for request. */
net_lockedwait_uninterruptible(&dev->req.acksem);
}
else
{
ninfo("usockid=%d; daemon abruptly closed /dev/usrsock.\n",
conn->usockid);
}
/* Free request line for next command. */
usrsockdev_semgive(&dev->req.sem);
--dev->req.nbusy; /* net_lock held. */
return OK;
}
/****************************************************************************
* Name: usrsockdev_register
*
* Description:
* Register /dev/usrsock
*
****************************************************************************/
void usrsockdev_register(void)
{
/* Initialize device private structure. */
g_usrsockdev.ocount = 0;
g_usrsockdev.req.nbusy = 0;
nxsem_init(&g_usrsockdev.devsem, 0, 1);
nxsem_init(&g_usrsockdev.req.sem, 0, 1);
nxsem_init(&g_usrsockdev.req.acksem, 0, 0);
nxsem_set_protocol(&g_usrsockdev.req.acksem, SEM_PRIO_NONE);
register_driver("/dev/usrsock", &g_usrsockdevops, 0666,
&g_usrsockdev);
}
#endif /* CONFIG_NET && CONFIG_NET_USRSOCK */