| /**************************************************************************** |
| * drivers/usrsock/usrsock_dev.c |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| #if defined(CONFIG_NET_USRSOCK_DEVICE) |
| |
| #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 <nuttx/debug.h> |
| |
| #include <arch/irq.h> |
| |
| #include <nuttx/random.h> |
| #include <nuttx/fs/fs.h> |
| #include <nuttx/mutex.h> |
| #include <nuttx/net/net.h> |
| #include <nuttx/net/usrsock.h> |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| #ifndef CONFIG_NET_USRSOCKDEV_NPOLLWAITERS |
| # define CONFIG_NET_USRSOCKDEV_NPOLLWAITERS 1 |
| #endif |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| struct usrsockdev_s |
| { |
| mutex_t devlock; /* 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 */ |
| } req; |
| FAR 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 */ |
| NULL, /* mmap */ |
| NULL, /* truncate */ |
| usrsockdev_poll /* poll */ |
| }; |
| |
| static struct usrsockdev_s g_usrsockdev = |
| { |
| NXMUTEX_INITIALIZER |
| }; |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * 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_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; |
| } |
| |
| dev = inode->i_private; |
| |
| DEBUGASSERT(dev); |
| |
| ret = nxmutex_lock(&dev->devlock); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| /* Is request available? */ |
| |
| if (dev->req.iov) |
| { |
| ssize_t rlen; |
| |
| /* Copy request to user-space. */ |
| |
| rlen = usrsock_iovec_get(buffer, len, dev->req.iov, dev->req.iovcnt, |
| dev->req.pos, NULL); |
| if (rlen < 0) |
| { |
| /* Tried reading beyond buffer. */ |
| |
| len = 0; |
| } |
| else |
| { |
| dev->req.pos += rlen; |
| len = rlen; |
| } |
| } |
| else |
| { |
| len = 0; |
| } |
| |
| nxmutex_unlock(&dev->devlock); |
| 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; |
| } |
| |
| dev = inode->i_private; |
| |
| DEBUGASSERT(dev); |
| |
| ret = nxmutex_lock(&dev->devlock); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| /* 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 = usrsock_iovec_get(NULL, 0, dev->req.iov, dev->req.iovcnt, |
| pos, NULL); |
| if (rlen < 0) |
| { |
| /* Tried seek beyond buffer. */ |
| |
| pos = -EINVAL; |
| } |
| else |
| { |
| dev->req.pos = pos; |
| } |
| } |
| else |
| { |
| pos = 0; |
| } |
| |
| nxmutex_unlock(&dev->devlock); |
| return pos; |
| } |
| |
| /**************************************************************************** |
| * 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 usrsockdev_s *dev; |
| bool req_done = false; |
| ssize_t ret = 0; |
| |
| if (len == 0) |
| { |
| return 0; |
| } |
| |
| if (buffer == NULL) |
| { |
| return -EINVAL; |
| } |
| |
| dev = inode->i_private; |
| |
| DEBUGASSERT(dev); |
| |
| ret = nxmutex_lock(&dev->devlock); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| ret = usrsock_response(buffer, len, &req_done); |
| if (req_done && dev->req.iov) |
| { |
| dev->req.iov = NULL; |
| dev->req.pos = 0; |
| dev->req.iovcnt = 0; |
| } |
| |
| nxmutex_unlock(&dev->devlock); |
| 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; |
| |
| dev = inode->i_private; |
| |
| DEBUGASSERT(dev); |
| |
| ret = nxmutex_lock(&dev->devlock); |
| 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; |
| } |
| |
| nxmutex_unlock(&dev->devlock); |
| 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; |
| int ret; |
| |
| dev = inode->i_private; |
| |
| DEBUGASSERT(dev); |
| |
| ret = nxmutex_lock(&dev->devlock); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| ninfo("closing /dev/usrsock\n"); |
| |
| /* Decrement the references to the driver. */ |
| |
| dev->ocount--; |
| DEBUGASSERT(dev->ocount == 0); |
| ret = OK; |
| dev->req.iov = NULL; |
| dev->req.iovcnt = 0; |
| dev->req.pos = 0; |
| |
| nxmutex_unlock(&dev->devlock); |
| usrsock_abort(); |
| |
| 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; |
| int ret; |
| int i; |
| |
| 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 = nxmutex_lock(&dev->devlock); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| if (setup) |
| { |
| /* This is a request to set up the poll. Find an available |
| * slot for the poll structure reference |
| */ |
| |
| for (i = 0; i < nitems(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 >= nitems(dev->pollfds)) |
| { |
| fds->priv = NULL; |
| ret = -EBUSY; |
| goto errout; |
| } |
| |
| /* Should immediately notify on any of the requested events? */ |
| |
| /* Notify the POLLIN event if pending request. */ |
| |
| if (dev->req.iov != NULL && |
| !(usrsock_iovec_get(NULL, 0, dev->req.iov, |
| dev->req.iovcnt, dev->req.pos, NULL) < 0)) |
| { |
| poll_notify(&fds, 1, POLLIN); |
| } |
| } |
| 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: |
| nxmutex_unlock(&dev->devlock); |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: usrsock_request |
| ****************************************************************************/ |
| |
| int usrsock_request(FAR struct iovec *iov, unsigned int iovcnt) |
| { |
| FAR struct usrsockdev_s *dev = &g_usrsockdev; |
| int ret = 0; |
| |
| /* Set outstanding request for daemon to handle. */ |
| |
| usrsock_mutex_timedlock(&dev->devlock, UINT_MAX); |
| |
| if (usrsockdev_is_opened(dev)) |
| { |
| DEBUGASSERT(dev->req.iov == NULL); |
| dev->req.iov = iov; |
| dev->req.pos = 0; |
| dev->req.iovcnt = iovcnt; |
| |
| /* Notify daemon of new request. */ |
| |
| poll_notify(dev->pollfds, nitems(dev->pollfds), POLLIN); |
| } |
| else |
| { |
| ninfo("daemon abruptly closed /dev/usrsock.\n"); |
| ret = -ENETDOWN; |
| } |
| |
| nxmutex_unlock(&dev->devlock); |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: usrsock_register |
| * |
| * Description: |
| * Register /dev/usrsock |
| * |
| ****************************************************************************/ |
| |
| void usrsock_register(void) |
| { |
| register_driver("/dev/usrsock", &g_usrsockdevops, 0666, |
| &g_usrsockdev); |
| } |
| |
| #endif /* CONFIG_NET_USRSOCK_DEVICE */ |