6a3c2aded6
* Simplify EINTR/ECANCEL error handling 1. Add semaphore uninterruptible wait function 2 .Replace semaphore wait loop with a single uninterruptible wait 3. Replace all sem_xxx to nxsem_xxx * Unify the void cast usage 1. Remove void cast for function because many place ignore the returned value witout cast 2. Replace void cast for variable with UNUSED macro
1257 lines
31 KiB
C
1257 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 <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"
|
|
|
|
/****************************************************************************
|
|
* 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 void usrsockdev_semtake(FAR sem_t *sem)
|
|
{
|
|
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;
|
|
|
|
if (len == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (buffer == NULL)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
DEBUGASSERT(inode);
|
|
|
|
dev = inode->i_private;
|
|
|
|
DEBUGASSERT(dev);
|
|
|
|
usrsockdev_semtake(&dev->devsem);
|
|
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;
|
|
|
|
if (whence != SEEK_CUR && whence != SEEK_SET)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
DEBUGASSERT(inode);
|
|
|
|
dev = inode->i_private;
|
|
|
|
DEBUGASSERT(dev);
|
|
|
|
usrsockdev_semtake(&dev->devsem);
|
|
net_lock();
|
|
|
|
/* Is request available? */
|
|
|
|
if (dev->req.iov)
|
|
{
|
|
ssize_t rlen;
|
|
|
|
if (whence == SEEK_CUR)
|
|
{
|
|
pos = dev->req.pos + offset;
|
|
}
|
|
else if (whence == SEEK_SET)
|
|
{
|
|
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: %d, 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: %d\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);
|
|
|
|
usrsockdev_semtake(&dev->devsem);
|
|
|
|
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);
|
|
|
|
usrsockdev_semtake(&dev->devsem);
|
|
|
|
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;
|
|
struct timespec abstime;
|
|
int ret;
|
|
|
|
DEBUGASSERT(inode);
|
|
|
|
dev = inode->i_private;
|
|
|
|
DEBUGASSERT(dev);
|
|
|
|
usrsockdev_semtake(&dev->devsem);
|
|
|
|
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.
|
|
*/
|
|
|
|
DEBUGVERIFY(clock_gettime(CLOCK_REALTIME, &abstime));
|
|
|
|
abstime.tv_sec += 0;
|
|
abstime.tv_nsec += 10 * NSEC_PER_MSEC;
|
|
if (abstime.tv_nsec >= NSEC_PER_SEC)
|
|
{
|
|
abstime.tv_sec++;
|
|
abstime.tv_nsec -= NSEC_PER_SEC;
|
|
}
|
|
|
|
ret = net_timedwait(&dev->req.sem, &abstime);
|
|
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 = OK;
|
|
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? */
|
|
|
|
usrsockdev_semtake(&dev->devsem);
|
|
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;
|
|
int ret;
|
|
|
|
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);
|
|
ret = -ESHUTDOWN;
|
|
}
|
|
|
|
/* Free request line for next command. */
|
|
|
|
usrsockdev_semgive(&dev->req.sem);
|
|
|
|
--dev->req.nbusy; /* net_lock held. */
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* 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_setprotocol(&g_usrsockdev.req.acksem, SEM_PRIO_NONE);
|
|
|
|
register_driver("/dev/usrsock", &g_usrsockdevops, 0666,
|
|
&g_usrsockdev);
|
|
}
|
|
|
|
#endif /* CONFIG_NET && CONFIG_NET_USRSOCK */
|