diff --git a/ChangeLog b/ChangeLog index 910c764f3f0..dab311760b5 100755 --- a/ChangeLog +++ b/ChangeLog @@ -11810,3 +11810,6 @@ driver already supports a 1-wire interface, but this driver uses the same serial logic to implement a lower half driver much like the I2C lower half driver. From Aleksandr Vyhovanec (2015-05-25). + * fs/: Add logic to detach a file structure from a file descriptor. + This is for use only within the OS. It permits an open file or driver + to be used across multiple threads (2016-05-26). diff --git a/configs/nucleo-f4x1re/src/stm32_ajoystick.c b/configs/nucleo-f4x1re/src/stm32_ajoystick.c index 5079c98acdb..61eaccf244f 100644 --- a/configs/nucleo-f4x1re/src/stm32_ajoystick.c +++ b/configs/nucleo-f4x1re/src/stm32_ajoystick.c @@ -46,6 +46,7 @@ #include #include +#include #include #include "stm32_gpio.h" @@ -108,10 +109,6 @@ AJOY_BUTTON_7_BIT ) #endif -/**************************************************************************** - * Private Types - ****************************************************************************/ - /**************************************************************************** * Private Function Prototypes ****************************************************************************/ @@ -158,9 +155,9 @@ static const struct ajoy_lowerhalf_s g_ajoylower = }; #ifndef NO_JOYSTICK_ADC -/* Descriptor for the open ADC driver */ +/* Thread-independent file structure for the open ADC driver */ -static int g_adcfd = -1; +static struct file g_adcfile; #endif /* Current interrupt handler and argument */ @@ -209,7 +206,7 @@ static int ajoy_sample(FAR const struct ajoy_lowerhalf_s *lower, * channels are enabled). */ - nread = read(g_adcfd, adcmsg, MAX_ADC_CHANNELS * sizeof(struct adc_msg_s)); + nread = file_read(&g_adcfile, adcmsg, MAX_ADC_CHANNELS * sizeof(struct adc_msg_s)); if (nread < 0) { int errcode = get_errno(); @@ -454,6 +451,8 @@ int board_ajoy_initialize(void) int i; #ifndef NO_JOYSTICK_ADC + int fd; + ivdbg("Initialize ADC driver: /dev/adc0\n"); /* Initialize ADC. We will need this to read the ADC inputs */ @@ -465,18 +464,27 @@ int board_ajoy_initialize(void) return ret; } - /* Open the ADC driver for reading. - * REVISIT: This can't work! The file descriptor is only valid in the - * task that opened the file. Not useful for a sharable driver. - */ + /* Open the ADC driver for reading. */ - g_adcfd = open("/dev/adc0", O_RDONLY); - if (g_adcfd < 0) + fd = open("/dev/adc0", O_RDONLY); + if (fd < 0) { int errcode = get_errno(); idbg("ERROR: Failed to open /dev/adc0: %d\n", errcode); return -errcode; } + + /* Detach the file structure from the file descriptor so that it can be + * used on any thread. + */ + + ret = file_detach(fd, &g_adcfile); + if (ret < 0) + { + idbg("ERROR: Failed to detach from file descriptor: %d\n", ret); + (void)close(fd); + return ret; + } #endif /* Configure the GPIO pins as interrupting inputs. NOTE: This is @@ -500,8 +508,7 @@ int board_ajoy_initialize(void) { idbg("ERROR: ajoy_register failed: %d\n", ret); #ifndef NO_JOYSTICK_ADC - close(g_adcfd); - g_adcfd = -1; + file_close_detached(&g_adcfile); #endif } diff --git a/configs/nucleo-l476rg/src/stm32_ajoystick.c b/configs/nucleo-l476rg/src/stm32_ajoystick.c index 53d58a77d9b..bd57040a78f 100644 --- a/configs/nucleo-l476rg/src/stm32_ajoystick.c +++ b/configs/nucleo-l476rg/src/stm32_ajoystick.c @@ -1,7 +1,7 @@ /**************************************************************************** * configs/nucleo-f3x1re/src/stm32_ajoystick.c * - * Copyright (C) 2014 Gregory Nutt. All rights reserved. + * Copyright (C) 2014, 2016 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -45,6 +45,7 @@ #include #include +#include #include #include "stm32l4_gpio.h" @@ -107,10 +108,6 @@ AJOY_BUTTON_7_BIT ) #endif -/**************************************************************************** - * Private Types - ****************************************************************************/ - /**************************************************************************** * Private Function Prototypes ****************************************************************************/ @@ -157,9 +154,9 @@ static const struct ajoy_lowerhalf_s g_ajoylower = }; #ifndef NO_JOYSTICK_ADC -/* Descriptor for the open ADC driver */ +/* Thread-independent file structure for the open ADC driver */ -static int g_adcfd = -1; +static struct file g_adcfile; #endif /* Current interrupt handler and argument */ @@ -208,7 +205,7 @@ static int ajoy_sample(FAR const struct ajoy_lowerhalf_s *lower, * channels are enabled). */ - nread = read(g_adcfd, adcmsg, MAX_ADC_CHANNELS * sizeof(struct adc_msg_s)); + nread = file_read(&g_adcfile, adcmsg, MAX_ADC_CHANNELS * sizeof(struct adc_msg_s)); if (nread < 0) { int errcode = get_errno(); @@ -453,6 +450,8 @@ int board_ajoy_initialize(void) int i; #ifndef NO_JOYSTICK_ADC + int fd; + ivdbg("Initialize ADC driver: /dev/adc0\n"); /* Initialize ADC. We will need this to read the ADC inputs */ @@ -464,15 +463,27 @@ int board_ajoy_initialize(void) return ret; } - /* Open the ADC driver for reading */ + /* Open the ADC driver for reading. */ - g_adcfd = open("/dev/adc0", O_RDONLY); - if (g_adcfd < 0) + fd = open("/dev/adc0", O_RDONLY); + if (fd < 0) { int errcode = get_errno(); idbg("ERROR: Failed to open /dev/adc0: %d\n", errcode); return -errcode; } + + /* Detach the file structure from the file descriptor so that it can be + * used on any thread. + */ + + ret = file_detach(fd, &g_adcfile); + if (ret < 0) + { + idbg("ERROR: Failed to detach from file descriptor: %d\n", ret); + (void)close(fd); + return ret; + } #endif /* Configure the GPIO pins as interrupting inputs. NOTE: This is @@ -496,8 +507,7 @@ int board_ajoy_initialize(void) { idbg("ERROR: ajoy_register failed: %d\n", ret); #ifndef NO_JOYSTICK_ADC - close(g_adcfd); - g_adcfd = -1; + file_close_detached(&g_adcfile); #endif } diff --git a/configs/sama5d3-xplained/src/sam_ajoystick.c b/configs/sama5d3-xplained/src/sam_ajoystick.c index dcb941593b5..d05f46eeaf3 100644 --- a/configs/sama5d3-xplained/src/sam_ajoystick.c +++ b/configs/sama5d3-xplained/src/sam_ajoystick.c @@ -46,6 +46,7 @@ #include #include +#include #include #include "sam_pio.h" @@ -93,10 +94,6 @@ AJOY_BUTTON_5_BIT | AJOY_BUTTON_6_BIT | \ AJOY_BUTTON_7_BIT ) -/**************************************************************************** - * Private Types - ****************************************************************************/ - /**************************************************************************** * Private Function Prototypes ****************************************************************************/ @@ -141,9 +138,9 @@ static const struct ajoy_lowerhalf_s g_ajoylower = .al_enable = ajoy_enable, }; -/* Descriptor for the open ADC driver */ +/* Thread-independent file structure for the open ADC driver */ -static int g_adcfd = -1; +static struct file g_adcfile; /* Current interrupt handler and argument */ @@ -190,7 +187,7 @@ static int ajoy_sample(FAR const struct ajoy_lowerhalf_s *lower, * channels are enabled). */ - nread = read(g_adcfd, adcmsg, SAM_ADC_NCHANNELS * sizeof(struct adc_msg_s)); + nread = file_read(&g_adcfile, adcmsg, MAX_ADC_CHANNELS * sizeof(struct adc_msg_s)); if (nread < 0) { int errcode = get_errno(); @@ -405,6 +402,7 @@ static int ajoy_interrupt(int irq, FAR void *context) int sam_ajoy_initialization(void) { int ret; + int fd; int i; /* Initialize ADC. We will need this to read the ADC inputs */ @@ -416,19 +414,28 @@ int sam_ajoy_initialization(void) return ret; } - /* Open the ADC driver for reading. - * REVISIT: This can't work! The file descriptor is only valid in the - * task that opened the file. Not useful for a sharable driver. - */ + /* Open the ADC driver for reading. */ - g_adcfd = open("/dev/adc0", O_RDONLY); - if (g_adcfd < 0) + fd = open("/dev/adc0", O_RDONLY); + if (fd < 0) { int errcode = get_errno(); idbg("ERROR: Failed to open /dev/adc0: %d\n", errcode); return -errcode; } + /* Detach the file structure from the file descriptor so that it can be + * used on any thread. + */ + + ret = file_detach(fd, &g_adcfile); + if (ret < 0) + { + idbg("ERROR: Failed to detach from file descriptor: %d\n", ret); + (void)close(fd); + return ret; + } + /* Configure the GPIO pins as interrupting inputs. */ for (i = 0; i < AJOY_NGPIOS; i++) @@ -452,8 +459,7 @@ int sam_ajoy_initialization(void) if (ret < 0) { idbg("ERROR: ajoy_register failed: %d\n", ret); - close(g_adcfd); - g_adcfd = -1; + file_close_detached(&g_adcfile); } return ret; diff --git a/fs/inode/Make.defs b/fs/inode/Make.defs index 14502451053..753a0706eee 100644 --- a/fs/inode/Make.defs +++ b/fs/inode/Make.defs @@ -39,7 +39,7 @@ ifneq ($(CONFIG_NFILE_DESCRIPTORS),0) CSRCS += fs_files.c fs_foreachinode.c fs_inode.c fs_inodeaddref.c CSRCS += fs_inodebasename.c fs_inodefind.c fs_inoderelease.c -CSRCS += fs_inoderemove.c fs_inodereserve.c +CSRCS += fs_inoderemove.c fs_inodereserve.c fs_filedetach.c # Include inode/utils build support diff --git a/fs/inode/fs_filedetach.c b/fs/inode/fs_filedetach.c new file mode 100644 index 00000000000..8b13250cbc1 --- /dev/null +++ b/fs/inode/fs_filedetach.c @@ -0,0 +1,217 @@ +/**************************************************************************** + * fs/inode/fs_filedetach.c + * + * Copyright (C) 2016 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include +#include + +#include "inode/inode.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: _files_semtake + ****************************************************************************/ + +static inline void _files_semtake(FAR struct filelist *list) +{ + /* Take the semaphore (perhaps waiting) */ + + while (sem_wait(&list->fl_sem) != 0) + { + /* The only case that an error should occur here is if + * the wait was awakened by a signal. + */ + + DEBUGASSERT(get_errno() == EINTR); + } +} + +/**************************************************************************** + * Name: _files_semgive + ****************************************************************************/ + +#define _files_semgive(list) sem_post(&list->fl_sem) + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: file_detach + * + * Description: + * This function is used to device drivers to create a task-independent + * handle to an entity in the file system. file_detach() duplicates the + * 'struct file' that underlies the file descriptor, then closes the file + * descriptor. + * + * This function will fail if fd is not a valid file descriptor. In + * particular, it will fail if fd is a socket descriptor. + * + * Input Parameters: + * fd - The file descriptor to be detached. This descriptor will be + * closed and invalid if the file was successfully detached. + * filep - A pointer to a user provided memory location in which to + * received the duplicated, detached file structure. + * + * Returned Value: + * Zero (OK) is returned on success; A negated errno value is returned on + * any failure to indicate the nature of the failure. + * + ****************************************************************************/ + +#if CONFIG_NFILE_DESCRIPTORS > 0 +int file_detach(int fd, FAR struct file *filep) +{ + FAR struct filelist *list; + FAR struct file *parent; + + /* Verify the file descriptor range */ + + if (fd < 0 || fd >= CONFIG_NFILE_DESCRIPTORS) + { + /* Not a file descriptor (might be a socket descriptor) */ + + return -EBADF; + } + + /* Get the thread-specific file list. It should never be NULL in this + * context. + */ + + list = sched_getfiles(); + DEBUGASSERT(list != NULL); + + /* If the file was properly opened, there should be an inode assigned */ + + _files_semtake(list); + parent = &list->fl_files[fd]; + if (parent->f_inode == NULL) + { + /* File is not open */ + + _files_semgive(list); + return -EBADF; + } + + /* Duplicate the 'struct file' content into the user-provided file + * structure. + */ + + filep->f_oflags = parent->f_oflags; + filep->f_pos = parent->f_pos; + filep->f_inode = parent->f_inode; + filep->f_priv = parent->f_priv; + + /* Release the file descriptore *without* calling the drive close method + * and without decrementing the inode reference count. That will be done + * in file_close_detached(). + */ + + parent->f_oflags = 0; + parent->f_pos = 0; + parent->f_inode = NULL; + parent->f_priv = NULL; + + _files_semgive(list); + return OK; +} +#endif + +/**************************************************************************** + * Name: file_close_detached + * + * Description: + * Close a file that was previously detached with file_detach(). + * + * REVISIT: This is essentially the same as _files_close() + * + * Input Parameters: + * filep - A pointer to a user provided memory location containing the + * open file data returned by file_detach(). + * + * Returned Value: + * Zero (OK) is returned on success; A negated errno value is returned on + * any failure to indicate the nature of the failure. + * + ****************************************************************************/ + +int file_close_detached(FAR struct file *filep) +{ + struct inode *inode; + int ret = OK; + + DEBUGASSERT(filep != NULL); + inode = filep->f_inode; + + /* Check if the struct file is open (i.e., assigned an inode) */ + + if (inode) + { + /* Close the file, driver, or mountpoint. */ + + if (inode->u.i_ops && inode->u.i_ops->close) + { + /* Perform the close operation */ + + ret = inode->u.i_ops->close(filep); + } + + /* And release the inode */ + + inode_release(inode); + + /* Reset the user file struct instance so that it cannot be reused. */ + + filep->f_oflags = 0; + filep->f_pos = 0; + filep->f_inode = NULL; + filep->f_priv = NULL; + } + + return ret; +} diff --git a/fs/inode/fs_files.c b/fs/inode/fs_files.c index 84f78ee4009..7c4665640e5 100644 --- a/fs/inode/fs_files.c +++ b/fs/inode/fs_files.c @@ -65,7 +65,7 @@ static void _files_semtake(FAR struct filelist *list) while (sem_wait(&list->fl_sem) != 0) { - /* The only case that an error should occr here is if + /* The only case that an error should occur here is if * the wait was awakened by a signal. */ @@ -362,9 +362,9 @@ int files_close(int fd) /* If the file was properly opened, there should be an inode assigned */ if (fd < 0 || fd >= CONFIG_NFILE_DESCRIPTORS || !list->fl_files[fd].f_inode) - { - return -EBADF; - } + { + return -EBADF; + } /* Perform the protected close operation */ diff --git a/fs/vfs/fs_close.c b/fs/vfs/fs_close.c index d54e5da9ccc..3a52d0c086f 100644 --- a/fs/vfs/fs_close.c +++ b/fs/vfs/fs_close.c @@ -105,7 +105,7 @@ int close(int fd) #if CONFIG_NFILE_DESCRIPTORS > 0 /* Close the driver or mountpoint. NOTES: (1) there is no - * exclusion mechanism here , the driver or mountpoint must be + * exclusion mechanism here, the driver or mountpoint must be * able to handle concurrent operations internally, (2) The driver * may have been opened numerous times (for different file * descriptors) and must also handle being closed numerous times. diff --git a/include/nuttx/fs/fs.h b/include/nuttx/fs/fs.h index b82f128e27f..b415725ee8f 100644 --- a/include/nuttx/fs/fs.h +++ b/include/nuttx/fs/fs.h @@ -532,7 +532,7 @@ int file_dup2(FAR struct file *filep1, FAR struct file *filep2); * function IS dup(). * * This alternative naming is used when dup could operate on both file and - * socket descritors to avoid drawing unused socket support into the link. + * socket descriptors to avoid drawing unused socket support into the link. * ****************************************************************************/ @@ -574,6 +574,52 @@ int fs_dupfd2(int fd1, int fd2); #endif #endif +/**************************************************************************** + * Name: file_detach + * + * Description: + * This function is used to device drivers to create a task-independent + * handle to an entity in the file system. file_detach() duplicates the + * 'struct file' that underlies the file descriptor, then closes the file + * descriptor. + * + * This function will fail if fd is not a valid file descriptor. In + * particular, it will fail if fd is a socket descriptor. + * + * Input Parameters: + * fd - The file descriptor to be detached. This descriptor will be + * closed and invalid if the file was successfully detached. + * filep - A pointer to a user provided memory location in which to + * received the duplicated, detached file structure. + * + * Returned Value: + * Zero (OK) is returned on success; A negated errno value is returned on + * any failure to indicate the nature of the failure. + * + ****************************************************************************/ + +#if CONFIG_NFILE_DESCRIPTORS > 0 +int file_detach(int fd, FAR struct file *filep); +#endif + +/**************************************************************************** + * Name: file_close_detached + * + * Description: + * Close a file that was previously detached with file_detach(). + * + * Input Parameters: + * filep - A pointer to a user provided memory location containing the + * open file data returned by file_detach(). + * + * Returned Value: + * Zero (OK) is returned on success; A negated errno value is returned on + * any failure to indicate the nature of the failure. + * + ****************************************************************************/ + +int file_close_detached(FAR struct file *filep); + /**************************************************************************** * Name: open_blockdriver *