diff options
author | px4dev <px4@purgatory.org> | 2012-08-04 15:12:36 -0700 |
---|---|---|
committer | px4dev <px4@purgatory.org> | 2012-08-04 15:12:36 -0700 |
commit | 8a365179eafdf3aea98e60ab9f5882b200d4c759 (patch) | |
tree | 4f38d6d4cd80bd0b6e22e2bb534c3f117ce44e56 /nuttx/drivers/pipes | |
download | px4-firmware-8a365179eafdf3aea98e60ab9f5882b200d4c759.tar.gz px4-firmware-8a365179eafdf3aea98e60ab9f5882b200d4c759.tar.bz2 px4-firmware-8a365179eafdf3aea98e60ab9f5882b200d4c759.zip |
Fresh import of the PX4 firmware sources.
Diffstat (limited to 'nuttx/drivers/pipes')
-rw-r--r-- | nuttx/drivers/pipes/Kconfig | 4 | ||||
-rw-r--r-- | nuttx/drivers/pipes/Make.defs | 46 | ||||
-rw-r--r-- | nuttx/drivers/pipes/fifo.c | 139 | ||||
-rw-r--r-- | nuttx/drivers/pipes/pipe.c | 286 | ||||
-rw-r--r-- | nuttx/drivers/pipes/pipe_common.c | 682 | ||||
-rw-r--r-- | nuttx/drivers/pipes/pipe_common.h | 139 |
6 files changed, 1296 insertions, 0 deletions
diff --git a/nuttx/drivers/pipes/Kconfig b/nuttx/drivers/pipes/Kconfig new file mode 100644 index 000000000..ae2bf3130 --- /dev/null +++ b/nuttx/drivers/pipes/Kconfig @@ -0,0 +1,4 @@ +# +# For a description of the syntax of this configuration file, +# see misc/tools/kconfig-language.txt. +# diff --git a/nuttx/drivers/pipes/Make.defs b/nuttx/drivers/pipes/Make.defs new file mode 100644 index 000000000..836505481 --- /dev/null +++ b/nuttx/drivers/pipes/Make.defs @@ -0,0 +1,46 @@ +############################################################################ +# drivers/pipes/Make.defs +# +# Copyright (C) 2009, 2011 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt <gnutt@nuttx.org> +# +# 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. +# +############################################################################ + +ifneq ($(CONFIG_NFILE_DESCRIPTORS),0) + +# Include pipe driver + +CSRCS += pipe.c fifo.c pipe_common.c + +# Include pipe build support + +DEPPATH += --dep-path pipes +VPATH += :pipes +endif diff --git a/nuttx/drivers/pipes/fifo.c b/nuttx/drivers/pipes/fifo.c new file mode 100644 index 000000000..03aafd0f0 --- /dev/null +++ b/nuttx/drivers/pipes/fifo.c @@ -0,0 +1,139 @@ +/**************************************************************************** + * drivers/pipes/fifo.c + * + * Copyright (C) 2008-2009 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <gnutt@nuttx.org> + * + * 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> + +#include <sys/types.h> +#include <stdint.h> +#include <nuttx/fs/fs.h> +#include <errno.h> + +#include "pipe_common.h" + +#if CONFIG_DEV_PIPE_SIZE > 0 + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations fifo_fops = +{ + pipecommon_open, /* open */ + pipecommon_close, /* close */ + pipecommon_read, /* read */ + pipecommon_write, /* write */ + 0, /* seek */ + 0 /* ioctl */ +#ifndef CONFIG_DISABLE_POLL + , pipecommon_poll /* poll */ +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mkfifo + * + * Description: + * mkfifo() makes a FIFO device driver file with name 'pathname.' Unlike + * Linux, a NuttX FIFO is not a special file type but simply a device driver + * instance. 'mode' specifies the FIFO's permissions. + * + * Once the FIFO has been created by mkfifo(), any thread can open it for + * reading or writing, in the same way as an ordinary file. However, it must + * have been opened from both reading and writing before input or output + * can be performed. This FIFO implementation will block all attempts to + * open a FIFO read-only until at least one thread has opened the FIFO for + * writing. + * + * If all threads that write to the FIFO have closed, subsequent calls to + * read() on the FIFO will return 0 (end-of-file). + * + * Inputs: + * pathname - The full path to the FIFO instance to attach to or to create + * (if not already created). + * mode - Ignored for now + * + * Return: + * 0 is returned on success; otherwise, -1 is returned with errno set + * appropriately. + * + ****************************************************************************/ + +int mkfifo(FAR const char *pathname, mode_t mode) +{ + struct pipe_dev_s *dev; + int ret; + + /* Allocate and initialize a new device structure instance */ + + dev = pipecommon_allocdev(); + if (!dev) + { + return -ENOMEM; + } + + ret = register_driver(pathname, &fifo_fops, mode, (void*)dev); + if (ret != 0) + { + pipecommon_freedev(dev); + } + + return ret; +} + +#endif /* CONFIG_DEV_PIPE_SIZE > 0 */ diff --git a/nuttx/drivers/pipes/pipe.c b/nuttx/drivers/pipes/pipe.c new file mode 100644 index 000000000..20c160475 --- /dev/null +++ b/nuttx/drivers/pipes/pipe.c @@ -0,0 +1,286 @@ +/**************************************************************************** + * drivers/pipes/pipe.c + * + * Copyright (C) 2008-2009 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <gnutt@nuttx.org> + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Compilation Switches + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <sys/types.h> +#include <nuttx/fs/fs.h> +#include <stdio.h> +#include <unistd.h> +#include <semaphore.h> +#include <fcntl.h> +#include <errno.h> + +#include "pipe_common.h" + +#if CONFIG_DEV_PIPE_SIZE > 0 + +/**************************************************************************** + * Definitions + ****************************************************************************/ + +#define MAX_PIPES 32 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int pipe_close(FAR struct file *filep); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations pipe_fops = +{ + pipecommon_open, /* open */ + pipe_close, /* close */ + pipecommon_read, /* read */ + pipecommon_write, /* write */ + 0, /* seek */ + 0 /* ioctl */ +#ifndef CONFIG_DISABLE_POLL + , pipecommon_poll /* poll */ +#endif +}; + +static sem_t g_pipesem = { 1 }; +static uint32_t g_pipeset = 0; +static uint32_t g_pipecreated = 0; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pipe_allocate + ****************************************************************************/ + +static inline int pipe_allocate(void) +{ + int pipeno; + int ret = -ENFILE; + + for (pipeno = 0; pipeno < MAX_PIPES; pipeno++) + { + if ((g_pipeset & (1 << pipeno)) == 0) + { + g_pipeset |= (1 << pipeno); + ret = pipeno; + break; + } + } + + return ret; +} + +/**************************************************************************** + * Name: pipe_free + ****************************************************************************/ + +static inline void pipe_free(int pipeno) +{ + int ret = sem_wait(&g_pipesem); + if (ret == 0) + { + g_pipeset &= ~(1 << pipeno); + (void)sem_post(&g_pipesem); + } +} + +/**************************************************************************** + * Name: pipe_close + ****************************************************************************/ + +static int pipe_close(FAR struct file *filep) +{ + struct inode *inode = filep->f_inode; + struct pipe_dev_s *dev = inode->i_private; + int ret; + + /* Some sanity checking */ +#if CONFIG_DEBUG + if (!dev) + { + return -EBADF; + } +#endif + + /* Perform common close operations */ + + ret = pipecommon_close(filep); + if (ret == 0 && dev->d_refs == 0) + { + /* Release the pipe when there are no further open references to it. */ + + pipe_free(dev->d_pipeno); + } + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pipe + * + * Description: + * pipe() creates a pair of file descriptors, pointing to a pipe inode, and + * places them in the array pointed to by 'filedes'. filedes[0] is for reading, + * filedes[1] is for writing. + * + * Inputs: + * filedes[2] - The user provided array in which to catch the pipe file + * descriptors + * + * Return: + * 0 is returned on success; otherwise, -1 is returned with errno set + * appropriately. + * + ****************************************************************************/ + +int pipe(int filedes[2]) +{ + struct pipe_dev_s *dev = NULL; + char devname[16]; + int pipeno; + int err; + int ret; + + /* Get exclusive access to the pipe allocation data */ + + ret = sem_wait(&g_pipesem); + if (ret < 0) + { + /* sem_wait() will have already set errno */ + + return ERROR; + } + + /* Allocate a minor number for the pipe device */ + + pipeno = pipe_allocate(); + if (pipeno < 0) + { + (void)sem_post(&g_pipesem); + err = -pipeno; + goto errout; + } + + /* Create a pathname to the pipe device */ + + sprintf(devname, "/dev/pipe%d", pipeno); + + /* Check if the pipe device has already been created */ + + if ((g_pipecreated & (1 << pipeno)) == 0) + { + /* No.. Allocate and initialize a new device structure instance */ + + dev = pipecommon_allocdev(); + if (!dev) + { + (void)sem_post(&g_pipesem); + err = ENOMEM; + goto errout_with_pipe; + } + + dev->d_pipeno = pipeno; + + /* Register the pipe device */ + + ret = register_driver(devname, &pipe_fops, 0666, (void*)dev); + if (ret != 0) + { + (void)sem_post(&g_pipesem); + err = -ret; + goto errout_with_dev; + } + + /* Remember that we created this device */ + + g_pipecreated |= (1 << pipeno); + } + + (void)sem_post(&g_pipesem); + + /* Get a write file descriptor */ + + filedes[1] = open(devname, O_WRONLY); + if (filedes[1] < 0) + { + err = -filedes[1]; + goto errout_with_driver; + } + + /* Get a read file descriptor */ + + filedes[0] = open(devname, O_RDONLY); + if (filedes[0] < 0) + { + err = -filedes[0]; + goto errout_with_wrfd; + } + + return OK; + +errout_with_wrfd: + close(filedes[1]); +errout_with_driver: + unregister_driver(devname); +errout_with_dev: + pipecommon_freedev(dev); +errout_with_pipe: + pipe_free(pipeno); +errout: + errno = err; + return ERROR; +} + +#endif /* CONFIG_DEV_PIPE_SIZE > 0 */ diff --git a/nuttx/drivers/pipes/pipe_common.c b/nuttx/drivers/pipes/pipe_common.c new file mode 100644 index 000000000..5f61fdd8e --- /dev/null +++ b/nuttx/drivers/pipes/pipe_common.c @@ -0,0 +1,682 @@ +/**************************************************************************** + * drivers/pipes/pipe_common.c + * + * Copyright (C) 2008-2009, 2011 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <gnutt@nuttx.org> + * + * 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> + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdint.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <sched.h> +#include <semaphore.h> +#include <fcntl.h> +#include <errno.h> +#include <assert.h> +#include <debug.h> + +#include <nuttx/kmalloc.h> +#include <nuttx/fs/fs.h> +#if CONFIG_DEBUG +# include <nuttx/arch.h> +#endif + +#include "pipe_common.h" + +#if CONFIG_DEV_PIPE_SIZE > 0 + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* CONFIG_DEV_PIPEDUMP will dump the contents of each transfer into and out + * of the pipe. + */ + +#ifdef CONFIG_DEV_PIPEDUMP +# define pipe_dumpbuffer(m,a,n) lib_dumpbuffer(m,a,n) +#else +# define pipe_dumpbuffer(m,a,n) +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void pipecommon_semtake(sem_t *sem); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pipecommon_semtake + ****************************************************************************/ + +static void pipecommon_semtake(sem_t *sem) +{ + while (sem_wait(sem) != 0) + { + /* The only case that an error should occur here is if the wait was + * awakened by a signal. + */ + + ASSERT(errno == EINTR); + } +} + +/**************************************************************************** + * Name: pipecommon_pollnotify + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_POLL +static void pipecommon_pollnotify(FAR struct pipe_dev_s *dev, pollevent_t eventset) +{ + int i; + + for (i = 0; i < CONFIG_DEV_PIPE_NPOLLWAITERS; i++) + { + struct pollfd *fds = dev->d_fds[i]; + if (fds) + { + fds->revents |= (fds->events & eventset); + if (fds->revents != 0) + { + fvdbg("Report events: %02x\n", fds->revents); + sem_post(fds->sem); + } + } + } +} +#else +# define pipecommon_pollnotify(dev,event) +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pipecommon_allocdev + ****************************************************************************/ + +FAR struct pipe_dev_s *pipecommon_allocdev(void) +{ + struct pipe_dev_s *dev; + + /* Allocate a private structure to manage the pipe */ + + dev = (struct pipe_dev_s *)kmalloc(sizeof(struct pipe_dev_s)); + if (dev) + { + /* Initialize the private structure */ + + memset(dev, 0, sizeof(struct pipe_dev_s)); + sem_init(&dev->d_bfsem, 0, 1); + sem_init(&dev->d_rdsem, 0, 0); + sem_init(&dev->d_wrsem, 0, 0); + } + + return dev; +} + +/**************************************************************************** + * Name: pipecommon_freedev + ****************************************************************************/ + +void pipecommon_freedev(FAR struct pipe_dev_s *dev) +{ + sem_destroy(&dev->d_bfsem); + sem_destroy(&dev->d_rdsem); + sem_destroy(&dev->d_wrsem); + kfree(dev); +} + +/**************************************************************************** + * Name: pipecommon_open + ****************************************************************************/ + +int pipecommon_open(FAR struct file *filep) +{ + struct inode *inode = filep->f_inode; + struct pipe_dev_s *dev = inode->i_private; + int sval; + int ret; + + /* Some sanity checking */ +#if CONFIG_DEBUG + if (!dev) + { + return -EBADF; + } +#endif + + /* Make sure that we have exclusive access to the device structure. The + * sem_wait() call should fail only if we are awakened by a signal. + */ + + ret = sem_wait(&dev->d_bfsem); + if (ret != OK) + { + fdbg("sem_wait failed: %d\n", errno); + DEBUGASSERT(errno > 0); + return -errno; + } + + /* If this the first reference on the device, then allocate the buffer */ + + if (dev->d_refs == 0) + { + dev->d_buffer = (uint8_t*)kmalloc(CONFIG_DEV_PIPE_SIZE); + if (!dev->d_buffer) + { + (void)sem_post(&dev->d_bfsem); + return -ENOMEM; + } + } + + /* Increment the reference count on the pipe instance */ + + dev->d_refs++; + + /* If opened for writing, increment the count of writers on on the pipe instance */ + + if ((filep->f_oflags & O_WROK) != 0) + { + dev->d_nwriters++; + + /* If this this is the first writer, then the read semaphore indicates the + * number of readers waiting for the first writer. Wake them all up. + */ + + if (dev->d_nwriters == 1) + { + while (sem_getvalue(&dev->d_rdsem, &sval) == 0 && sval < 0) + { + sem_post(&dev->d_rdsem); + } + } + } + + /* If opened for read-only, then wait for at least one writer on the pipe */ + + sched_lock(); + (void)sem_post(&dev->d_bfsem); + if ((filep->f_oflags & O_RDWR) == O_RDONLY && dev->d_nwriters < 1) + { + /* NOTE: d_rdsem is normally used when the read logic waits for more + * data to be written. But until the first writer has opened the + * pipe, the meaning is different: it is used prevent O_RDONLY open + * calls from returning until there is at least one writer on the pipe. + * This is required both by spec and also because it prevents + * subsequent read() calls from returning end-of-file because there is + * no writer on the pipe. + */ + + ret = sem_wait(&dev->d_rdsem); + if (ret != OK) + { + /* The sem_wait() call should fail only if we are awakened by + * a signal. + */ + + fdbg("sem_wait failed: %d\n", errno); + DEBUGASSERT(errno > 0); + ret = -errno; + + /* Immediately close the pipe that we just opened */ + + (void)pipecommon_close(filep); + } + } + + sched_unlock(); + return ret; +} + +/**************************************************************************** + * Name: pipecommon_close + ****************************************************************************/ + +int pipecommon_close(FAR struct file *filep) +{ + struct inode *inode = filep->f_inode; + struct pipe_dev_s *dev = inode->i_private; + int sval; + + /* Some sanity checking */ +#if CONFIG_DEBUG + if (!dev) + { + return -EBADF; + } +#endif + + /* Make sure that we have exclusive access to the device structure. + * NOTE: close() is supposed to return EINTR if interrupted, however + * I've never seen anyone check that. + */ + + pipecommon_semtake(&dev->d_bfsem); + + /* Check if the decremented reference count would go to zero */ + + if (dev->d_refs > 1) + { + /* No.. then just decrement the reference count */ + + dev->d_refs--; + + /* If opened for writing, decrement the count of writers on on the pipe instance */ + + if ((filep->f_oflags & O_WROK) != 0) + { + /* If there are no longer any writers on the pipe, then notify all of the + * waiting readers that they must return end-of-file. + */ + + if (--dev->d_nwriters <= 0) + { + while (sem_getvalue(&dev->d_rdsem, &sval) == 0 && sval < 0) + { + sem_post(&dev->d_rdsem); + } + } + } + } + else + { + /* Yes... deallocate the buffer */ + + kfree(dev->d_buffer); + dev->d_buffer = NULL; + + /* And reset all counts and indices */ + + dev->d_wrndx = 0; + dev->d_rdndx = 0; + dev->d_refs = 0; + dev->d_nwriters = 0; + } + + sem_post(&dev->d_bfsem); + return OK; +} + +/**************************************************************************** + * Name: pipecommon_read + ****************************************************************************/ + +ssize_t pipecommon_read(FAR struct file *filep, FAR char *buffer, size_t len) +{ + struct inode *inode = filep->f_inode; + struct pipe_dev_s *dev = inode->i_private; +#ifdef CONFIG_DEV_PIPEDUMP + FAR uint8_t *start = (uint8_t*)buffer; +#endif + ssize_t nread = 0; + int sval; + int ret; + + /* Some sanity checking */ +#if CONFIG_DEBUG + if (!dev) + { + return -ENODEV; + } +#endif + + /* Make sure that we have exclusive access to the device structure */ + + if (sem_wait(&dev->d_bfsem) < 0) + { + return ERROR; + } + + /* If the pipe is empty, then wait for something to be written to it */ + + while (dev->d_wrndx == dev->d_rdndx) + { + /* If O_NONBLOCK was set, then return EGAIN */ + + if (filep->f_oflags & O_NONBLOCK) + { + sem_post(&dev->d_bfsem); + return -EAGAIN; + } + + /* If there are no writers on the pipe, then return end of file */ + + if (dev->d_nwriters <= 0) + { + sem_post(&dev->d_bfsem); + return 0; + } + + /* Otherwise, wait for something to be written to the pipe */ + + sched_lock(); + sem_post(&dev->d_bfsem); + ret = sem_wait(&dev->d_rdsem); + sched_unlock(); + + if (ret < 0 || sem_wait(&dev->d_bfsem) < 0) + { + return ERROR; + } + } + + /* Then return whatever is available in the pipe (which is at least one byte) */ + + nread = 0; + while (nread < len && dev->d_wrndx != dev->d_rdndx) + { + *buffer++ = dev->d_buffer[dev->d_rdndx]; + if (++dev->d_rdndx >= CONFIG_DEV_PIPE_SIZE) + { + dev->d_rdndx = 0; + } + nread++; + } + + /* Notify all waiting writers that bytes have been removed from the buffer */ + + while (sem_getvalue(&dev->d_wrsem, &sval) == 0 && sval < 0) + { + sem_post(&dev->d_wrsem); + } + + /* Notify all poll/select waiters that they can write to the FIFO */ + + pipecommon_pollnotify(dev, POLLOUT); + + sem_post(&dev->d_bfsem); + pipe_dumpbuffer("From PIPE:", start, nread); + return nread; +} + +/**************************************************************************** + * Name: pipecommon_write + ****************************************************************************/ + +ssize_t pipecommon_write(FAR struct file *filep, FAR const char *buffer, size_t len) +{ + struct inode *inode = filep->f_inode; + struct pipe_dev_s *dev = inode->i_private; + ssize_t nwritten = 0; + ssize_t last; + int nxtwrndx; + int sval; + + /* Some sanity checking */ + +#if CONFIG_DEBUG + if (!dev) + { + return -ENODEV; + } +#endif + + pipe_dumpbuffer("To PIPE:", (uint8_t*)buffer, len); + + /* At present, this method cannot be called from interrupt handlers. That is + * because it calls sem_wait (via pipecommon_semtake below) and sem_wait cannot + * be called from interrupt level. This actually happens fairly commonly + * IF dbg() is called from interrupt handlers and stdout is being redirected + * via a pipe. In that case, the debug output will try to go out the pipe + * (interrupt handlers should use the lldbg() APIs). + * + * On the other hand, it would be very valuable to be able to feed the pipe + * from an interrupt handler! TODO: Consider disabling interrupts instead + * of taking semaphores so that pipes can be written from interupt handlers + */ + + DEBUGASSERT(up_interrupt_context() == false) + + /* Make sure that we have exclusive access to the device structure */ + + if (sem_wait(&dev->d_bfsem) < 0) + { + return ERROR; + } + + /* Loop until all of the bytes have been written */ + + last = 0; + for (;;) + { + /* Calculate the write index AFTER the next byte is written */ + + nxtwrndx = dev->d_wrndx + 1; + if (nxtwrndx >= CONFIG_DEV_PIPE_SIZE) + { + nxtwrndx = 0; + } + + /* Would the next write overflow the circular buffer? */ + + if (nxtwrndx != dev->d_rdndx) + { + /* No... copy the byte */ + + dev->d_buffer[dev->d_wrndx] = *buffer++; + dev->d_wrndx = nxtwrndx; + + /* Is the write complete? */ + + if (++nwritten >= len) + { + /* Yes.. Notify all of the waiting readers that more data is available */ + + while (sem_getvalue(&dev->d_rdsem, &sval) == 0 && sval < 0) + { + sem_post(&dev->d_rdsem); + } + + /* Notify all poll/select waiters that they can write to the FIFO */ + + pipecommon_pollnotify(dev, POLLIN); + + /* Return the number of bytes written */ + + sem_post(&dev->d_bfsem); + return len; + } + } + else + { + /* There is not enough room for the next byte. Was anything written in this pass? */ + + if (last < nwritten) + { + /* Yes.. Notify all of the waiting readers that more data is available */ + + while (sem_getvalue(&dev->d_rdsem, &sval) == 0 && sval < 0) + { + sem_post(&dev->d_rdsem); + } + } + last = nwritten; + + /* If O_NONBLOCK was set, then return partial bytes written or EGAIN */ + + if (filep->f_oflags & O_NONBLOCK) + { + if (nwritten == 0) + { + nwritten = -EAGAIN; + } + sem_post(&dev->d_bfsem); + return nwritten; + } + + /* There is more to be written.. wait for data to be removed from the pipe */ + + sched_lock(); + sem_post(&dev->d_bfsem); + pipecommon_semtake(&dev->d_wrsem); + sched_unlock(); + pipecommon_semtake(&dev->d_bfsem); + } + } +} + +/**************************************************************************** + * Name: pipecommon_poll + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_POLL +int pipecommon_poll(FAR struct file *filep, FAR struct pollfd *fds, + bool setup) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct pipe_dev_s *dev = inode->i_private; + pollevent_t eventset; + pipe_ndx_t nbytes; + int ret = OK; + int i; + + /* Some sanity checking */ + +#if CONFIG_DEBUG + if (!dev || !fds) + { + return -ENODEV; + } +#endif + + /* Are we setting up the poll? Or tearing it down? */ + + pipecommon_semtake(&dev->d_bfsem); + if (setup) + { + /* This is a request to set up the poll. Find an available + * slot for the poll structure reference + */ + + for (i = 0; i < CONFIG_DEV_PIPE_NPOLLWAITERS; i++) + { + /* Find an available slot */ + + if (!dev->d_fds[i]) + { + /* Bind the poll structure and this slot */ + + dev->d_fds[i] = fds; + fds->priv = &dev->d_fds[i]; + break; + } + } + + if (i >= CONFIG_DEV_PIPE_NPOLLWAITERS) + { + fds->priv = NULL; + ret = -EBUSY; + goto errout; + } + + /* Should immediately notify on any of the requested events? + * First, determine how many bytes are in the buffer + */ + + if (dev->d_wrndx >= dev->d_rdndx) + { + nbytes = dev->d_wrndx - dev->d_rdndx; + } + else + { + nbytes = (CONFIG_DEV_PIPE_SIZE-1) + dev->d_wrndx - dev->d_rdndx; + } + + /* Notify the POLLOUT event if the pipe is not full */ + + eventset = 0; + if (nbytes < (CONFIG_DEV_PIPE_SIZE-1)) + { + eventset |= POLLOUT; + } + + /* Notify the POLLIN event if the pipe is not empty */ + + if (nbytes > 0) + { + eventset |= POLLIN; + } + + if (eventset) + { + pipecommon_pollnotify(dev, eventset); + } + } + else + { + /* This is a request to tear down the poll. */ + + struct pollfd **slot = (struct pollfd **)fds->priv; + +#ifdef CONFIG_DEBUG + if (!slot) + { + ret = -EIO; + goto errout; + } +#endif + + /* Remove all memory of the poll setup */ + + *slot = NULL; + fds->priv = NULL; + } + +errout: + sem_post(&dev->d_bfsem); + return ret; +} +#endif + +#endif /* CONFIG_DEV_PIPE_SIZE > 0 */ diff --git a/nuttx/drivers/pipes/pipe_common.h b/nuttx/drivers/pipes/pipe_common.h new file mode 100644 index 000000000..44822e07f --- /dev/null +++ b/nuttx/drivers/pipes/pipe_common.h @@ -0,0 +1,139 @@ +/**************************************************************************** + * drivers/pipe/pipe_common.h + * + * Copyright (C) 2008-2009 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <gnutt@nuttx.org> + * + * 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. + * + ****************************************************************************/ + +#ifndef __DRIVERS_PIPES_PIPE_COMMON_H +#define __DRIVERS_PIPES_PIPE_COMMON_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> +#include <sys/types.h> + +#include <stdint.h> +#include <stdbool.h> +#include <poll.h> + +#ifndef CONFIG_DEV_PIPE_SIZE +# define CONFIG_DEV_PIPE_SIZE 1024 +#endif + +#if CONFIG_DEV_PIPE_SIZE > 0 + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Maximum number of threads than can be waiting for POLL events */ + +#ifndef CONFIG_DEV_PIPE_NPOLLWAITERS +# define CONFIG_DEV_PIPE_NPOLLWAITERS 2 +#endif + +/* Maximum number of open's supported on pipe */ + +#define CONFIG_DEV_PIPE_MAXUSER 255 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Make the buffer index as small as possible for the configured pipe size */ + +#if CONFIG_DEV_PIPE_SIZE > 65535 +typedef uint32_t pipe_ndx_t; /* 32-bit index */ +#elif CONFIG_DEV_PIPE_SIZE > 255 +typedef uint16_t pipe_ndx_t; /* 16-bit index */ +#else +typedef uint8_t pipe_ndx_t; /* 8-bit index */ +#endif + +/* This structure represents the state of one pipe. A reference to this + * structure is retained in the i_private field of the inode whenthe pipe/fifo + * device is registered. + */ + +struct pipe_dev_s +{ + sem_t d_bfsem; /* Used to serialize access to d_buffer and indices */ + sem_t d_rdsem; /* Empty buffer - Reader waits for data write */ + sem_t d_wrsem; /* Full buffer - Writer waits for data read */ + pipe_ndx_t d_wrndx; /* Index in d_buffer to save next byte written */ + pipe_ndx_t d_rdndx; /* Index in d_buffer to return the next byte read */ + uint8_t d_refs; /* References counts on pipe (limited to 255) */ + uint8_t d_nwriters; /* Number of reference counts for write access */ + uint8_t d_pipeno; /* Pipe minor number */ + uint8_t *d_buffer; /* Buffer allocated when device opened */ + + /* The following is a list if poll structures of threads waiting for + * driver events. The 'struct pollfd' reference for each open is also + * retained in the f_priv field of the 'struct file'. + */ + +#ifndef CONFIG_DISABLE_POLL + struct pollfd *d_fds[CONFIG_DEV_PIPE_NPOLLWAITERS]; +#endif +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +# define EXTERN extern "C" +extern "C" { +#else +# define EXTERN extern +#endif + +EXTERN FAR struct pipe_dev_s *pipecommon_allocdev(void); +EXTERN void pipecommon_freedev(FAR struct pipe_dev_s *dev); +EXTERN int pipecommon_open(FAR struct file *filep); +EXTERN int pipecommon_close(FAR struct file *filep); +EXTERN ssize_t pipecommon_read(FAR struct file *, FAR char *, size_t); +EXTERN ssize_t pipecommon_write(FAR struct file *, FAR const char *, size_t); +#ifndef CONFIG_DISABLE_POLL +EXTERN int pipecommon_poll(FAR struct file *filep, FAR struct pollfd *fds, + bool setup); +#endif + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_DEV_PIPE_SIZE > 0 */ +#endif /* __DRIVERS_PIPES_PIPE_COMMON_H */ |