diff --git a/boards/arm/samv7/common/src/sam_automount.c b/boards/arm/samv7/common/src/sam_automount.c index 461f52988f..f00b6aa996 100644 --- a/boards/arm/samv7/common/src/sam_automount.c +++ b/boards/arm/samv7/common/src/sam_automount.c @@ -255,7 +255,7 @@ void sam_automount_initialize(void) { ferr("ERROR: Failed to initialize auto-mounter for HSMCI0\n"); } -#endif +#endif /* CONFIG_SAMV7_HSMCI0_AUTOMOUNT */ } /**************************************************************************** diff --git a/fs/Kconfig b/fs/Kconfig index 67243a078c..24daacc5c9 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -42,6 +42,22 @@ config FS_AUTOMOUNTER_DEBUG system debug is not enable. This is useful primarily for in vivo unit testing of the auto-mount feature. +config FS_AUTOMOUNTER_DRIVER + bool "Auto-mounter driver" + default n + depends on FS_AUTOMOUNTER + ---help--- + Enabling this option will lead to registering of a character driver + on FS_AUTOMOUNTER_VFS_PATH + mount point path for auto-mounter. + Example: /var/mnt/sdcard0 + +config FS_AUTOMOUNTER_VFS_PATH + string "Path to auto-mounter driver" + default "/var" + depends on FS_AUTOMOUNTER_DRIVER + ---help--- + The path to where auto-mounter driver will exist in the VFS namespace. + config FS_NEPOLL_DESCRIPTORS int "Maximum number of default epoll descriptors for epoll_create1(2)" default 8 diff --git a/fs/mount/fs_automount.c b/fs/mount/fs_automount.c index d1d09a5910..3bd42193c6 100644 --- a/fs/mount/fs_automount.c +++ b/fs/mount/fs_automount.c @@ -40,6 +40,14 @@ #include #include +#ifdef CONFIG_FS_AUTOMOUNTER_DRIVER +# include + +# include +# include +# include +#endif /* CONFIG_FS_AUTOMOUNTER_DRIVER */ + #include "inode/inode.h" /**************************************************************************** @@ -51,6 +59,10 @@ * CONFIG_FS_AUTOMOUNTER - Enables AUTOMOUNT support */ +#ifndef CONFIG_FS_AUTOMOUNTER_VFS_PATH +# define CONFIG_FS_AUTOMOUNTER_VFS_PATH "/var" +#endif + /* Pre-requisites */ #ifndef CONFIG_SCHED_WORKQUEUE @@ -75,24 +87,307 @@ struct automounter_state_s struct wdog_s wdog; /* Delay to retry un-mounts */ bool mounted; /* True: Volume has been mounted */ bool inserted; /* True: Media has been inserted */ + +#ifdef CONFIG_FS_AUTOMOUNTER_DRIVER + sem_t exclsem; /* Supports exclusive access to the device */ + bool registered; /* True: if driver has been registered */ + + /* The following is a singly linked list of open references to the + * automounter device. + */ + + FAR struct automounter_open_s *ao_open; +#endif /* CONFIG_FS_AUTOMOUNTER_DRIVER */ }; +/* This structure describes the state of one open automounter driver + * instance + */ + +#ifdef CONFIG_FS_AUTOMOUNTER_DRIVER +struct automounter_open_s +{ + /* Supports a singly linked list */ + + FAR struct automounter_open_s *ao_flink; + + /* Mount event notification information */ + + pid_t ao_pid; + struct automount_notify_s ao_notify; + struct sigwork_s ao_work; +}; +#endif /* CONFIG_FS_AUTOMOUNTER_DRIVER */ + /**************************************************************************** * Private Function Prototypes ****************************************************************************/ +#ifdef CONFIG_FS_AUTOMOUNTER_DRIVER +static void automount_notify(FAR struct automounter_state_s *priv); + +static int automount_open(FAR struct file *filep); +static int automount_close(FAR struct file *filep); +static int automount_ioctl(FAR struct file *filep, int cmd, + unsigned long arg); +#endif /* CONFIG_FS_AUTOMOUNTER_DRIVER */ + static int automount_findinode(FAR const char *path); static void automount_mount(FAR struct automounter_state_s *priv); static int automount_unmount(FAR struct automounter_state_s *priv); static void automount_timeout(wdparm_t arg); static void automount_worker(FAR void *arg); static int automount_interrupt(FAR const struct automount_lower_s *lower, - FAR void *arg, bool inserted); + FAR void *arg, bool inserted); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#ifdef CONFIG_FS_AUTOMOUNTER_DRIVER +static const struct file_operations automount_fops = +{ + automount_open, /* open */ + automount_close, /* close */ + NULL, /* read */ + NULL, /* write */ + NULL, /* seek */ + automount_ioctl, /* ioctl */ + NULL /* poll */ +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + , NULL /* unlink */ +#endif +}; +#endif /* CONFIG_FS_AUTOMOUNTER_DRIVER */ /**************************************************************************** * Private Functions ****************************************************************************/ +#ifdef CONFIG_FS_AUTOMOUNTER_DRIVER + +/**************************************************************************** + * Name: automount_notify + ****************************************************************************/ + +static void automount_notify(FAR struct automounter_state_s *priv) +{ + FAR struct automounter_open_s *opriv; + int ret; + + /* Get exclusive access to the driver structure */ + + ret = nxsem_wait_uninterruptible(&priv->exclsem); + if (ret < 0) + { + ierr("ERROR: nxsem_wait_uninterruptible failed: %d\n", ret); + return; + } + + /* Visit each opened reference to the device */ + + for (opriv = priv->ao_open; opriv != NULL; opriv = opriv->ao_flink) + { + /* Have any signal events occurred? */ + + if ((priv->mounted && opriv->ao_notify.an_mount) || + (!priv->mounted && opriv->ao_notify.an_umount)) + { + /* Yes.. Signal the waiter */ + + opriv->ao_notify.an_event.sigev_value.sival_int = priv->mounted; + nxsig_notification(opriv->ao_pid, &opriv->ao_notify.an_event, + SI_QUEUE, &opriv->ao_work); + } + } + + nxsem_post(&priv->exclsem); +} + +/**************************************************************************** + * Name: automount_open + ****************************************************************************/ + +static int automount_open(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct automounter_state_s *priv = inode->i_private; + FAR struct automounter_open_s *opriv; + int ret; + + /* Get exclusive access to the driver structure */ + + ret = nxsem_wait(&priv->exclsem); + if (ret < 0) + { + ierr("ERROR: nxsem_wait failed: %d\n", ret); + return ret; + } + + /* Allocate a new open structure */ + + opriv = (FAR struct automounter_open_s *)kmm_zalloc( + sizeof(struct automounter_open_s)); + if (opriv == NULL) + { + ierr("ERROR: Failed to allocate open structure\n"); + ret = -ENOMEM; + goto errout_with_exclsem; + } + + /* Attach the open structure to the device */ + + opriv->ao_flink = priv->ao_open; + priv->ao_open = opriv; + + /* Attach the open structure to the file structure */ + + filep->f_priv = (FAR void *)opriv; + ret = OK; + +errout_with_exclsem: + nxsem_post(&priv->exclsem); + return ret; +} + +/**************************************************************************** + * Name: automount_close + ****************************************************************************/ + +static int automount_close(FAR struct file *filep) +{ + FAR struct inode *inode; + FAR struct automounter_state_s *priv; + FAR struct automounter_open_s *opriv; + FAR struct automounter_open_s *curr; + FAR struct automounter_open_s *prev; + int ret; + + DEBUGASSERT(filep && filep->f_priv && filep->f_inode); + opriv = filep->f_priv; + inode = filep->f_inode; + DEBUGASSERT(inode->i_private); + priv = (FAR struct automounter_state_s *)inode->i_private; + + /* Get exclusive access to the driver structure */ + + ret = nxsem_wait(&priv->exclsem); + if (ret < 0) + { + ierr("ERROR: nxsem_wait failed: %d\n", ret); + return ret; + } + + /* Find the open structure in the list of open structures for the device */ + + for (prev = NULL, curr = priv->ao_open; + curr != NULL && curr != opriv; + prev = curr, curr = curr->ao_flink); + + DEBUGASSERT(curr); + if (curr == NULL) + { + ierr("ERROR: Failed to find open entry\n"); + ret = -ENOENT; + goto errout_with_exclsem; + } + + /* Remove the structure from the device */ + + if (prev != NULL) + { + prev->ao_flink = opriv->ao_flink; + } + else + { + priv->ao_open = opriv->ao_flink; + } + + /* Cancel any pending notification */ + + nxsig_cancel_notification(&opriv->ao_work); + + /* And free the open structure */ + + kmm_free(opriv); + + ret = OK; + +errout_with_exclsem: + nxsem_post(&priv->exclsem); + return ret; +} + +/**************************************************************************** + * Name: automount_ioctl + ****************************************************************************/ + +static int automount_ioctl(FAR struct file *filep, int cmd, + unsigned long arg) +{ + FAR struct inode *inode; + FAR struct automounter_state_s *priv; + FAR struct automounter_open_s *opriv; + int ret; + + DEBUGASSERT(filep && filep->f_priv && filep->f_inode); + opriv = filep->f_priv; + inode = filep->f_inode; + DEBUGASSERT(inode->i_private); + priv = (FAR struct automounter_state_s *)inode->i_private; + + /* Get exclusive access to the driver structure */ + + ret = nxsem_wait(&priv->exclsem); + if (ret < 0) + { + ierr("ERROR: nxsem_wait failed: %d\n", ret); + return ret; + } + + /* Handle the ioctl command */ + + ret = -EINVAL; + switch (cmd) + { + /* Command: FIOC_NOTIFY + * Description: Register to receive a signal whenever volume is mounted + * or unmounted by automounter. + * Argument: A read-only pointer to an instance of struct + * automount_notify_s + * Return: Zero (OK) on success. Minus one will be returned on + * failure with the errno value set appropriately. + */ + + case FIOC_NOTIFY: + { + FAR struct automount_notify_s *notify = + (FAR struct automount_notify_s *)((uintptr_t)arg); + + if (notify != NULL) + { + /* Save the notification events */ + + opriv->ao_notify.an_mount = notify->an_mount; + opriv->ao_notify.an_umount = notify->an_umount; + opriv->ao_notify.an_event = notify->an_event; + opriv->ao_pid = getpid(); + ret = OK; + } + } + break; + + default: + ierr("ERROR: Unrecognized command: %d\n", cmd); + ret = -ENOTTY; + break; + } + + nxsem_post(&priv->exclsem); + return ret; +} +#endif /* CONFIG_FS_AUTOMOUNTER_DRIVER */ + /**************************************************************************** * Name: automount_findinode * @@ -229,6 +524,11 @@ static void automount_mount(FAR struct automounter_state_s *priv) /* Indicate that the volume is mounted */ priv->mounted = true; + +#ifdef CONFIG_FS_AUTOMOUNTER_DRIVER + automount_notify(priv); +#endif /* CONFIG_FS_AUTOMOUNTER_DRIVER */ + break; default: @@ -313,7 +613,15 @@ static int automount_unmount(FAR struct automounter_state_s *priv) * media. Nice job, Mr. user. */ - priv->mounted = false; + if (priv->mounted) + { + priv->mounted = false; + +#ifdef CONFIG_FS_AUTOMOUNTER_DRIVER + automount_notify(priv); +#endif /* CONFIG_FS_AUTOMOUNTER_DRIVER */ + } + return OK; default: @@ -515,6 +823,9 @@ FAR void *automount_initialize(FAR const struct automount_lower_s *lower) { FAR struct automounter_state_s *priv; int ret; +#ifdef CONFIG_FS_AUTOMOUNTER_DRIVER + char devpath[PATH_MAX]; +#endif /* CONFIG_FS_AUTOMOUNTER_DRIVER */ finfo("lower=%p\n", lower); DEBUGASSERT(lower); @@ -524,7 +835,7 @@ FAR void *automount_initialize(FAR const struct automount_lower_s *lower) priv = (FAR struct automounter_state_s *) kmm_zalloc(sizeof(struct automounter_state_s)); - if (!priv) + if (priv == NULL) { ferr("ERROR: Failed to allocate state structure\n"); return NULL; @@ -551,6 +862,27 @@ FAR void *automount_initialize(FAR const struct automount_lower_s *lower) ferr("ERROR: Failed to schedule work: %d\n", ret); } +#ifdef CONFIG_FS_AUTOMOUNTER_DRIVER + + /* Initialize the new automount driver instance */ + + nxsem_init(&priv->exclsem, 0, 1); + + /* Register driver */ + + sprintf(devpath, CONFIG_FS_AUTOMOUNTER_VFS_PATH "%s", lower->mountpoint); + + ret = register_driver(devpath, &automount_fops, 0444, priv); + if (ret < 0) + { + ferr("ERROR: Failed to register automount driver: %d\n", ret); + automount_uninitialize(priv); + return NULL; + } + + priv->registered = true; +#endif /* CONFIG_FS_AUTOMOUNTER_DRIVER */ + /* Attach and enable automounter interrupts */ ret = AUTOMOUNT_ATTACH(lower, automount_interrupt, priv); @@ -594,6 +926,20 @@ void automount_uninitialize(FAR void *handle) AUTOMOUNT_DISABLE(lower); AUTOMOUNT_DETACH(lower); +#ifdef CONFIG_FS_AUTOMOUNTER_DRIVER + if (priv->registered) + { + char devpath[PATH_MAX]; + + sprintf(devpath, CONFIG_FS_AUTOMOUNTER_VFS_PATH "%s", + lower->mountpoint); + + unregister_driver(devpath); + } + + nxsem_destroy(&priv->exclsem); +#endif /* CONFIG_FS_AUTOMOUNTER_DRIVER */ + /* Cancel the watchdog timer */ wd_cancel(&priv->wdog); diff --git a/include/nuttx/fs/automount.h b/include/nuttx/fs/automount.h index 7c4c1987e1..0f87a2ea63 100644 --- a/include/nuttx/fs/automount.h +++ b/include/nuttx/fs/automount.h @@ -27,6 +27,7 @@ #include #include +#include #include @@ -70,6 +71,22 @@ typedef CODE int (*automount_handler_t)(FAR const struct automount_lower_s *lower, FAR void *arg, bool inserted); +#ifdef CONFIG_FS_AUTOMOUNTER_DRIVER + +/* A reference to this structure is provided with the FIOC_NOTIFY IOCTL + * command and describes the conditions under which the client would like + * to receive notification. + */ + +struct automount_notify_s +{ + bool an_mount; /* FS mount to be notified */ + bool an_umount; /* FS umount to be notified */ + struct sigevent an_event; /* Describe the way a task is to be notified */ +}; + +#endif /* CONFIG_FS_AUTOMOUNTER_DRIVER */ + /* A reference to a structure of this type must be passed to the FS * automounter. This structure provides information about the volume to be * mounted and provides board-specific hooks. diff --git a/include/nuttx/fs/ioctl.h b/include/nuttx/fs/ioctl.h index fc376837b6..3c3038565b 100644 --- a/include/nuttx/fs/ioctl.h +++ b/include/nuttx/fs/ioctl.h @@ -147,11 +147,12 @@ #define FIOC_INTEGRITY _FIOC(0x0005) /* Run a consistency check on the * file system media. * IN: None - * OUT: None */ + * OUT: None + */ #define FIOC_DUMP _FIOC(0x0006) /* Dump logical content of media. * IN: None - * OUT: None */ - + * OUT: None + */ #define FIONREAD _FIOC(0x0007) /* IN: Location to return value (int *) * OUT: Bytes readable from this fd */ @@ -176,6 +177,11 @@ #define FIONCLEX _FIOC(0x000d) /* IN: None * OUT: None */ +#define FIOC_NOTIFY _FIOC(0x000e) /* IN: Pointer to struct automount_notify_s + * holding automount notification + * configuration + * OUT: None + */ /* NuttX file system ioctl definitions **************************************/