diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog index fe7e73fe4..fc33a6d81 100644 --- a/nuttx/ChangeLog +++ b/nuttx/ChangeLog @@ -375,3 +375,5 @@ * Add test for recursive mutexes * Correct bug in recursive mutex logic * Add mkfifo() + * Add pipe() and test for both pipes and fifos + diff --git a/nuttx/Documentation/NuttX.html b/nuttx/Documentation/NuttX.html index 70ab56081..70b781c8e 100644 --- a/nuttx/Documentation/NuttX.html +++ b/nuttx/Documentation/NuttX.html @@ -8,7 +8,7 @@

NuttX RTOS

-

Last Updated: July 20, 2008

+

Last Updated: July 26, 2008

@@ -1024,6 +1024,7 @@ nuttx-0.3.12 2008-xx-xx Gregory Nutt <spudmonkey@racsa.co.cr> * Add test for recursive mutexes * Correct bug in recursive mutex logic * Add mkfifo() + * Add pipe() and test for both pipes and fifos pascal-0.1.3 2008-xx-xx Gregory Nutt <spudmonkey@racsa.co.cr> diff --git a/nuttx/drivers/Makefile b/nuttx/drivers/Makefile index 19e775e8a..53eff3f11 100644 --- a/nuttx/drivers/Makefile +++ b/nuttx/drivers/Makefile @@ -44,7 +44,7 @@ AOBJS = $(ASRCS:.S=$(OBJEXT)) CSRCS = ifneq ($(CONFIG_NFILE_DESCRIPTORS),0) -CSRCS += dev_null.c dev_zero.c fifo.c serial.c lowconsole.c can.c +CSRCS += dev_null.c dev_zero.c pipe.c fifo.c pipe-common.c serial.c lowconsole.c can.c ifneq ($(CONFIG_DISABLE_MOUNTPOINT),y) CSRCS += ramdisk.c endif diff --git a/nuttx/drivers/fifo.c b/nuttx/drivers/fifo.c index c9fa41ea3..8435c597f 100644 --- a/nuttx/drivers/fifo.c +++ b/nuttx/drivers/fifo.c @@ -44,406 +44,43 @@ #include #include -#include -#include -#include -#include -#include -#include -#include -#include #include +#include + +#include "pipe-common.h" -#ifndef CONFIG_DEV_FIFO_SIZE -# define CONFIG_DEV_FIFO_SIZE 1024 -#endif #if CONFIG_DEV_FIFO_SIZE > 0 /**************************************************************************** * Definitions ****************************************************************************/ -/* Maximum number of open's supported on FIFO */ - -#define CONFIG_DEV_FIFO_MAXUSER 255 - /**************************************************************************** * Private Types ****************************************************************************/ -/* Make the FIFO index as small as possible for the configured FIFO size */ - -#if CONFIG_DEV_FIFO_SIZE > 65535 -typedef uint32 fifo_ndx_t; /* 32-bit index */ -#elif CONFIG_DEV_FIFO_SIZE > 255 -typedef uint16 fifo_ndx_t; /* 16-bit index */ -#else -typedef ubyte fifo_ndx_t; /* 8-bit index */ -#endif - -struct fifo_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 */ - fifo_ndx_t d_wrndx; /* Index in d_buffer to save next byte written */ - fifo_ndx_t d_rdndx; /* Index in d_buffer to return the next byte read */ - ubyte d_refs; /* References counts on FIFO (limited to 255) */ - ubyte d_nreaders; /* Number of readers waiting for write data to empty buffer */ - ubyte d_nwriters; /* Number of writers wiating for data read out of full buffer */ - ubyte d_buffer[CONFIG_DEV_FIFO_SIZE]; -}; - /**************************************************************************** * Private Function Prototypes ****************************************************************************/ -static inline FAR struct fifo_dev_s *fifo_allocdev(void); -static inline void fifo_freedev(FAR struct fifo_dev_s *dev); -static void fifo_semtake(sem_t *sem); - -static int fifo_open(FAR struct file *filep); -static int fifo_close(FAR struct file *filep); -static ssize_t fifo_read(FAR struct file *, FAR char *, size_t); -static ssize_t fifo_write(FAR struct file *, FAR const char *, size_t); - /**************************************************************************** * Private Data ****************************************************************************/ static struct file_operations fifo_fops = { - fifo_open, /* open */ - fifo_close, /* close */ - fifo_read, /* read */ - fifo_write, /* write */ - 0, /* seek */ - 0 /* ioctl */ + pipecommon_open, /* open */ + pipecommon_close, /* close */ + pipecommon_read, /* read */ + pipecommon_write, /* write */ + 0, /* seek */ + 0 /* ioctl */ }; /**************************************************************************** * Private Functions ****************************************************************************/ -/**************************************************************************** - * Name: fifo_allocdev - ****************************************************************************/ -static inline FAR struct fifo_dev_s *fifo_allocdev(void) -{ - struct fifo_dev_s *dev; - - /* Allocate a private structure to manage the FIFO */ - - dev = (struct fifo_dev_s *)malloc(sizeof(struct fifo_dev_s)); - if (dev) - { - /* Initialize the private structure */ - - sem_init(&dev->d_bfsem, 0, 1); - sem_init(&dev->d_rdsem, 0, 0); - sem_init(&dev->d_wrsem, 0, 0); - dev->d_wrndx = 0; - dev->d_rdndx = 0; - dev->d_refs = 0; - dev->d_nreaders = 0; - dev->d_nwriters = 0; - } - return dev; -} - -/**************************************************************************** - * Name: fifo_freedev - ****************************************************************************/ -static inline void fifo_freedev(FAR struct fifo_dev_s *dev) -{ - sem_destroy(&dev->d_bfsem); - sem_destroy(&dev->d_rdsem); - sem_destroy(&dev->d_wrsem); - free(dev); -} - -/**************************************************************************** - * Name: fifo_semtake - ****************************************************************************/ -static void fifo_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: fifo_open - ****************************************************************************/ -static int fifo_open(FAR struct file *filep) -{ - struct inode *inode = filep->f_inode; - struct fifo_dev_s *dev = inode->i_private; - - /* The device structure will be allocated the first time that the FIFO is - * opened - */ - - if (!dev) - { - /* Allocate and initialize a new device structure instance */ - - dev = fifo_allocdev(); - if (!dev) - { - return -ENOMEM; - } - - /* Set the private data on the inode to this instance */ - - inode->i_private = dev; - } - - /* Make sure that we have exclusive access to the device structure */ - - if (sem_wait(&dev->d_bfsem) == 0) - { - /* Increment the reference count on the fifo instance */ - - dev->d_refs++; - (void)sem_post(&dev->d_bfsem); - return OK; - } - return ERROR; -} - -/**************************************************************************** - * Name: fifo_close - ****************************************************************************/ -static int fifo_close(FAR struct file *filep) -{ - struct inode *inode = filep->f_inode; - struct fifo_dev_s *dev = inode->i_private; - - /* 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. - */ - - fifo_semtake(&dev->d_bfsem); - - /* Check if the decremented reference count would be zero */ - - if (dev->d_refs > 1) - { - /* No.. then just decrement the reference count */ - - dev->d_refs--; - sem_post(&dev->d_bfsem); - } - else - { - /* Then nothing else can be holding the semaphore, so it is save to */ - - inode->i_private = NULL; - sem_post(&dev->d_bfsem); - - /* Then free the fifo structure instance */ - - fifo_freedev(dev); - } - return OK; -} - -/**************************************************************************** - * Name: fifo_read - ****************************************************************************/ -static ssize_t fifo_read(FAR struct file *filep, FAR char *buffer, size_t len) -{ - struct inode *inode = filep->f_inode; - struct fifo_dev_s *dev = inode->i_private; - ssize_t nread = 0; - 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 fifo 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; - } - - /* Otherwise, wait for something to be written to the FIFO */ - - dev->d_nreaders++; - 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 FIFO (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_FIFO_SIZE) - { - dev->d_rdndx = 0; - } - nread++; - } - - /* Notify any waiting writers that bytes have been removed from the buffer */ - - if (dev->d_nwriters > 0) - { - dev->d_nwriters--; - sem_post(&dev->d_wrsem); - } - - sem_post(&dev->d_bfsem); - return nread; -} - -/**************************************************************************** - * Name: fifo_write - ****************************************************************************/ -static ssize_t fifo_write(FAR struct file *filep, FAR const char *buffer, size_t len) -{ - struct inode *inode = filep->f_inode; - struct fifo_dev_s *dev = inode->i_private; - ssize_t nwritten = 0; - ssize_t last; - int nxtwrndx; - - /* 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; - } - - /* 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_FIFO_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 the waiting readers that more data is available */ - - if (dev->d_nreaders > 0) - { - dev->d_nreaders--; - sem_post(&dev->d_rdsem); - } - - /* 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 the waiting readers that more data is available */ - - if (dev->d_nreaders > 0) - { - dev->d_nreaders--; - 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 FIFO */ - - dev->d_nwriters++; - sched_lock(); - sem_post(&dev->d_bfsem); - fifo_semtake(&dev->d_wrsem); - sched_unlock(); - fifo_semtake(&dev->d_bfsem); - } - } -} - /**************************************************************************** * Public Functions ****************************************************************************/ @@ -457,8 +94,10 @@ static ssize_t fifo_write(FAR struct file *filep, FAR const char *buffer, size_t * 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. NuttX FIFOs need - * not be open at both ends before input or output operations on it. + * 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. Unlike other mkfifo() implementations, this one will + * NOT block when the FIFO is opened on only one end. * * Inputs: * pathname - The full path to the FIFO instance to attach to or to create @@ -472,6 +111,22 @@ static ssize_t fifo_write(FAR struct file *filep, FAR const char *buffer, size_t ****************************************************************************/ int mkfifo(FAR const char *pathname, mode_t mode) { - return register_driver(pathname, &fifo_fops, mode, NULL); + 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 +#endif /* CONFIG_DEV_FIFO_SIZE > 0 */ diff --git a/nuttx/drivers/pipe-common.c b/nuttx/drivers/pipe-common.c new file mode 100644 index 000000000..a2bf391aa --- /dev/null +++ b/nuttx/drivers/pipe-common.c @@ -0,0 +1,421 @@ +/**************************************************************************** + * drivers/pipe-common.c + * + * Copyright (C) 2008 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pipe-common.h" + +#if CONFIG_DEV_FIFO_SIZE > 0 + +/**************************************************************************** + * Definitions + ****************************************************************************/ + +/**************************************************************************** + * 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); + } +} + +/**************************************************************************** + * 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 FIFO */ + + dev = (struct pipe_dev_s *)malloc(sizeof(struct pipe_dev_s)); + if (dev) + { + /* Initialize the private structure */ + + memset(&dev->s, 0, sizeof(struct pipe_state_s)); + sem_init(&dev->s.d_bfsem, 0, 1); + sem_init(&dev->s.d_rdsem, 0, 0); + sem_init(&dev->s.d_wrsem, 0, 0); + } + return dev; +} + +/**************************************************************************** + * Name: pipecommon_freedev + ****************************************************************************/ +void pipecommon_freedev(FAR struct pipe_dev_s *dev) +{ + sem_destroy(&dev->s.d_bfsem); + sem_destroy(&dev->s.d_rdsem); + sem_destroy(&dev->s.d_wrsem); + free(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; + + /* Some sanity checking */ +#if CONFIG_DEBUG + if (!dev) + { + return -EBADF; + } +#endif + /* Make sure that we have exclusive access to the device structure */ + + if (sem_wait(&dev->s.d_bfsem) == 0) + { + /* Increment the reference count on the pipe instance */ + + dev->s.d_refs++; + + /* If opened for writing, increment the count of writers on on the pipe instance */ + + if ((filep->f_oflags & O_WROK) != 0) + { + dev->s.d_nwriters++; + } + + (void)sem_post(&dev->s.d_bfsem); + return OK; + } + return ERROR; +} + +/**************************************************************************** + * Name: pipecommon_close + ****************************************************************************/ +int pipecommon_close(FAR struct file *filep) +{ + struct inode *inode = filep->f_inode; + struct pipe_dev_s *dev = inode->i_private; + + /* 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->s.d_bfsem); + + /* Check if the decremented reference count would be zero */ + + if (dev->s.d_refs > 1) + { + /* No.. then just decrement the reference count */ + + dev->s.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 any + * waiting readers that must return end-of-file. + */ + + if (--dev->s.d_nwriters <= 0 && dev->s.d_rdwaiters > 0) + { + dev->s.d_rdwaiters--; + sem_post(&dev->s.d_rdsem); + } + } + sem_post(&dev->s.d_bfsem); + } + else + { + /* Then nothing else can be holding the semaphore, so it is save to */ + + inode->i_private = NULL; + sem_post(&dev->s.d_bfsem); + + /* Then free the fifo structure instance */ + + pipecommon_freedev(dev); + } + 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; + ssize_t nread = 0; + 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->s.d_bfsem) < 0) + { + return ERROR; + } + + /* If the fifo is empty, then wait for something to be written to it */ + + while (dev->s.d_wrndx == dev->s.d_rdndx) + { + /* If O_NONBLOCK was set, then return EGAIN */ + + if (filep->f_oflags & O_NONBLOCK) + { + sem_post(&dev->s.d_bfsem); + return -EAGAIN; + } + + /* If there are no writers on the pipe, then return end of file */ + + if (dev->s.d_nwriters <= 0) + { + sem_post(&dev->s.d_bfsem); + return 0; + } + + /* Otherwise, wait for something to be written to the FIFO */ + + dev->s.d_rdwaiters++; + sched_lock(); + sem_post(&dev->s.d_bfsem); + ret = sem_wait(&dev->s.d_rdsem); + sched_unlock(); + if (ret < 0 || sem_wait(&dev->s.d_bfsem) < 0) + { + return ERROR; + } + } + + /* Then return whatever is available in the FIFO (which is at least one byte) */ + + nread = 0; + while (nread < len && dev->s.d_wrndx != dev->s.d_rdndx) + { + *buffer++ = dev->d_buffer[dev->s.d_rdndx]; + if (++dev->s.d_rdndx >= CONFIG_DEV_FIFO_SIZE) + { + dev->s.d_rdndx = 0; + } + nread++; + } + + /* Notify any waiting writers that bytes have been removed from the buffer */ + + if (dev->s.d_nwrwaiters > 0) + { + dev->s.d_nwrwaiters--; + sem_post(&dev->s.d_wrsem); + } + + sem_post(&dev->s.d_bfsem); + 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; + + /* 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->s.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->s.d_wrndx + 1; + if (nxtwrndx >= CONFIG_DEV_FIFO_SIZE) + { + nxtwrndx = 0; + } + + /* Would the next write overflow the circular buffer? */ + + if (nxtwrndx != dev->s.d_rdndx) + { + /* No... copy the byte */ + + dev->d_buffer[dev->s.d_wrndx] = *buffer++; + dev->s.d_wrndx = nxtwrndx; + + /* Is the write complete? */ + + if (++nwritten >= len) + { + /* Yes.. Notify the waiting readers that more data is available */ + + if (dev->s.d_rdwaiters > 0) + { + dev->s.d_rdwaiters--; + sem_post(&dev->s.d_rdsem); + } + + /* Return the number of bytes written */ + + sem_post(&dev->s.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 the waiting readers that more data is available */ + + if (dev->s.d_rdwaiters > 0) + { + dev->s.d_rdwaiters--; + sem_post(&dev->s.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->s.d_bfsem); + return nwritten; + } + + /* There is more to be written.. wait for data to be removed from the FIFO */ + + dev->s.d_nwrwaiters++; + sched_lock(); + sem_post(&dev->s.d_bfsem); + pipecommon_semtake(&dev->s.d_wrsem); + sched_unlock(); + pipecommon_semtake(&dev->s.d_bfsem); + } + } +} + +#endif /* CONFIG_DEV_FIFO_SIZE > 0 */ diff --git a/nuttx/drivers/pipe-common.h b/nuttx/drivers/pipe-common.h new file mode 100644 index 000000000..36b424d93 --- /dev/null +++ b/nuttx/drivers/pipe-common.h @@ -0,0 +1,117 @@ +/**************************************************************************** + * drivers/pipe-common.h + * + * Copyright (C) 2008 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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_PIPE_COMMON_H +#define __DRIVERS_PIPE_COMMON_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#ifndef CONFIG_DEV_FIFO_SIZE +# define CONFIG_DEV_FIFO_SIZE 1024 +#endif +#if CONFIG_DEV_FIFO_SIZE > 0 + +/**************************************************************************** + * Definitions + ****************************************************************************/ + +/* Maximum number of open's supported on FIFO */ + +#define CONFIG_DEV_FIFO_MAXUSER 255 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Make the FIFO index as small as possible for the configured FIFO size */ + +#if CONFIG_DEV_FIFO_SIZE > 65535 +typedef uint32 pipe_ndx_t; /* 32-bit index */ +#elif CONFIG_DEV_FIFO_SIZE > 255 +typedef uint16 pipe_ndx_t; /* 16-bit index */ +#else +typedef ubyte pipe_ndx_t; /* 8-bit index */ +#endif + +struct pipe_state_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 */ + ubyte d_refs; /* References counts on FIFO (limited to 255) */ + ubyte d_nwriters; /* Number of open counts for write access */ + ubyte d_rdwaiters; /* Number of readers waiting for write data to empty buffer */ + ubyte d_nwrwaiters; /* Number of writers wiating for data read out of full buffer */ + ubyte d_pipeno; /* Pipe minor number */ +}; + +struct pipe_dev_s +{ + struct pipe_state_s s; + ubyte d_buffer[CONFIG_DEV_FIFO_SIZE]; +}; + +/**************************************************************************** + * 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); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_DEV_FIFO_SIZE > 0 */ +#endif /* __DRIVERS_PIPE_COMMON_H */ diff --git a/nuttx/drivers/pipe.c b/nuttx/drivers/pipe.c new file mode 100644 index 000000000..fd5292cc1 --- /dev/null +++ b/nuttx/drivers/pipe.c @@ -0,0 +1,257 @@ +/**************************************************************************** + * drivers/pipe.c + * + * Copyright (C) 2008 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 + +#include +#include +#include +#include +#include +#include +#include + +#include "pipe-common.h" + +#if CONFIG_DEV_FIFO_SIZE > 0 + +/**************************************************************************** + * Definitions + ****************************************************************************/ + +#define MAX_PIPES 32 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int pipe_close(FAR struct file *filep); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct file_operations pipe_fops = +{ + pipecommon_open, /* open */ + pipe_close, /* close */ + pipecommon_read, /* read */ + pipecommon_write, /* write */ + 0, /* seek */ + 0 /* ioctl */ +}; + +static sem_t g_pipesem = { 1 }; +static uint32 g_pipeset = 0; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pipe_allocate + ****************************************************************************/ +static inline int pipe_allocate(void) +{ + int pipeno; + int ret = sem_wait(&g_pipesem); + if (ret >= 0) + { + ret = -ENFILE; + for (pipeno = 0; pipeno < MAX_PIPES; pipeno++) + { + if ((g_pipeset & (1 << pipeno)) == 0) + { + g_pipeset |= (1 << pipeno); + ret = pipeno; + break; + } + } + (void)sem_post(&g_pipesem); + } + 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; + ubyte pipeno; + int ret; + + /* Some sanity checking */ +#if CONFIG_DEBUG + if (!dev) + { + return -EBADF; + } +#endif + pipeno = dev->s.d_pipeno; + + /* Perform common close operations */ + + ret = pipecommon_close(filep); + if (ret == 0 && !inode->i_private) + { + char devname[16]; + sprintf(devname, "/dev/pipe%d", pipeno); + unlink(devname); + pipe_free(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; + char devname[16]; + int pipeno; + int err; + int ret; + + /* Allocate a minor number for the pipe device */ + + pipeno = pipe_allocate(); + if (pipeno < 0) + { + err = -pipeno; + goto errout; + } + + /* Allocate and initialize a new device structure instance */ + + dev = pipecommon_allocdev(); + if (!dev) + { + pipe_free(pipeno); + err = ENOMEM; + goto errout; + } + dev->s.d_pipeno = pipeno; + + /* Create a pathname to the pipe device */ + + sprintf(devname, "/dev/pipe%d", pipeno); + + /* Register the pipe device */ + + ret = register_driver(devname, &pipe_fops, 0666, (void*)dev); + if (ret != 0) + { + err = -ret; + goto errout_with_dev; + } + + /* 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: + errno = err; + return ERROR; +} + +#endif /* CONFIG_DEV_FIFO_SIZE > 0 */ diff --git a/nuttx/examples/pipe/pipe_main.c b/nuttx/examples/pipe/pipe_main.c index eea6a67a6..5b3874171 100644 --- a/nuttx/examples/pipe/pipe_main.c +++ b/nuttx/examples/pipe/pipe_main.c @@ -102,9 +102,12 @@ static void *reader(pthread_addr_t pvarg) } else if (ret == 0) { -#warning BUG: pipe should return zero when writer closes - fprintf(stderr, "reader: Received zero bytes\n"); - return (void*)2; + if (nbytes < NREAD_BYTES) + { + fprintf(stderr, "reader: Too few bytes read -- aborting: %d\n", nbytes); + return (void*)2; + } + break; } for (ndx = 0; ndx < ret; ndx++) { @@ -121,6 +124,11 @@ static void *reader(pthread_addr_t pvarg) value++; } nbytes += ret; + if (nbytes > NREAD_BYTES) + { + fprintf(stderr, "reader: Too many bytes read -- aborting: %d\n", nbytes); + return (void*)3; + } } printf("reader: %d bytes read\n", nbytes); return (void*)0; @@ -252,8 +260,7 @@ void user_initialize(void) int user_start(int argc, char *argv[]) { - int fdin; - int fdout; + int filedes[2]; int ret; /* Test FIFO logic */ @@ -266,26 +273,33 @@ int user_start(int argc, char *argv[]) return 1; } - fdin = open(CONFIG_EXAMPLES_FIFO_PATH, O_RDONLY); - if (fdin < 0) + /* Open open end of the FIFO for reading and the other end for writing. NOTE: + * the following would not work on most FIFO implementations because the attempt + * to open just one end of the FIFO would block. The NuttX FIFOs do not block. + */ + + filedes[1] = open(CONFIG_EXAMPLES_FIFO_PATH, O_WRONLY); + if (filedes[1] < 0) + { + fprintf(stderr, "user_start: Failed to open FIFO %s for writing, errno=%d\n", + CONFIG_EXAMPLES_FIFO_PATH, errno); + close(filedes[0]); + return 3; + } + + filedes[0] = open(CONFIG_EXAMPLES_FIFO_PATH, O_RDONLY); + if (filedes[0] < 0) { fprintf(stderr, "user_start: Failed to open FIFO %s for reading, errno=%d\n", CONFIG_EXAMPLES_FIFO_PATH, errno); return 2; } - fdout = open(CONFIG_EXAMPLES_FIFO_PATH, O_WRONLY); - if (fdout < 0) - { - fprintf(stderr, "user_start: Failed to open FIFO %s for writing, errno=%d\n", - CONFIG_EXAMPLES_FIFO_PATH, errno); - close(fdin); - return 3; - } + /* Then perform the test using those file descriptors */ - ret = perform_test(fdin, fdout); - close(fdin); - close(fdout); + ret = perform_test(filedes[0], filedes[1]); + close(filedes[0]); + close(filedes[1]); unlink(CONFIG_EXAMPLES_FIFO_PATH); if (ret != 0) { @@ -297,7 +311,25 @@ int user_start(int argc, char *argv[]) /* Test PIPE logic */ printf("user_start: Performing pipe test\n"); - /* Not yet implemented */ + ret = pipe(filedes); + if (ret < 0) + { + fprintf(stderr, "user_start: pipe failed with errno=%d\n", errno); + return 1; + } + + /* Then perform the test using those file descriptors */ + + ret = perform_test(filedes[0], filedes[1]); + close(filedes[0]); + close(filedes[1]); + unlink(CONFIG_EXAMPLES_FIFO_PATH); + if (ret != 0) + { + fprintf(stderr, "user_start: PIPE test FAILED\n"); + return 4; + } + printf("user_start: PIPE test PASSED\n"); fflush(stdout); return 0; diff --git a/nuttx/include/stddef.h b/nuttx/include/stddef.h index 09cfa5369..0bbe97839 100644 --- a/nuttx/include/stddef.h +++ b/nuttx/include/stddef.h @@ -1,7 +1,7 @@ -/************************************************************ - * stddef.h +/**************************************************************************** + * include/stddef.h * - * Copyright (C) 2007 Gregory Nutt. All rights reserved. + * Copyright (C) 2007, 2008 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -14,7 +14,7 @@ * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. - * 3. Neither the name Gregory Nutt nor the names of its contributors may be + * 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. * @@ -31,19 +31,19 @@ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - ************************************************************/ + ****************************************************************************/ #ifndef __STDDEF_H #define __STDDEF_H -/************************************************************ +/**************************************************************************** * Included Files - ************************************************************/ + ****************************************************************************/ #include -/************************************************************ +/**************************************************************************** * Type Definitions - ************************************************************/ + ****************************************************************************/ #endif /* __STDDEF_H */ diff --git a/nuttx/include/unistd.h b/nuttx/include/unistd.h index 4cce106e8..e2b88686f 100644 --- a/nuttx/include/unistd.h +++ b/nuttx/include/unistd.h @@ -121,7 +121,7 @@ EXTERN int optopt; /* unrecognized option character */ /* Task Control Interfaces */ EXTERN pid_t getpid(void); -EXTERN void _exit(int status) noreturn_function; +EXTERN void _exit(int status) noreturn_function; EXTERN unsigned int sleep(unsigned int seconds); EXTERN void usleep(unsigned long usec); @@ -135,15 +135,17 @@ EXTERN off_t lseek(int fd, off_t offset, int whence); EXTERN int read(int fd, FAR void *buf, unsigned int nbytes); EXTERN int write(int fd, FAR const void *buf, unsigned int nbytes); +/* Special devices */ +EXTERN int pipe(int filedes[2]); + /* File path operations */ -EXTERN int unlink(FAR const char *pathname); -EXTERN int rmdir(FAR const char *pathname); +EXTERN int unlink(FAR const char *pathname); +EXTERN int rmdir(FAR const char *pathname); /* Other */ -EXTERN int getopt(int argc, FAR char *const argv[], FAR const char *optstring); -EXTERN int pipe(int filedes[2]); +EXTERN int getopt(int argc, FAR char *const argv[], FAR const char *optstring); #undef EXTERN #if defined(__cplusplus)