diff options
-rw-r--r-- | apps/nshlib/nsh_mntcmds.c | 9 | ||||
-rw-r--r-- | nuttx/ChangeLog | 4 | ||||
-rw-r--r-- | nuttx/drivers/mtd/Kconfig | 10 | ||||
-rw-r--r-- | nuttx/drivers/mtd/Make.defs | 6 | ||||
-rw-r--r-- | nuttx/drivers/mtd/at24xx.c | 6 | ||||
-rw-r--r-- | nuttx/drivers/mtd/at25.c | 6 | ||||
-rw-r--r-- | nuttx/drivers/mtd/at45db.c | 6 | ||||
-rw-r--r-- | nuttx/drivers/mtd/m25px.c | 6 | ||||
-rw-r--r-- | nuttx/drivers/mtd/mtd_partition.c | 405 | ||||
-rw-r--r-- | nuttx/drivers/mtd/mtd_procfs.c | 352 | ||||
-rw-r--r-- | nuttx/drivers/mtd/rammtd.c | 7 | ||||
-rw-r--r-- | nuttx/drivers/mtd/ramtron.c | 6 | ||||
-rw-r--r-- | nuttx/drivers/mtd/sst25.c | 6 | ||||
-rw-r--r-- | nuttx/drivers/mtd/sst25xx.c | 6 | ||||
-rw-r--r-- | nuttx/drivers/mtd/sst39vf.c | 6 | ||||
-rw-r--r-- | nuttx/drivers/mtd/w25.c | 6 | ||||
-rw-r--r-- | nuttx/fs/procfs/Kconfig | 46 | ||||
-rw-r--r-- | nuttx/fs/procfs/Make.defs | 2 | ||||
-rw-r--r-- | nuttx/fs/procfs/fs_procfs.c | 991 | ||||
-rw-r--r-- | nuttx/fs/procfs/fs_procfsproc.c | 1003 | ||||
-rw-r--r-- | nuttx/fs/procfs/fs_skeleton.c | 460 | ||||
-rw-r--r-- | nuttx/fs/smartfs/Make.defs | 2 | ||||
-rw-r--r-- | nuttx/fs/smartfs/smartfs_procfs.c | 460 | ||||
-rw-r--r-- | nuttx/include/nuttx/fs/procfs.h | 144 | ||||
-rw-r--r-- | nuttx/include/nuttx/mtd/mtd.h | 46 |
25 files changed, 3332 insertions, 669 deletions
diff --git a/apps/nshlib/nsh_mntcmds.c b/apps/nshlib/nsh_mntcmds.c index ee242bece..741327405 100644 --- a/apps/nshlib/nsh_mntcmds.c +++ b/apps/nshlib/nsh_mntcmds.c @@ -194,31 +194,34 @@ static int df_man_readable_handler(FAR const char *mountpoint, /* Find the label for size */ which = 0; - while (size >= 9999 || (size & 0x3FF) == 0 && size) + while (size >= 9999 || (size & 0x3ff) == 0 && size) { which++; size >>= 10; } + sizelabel = labels[which]; /* Find the label for free */ which = 0; - while (free >= 9999 || (free & 0x3FF) == 0 && size) + while (free >= 9999 || (free & 0x3ff) == 0 && free) { which++; free >>= 10; } + freelabel = labels[which]; /* Find the label for used */ which = 0; - while (used >= 9999 || (used & 0x3FF) == 0 && size) + while (used >= 9999 || (used & 0x3ff) == 0 && used) { which++; used >>= 10; } + usedlabel = labels[which]; #ifndef CONFIG_NUTTX_KERNEL diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog index 792230a0a..a0736f6bb 100644 --- a/nuttx/ChangeLog +++ b/nuttx/ChangeLog @@ -6200,3 +6200,7 @@ * libc/misc/lib_match.c: Pattern matching logic extended to handle matches to sets of characters and ranges of character values. From Ken Pettit (2013-12-12). + * fs/procfs, drivers/mtd, fs/smartfs, includes/nuttx/fs, .. to many + files to list: Extensive changes by Ken Pettit to extend procfs/ + functionality. Includes some incomplete changes related to + SmartFS (2013-12-12). diff --git a/nuttx/drivers/mtd/Kconfig b/nuttx/drivers/mtd/Kconfig index f5b0e9574..c4bb65461 100644 --- a/nuttx/drivers/mtd/Kconfig +++ b/nuttx/drivers/mtd/Kconfig @@ -26,6 +26,16 @@ config MTD_PARTITION managing the sub-region of flash beginning at 'offset' (in blocks) and of size 'nblocks' on the device specified by 'mtd'. +config MTD_PARTITION_NAMES + bool "Support MTD partition naming" + depends on FS_PROCFS + depends on MTD_PARTITION + default n + ---help--- + MTD partitions can be assigned a name for reporting via the procfs + file system interface. This adds an API which must be called to + specify the partition name. + config MTD_BYTE_WRITE bool "Byte write" default n diff --git a/nuttx/drivers/mtd/Make.defs b/nuttx/drivers/mtd/Make.defs index 758073e36..718508185 100644 --- a/nuttx/drivers/mtd/Make.defs +++ b/nuttx/drivers/mtd/Make.defs @@ -86,6 +86,12 @@ CSRCS += smart.c endif endif +ifeq ($(CONFIG_FS_PROCFS),y) +ifneq ($(CONFIG_FS_PROCFS_EXCLUDE_MTD),y) +CSRCS += mtd_procfs.c +endif +endif + # Include MTD driver support DEPPATH += --dep-path mtd diff --git a/nuttx/drivers/mtd/at24xx.c b/nuttx/drivers/mtd/at24xx.c index abf4d84d8..18e011634 100644 --- a/nuttx/drivers/mtd/at24xx.c +++ b/nuttx/drivers/mtd/at24xx.c @@ -429,6 +429,12 @@ FAR struct mtd_dev_s *at24c_initialize(FAR struct i2c_dev_s *dev) priv->dev = dev; } + /* Register the MTD with the procfs system if enabled */ + +#ifdef CONFIG_MTD_REGISTRATION + mtd_register(&priv->mtd, "at24xx"); +#endif + /* Return the implementation-specific state structure as the MTD device */ fvdbg("Return %p\n", priv); diff --git a/nuttx/drivers/mtd/at25.c b/nuttx/drivers/mtd/at25.c index 880398aa1..25fa82d21 100644 --- a/nuttx/drivers/mtd/at25.c +++ b/nuttx/drivers/mtd/at25.c @@ -709,6 +709,12 @@ FAR struct mtd_dev_s *at25_initialize(FAR struct spi_dev_s *dev) } } + /* Register the MTD with the procfs system if enabled */ + +#ifdef CONFIG_MTD_REGISTRATION + mtd_register(&priv->mtd, "at25"); +#endif + /* Return the implementation-specific state structure as the MTD device */ fvdbg("Return %p\n", priv); diff --git a/nuttx/drivers/mtd/at45db.c b/nuttx/drivers/mtd/at45db.c index 372e6be57..6c5bcf3ca 100644 --- a/nuttx/drivers/mtd/at45db.c +++ b/nuttx/drivers/mtd/at45db.c @@ -886,6 +886,12 @@ FAR struct mtd_dev_s *at45db_initialize(FAR struct spi_dev_s *spi) at45db_unlock(priv); } + /* Register the MTD with the procfs system if enabled */ + +#ifdef CONFIG_MTD_REGISTRATION + mtd_register(&priv->mtd, "at45db"); +#endif + fvdbg("Return %p\n", priv); return (FAR struct mtd_dev_s *)priv; diff --git a/nuttx/drivers/mtd/m25px.c b/nuttx/drivers/mtd/m25px.c index 3268a9f9f..3e241c13a 100644 --- a/nuttx/drivers/mtd/m25px.c +++ b/nuttx/drivers/mtd/m25px.c @@ -1032,6 +1032,12 @@ FAR struct mtd_dev_s *m25p_initialize(FAR struct spi_dev_s *dev) } } + /* Register the MTD with the procfs system if enabled */ + +#ifdef CONFIG_MTD_REGISTRATION + mtd_register(&priv->mtd, "m25px"); +#endif + /* Return the implementation-specific state structure as the MTD device */ fvdbg("Return %p\n", priv); diff --git a/nuttx/drivers/mtd/mtd_partition.c b/nuttx/drivers/mtd/mtd_partition.c index 1e788cf36..9fca44011 100644 --- a/nuttx/drivers/mtd/mtd_partition.c +++ b/nuttx/drivers/mtd/mtd_partition.c @@ -45,10 +45,17 @@ #include <errno.h> #include <assert.h> #include <debug.h> +#include <string.h> +#include <fcntl.h> +#include <stdio.h> +#include <sys/stat.h> #include <nuttx/mtd/mtd.h> #include <nuttx/kmalloc.h> #include <nuttx/fs/ioctl.h> +#ifdef CONFIG_FS_PROCFS +#include <nuttx/fs/procfs.h> +#endif /**************************************************************************** * Pre-processor Definitions @@ -81,7 +88,24 @@ struct mtd_partition_s * sub-region */ off_t blocksize; /* The size of one read/write block */ uint16_t blkpererase; /* Number of R/W blocks in one erase block */ + +#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_PROCFS_EXCLUDE_PARTITIONS) + struct mtd_partition_s *pnext; /* Pointer to next partition struct */ +#endif +#ifdef CONFIG_MTD_PARTITION_NAMES + FAR const char *name; /* Name of the partition */ +#endif +}; + +/* This structure describes one open "file" */ + +#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_PROCFS_EXCLUDE_PARTITIONS) +struct part_procfs_file_s +{ + struct procfs_file_s base; /* Base open file structure */ + struct mtd_partition_s *nextpart; }; +#endif /**************************************************************************** * Private Function Prototypes @@ -89,23 +113,70 @@ struct mtd_partition_s /* MTD driver methods */ -static int part_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks); -static ssize_t part_bread(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks, - FAR uint8_t *buf); -static ssize_t part_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks, - FAR const uint8_t *buf); -static ssize_t part_read(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes, - FAR uint8_t *buffer); +static int part_erase(FAR struct mtd_dev_s *dev, off_t startblock, + size_t nblocks); +static ssize_t part_bread(FAR struct mtd_dev_s *dev, off_t startblock, + size_t nblocks, FAR uint8_t *buf); +static ssize_t part_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, + size_t nblocks, FAR const uint8_t *buf); +static ssize_t part_read(FAR struct mtd_dev_s *dev, off_t offset, + size_t nbytes, FAR uint8_t *buffer); #ifdef CONFIG_MTD_BYTE_WRITE -static ssize_t part_write(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes, - FAR const uint8_t *buffer); +static ssize_t part_write(FAR struct mtd_dev_s *dev, off_t offset, + size_t nbytes, FAR const uint8_t *buffer); +#endif +static int part_ioctl(FAR struct mtd_dev_s *dev, int cmd, + unsigned long arg); + +/* File system methods */ + +#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_PROCFS_EXCLUDE_PARTITIONS) +static int part_procfs_open(FAR struct file *filep, + FAR const char *relpath, int oflags, mode_t mode); +static int part_procfs_close(FAR struct file *filep); +static ssize_t part_procfs_read(FAR struct file *filep, FAR char *buffer, + size_t buflen); + +static int part_procfs_dup(FAR const struct file *oldp, + FAR struct file *newp); + +#if 0 /* Not implemented */ +static int part_procfs_opendir(const char *relpath, + FAR struct fs_dirent_s *dir); +static int part_procfs_closedir(FAR struct fs_dirent_s *dir); +static int part_procfs_readdir(FAR struct fs_dirent_s *dir); +static int part_procfs_rewinddir(FAR struct fs_dirent_s *dir); +#endif + +static int part_procfs_stat(FAR const char *relpath, + FAR struct stat *buf); #endif -static int part_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg); /**************************************************************************** * Private Data ****************************************************************************/ +#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_PROCFS_EXCLUDE_PARTITIONS) +struct mtd_partition_s *g_pfirstpartition = NULL; + +const struct procfs_operations part_procfsoperations = +{ + part_procfs_open, /* open */ + part_procfs_close, /* close */ + part_procfs_read, /* read */ + NULL, /* write */ + + part_procfs_dup, /* dup */ + + NULL, /* opendir */ + NULL, /* closedir */ + NULL, /* readdir */ + NULL, /* rewinddir */ + + part_procfs_stat /* stat */ +}; +#endif + /**************************************************************************** * Name: part_blockcheck * @@ -397,6 +468,269 @@ static int part_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg) return ret; } +#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_PROCFS_EXCLUDE_PARTITIONS) + +/**************************************************************************** + * Name: part_procfs_open + ****************************************************************************/ + +static int part_procfs_open(FAR struct file *filep, FAR const char *relpath, + int oflags, mode_t mode) +{ + FAR struct part_procfs_file_s *attr; + + fvdbg("Open '%s'\n", relpath); + + /* PROCFS is read-only. Any attempt to open with any kind of write + * access is not permitted. + * + * REVISIT: Write-able proc files could be quite useful. + */ + + if ((oflags & O_WRONLY) != 0 || (oflags & O_RDONLY) == 0) + { + fdbg("ERROR: Only O_RDONLY supported\n"); + return -EACCES; + } + + /* Allocate a container to hold the task and attribute selection */ + + attr = (FAR struct part_procfs_file_s *)kzalloc(sizeof(struct part_procfs_file_s)); + if (!attr) + { + fdbg("ERROR: Failed to allocate file attributes\n"); + return -ENOMEM; + } + + /* Initialize the file attributes */ + + attr->nextpart = g_pfirstpartition; + + /* Save the index as the open-specific state in filep->f_priv */ + + filep->f_priv = (FAR void *)attr; + return OK; +} + +/**************************************************************************** + * Name: part_procfs_close + ****************************************************************************/ + +static int part_procfs_close(FAR struct file *filep) +{ + FAR struct part_procfs_file_s *attr; + + /* Recover our private data from the struct file instance */ + + attr = (FAR struct part_procfs_file_s *)filep->f_priv; + DEBUGASSERT(attr); + + /* Release the file attributes structure */ + + kfree(attr); + filep->f_priv = NULL; + return OK; +} + +/**************************************************************************** + * Name: part_procfs_read + ****************************************************************************/ + +static ssize_t part_procfs_read(FAR struct file *filep, FAR char *buffer, + size_t buflen) +{ + FAR struct part_procfs_file_s *attr; + ssize_t ret, total = 0, blkpererase; + FAR struct mtd_geometry_s geo; +#ifdef CONFIG_MTD_PARTITION_NAMES + char partname[11]; + FAR const char *ptr; + uint8_t x; +#endif + + fvdbg("buffer=%p buflen=%d\n", buffer, (int)buflen); + + /* Recover our private data from the struct file instance */ + + attr = (FAR struct part_procfs_file_s *)filep->f_priv; + DEBUGASSERT(attr); + + /* Provide the requested data */ + + if (attr->nextpart == g_pfirstpartition) + { +#ifdef CONFIG_MTD_PARTITION_NAMES + total = snprintf(buffer, buflen, "Name Start Size"); +#else + total = snprintf(buffer, buflen, " Start Size"); +#endif + +#ifndef CONFIG_FS_PROCFS_EXCLUDE_MTD + total += snprintf(&buffer[total], buflen - total, " MTD\n"); +#else + total += snprintf(&buffer[total], buflen - total, "\n"); +#endif + } + + while (attr->nextpart) + { + /* Get the geometry of the FLASH device */ + + ret = attr->nextpart->parent->ioctl(attr->nextpart->parent, MTDIOC_GEOMETRY, + (unsigned long)((uintptr_t)&geo)); + if (ret < 0) + { + fdbg("ERROR: mtd->ioctl failed: %d\n", ret); + return 0; + } + + /* Get the number of blocks per erase. There must be an even number of + * blocks in one erase blocks. + */ + + blkpererase = geo.erasesize / geo.blocksize; + + /* Copy data from the next known partition */ + +#ifdef CONFIG_MTD_PARTITION_NAMES + if (attr->nextpart->name == NULL) + { + strcpy(partname, "(noname) "); + } + else + { + ptr = attr->nextpart->name; + for (x = 0; x < sizeof(partname) - 1; x++) + { + /* Test for end of partition name */ + + if (*ptr == ',' || *ptr == '\0') + { + /* Perform space fill for alignment */ + + partname[x] = ' '; + } + else + { + /* Copy next byte of partition name */ + + partname[x] = *ptr++; + } + } + + partname[x] = '\0'; + } + + /* Terminate the partition name and add to output buffer */ + + ret = snprintf(&buffer[total], buflen - total, "%s%7d %7d", + partname, attr->nextpart->firstblock / blkpererase, + attr->nextpart->neraseblocks); +#else + ret = snprintf(&buffer[total], buflen - total, "%7d %7d", + attr->nextpart->firstblock / blkpererase, + attr->nextpart->neraseblocks); +#endif + +#ifndef CONFIG_FS_PROCFS_EXCLUDE_MTD + if (ret + total < buflen) + { + ret += snprintf(&buffer[total + ret], buflen - (total + ret), + " %s\n", attr->nextpart->parent->name); + } +#else + if (ret + total < buflen) + { + ret += snprintf(&buffer[total + ret], buflen - (total + ret), "\n"); + } +#endif + + if (ret + total < buflen) + { + /* It fit in the buffer totally. Advance total and move to + * next partition. + */ + total += ret; + attr->nextpart = attr->nextpart->pnext; + } + else + { + /* This one didn't fit completely. Truncate the partial + * entry and break the loop. + */ + buffer[total] = '\0'; + break; + } + } + + /* Update the file offset */ + + if (total > 0) + { + filep->f_pos += total; + } + + return total; +} + +/**************************************************************************** + * Name: part_procfs_dup + * + * Description: + * Duplicate open file data in the new file structure. + * + ****************************************************************************/ + +static int part_procfs_dup(FAR const struct file *oldp, FAR struct file *newp) +{ + FAR struct part_procfs_file_s *oldattr; + FAR struct part_procfs_file_s *newattr; + + fvdbg("Dup %p->%p\n", oldp, newp); + + /* Recover our private data from the old struct file instance */ + + oldattr = (FAR struct part_procfs_file_s *)oldp->f_priv; + DEBUGASSERT(oldattr); + + /* Allocate a new container to hold the task and attribute selection */ + + newattr = (FAR struct part_procfs_file_s *)kzalloc(sizeof(struct part_procfs_file_s)); + if (!newattr) + { + fdbg("ERROR: Failed to allocate file attributes\n"); + return -ENOMEM; + } + + /* The copy the file attribtes from the old attributes to the new */ + + memcpy(newattr, oldattr, sizeof(struct part_procfs_file_s)); + + /* Save the new attributes in the new file structure */ + + newp->f_priv = (FAR void *)newattr; + return OK; +} + +/**************************************************************************** + * Name: part_procfs_stat + * + * Description: Return information about a file or directory + * + ****************************************************************************/ + +static int part_procfs_stat(const char *relpath, struct stat *buf) +{ + /* File/directory size, access block size */ + + buf->st_mode = S_IFREG|S_IROTH|S_IRGRP|S_IRUSR; + buf->st_size = 0; + buf->st_blksize = 0; + buf->st_blocks = 0; + return OK; +} +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -500,8 +834,59 @@ FAR struct mtd_dev_s *mtd_partition(FAR struct mtd_dev_s *mtd, off_t firstblock, part->blocksize = geo.blocksize; part->blkpererase = blkpererase; +#ifdef CONFIG_MTD_PARTITION_NAMES + part->name = NULL; +#endif + +#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_PROCFS_EXCLUDE_PARTITIONS) + /* Add this parition to the list of known partitions */ + + if (g_pfirstpartition == NULL) + { + g_pfirstpartition = part; + } + else + { + struct mtd_partition_s *plast; + + /* Add the partition to the end of the list */ + part->pnext = NULL; + + plast = g_pfirstpartition; + while (plast->pnext != NULL) + { + /* Get pointer to next partition */ + plast = plast->pnext; + } + + plast->pnext = part; + } +#endif + /* Return the implementation-specific state structure as the MTD device */ return &part->child; } +/**************************************************************************** + * Name: mtd_setpartitionname + * + * Description: + * Sets the name of the specified partition. + * + ****************************************************************************/ +#ifdef CONFIG_MTD_PARTITION_NAMES + +int mtd_setpartitionname(FAR struct mtd_dev_s *mtd, FAR const char *name) +{ + FAR struct mtd_partition_s *priv = (FAR struct mtd_partition_s *)mtd; + + DEBUGASSERT(mtd); + DEBUGASSERT(name); + + /* Allocate space for the name */ + priv->name = name; + return OK; +} +#endif + diff --git a/nuttx/drivers/mtd/mtd_procfs.c b/nuttx/drivers/mtd/mtd_procfs.c new file mode 100644 index 000000000..2a2ba32a2 --- /dev/null +++ b/nuttx/drivers/mtd/mtd_procfs.c @@ -0,0 +1,352 @@ +/**************************************************************************** + * drivers/mtd/mtd_procfs.c + * + * Copyright (C) 2013 Ken Pettit. All rights reserved. + * Author: Ken Pettit <pettitkd@gmail.com> + * + * 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/statfs.h> +#include <sys/stat.h> + +#include <stdint.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <assert.h> +#include <errno.h> +#include <debug.h> + +#include <nuttx/arch.h> +#include <nuttx/kmalloc.h> +#include <nuttx/fs/fs.h> +#include <nuttx/fs/procfs.h> +#include <nuttx/mtd/mtd.h> + +#if !defined(CONFIG_FS_PROCFS_EXCLUDE_MTD) && defined(CONFIG_FS_PROCFS) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ +/* This structure describes one open "file" */ + +struct mtd_file_s +{ + struct procfs_file_s base; /* Base open file structure */ + FAR struct mtd_dev_s *pnextmtd; /* Pointer to next registered MTD */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ +/* File system methods */ + +static int mtd_open(FAR struct file *filep, FAR const char *relpath, + int oflags, mode_t mode); +static int mtd_close(FAR struct file *filep); +static ssize_t mtd_read(FAR struct file *filep, FAR char *buffer, + size_t buflen); + +static int mtd_dup(FAR const struct file *oldp, + FAR struct file *newp); + +static int mtd_stat(FAR const char *relpath, FAR struct stat *buf); + +/**************************************************************************** + * Private Variables + ****************************************************************************/ + +/**************************************************************************** + * Public Variables + ****************************************************************************/ + +/* See fs_mount.c -- this structure is explicitly externed there. + * We use the old-fashioned kind of initializers so that this will compile + * with any compiler. + */ + +const struct procfs_operations mtd_procfsoperations = +{ + mtd_open, /* open */ + mtd_close, /* close */ + mtd_read, /* read */ + NULL, /* write */ + + mtd_dup, /* dup */ + + NULL, /* opendir */ + NULL, /* closedir */ + NULL, /* readdir */ + NULL, /* rewinddir */ + + mtd_stat /* stat */ +}; + +/* MTD registration variables */ + +static struct mtd_dev_s *g_pfirstmtd = NULL; +static uint8_t g_nextmtdno = 0; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mtd_open + ****************************************************************************/ + +static int mtd_open(FAR struct file *filep, FAR const char *relpath, + int oflags, mode_t mode) +{ + FAR struct mtd_file_s *attr; + + fvdbg("Open '%s'\n", relpath); + + /* PROCFS is read-only. Any attempt to open with any kind of write + * access is not permitted. + * + * REVISIT: Write-able proc files could be quite useful. + */ + + if ((oflags & O_WRONLY) != 0 || (oflags & O_RDONLY) == 0) + { + fdbg("ERROR: Only O_RDONLY supported\n"); + return -EACCES; + } + + /* Allocate a context structure */ + + attr = (FAR struct mtd_file_s *)kzalloc(sizeof(struct mtd_file_s)); + if (!attr) + { + fdbg("ERROR: Failed to allocate file attributes\n"); + return -ENOMEM; + } + + attr->pnextmtd = g_pfirstmtd; + + /* Save the context as the open-specific state in filep->f_priv */ + + filep->f_priv = (FAR void *)attr; + return OK; +} + +/**************************************************************************** + * Name: mtd_close + ****************************************************************************/ + +static int mtd_close(FAR struct file *filep) +{ + FAR struct mtd_file_s *attr; + + /* Recover our private data from the struct file instance */ + + attr = (FAR struct mtd_file_s *)filep->f_priv; + DEBUGASSERT(attr); + + /* Release the file attributes structure */ + + kfree(attr); + filep->f_priv = NULL; + return OK; +} + +/**************************************************************************** + * Name: mtd_read + ****************************************************************************/ + +static ssize_t mtd_read(FAR struct file *filep, FAR char *buffer, + size_t buflen) +{ + FAR struct mtd_file_s *priv; + ssize_t total = 0, ret; + + fvdbg("buffer=%p buflen=%d\n", buffer, (int)buflen); + + /* Recover our private data from the struct file instance */ + + priv = (FAR struct mtd_file_s *)filep->f_priv; + DEBUGASSERT(priv); + + /* Provide the requested data */ + + if (priv->pnextmtd == g_pfirstmtd) + { + total = snprintf(buffer, buflen, "Num Device\n"); + } + + while (priv->pnextmtd) + { + ret = snprintf(&buffer[total], buflen - total, "%-5d%s\n", + priv->pnextmtd->mtdno, priv->pnextmtd->name); + + if (ret + total < buflen) + { + total += ret; + priv->pnextmtd = priv->pnextmtd->pnext; + } + else + { + buffer[total] = '\0'; + break; + } + } + + /* Update the file offset */ + + if (total > 0) + { + filep->f_pos += total; + } + + return total; +} + +/**************************************************************************** + * Name: mtd_dup + * + * Description: + * Duplicate open file data in the new file structure. + * + ****************************************************************************/ + +static int mtd_dup(FAR const struct file *oldp, FAR struct file *newp) +{ + FAR struct mtd_file_s *oldattr; + FAR struct mtd_file_s *newattr; + + fvdbg("Dup %p->%p\n", oldp, newp); + + /* Recover our private data from the old struct file instance */ + + oldattr = (FAR struct mtd_file_s *)oldp->f_priv; + DEBUGASSERT(oldattr); + + /* Allocate a new container to hold the task and attribute selection */ + + newattr = (FAR struct mtd_file_s *)kzalloc(sizeof(struct mtd_file_s)); + if (!newattr) + { + fdbg("ERROR: Failed to allocate file attributes\n"); + return -ENOMEM; + } + + /* The copy the file attribtes from the old attributes to the new */ + + memcpy(newattr, oldattr, sizeof(struct mtd_file_s)); + + /* Save the new attributes in the new file structure */ + + newp->f_priv = (FAR void *)newattr; + return OK; +} + +/**************************************************************************** + * Name: mtd_stat + * + * Description: Return information about a file or directory + * + ****************************************************************************/ + +static int mtd_stat(const char *relpath, struct stat *buf) +{ + /* File/directory size, access block size */ + + buf->st_mode = S_IFREG|S_IROTH|S_IRGRP|S_IRUSR; + buf->st_size = 0; + buf->st_blksize = 0; + buf->st_blocks = 0; + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mtd_register + * + * Description: + * Registers MTD device with the procfs file system. This assigns a unique + * MTD number and associates the given device name, then add adds it to + * the list of registered devices. + * + * In an embedded system, this all is really unnecessary, but is provided + * in the procfs system simply for information purposes (if desired). + * + ****************************************************************************/ +int mtd_register(FAR struct mtd_dev_s *mtd, FAR const char *name) +{ + FAR struct mtd_dev_s *plast; + + /* Assign the MTD number and device name */ + + mtd->mtdno = g_nextmtdno++; + mtd->name = name; + mtd->pnext = NULL; + + /* Add to the list of registered devices */ + + if (g_pfirstmtd == NULL) + { + g_pfirstmtd = mtd; + } + else + { + /* Insert at end of list */ + + plast = g_pfirstmtd; + while (plast->pnext) + { + /* Skip to next entry as long as there is one */ + + plast = plast->pnext; + } + + /* Now insert at this location */ + + plast->pnext = mtd; + } +} + +#endif /* !CONFIG_DISABLE_MOUNTPOINT && CONFIG_FS_PROCFS */ diff --git a/nuttx/drivers/mtd/rammtd.c b/nuttx/drivers/mtd/rammtd.c index c901499bc..907337b54 100644 --- a/nuttx/drivers/mtd/rammtd.c +++ b/nuttx/drivers/mtd/rammtd.c @@ -474,5 +474,12 @@ FAR struct mtd_dev_s *rammtd_initialize(FAR uint8_t *start, size_t size) priv->start = start; priv->nblocks = nblocks; + + /* Register the MTD with the procfs system if enabled */ + +#ifdef CONFIG_MTD_REGISTRATION + mtd_register(&priv->mtd, "rammtd"); +#endif + return &priv->mtd; } diff --git a/nuttx/drivers/mtd/ramtron.c b/nuttx/drivers/mtd/ramtron.c index a77d7d82b..e1e9908f1 100644 --- a/nuttx/drivers/mtd/ramtron.c +++ b/nuttx/drivers/mtd/ramtron.c @@ -689,6 +689,12 @@ FAR struct mtd_dev_s *ramtron_initialize(FAR struct spi_dev_s *dev) } } + /* Register the MTD with the procfs system if enabled */ + +#ifdef CONFIG_MTD_REGISTRATION + mtd_register(&priv->mtd, "ramtron"); +#endif + /* Return the implementation-specific state structure as the MTD device */ fvdbg("Return %p\n", priv); diff --git a/nuttx/drivers/mtd/sst25.c b/nuttx/drivers/mtd/sst25.c index 358a7763f..46f6350ea 100644 --- a/nuttx/drivers/mtd/sst25.c +++ b/nuttx/drivers/mtd/sst25.c @@ -1256,6 +1256,12 @@ FAR struct mtd_dev_s *sst25_initialize(FAR struct spi_dev_s *dev) } } + /* Register the MTD with the procfs system if enabled */ + +#ifdef CONFIG_MTD_REGISTRATION + mtd_register(&priv->mtd, "sst25"); +#endif + /* Return the implementation-specific state structure as the MTD device */ fvdbg("Return %p\n", priv); diff --git a/nuttx/drivers/mtd/sst25xx.c b/nuttx/drivers/mtd/sst25xx.c index b00f4d32a..da4204889 100644 --- a/nuttx/drivers/mtd/sst25xx.c +++ b/nuttx/drivers/mtd/sst25xx.c @@ -993,6 +993,12 @@ FAR struct mtd_dev_s *sst25xx_initialize(FAR struct spi_dev_s *dev) } } + /* Register the MTD with the procfs system if enabled */ + +#ifdef CONFIG_MTD_REGISTRATION + mtd_register(&priv->mtd, "sst25xx"); +#endif + /* Return the implementation-specific state structure as the MTD device */ fvdbg("Return %p\n", priv); diff --git a/nuttx/drivers/mtd/sst39vf.c b/nuttx/drivers/mtd/sst39vf.c index 0f7f49cf6..d42550574 100644 --- a/nuttx/drivers/mtd/sst39vf.c +++ b/nuttx/drivers/mtd/sst39vf.c @@ -844,6 +844,12 @@ FAR struct mtd_dev_s *sst39vf_initialize(void) return NULL; } + /* Register the MTD with the procfs system if enabled */ + +#ifdef CONFIG_MTD_REGISTRATION + mtd_register(&priv->mtd, "sst39vf"); +#endif + /* Return the state structure as the MTD device */ return (FAR struct mtd_dev_s *)&g_sst39vf; diff --git a/nuttx/drivers/mtd/w25.c b/nuttx/drivers/mtd/w25.c index 097662d09..0bddf8400 100644 --- a/nuttx/drivers/mtd/w25.c +++ b/nuttx/drivers/mtd/w25.c @@ -1174,6 +1174,12 @@ FAR struct mtd_dev_s *w25_initialize(FAR struct spi_dev_s *spi) } } + /* Register the MTD with the procfs system if enabled */ + +#ifdef CONFIG_MTD_REGISTRATION + mtd_register(&priv->mtd, "w25"); +#endif + /* Return the implementation-specific state structure as the MTD device */ fvdbg("Return %p\n", priv); diff --git a/nuttx/fs/procfs/Kconfig b/nuttx/fs/procfs/Kconfig index 9f20aa672..6c1c34bf0 100644 --- a/nuttx/fs/procfs/Kconfig +++ b/nuttx/fs/procfs/Kconfig @@ -6,9 +6,45 @@ config FS_PROCFS bool "PROCFS File System" default n - select FS_WRITABLE + select FS_READABLE ---help--- - The PROCFS file system is provides access to task status through the - NuttX file system. The PROCFS may, for example, be mount at /proc. - Then information about all of the currently active tasks and threads - will be available in proc/. + The PROCFS file system provides access to task status and other driver + status through the NuttX file system. The PROCFS may, for example, be + mount at /proc. Then information about all of the currently active + tasks and threads will be available in /proc. + +if FS_PROCFS + +menu "Exclude individual procfs entries" + +config FS_PROCFS_EXCLUDE_MOUNTS + bool "Exclude mounts" + depends on !DISABLE_MOUNTPOINT + default n + +config FS_PROCFS_EXCLUDE_MTD + bool "Exclude mtd" + depends on MTD + default n + +config FS_PROCFS_EXCLUDE_PROCESS + bool "Exclude process information" + default n + ---help--- + Causes the process information to be excluded from the procfs system. + This will reduce code space, but then giving access to process info + was kinda the whole point of procfs, but hey, whatever. + +config FS_PROCFS_EXCLUDE_PARTITIONS + bool "Exclude partitions" + depends on MTD_PARTITION + default n + +config FS_PROCFS_EXCLUDE_SMARTFS + bool "Exclude fs/smartfs" + depends on FS_SMARTFS + default n + +endmenu + +endif diff --git a/nuttx/fs/procfs/Make.defs b/nuttx/fs/procfs/Make.defs index 8eae9027c..b39fa782d 100644 --- a/nuttx/fs/procfs/Make.defs +++ b/nuttx/fs/procfs/Make.defs @@ -37,7 +37,7 @@ ifeq ($(CONFIG_FS_PROCFS),y) # Files required for procfs file system support ASRCS += -CSRCS += fs_procfs.c +CSRCS += fs_procfs.c fs_procfsproc.c # Include procfs build support diff --git a/nuttx/fs/procfs/fs_procfs.c b/nuttx/fs/procfs/fs_procfs.c index 599052603..2bd091bb6 100644 --- a/nuttx/fs/procfs/fs_procfs.c +++ b/nuttx/fs/procfs/fs_procfs.c @@ -57,7 +57,9 @@ #include <nuttx/sched.h> #include <nuttx/kmalloc.h> #include <nuttx/fs/fs.h> +#include <nuttx/fs/procfs.h> #include <nuttx/fs/dirent.h> +#include <nuttx/regex.h> #include <arch/irq.h> @@ -67,65 +69,42 @@ * Pre-processor Definitions ****************************************************************************/ -#define STATUS_LINELEN 32 - -#ifndef MIN -# define MIN(a,b) ((a < b) ? a : b) -#endif +#define PROCFS_NATTRS 2 /**************************************************************************** - * Private Types + * External Definitons ****************************************************************************/ -/* This enumeration identifies all of the thread attributes that can be - * accessed via the procfs file system. - */ - -enum procfs_attr_e -{ - PROCFS_STATUS = 0, /* Task/thread status */ - PROCFS_CMDLINE, /* Command line */ -}; -#define PROCFS_NATTRS 2 - -/* This structure describes one open "file" */ - -struct procfs_file_s -{ - pid_t pid; /* Task/thread ID */ - uint8_t attr; /* See enum procfs_attr_e */ - char line[STATUS_LINELEN]; /* Pre-allocated buffer for formatted lines */ -}; -/* The generic proc/ pseudo directory structure */ +extern const struct procfs_operations process_operations; +extern const struct procfs_operations mtd_procfsoperations; +extern const struct procfs_operations part_procfsoperations; +extern const struct procfs_operations smartfs_procfsoperations; -struct procfs_level_s -{ - uint8_t level; /* Directory level. Currently 0 or 1 */ - uint16_t index; /* Index to the next directory entry */ - uint16_t nentries; /* Number of directory entries */ -}; - -/* Level 0 is the directory of active tasks */ +/**************************************************************************** + * Private Types + ****************************************************************************/ +/* Table of all known / pre-registered procfs handlers / participants. */ -struct procfs_level0_s +static const struct procfs_entry_s g_procfsentries[] = { - uint8_t level; /* Directory level. Currently 0 or 1 */ - uint16_t index; /* Index to the next directory entry */ - uint16_t nentries; /* Number of directory entries */ - - pid_t pid[CONFIG_MAX_TASKS]; /* Snapshot of all active task IDs */ +#ifndef CONFIG_FS_PROCFS_EXCLUDE_PROCESS + { "[0-9]*/*", &process_operations }, + { "[0-9]*", &process_operations }, +#endif +#if defined(CONFIG_FS_SMARTFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS) +//{ "fs/smartfs", &smartfs_procfsoperations }, + { "fs/smartfs**", &smartfs_procfsoperations }, +#endif +#if defined(CONFIG_MTD) && !defined(CONFIG_FS_PROCFS_EXCLUDE_MTD) + { "mtd", &mtd_procfsoperations }, +#endif +#if defined(CONFIG_MTD_PARTITION) && !defined(CONFIG_FS_PROCFS_EXCLUDE_PARTITON) + { "partitions", &part_procfsoperations }, +#endif }; -/* Level 1 is the directory of task attributes */ - -struct procfs_level1_s -{ - uint8_t level; /* Directory level. Currently 0 or 1 */ - uint16_t index; /* Index to the next directory entry */ - uint16_t nentries; /* Number of directory entries */ - - pid_t pid; /* ID of task for attributes */ -}; +static const uint8_t g_procfsentrycount = sizeof(g_procfsentries) / + sizeof(struct procfs_entry_s); /**************************************************************************** * Private Function Prototypes @@ -133,16 +112,6 @@ struct procfs_level1_s /* Helpers */ static void procfs_enum(FAR struct tcb_s *tcb, FAR void *arg); -static int procfs_findattr(FAR const char *attr); -static size_t procfs_addline(FAR struct procfs_file_s *attr, - FAR char *buffer, size_t buflen, size_t linesize, - off_t *offset); -static ssize_t procfs_status(FAR struct procfs_file_s *attr, - FAR struct tcb_s *tcb, FAR char *buffer, size_t buflen, - off_t offset); -static ssize_t procfs_cmdline(FAR struct procfs_file_s *attr, - FAR struct tcb_s *tcb, FAR char *buffer, size_t buflen, - off_t offset); /* File system methods */ @@ -216,45 +185,52 @@ const struct mountpt_operations procfs_operations = procfs_stat /* stat */ }; -/* This is the list of all attribute strings. Indexing is with the same - * values as enum procfs_attr_e. +/* Level 0 contains the directory of active tasks in addition to other + * statically registered entries with custom handlers. This strcture + * contains a snapshot of the active tasks when the directory is first + * opened. */ -static const char *g_attrstrings[PROCFS_NATTRS] = +struct procfs_level0_s { - "status", - "cmdline" -}; + uint8_t level; /* Directory level. Currently 0 or 1 */ + uint8_t lastlen; /* length of last reported static dir */ + uint16_t index; /* Index to the next directory entry */ + uint16_t nentries; /* Number of directory entries */ + pid_t pid[CONFIG_MAX_TASKS]; /* Snapshot of all active task IDs */ + FAR const char *lastread; /* Pointer to last static dir read */ -static const char *g_statenames[] = -{ - "Invalid", - "Pending unlock", - "Ready", - "Running", - "Inactive", - "Semaphore wait", -#ifndef CONFIG_DISABLE_MQUEUE - "Signal wait", -#endif -#ifndef CONFIG_DISABLE_MQUEUE - "MQ not empty wait", - "MQ no full wait" -#endif + /* Pointer to procfs handler entry */ + + FAR const struct procfs_entry_s *procfsentry; }; -static const char *g_ttypenames[4] = +/* Level 1 is an internal virtual directory (such as /proc/fs) which + * will contain one or more additional static entries based on the + * configuration. + */ + +struct procfs_level1_s { - "Task", - "pthread", - "Kernel thread", - "--?--" + uint8_t level; /* Directory level. Currently 0 or 1 */ + uint8_t lastlen; /* length of last reported static dir */ + uint8_t subdirlen; /* Length of the subdir search */ + uint16_t index; /* Index to the next directory entry */ + uint16_t nentries; /* Number of directory entries */ + uint16_t firstindex; /* Index of 1st entry matching this subdir */ + FAR const char *lastread; /* Pointer to last static dir read */ + + /* Pointer to procfs handler entry */ + + FAR const struct procfs_entry_s *procfsentry; }; /**************************************************************************** * Private Functions ****************************************************************************/ +#ifndef CONFIG_FS_PROCFS_EXCLUDE_PROCESS + /**************************************************************************** * Name: procfs_enum ****************************************************************************/ @@ -274,264 +250,8 @@ static void procfs_enum(FAR struct tcb_s *tcb, FAR void *arg) dir->pid[index] = tcb->pid; dir->nentries = index + 1; } - -/**************************************************************************** - * Name: procfs_findattr - ****************************************************************************/ - -static int procfs_findattr(FAR const char *attr) -{ - int i; - - /* Search every string in g_attrstrings or until a match is found */ - - for (i = 0; i < PROCFS_NATTRS; i++) - { - if (strcmp(g_attrstrings[i], attr) == 0) - { - return i; - } - } - - /* Not found */ - - return -ENOENT; -} - -/**************************************************************************** - * Name: procfs_addline - ****************************************************************************/ - -static size_t procfs_addline(FAR struct procfs_file_s *attr, - FAR char *buffer, size_t buflen, - size_t linesize, off_t *offset) -{ - size_t copysize; - size_t lnoffset; - - /* Will this line take us past the offset? */ - - lnoffset = *offset; - if (linesize < lnoffset) - { - /* No... decrement the offset and return without doing anything */ - - *offset -= linesize; - return 0; - } - - /* Handle the remaining offset */ - - linesize -= lnoffset; - buffer += lnoffset; - *offset = 0; - - /* Copy the line into the user buffer */ - - copysize = MIN(linesize, buflen); - memcpy(buffer, &attr->line[lnoffset], copysize); - return copysize; -} - -/**************************************************************************** - * Name: procfs_status - ****************************************************************************/ - -static ssize_t procfs_status(FAR struct procfs_file_s *attr, - FAR struct tcb_s *tcb, FAR char *buffer, - size_t buflen, off_t offset) -{ - FAR const char *name; - size_t remaining; - size_t linesize; - size_t copysize; - size_t totalsize; - - remaining = buflen; - totalsize = 0; - - /* Show the task name */ - -#if CONFIG_TASK_NAME_SIZE > 0 - name = tcb->name; -#else - name = "<noname>"; -#endif - linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%s\n", - "Name:", name); - copysize = procfs_addline(attr, buffer, remaining, linesize, &offset); - - totalsize += copysize; - buffer += copysize; - remaining -= copysize; - - if (totalsize >= buflen) - { - return totalsize; - } - - /* Show the thread type */ - - linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%s\n", "Type:", - g_ttypenames[(tcb->flags & TCB_FLAG_TTYPE_MASK) >> - TCB_FLAG_TTYPE_SHIFT]); - copysize = procfs_addline(attr, buffer, remaining, linesize, &offset); - - totalsize += copysize; - buffer += copysize; - remaining -= copysize; - - if (totalsize >= buflen) - { - return totalsize; - } - - /* Show the thread state */ - - linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%s\n", "State:", - g_statenames[tcb->task_state]); - copysize = procfs_addline(attr, buffer, remaining, linesize, &offset); - - totalsize += copysize; - buffer += copysize; - remaining -= copysize; - - if (totalsize >= buflen) - { - return totalsize; - } - - /* Show the thread priority */ - -#ifdef CONFIG_PRIORITY_INHERITANCE - linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%d (%d)\n", "Priority:", - tcb->sched_priority, tcb->base_priority); -#else - linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%d\n", "Priority:", - tcb->sched_priority); -#endif - copysize = procfs_addline(attr, buffer, remaining, linesize, &offset); - - totalsize += copysize; - buffer += copysize; - remaining -= copysize; - - if (totalsize >= buflen) - { - return totalsize; - } - - /* Show the scheduler */ - - linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%s\n", "Scheduler:", - tcb->flags & TCB_FLAG_ROUND_ROBIN ? "SCHED_RR" : "SCHED_FIFO"); - copysize = procfs_addline(attr, buffer, remaining, linesize, &offset); - - totalsize += copysize; - buffer += copysize; - remaining -= copysize; - - if (totalsize >= buflen) - { - return totalsize; - } - - /* Show the signal mast */ - -#ifndef CONFIG_DISABLE_SIGNALS - linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%08x\n", "SigMask:", - tcb->sigprocmask); - copysize = procfs_addline(attr, buffer, remaining, linesize, &offset); - - totalsize += copysize; -#endif - - return totalsize; -} - -/**************************************************************************** - * Name: procfs_cmdline - ****************************************************************************/ - -static ssize_t procfs_cmdline(FAR struct procfs_file_s *attr, - FAR struct tcb_s *tcb, FAR char *buffer, - size_t buflen, off_t offset) -{ - FAR struct task_tcb_s *ttcb; - FAR const char *name; - FAR char **argv; - size_t remaining; - size_t linesize; - size_t copysize; - size_t totalsize; - - remaining = buflen; - totalsize = 0; - - /* Show the task name */ - -#if CONFIG_TASK_NAME_SIZE > 0 - name = tcb->name; -#else - name = "<noname>"; -#endif - linesize = strlen(name); - memcpy(attr->line, name, linesize); - copysize = procfs_addline(attr, buffer, remaining, linesize, &offset); - - totalsize += copysize; - buffer += copysize; - remaining -= copysize; - - if (totalsize >= buflen) - { - return totalsize; - } - -#ifndef CONFIG_DISABLE_PTHREAD - /* Show the pthread argument */ - - if ((tcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_PTHREAD) - { - FAR struct pthread_tcb_s *ptcb = (FAR struct pthread_tcb_s *)tcb; - - linesize = snprintf(attr->line, STATUS_LINELEN, " 0x%p\n", ptcb->arg); - copysize = procfs_addline(attr, buffer, remaining, linesize, &offset); - - totalsize += copysize; - buffer += copysize; - remaining -= copysize; - - return totalsize; - } #endif - /* Show the task argument list (skipping over the name) */ - - ttcb = (FAR struct task_tcb_s *)tcb; - - for (argv = ttcb->argv + 1; *argv; argv++) - { - linesize = snprintf(attr->line, STATUS_LINELEN, " %s", *argv); - copysize = procfs_addline(attr, buffer, remaining, linesize, &offset); - - totalsize += copysize; - buffer += copysize; - remaining -= copysize; - - if (totalsize >= buflen) - { - return totalsize; - } - } - - linesize = snprintf(attr->line, STATUS_LINELEN, "\n"); - copysize = procfs_addline(attr, buffer, remaining, linesize, &offset); - - totalsize += copysize; - return totalsize; -} - /**************************************************************************** * Name: procfs_open ****************************************************************************/ @@ -539,96 +259,36 @@ static ssize_t procfs_cmdline(FAR struct procfs_file_s *attr, static int procfs_open(FAR struct file *filep, FAR const char *relpath, int oflags, mode_t mode) { - FAR struct procfs_file_s *attr; - FAR struct tcb_s *tcb; - FAR char *ptr; - irqstate_t flags; - unsigned long tmp; - pid_t pid; - int attrndx; + int x, ret = -ENOENT; fvdbg("Open '%s'\n", relpath); - /* PROCFS is read-only. Any attempt to open with any kind of write - * access is not permitted. - * - * REVISIT: Write-able proc files could be quite useful. - */ + /* Perform the stat based on the procfs_entry operations */ - if ((oflags & O_WRONLY) != 0 || (oflags & O_RDONLY) == 0) + for (x = 0; x < g_procfsentrycount; x++) { - fdbg("ERROR: Only O_RDONLY supported\n"); - return -EACCES; - } - - /* The first segment of the relative path should be a task/thread ID */ + /* Test if the path matches this entry's specification */ - ptr = NULL; - tmp = strtoul(relpath, &ptr, 10); - - if (!ptr || *ptr != '/') - { - fdbg("ERROR: Invalid path \"%s\"\n", relpath); - return -ENOENT; - } - - /* Skip over the slash */ - - ptr++; - - /* A valid PID would be in the range of 0-32767 (0 is reserved for the - * IDLE thread). - */ - - if (tmp >= 32768) - { - fdbg("ERROR: Invalid PID %ld\n", tmp); - return -ENOENT; - } - - /* Now verify that a task with this task/thread ID exists */ - - pid = (pid_t)tmp; - - flags = irqsave(); - tcb = sched_gettcb(pid); - irqrestore(flags); - - if (!tcb) - { - fdbg("ERROR: PID %d is no longer valid\n", (int)pid); - return -ENOENT; - } + if (match(g_procfsentries[x].pathpattern, relpath)) + { + /* Match found! Stat using this procfs entry */ - /* The second segment of the relpath should be a well known attribute of - * the task/thread. - */ + DEBUGASSERT(g_procfsentries[x].ops && + g_procfsentries[x].ops->open); - attrndx = procfs_findattr(ptr); - if (attrndx < 0) - { - fdbg("ERROR: Invalid attribute %s\n", ptr); - return -ENOENT; - } + ret = g_procfsentries[x].ops->open(filep, relpath, oflags, mode); - /* Allocate a container to hold the task and attribute selection */ + if (ret == OK) + { + DEBUGASSERT(filep->f_priv); - attr = (FAR struct procfs_file_s *)kzalloc(sizeof(struct procfs_file_s)); - if (!attr) - { - fdbg("ERROR: Failed to allocate file attributes\n"); - return -ENOMEM; + ((struct procfs_file_s *) filep->f_priv)->procfsentry = + &g_procfsentries[x]; + } + } } - /* Initialize the file attributes */ - - attr->pid = pid; - attr->attr = attrndx; - - /* Save the index as the open-specific state in filep->f_priv */ - - filep->f_priv = (FAR void *)attr; - return OK; + return ret; } /**************************************************************************** @@ -658,52 +318,19 @@ static int procfs_close(FAR struct file *filep) static ssize_t procfs_read(FAR struct file *filep, FAR char *buffer, size_t buflen) { - FAR struct procfs_file_s *attr; - FAR struct tcb_s *tcb; - irqstate_t flags; - ssize_t ret; + FAR struct procfs_file_s *handler; + ssize_t ret = 0; fvdbg("buffer=%p buflen=%d\n", buffer, (int)buflen); /* Recover our private data from the struct file instance */ - attr = (FAR struct procfs_file_s *)filep->f_priv; - DEBUGASSERT(attr); - - /* Verify that the thread is still valid */ - - flags = irqsave(); - tcb = sched_gettcb(attr->pid); - - if (!tcb) - { - fdbg("ERROR: PID %d is not valid\n", (int)attr->pid); - irqrestore(flags); - return -ENODEV; - } - - /* Provide the requested data */ - - switch (attr->attr) - { - default: - case PROCFS_STATUS: /* Task/thread status */ - ret = procfs_status(attr, tcb, buffer, buflen, filep->f_pos); - break; - - case PROCFS_CMDLINE: /* Command line */ - ret = procfs_cmdline(attr, tcb, buffer, buflen, filep->f_pos); - break; - } - - irqrestore(flags); + handler = (FAR struct procfs_file_s *)filep->f_priv; + DEBUGASSERT(handler); - /* Update the file offset */ + /* Call the handler's read routine */ - if (ret > 0) - { - filep->f_pos += ret; - } + ret = handler->procfsentry->ops->read(filep, buffer, buflen); return ret; } @@ -732,7 +359,6 @@ static int procfs_ioctl(FAR struct file *filep, int cmd, unsigned long arg) static int procfs_dup(FAR const struct file *oldp, FAR struct file *newp) { FAR struct procfs_file_s *oldattr; - FAR struct procfs_file_s *newattr; fvdbg("Dup %p->%p\n", oldp, newp); @@ -741,23 +367,9 @@ static int procfs_dup(FAR const struct file *oldp, FAR struct file *newp) oldattr = (FAR struct procfs_file_s *)oldp->f_priv; DEBUGASSERT(oldattr); - /* Allocate a new container to hold the task and attribute selection */ - - newattr = (FAR struct procfs_file_s *)kzalloc(sizeof(struct procfs_file_s)); - if (!newattr) - { - fdbg("ERROR: Failed to allocate file attributes\n"); - return -ENOMEM; - } + /* Allow lower-level handler do the dup to get it's extra data */ - /* The copy the file attribtes from the old attributes to the new */ - - memcpy(newattr, oldattr, sizeof(struct procfs_file_s)); - - /* Save the new attributes in the new file structure */ - - newp->f_priv = (FAR void *)newattr; - return OK; + return oldattr->procfsentry->ops->dup(oldp, newp); } /**************************************************************************** @@ -771,7 +383,7 @@ static int procfs_dup(FAR const struct file *oldp, FAR struct file *newp) static int procfs_opendir(FAR struct inode *mountpt, FAR const char *relpath, FAR struct fs_dirent_s *dir) { - FAR struct tcb_s *tcb; + FAR struct procfs_level0_s *level0; FAR void *priv = NULL; irqstate_t flags; @@ -786,8 +398,6 @@ static int procfs_opendir(FAR struct inode *mountpt, FAR const char *relpath, if (!relpath || relpath[0] == '\0') { - FAR struct procfs_level0_s *level0; - /* The path refers to the top level directory. Allocate the level0 * dirent structure. */ @@ -808,84 +418,87 @@ static int procfs_opendir(FAR struct inode *mountpt, FAR const char *relpath, * NOTE that interrupts must be disabled throughout the traversal. */ +#ifndef CONFIG_FS_PROCFS_EXCLUDE_PROCESS flags = irqsave(); sched_foreach(procfs_enum, level0); irqrestore(flags); +#else + level0->index = 0; + level0->nentries = 0; +#endif + + /* Initialze lastread entries */ + + level0->lastread = ""; + level0->lastlen = 0; + level0->procfsentry = NULL; priv = (FAR void *)level0; } else { - FAR struct procfs_level1_s *level1; - unsigned long tmp; - FAR char *ptr; - pid_t pid; - - /* Otherwise, the relative path should be a valid task/thread ID */ + int x, ret; + int len = strlen(relpath); - ptr = NULL; - tmp = strtoul(relpath, &ptr, 10); + /* Search the static array of procfs_entries */ - if (!ptr || (*ptr != '\0' && strcmp(ptr, "/") != 0)) + for (x = 0; x < g_procfsentrycount; x++) { - /* strtoul failed or there is something in the path after the pid */ - - fdbg("ERROR: Invalid path \"%s\"\n", relpath); - return -ENOENT; - } + /* Test if the path matches this entry's specification */ - /* A valid PID would be in the range of 0-32767 (0 is reserved for the - * IDLE thread). - */ + if (match(g_procfsentries[x].pathpattern, relpath)) + { + /* Match found! Call the handler's opendir routine */ - if (tmp >= 32768) - { - fdbg("ERROR: Invalid PID %ld\n", tmp); - return -ENOENT; - } + DEBUGASSERT(g_procfsentries[x].ops && g_procfsentries[x].ops->opendir); + ret = g_procfsentries[x].ops->opendir(relpath, dir); - /* Now verify that a task with this task/thread ID exists */ + if (ret == OK) + { + DEBUGASSERT(dir->u.procfs); - pid = (pid_t)tmp; + /* Set the procfs_entry handler */ - flags = irqsave(); - tcb = sched_gettcb(pid); - irqrestore(flags); + level0 = (FAR struct procfs_level0_s *) dir->u.procfs; + level0->procfsentry = &g_procfsentries[x]; + } - if (!tcb) - { - fdbg("ERROR: PID %d is not valid\n", (int)pid); - return -ENOENT; - } + return ret; + } - /* Was the <pid> the final element of the path? */ + /* Test for a sub-string match (e.g. "ls /proc/fs") */ - if (*ptr != '\0' && strcmp(ptr, "/") != 0) - { - /* There is something in the path after the pid */ + else if (strncmp(g_procfsentries[x].pathpattern, relpath, len) == 0) + { + FAR struct procfs_level1_s *level1; - fdbg("ERROR: Invalid path \"%s\"\n", relpath); - return -ENOENT; - } + /* Doing an intermediate directory search */ - /* The path refers to the 1st level sbdirectory. Allocate the level1 - * dirent structure. - */ + /* The path refers to the top level directory. Allocate the level0 + * dirent structure. + */ - level1 = (FAR struct procfs_level1_s *) - kzalloc(sizeof(struct procfs_level1_s)); + level1 = (FAR struct procfs_level1_s *) + kzalloc(sizeof(struct procfs_level1_s)); - if (!level1) - { - fdbg("ERROR: Failed to allocate the level1 directory structure\n"); - return -ENOMEM; - } + if (!level1) + { + fdbg("ERROR: Failed to allocate the level0 directory structure\n"); + return -ENOMEM; + } - level1->level = 1; - level1->nentries = PROCFS_NATTRS; - level1->pid = pid; + level1->level = 1; + level1->index = x; + level1->firstindex = x; + level1->subdirlen = len; + level1->lastread = ""; + level1->lastlen = 0; + level1->procfsentry = NULL; - priv = (FAR void *)level1; + priv = (FAR void *)level1; + break; + } + } } dir->u.procfs = priv; @@ -902,7 +515,7 @@ static int procfs_opendir(FAR struct inode *mountpt, FAR const char *relpath, static int procfs_closedir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir) { - FAR struct procfs_level_s *priv; + FAR struct procfs_dir_priv_s *priv; DEBUGASSERT(mountpt && dir && dir->u.procfs); priv = dir->u.procfs; @@ -925,95 +538,204 @@ static int procfs_closedir(FAR struct inode *mountpt, static int procfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir) { - FAR struct procfs_level_s *priv; + FAR struct procfs_dir_priv_s *priv; + FAR struct procfs_level0_s *level0; FAR struct tcb_s *tcb; + FAR const char *name; unsigned int index; irqstate_t flags; pid_t pid; - int ret; + int ret = -ENOENT; DEBUGASSERT(mountpt && dir && dir->u.procfs); priv = dir->u.procfs; - /* Have we reached the end of the directory */ + /* Are we reading the 1st directory level with dynamic PID and static + * entries? + */ - index = priv->index; - if (index >= priv->nentries) + if (priv->level == 0) { - /* We signal the end of the directory by returning the special - * error -ENOENT - */ + level0 = (FAR struct procfs_level0_s *)priv; - fvdbg("Entry %d: End of directory\n", index); - ret = -ENOENT; - } + /* Have we reached the end of the PID information */ - /* Are tranversing a first level directory of task IDs */ - - else if (priv->level == 0) - { - FAR struct procfs_level0_s *level0 = (FAR struct procfs_level0_s *)priv; + index = priv->index; + if (index >= priv->nentries) + { + /* We must report the next static entry ... no more PID entries. + * skip any entries with wildcards in the first segment of the + * directory name. + */ - /* Verify that the pid still refers to an active task/thread */ + while (index < priv->nentries + g_procfsentrycount) + { + name = g_procfsentries[index - priv->nentries].pathpattern; + while (*name != '/' && *name != '\0') + { + if (*name == '*' || *name == '[' || *name == '?') + { + /* Wildcard found. Skip this entry */ + + index++; + name = NULL; + break; + } + + name++; + } + + /* Test if we skipped this entry */ + + if (name != NULL) + { + /* This entry is okay to report. Test if it has a duplicate + * first level name as the one we just reported. This could + * happen in the event of procfs_entry_s such as: + * + * fs/smartfs + * fs/nfs + * fs/nxffs + */ + + name = g_procfsentries[index - priv->nentries].pathpattern; + if (!level0->lastlen || (strncmp(name, level0->lastread, + level0->lastlen) != 0)) + { + /* Not a duplicate, return the first segment of this + * entry + */ + + break; + } + else + { + /* Skip this entry ... duplicate 1st level name found */ + + index++; + } + } + } - pid = level0->pid[index]; + /* Test if we are at the end of the directory */ - flags = irqsave(); - tcb = sched_gettcb(pid); - irqrestore(flags); + if (index >= priv->nentries + g_procfsentrycount) + { + /* We signal the end of the directory by returning the special + * error -ENOENT + */ - if (!tcb) - { - fdbg("ERROR: PID %d is no longer valid\n", (int)pid); - return -ENOENT; + fvdbg("Entry %d: End of directory\n", index); + ret = -ENOENT; + } + else + { + /* Report the next static entry */ + + level0->lastlen = strcspn(name, "/"); + level0->lastread = name; + strncpy(dir->fd_dir.d_name, name, level0->lastlen); + dir->fd_dir.d_name[level0->lastlen] = '\0'; + + if (name[level0->lastlen] == '/') + { + dir->fd_dir.d_type = DTYPE_DIRECTORY; + } + else + { + dir->fd_dir.d_type = DTYPE_FILE; + } + + /* Advance to next entry for the next read */ + + priv->index = index; + ret = OK; + } } +#ifndef CONFIG_FS_PROCFS_EXCLUDE_PROCESS + else + { + /* Verify that the pid still refers to an active task/thread */ - /* Save the filename=pid and file type=directory */ + pid = level0->pid[index]; - dir->fd_dir.d_type = DTYPE_DIRECTORY; - snprintf(dir->fd_dir.d_name, NAME_MAX+1, "%d", (int)pid); + flags = irqsave(); + tcb = sched_gettcb(pid); + irqrestore(flags); - /* Set up the next directory entry offset. NOTE that we could use the - * standard f_pos instead of our own private index. - */ + if (!tcb) + { + fdbg("ERROR: PID %d is no longer valid\n", (int)pid); + return -ENOENT; + } - level0->index = index + 1; - ret = OK; - } + /* Save the filename=pid and file type=directory */ - /* No.. We must be tranversing a subdirectory of task attributes */ + dir->fd_dir.d_type = DTYPE_DIRECTORY; + snprintf(dir->fd_dir.d_name, NAME_MAX+1, "%d", (int)pid); - else - { - FAR struct procfs_level1_s *level1 = (FAR struct procfs_level1_s *)priv; + /* Set up the next directory entry offset. NOTE that we could use the + * standard f_pos instead of our own private index. + */ - DEBUGASSERT(priv->level == 1); + level0->index = index + 1; + ret = OK; + } +#endif /* CONFIG_FS_PROCFS_EXCLUDE_PROCESS */ + } - /* Verify that the pid still refers to an active task/thread */ + /* Are we reading in intermediate subdirectory? */ - pid = level1->pid; + else if (priv->level == 1 && priv->procfsentry == NULL) + { + FAR struct procfs_level1_s *level1; - flags = irqsave(); - tcb = sched_gettcb(pid); - irqrestore(flags); + level1 = (FAR struct procfs_level1_s *) priv; + + /* Test if this entry matches. We assume all entries of the same + * subdirectory are listed in order in the procfs_entry array. + */ - if (!tcb) + if (strncmp(g_procfsentries[level1->index].pathpattern, + g_procfsentries[level1->firstindex].pathpattern, + level1->subdirlen) == 0) { - fdbg("ERROR: PID %d is no longer valid\n", (int)pid); - return -ENOENT; - } + /* This entry matches. Report the subdir entry */ + + name = &g_procfsentries[level1->index].pathpattern[ + level1->subdirlen + 1]; + level1->lastlen = strcspn(name, "/"); + level1->lastread = name; + strncpy(dir->fd_dir.d_name, name, level1->lastlen); + dir->fd_dir.d_name[level1->lastlen] = '\0'; - /* Save the filename=pid and file type=directory */ + if (name[level1->lastlen] == '/') + { + dir->fd_dir.d_type = DTYPE_DIRECTORY; + } + else + { + dir->fd_dir.d_type = DTYPE_FILE; + } - dir->fd_dir.d_type = DTYPE_FILE; - strncpy(dir->fd_dir.d_name, g_attrstrings[index], NAME_MAX+1); + level1->index++; + ret = OK; + } + else + { + /* No more entries in the subdirectory */ - /* Set up the next directory entry offset. NOTE that we could use the - * standard f_pos instead of our own private index. + ret = -ENOENT; + } + } + else + { + /* We are performing a directory search of one of the subdirectories + * and we must let the handler perform the read. */ - level1->index = index + 1; - ret = OK; + DEBUGASSERT(priv->procfsentry && priv->procfsentry->ops->readdir); + ret = priv->procfsentry->ops->readdir(dir); } return ret; @@ -1028,12 +750,20 @@ static int procfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir) static int procfs_rewinddir(struct inode *mountpt, struct fs_dirent_s *dir) { - FAR struct procfs_level_s *priv; + FAR struct procfs_dir_priv_s *priv; DEBUGASSERT(mountpt && dir && dir->u.procfs); priv = dir->u.procfs; - priv->index = 0; + if (priv->level == 1 && priv->procfsentry == NULL) + { + priv->index = ((struct procfs_level1_s *) priv)->firstindex; + } + else + { + priv->index = 0; + } + return OK; } @@ -1098,12 +828,7 @@ static int procfs_statfs(struct inode *mountpt, struct statfs *buf) static int procfs_stat(struct inode *mountpt, const char *relpath, struct stat *buf) { - FAR struct tcb_s *tcb; - unsigned long tmp; - FAR char *ptr; - irqstate_t flags; - pid_t pid; - int ret; + int ret = -ENOSYS; /* Three path forms are accepted: * @@ -1120,71 +845,39 @@ static int procfs_stat(struct inode *mountpt, const char *relpath, /* It's a read-only directory */ buf->st_mode = S_IFDIR|S_IROTH|S_IRGRP|S_IRUSR; - + ret = OK; } else { - /* Otherwise, the first segment of the relative path should be a valid - * task/thread ID - */ + int x; + int len = strlen(relpath); - ptr = NULL; - tmp = strtoul(relpath, &ptr, 10); + /* Perform the stat based on the procfs_entry operations */ - if (!ptr) + for (x = 0; x < g_procfsentrycount; x++) { - fdbg("ERROR: Invalid path \"%s\"\n", relpath); - return -ENOENT; - } - - /* A valid PID would be in the range of 0-32767 (0 is reserved for the - * IDLE thread). - */ - - if (tmp >= 32768) - { - fdbg("ERROR: Invalid PID %ld\n", tmp); - return -ENOENT; - } + /* Test if the path matches this entry's specification */ - /* Now verify that a task with this task/thread ID exists */ - - pid = (pid_t)tmp; - - flags = irqsave(); - tcb = sched_gettcb(pid); - irqrestore(flags); - - if (!tcb) - { - fdbg("ERROR: PID %d is no longer valid\n", (int)pid); - return -ENOENT; - } + if (match(g_procfsentries[x].pathpattern, relpath)) + { + /* Match found! Stat using this procfs entry */ - /* Was the <pid> the final element of the path? */ + DEBUGASSERT(g_procfsentries[x].ops && + g_procfsentries[x].ops->stat); - if (*ptr == '\0' || strcmp(ptr, "/") == 0) - { - /* Yes ... It's a read-only directory */ + return g_procfsentries[x].ops->stat(relpath, buf); + } - buf->st_mode = S_IFDIR|S_IROTH|S_IRGRP|S_IRUSR; - } - else - { - /* Otherwise, the second segment of the relpath should be a well - * known attribute of the task/thread. - */ + /* Test for an internal subdirectory stat */ - ret = procfs_findattr(ptr); - if (ret < 0) + else if (strncmp(g_procfsentries[x].pathpattern, relpath, len) == 0) { - fdbg("ERROR: Invalid attribute %s\n", ptr); - return -ENOENT; - } - - /* It's a read-only file name */ + /* It's an internal subdirectory */ - buf->st_mode = S_IFREG|S_IROTH|S_IRGRP|S_IRUSR; + buf->st_mode = S_IFDIR|S_IROTH|S_IRGRP|S_IRUSR; + ret = OK; + break; + } } } @@ -1193,7 +886,7 @@ static int procfs_stat(struct inode *mountpt, const char *relpath, buf->st_size = 0; buf->st_blksize = 0; buf->st_blocks = 0; - return OK; + return ret; } /**************************************************************************** diff --git a/nuttx/fs/procfs/fs_procfsproc.c b/nuttx/fs/procfs/fs_procfsproc.c new file mode 100644 index 000000000..d646c6758 --- /dev/null +++ b/nuttx/fs/procfs/fs_procfsproc.c @@ -0,0 +1,1003 @@ +/**************************************************************************** + * fs/procfs/fs_procfsproc.c + * + * Copyright (C) 2013 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/statfs.h> +#include <sys/stat.h> + +#include <stdint.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <assert.h> +#include <errno.h> +#include <debug.h> + +#include <nuttx/arch.h> +#include <nuttx/sched.h> +#include <nuttx/kmalloc.h> +#include <nuttx/fs/fs.h> +#include <nuttx/fs/procfs.h> +#include <nuttx/fs/dirent.h> + +#include <arch/irq.h> + +#if !defined(CONFIG_DISABLE_MOUNTPOINT) && defined(CONFIG_FS_PROCFS) +#ifndef CONFIG_FS_PROCFS_EXCLUDE_PROCESS + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define STATUS_LINELEN 32 + +#ifndef MIN +# define MIN(a,b) ((a < b) ? a : b) +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ +/* This enumeration identifies all of the thread attributes that can be + * accessed via the procfs file system. + */ + +enum process_attr_e +{ + PROCFS_STATUS = 0, /* Task/thread status */ + PROCFS_CMDLINE, /* Command line */ +}; +#define PROCFS_NATTRS 2 + +/* This structure describes one open "file" */ + +struct process_file_s +{ + struct procfs_file_s base; /* Base open file structure */ + uint8_t type; /* See enum process_type_e */ + pid_t pid; /* Task/thread ID */ + uint8_t attr; /* See enum process_attr_e */ + char line[STATUS_LINELEN]; /* Pre-allocated buffer for formatted lines */ +}; + +/* Level 0 is the directory of active tasks */ + +struct process_level0_s +{ + uint8_t level; /* Directory level. Currently 0 or 1 */ + uint16_t index; /* Index to the next directory entry */ + uint16_t nentries; /* Number of directory entries */ + + pid_t pid[CONFIG_MAX_TASKS]; /* Snapshot of all active task IDs */ +}; + +/* Level 1 is the directory of task attributes */ + +struct process_level1_s +{ + struct procfs_dir_priv_s base; /* Base directory private data */ + + /* Our specific data for context control */ + + pid_t pid; /* ID of task for attributes */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ +/* Helpers */ + +static int process_findattr(FAR const char *attr); +static size_t process_addline(FAR struct process_file_s *attr, + FAR char *buffer, size_t buflen, size_t linesize, + off_t *offset); +static ssize_t process_status(FAR struct process_file_s *attr, + FAR struct tcb_s *tcb, FAR char *buffer, size_t buflen, + off_t offset); +static ssize_t process_cmdline(FAR struct process_file_s *attr, + FAR struct tcb_s *tcb, FAR char *buffer, size_t buflen, + off_t offset); + +/* File system methods */ + +static int process_open(FAR struct file *filep, FAR const char *relpath, + int oflags, mode_t mode); +static int process_close(FAR struct file *filep); +static ssize_t process_read(FAR struct file *filep, FAR char *buffer, + size_t buflen); + +static int process_dup(FAR const struct file *oldp, + FAR struct file *newp); + +static int process_opendir(const char *relpath, FAR struct fs_dirent_s *dir); +static int process_closedir(FAR struct fs_dirent_s *dir); +static int process_readdir(FAR struct fs_dirent_s *dir); +static int process_rewinddir(FAR struct fs_dirent_s *dir); + +static int process_stat(FAR const char *relpath, FAR struct stat *buf); + +/**************************************************************************** + * Private Variables + ****************************************************************************/ + +/**************************************************************************** + * Public Variables + ****************************************************************************/ + +/* See fs_mount.c -- this structure is explicitly externed there. + * We use the old-fashioned kind of initializers so that this will compile + * with any compiler. + */ + +const struct procfs_operations process_operations = +{ + process_open, /* open */ + process_close, /* close */ + process_read, /* read */ + NULL, /* write */ + + process_dup, /* dup */ + + process_opendir, /* opendir */ + process_closedir, /* closedir */ + process_readdir, /* readdir */ + process_rewinddir, /* rewinddir */ + + process_stat /* stat */ +}; + +/* This is the list of all attribute strings. Indexing is with the same + * values as enum process_attr_e. + */ + +static const char *g_attrstrings[PROCFS_NATTRS] = +{ + "status", + "cmdline" +}; + +static const char *g_statenames[] = +{ + "Invalid", + "Pending unlock", + "Ready", + "Running", + "Inactive", + "Semaphore wait", +#ifndef CONFIG_DISABLE_MQUEUE + "Signal wait", +#endif +#ifndef CONFIG_DISABLE_MQUEUE + "MQ not empty wait", + "MQ no full wait" +#endif +}; + +static const char *g_ttypenames[4] = +{ + "Task", + "pthread", + "Kernel thread", + "--?--" +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: process_findattr + ****************************************************************************/ + +static int process_findattr(FAR const char *attr) +{ + int i; + + /* Search every string in g_attrstrings or until a match is found */ + + for (i = 0; i < PROCFS_NATTRS; i++) + { + if (strcmp(g_attrstrings[i], attr) == 0) + { + return i; + } + } + + /* Not found */ + + return -ENOENT; +} + +/**************************************************************************** + * Name: process_addline + ****************************************************************************/ + +static size_t process_addline(FAR struct process_file_s *attr, + FAR char *buffer, size_t buflen, + size_t linesize, off_t *offset) +{ + size_t copysize; + size_t lnoffset; + + /* Will this line take us past the offset? */ + + lnoffset = *offset; + if (linesize < lnoffset) + { + /* No... decrement the offset and return without doing anything */ + + *offset -= linesize; + return 0; + } + + /* Handle the remaining offset */ + + linesize -= lnoffset; + buffer += lnoffset; + *offset = 0; + + /* Copy the line into the user buffer */ + + copysize = MIN(linesize, buflen); + memcpy(buffer, &attr->line[lnoffset], copysize); + return copysize; +} + +/**************************************************************************** + * Name: process_status + ****************************************************************************/ + +static ssize_t process_status(FAR struct process_file_s *attr, + FAR struct tcb_s *tcb, FAR char *buffer, + size_t buflen, off_t offset) +{ + FAR const char *name; + size_t remaining; + size_t linesize; + size_t copysize; + size_t totalsize; + + remaining = buflen; + totalsize = 0; + + /* Show the task name */ + +#if CONFIG_TASK_NAME_SIZE > 0 + name = tcb->name; +#else + name = "<noname>"; +#endif + linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%s\n", + "Name:", name); + copysize = process_addline(attr, buffer, remaining, linesize, &offset); + + totalsize += copysize; + buffer += copysize; + remaining -= copysize; + + if (totalsize >= buflen) + { + return totalsize; + } + + /* Show the thread type */ + + linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%s\n", "Type:", + g_ttypenames[(tcb->flags & TCB_FLAG_TTYPE_MASK) >> + TCB_FLAG_TTYPE_SHIFT]); + copysize = process_addline(attr, buffer, remaining, linesize, &offset); + + totalsize += copysize; + buffer += copysize; + remaining -= copysize; + + if (totalsize >= buflen) + { + return totalsize; + } + + /* Show the thread state */ + + linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%s\n", "State:", + g_statenames[tcb->task_state]); + copysize = process_addline(attr, buffer, remaining, linesize, &offset); + + totalsize += copysize; + buffer += copysize; + remaining -= copysize; + + if (totalsize >= buflen) + { + return totalsize; + } + + /* Show the thread priority */ + +#ifdef CONFIG_PRIORITY_INHERITANCE + linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%d (%d)\n", "Priority:", + tcb->sched_priority, tcb->base_priority); +#else + linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%d\n", "Priority:", + tcb->sched_priority); +#endif + copysize = process_addline(attr, buffer, remaining, linesize, &offset); + + totalsize += copysize; + buffer += copysize; + remaining -= copysize; + + if (totalsize >= buflen) + { + return totalsize; + } + + /* Show the scheduler */ + + linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%s\n", "Scheduler:", + tcb->flags & TCB_FLAG_ROUND_ROBIN ? "SCHED_RR" : "SCHED_FIFO"); + copysize = process_addline(attr, buffer, remaining, linesize, &offset); + + totalsize += copysize; + buffer += copysize; + remaining -= copysize; + + if (totalsize >= buflen) + { + return totalsize; + } + + /* Show the signal mast */ + +#ifndef CONFIG_DISABLE_SIGNALS + linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%08x\n", "SigMask:", + tcb->sigprocmask); + copysize = process_addline(attr, buffer, remaining, linesize, &offset); + + totalsize += copysize; +#endif + + return totalsize; +} + +/**************************************************************************** + * Name: process_cmdline + ****************************************************************************/ + +static ssize_t process_cmdline(FAR struct process_file_s *attr, + FAR struct tcb_s *tcb, FAR char *buffer, + size_t buflen, off_t offset) +{ + FAR struct task_tcb_s *ttcb; + FAR const char *name; + FAR char **argv; + size_t remaining; + size_t linesize; + size_t copysize; + size_t totalsize; + + remaining = buflen; + totalsize = 0; + + /* Show the task name */ + +#if CONFIG_TASK_NAME_SIZE > 0 + name = tcb->name; +#else + name = "<noname>"; +#endif + linesize = strlen(name); + memcpy(attr->line, name, linesize); + copysize = process_addline(attr, buffer, remaining, linesize, &offset); + + totalsize += copysize; + buffer += copysize; + remaining -= copysize; + + if (totalsize >= buflen) + { + return totalsize; + } + +#ifndef CONFIG_DISABLE_PTHREAD + /* Show the pthread argument */ + + if ((tcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_PTHREAD) + { + FAR struct pthread_tcb_s *ptcb = (FAR struct pthread_tcb_s *)tcb; + + linesize = snprintf(attr->line, STATUS_LINELEN, " 0x%p\n", ptcb->arg); + copysize = process_addline(attr, buffer, remaining, linesize, &offset); + + totalsize += copysize; + buffer += copysize; + remaining -= copysize; + + return totalsize; + } +#endif + + /* Show the task argument list (skipping over the name) */ + + ttcb = (FAR struct task_tcb_s *)tcb; + + for (argv = ttcb->argv + 1; *argv; argv++) + { + linesize = snprintf(attr->line, STATUS_LINELEN, " %s", *argv); + copysize = process_addline(attr, buffer, remaining, linesize, &offset); + + totalsize += copysize; + buffer += copysize; + remaining -= copysize; + + if (totalsize >= buflen) + { + return totalsize; + } + } + + linesize = snprintf(attr->line, STATUS_LINELEN, "\n"); + copysize = process_addline(attr, buffer, remaining, linesize, &offset); + + totalsize += copysize; + return totalsize; +} + +/**************************************************************************** + * Name: process_open + ****************************************************************************/ + +static int process_open(FAR struct file *filep, FAR const char *relpath, + int oflags, mode_t mode) +{ + FAR struct process_file_s *attr; + FAR struct tcb_s *tcb; + FAR char *ptr; + irqstate_t flags; + unsigned long tmp; + pid_t pid; + int attrndx; + + fvdbg("Open '%s'\n", relpath); + + /* PROCFS is read-only. Any attempt to open with any kind of write + * access is not permitted. + * + * REVISIT: Write-able proc files could be quite useful. + */ + + if ((oflags & O_WRONLY) != 0 || (oflags & O_RDONLY) == 0) + { + fdbg("ERROR: Only O_RDONLY supported\n"); + return -EACCES; + } + + /* The first segment of the relative path should be a task/thread ID */ + + ptr = NULL; + tmp = strtoul(relpath, &ptr, 10); + + if (!ptr || *ptr != '/') + { + fdbg("ERROR: Invalid path \"%s\"\n", relpath); + return -ENOENT; + } + + /* Skip over the slash */ + + ptr++; + + /* A valid PID would be in the range of 0-32767 (0 is reserved for the + * IDLE thread). + */ + + if (tmp >= 32768) + { + fdbg("ERROR: Invalid PID %ld\n", tmp); + return -ENOENT; + } + + /* Now verify that a task with this task/thread ID exists */ + + pid = (pid_t)tmp; + + flags = irqsave(); + tcb = sched_gettcb(pid); + irqrestore(flags); + + if (!tcb) + { + fdbg("ERROR: PID %d is no longer valid\n", (int)pid); + return -ENOENT; + } + + /* The second segment of the relpath should be a well known attribute of + * the task/thread. + */ + + attrndx = process_findattr(ptr); + if (attrndx < 0) + { + fdbg("ERROR: Invalid attribute %s\n", ptr); + return -ENOENT; + } + + /* Allocate a container to hold the task and attribute selection */ + + attr = (FAR struct process_file_s *)kzalloc(sizeof(struct process_file_s)); + if (!attr) + { + fdbg("ERROR: Failed to allocate file attributes\n"); + return -ENOMEM; + } + + /* Initialize the file attributes */ + + attr->pid = pid; + attr->attr = attrndx; + + /* Save the index as the open-specific state in filep->f_priv */ + + filep->f_priv = (FAR void *)attr; + return OK; +} + +/**************************************************************************** + * Name: process_close + ****************************************************************************/ + +static int process_close(FAR struct file *filep) +{ + FAR struct process_file_s *attr; + + /* Recover our private data from the struct file instance */ + + attr = (FAR struct process_file_s *)filep->f_priv; + DEBUGASSERT(attr); + + /* Release the file attributes structure */ + + kfree(attr); + filep->f_priv = NULL; + return OK; +} + +/**************************************************************************** + * Name: process_read + ****************************************************************************/ + +static ssize_t process_read(FAR struct file *filep, FAR char *buffer, + size_t buflen) +{ + FAR struct process_file_s *attr; + FAR struct tcb_s *tcb; + irqstate_t flags; + ssize_t ret; + + fvdbg("buffer=%p buflen=%d\n", buffer, (int)buflen); + + /* Recover our private data from the struct file instance */ + + attr = (FAR struct process_file_s *)filep->f_priv; + DEBUGASSERT(attr); + + /* Verify that the thread is still valid */ + + flags = irqsave(); + tcb = sched_gettcb(attr->pid); + + if (!tcb) + { + fdbg("ERROR: PID %d is not valid\n", (int)attr->pid); + irqrestore(flags); + return -ENODEV; + } + + /* Provide the requested data */ + + switch (attr->attr) + { + default: + case PROCFS_STATUS: /* Task/thread status */ + ret = process_status(attr, tcb, buffer, buflen, filep->f_pos); + break; + + case PROCFS_CMDLINE: /* Command line */ + ret = process_cmdline(attr, tcb, buffer, buflen, filep->f_pos); + break; + } + + irqrestore(flags); + + /* Update the file offset */ + + if (ret > 0) + { + filep->f_pos += ret; + } + + return ret; +} + +/**************************************************************************** + * Name: process_dup + * + * Description: + * Duplicate open file data in the new file structure. + * + ****************************************************************************/ + +static int process_dup(FAR const struct file *oldp, FAR struct file *newp) +{ + FAR struct process_file_s *oldattr; + FAR struct process_file_s *newattr; + + fvdbg("Dup %p->%p\n", oldp, newp); + + /* Recover our private data from the old struct file instance */ + + oldattr = (FAR struct process_file_s *)oldp->f_priv; + DEBUGASSERT(oldattr); + + /* Allocate a new container to hold the task and attribute selection */ + + newattr = (FAR struct process_file_s *)kzalloc(sizeof(struct process_file_s)); + if (!newattr) + { + fdbg("ERROR: Failed to allocate file attributes\n"); + return -ENOMEM; + } + + /* The copy the file attribtes from the old attributes to the new */ + + memcpy(newattr, oldattr, sizeof(struct process_file_s)); + + /* Save the new attributes in the new file structure */ + + newp->f_priv = (FAR void *)newattr; + return OK; +} + +/**************************************************************************** + * Name: process_opendir + * + * Description: + * Open a directory for read access + * + ****************************************************************************/ + +static int process_opendir(FAR const char *relpath, FAR struct fs_dirent_s *dir) +{ + FAR struct tcb_s *tcb; + FAR void *priv = NULL; + irqstate_t flags; + + fvdbg("relpath: \"%s\"\n", relpath ? relpath : "NULL"); + DEBUGASSERT(relpath && dir && !dir->u.procfs); + + /* The relative must be: + * + * "<pid>" - The sub-directory of task/thread attributes + */ + + FAR struct process_level1_s *level1; + unsigned long tmp; + FAR char *ptr; + pid_t pid; + + /* Otherwise, the relative path should be a valid task/thread ID */ + + ptr = NULL; + tmp = strtoul(relpath, &ptr, 10); + + if (!ptr || (*ptr != '\0' && strcmp(ptr, "/") != 0)) + { + /* strtoul failed or there is something in the path after the pid */ + + fdbg("ERROR: Invalid path \"%s\"\n", relpath); + return -ENOENT; + } + + /* A valid PID would be in the range of 0-32767 (0 is reserved for the + * IDLE thread). + */ + + if (tmp >= 32768) + { + fdbg("ERROR: Invalid PID %ld\n", tmp); + return -ENOENT; + } + + /* Now verify that a task with this task/thread ID exists */ + + pid = (pid_t)tmp; + + flags = irqsave(); + tcb = sched_gettcb(pid); + irqrestore(flags); + + if (!tcb) + { + fdbg("ERROR: PID %d is not valid\n", (int)pid); + return -ENOENT; + } + + /* Was the <pid> the final element of the path? */ + + if (*ptr != '\0' && strcmp(ptr, "/") != 0) + { + /* There is something in the path after the pid */ + + fdbg("ERROR: Invalid path \"%s\"\n", relpath); + return -ENOENT; + } + + /* The path refers to the 1st level sbdirectory. Allocate the level1 + * dirent structure. + */ + + level1 = (FAR struct process_level1_s *) + kzalloc(sizeof(struct process_level1_s)); + + if (!level1) + { + fdbg("ERROR: Failed to allocate the level1 directory structure\n"); + return -ENOMEM; + } + + level1->base.level = 1; + level1->base.nentries = PROCFS_NATTRS; + level1->base.index = 0; + level1->pid = pid; + + priv = (FAR void *)level1; + + dir->u.procfs = priv; + return OK; +} + +/**************************************************************************** + * Name: process_closedir + * + * Description: Close the directory listing + * + ****************************************************************************/ + +static int process_closedir(FAR struct fs_dirent_s *dir) +{ + FAR struct process_level0_s *priv; + + DEBUGASSERT(dir && dir->u.procfs); + priv = dir->u.procfs; + + if (priv) + { + kfree(priv); + } + + dir->u.procfs = NULL; + return OK; +} + +/**************************************************************************** + * Name: process_readdir + * + * Description: Read the next directory entry + * + ****************************************************************************/ + +static int process_readdir(struct fs_dirent_s *dir) +{ + FAR struct process_level1_s *level1; + FAR struct tcb_s *tcb; + unsigned int index; + irqstate_t flags; + pid_t pid; + int ret; + + DEBUGASSERT(dir && dir->u.procfs); + level1 = dir->u.procfs; + + /* Have we reached the end of the directory */ + + index = level1->base.index; + if (index >= level1->base.nentries) + { + /* We signal the end of the directory by returning the special + * error -ENOENT + */ + + fvdbg("Entry %d: End of directory\n", index); + ret = -ENOENT; + } + + /* We are tranversing a subdirectory of task attributes */ + + else + { + DEBUGASSERT(level1->base.level == 1); + + /* Verify that the pid still refers to an active task/thread */ + + pid = level1->pid; + + flags = irqsave(); + tcb = sched_gettcb(pid); + irqrestore(flags); + + if (!tcb) + { + fdbg("ERROR: PID %d is no longer valid\n", (int)pid); + return -ENOENT; + } + + /* Save the filename=pid and file type=directory */ + + dir->fd_dir.d_type = DTYPE_FILE; + strncpy(dir->fd_dir.d_name, g_attrstrings[index], NAME_MAX+1); + + /* Set up the next directory entry offset. NOTE that we could use the + * standard f_pos instead of our own private index. + */ + + level1->base.index = index + 1; + ret = OK; + } + + return ret; +} + +/**************************************************************************** + * Name: process_rewindir + * + * Description: Reset directory read to the first entry + * + ****************************************************************************/ + +static int process_rewinddir(struct fs_dirent_s *dir) +{ + FAR struct process_level0_s *priv; + + DEBUGASSERT(dir && dir->u.procfs); + priv = dir->u.procfs; + + priv->index = 0; + return OK; +} + +/**************************************************************************** + * Name: process_stat + * + * Description: Return information about a file or directory + * + ****************************************************************************/ + +static int process_stat(const char *relpath, struct stat *buf) +{ + FAR struct tcb_s *tcb; + unsigned long tmp; + FAR char *ptr; + irqstate_t flags; + pid_t pid; + int ret; + + /* Two path forms are accepted: + * + * "<pid>" - If <pid> refers to a currently active task/thread, then it + * is a directory + * "<pid>/<attr>" - If <attr> is a recognized attribute then, then it + * is a file. + */ + + ptr = NULL; + tmp = strtoul(relpath, &ptr, 10); + + if (!ptr) + { + fdbg("ERROR: Invalid path \"%s\"\n", relpath); + return -ENOENT; + } + + /* A valid PID would be in the range of 0-32767 (0 is reserved for the + * IDLE thread). + */ + + if (tmp >= 32768) + { + fdbg("ERROR: Invalid PID %ld\n", tmp); + return -ENOENT; + } + + /* Now verify that a task with this task/thread ID exists */ + + pid = (pid_t)tmp; + + flags = irqsave(); + tcb = sched_gettcb(pid); + irqrestore(flags); + + if (!tcb) + { + fdbg("ERROR: PID %d is no longer valid\n", (int)pid); + return -ENOENT; + } + + /* Was the <pid> the final element of the path? */ + + if (*ptr == '\0' || strcmp(ptr, "/") == 0) + { + /* Yes ... It's a read-only directory */ + + buf->st_mode = S_IFDIR|S_IROTH|S_IRGRP|S_IRUSR; + } + else + { + /* Otherwise, the second segment of the relpath should be a well + * known attribute of the task/thread. + */ + + ret = process_findattr(ptr); + if (ret < 0) + { + fdbg("ERROR: Invalid attribute %s\n", ptr); + return -ENOENT; + } + + /* It's a read-only file name */ + + buf->st_mode = S_IFREG|S_IROTH|S_IRGRP|S_IRUSR; + } + + /* File/directory size, access block size */ + + buf->st_size = 0; + buf->st_blksize = 0; + buf->st_blocks = 0; + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +#endif /* CONFIG_FS_PROCFS_EXCLUDE_PROCESS */ +#endif /* !CONFIG_DISABLE_MOUNTPOINT && CONFIG_FS_PROCFS */ diff --git a/nuttx/fs/procfs/fs_skeleton.c b/nuttx/fs/procfs/fs_skeleton.c new file mode 100644 index 000000000..e9b206941 --- /dev/null +++ b/nuttx/fs/procfs/fs_skeleton.c @@ -0,0 +1,460 @@ +/**************************************************************************** + * fs/procfs/fs_skeleton.c + * + * Copyright (C) 2013 Ken Pettit. All rights reserved. + * Author: Ken Pettit <pettitkd@gmail.com> + * + * 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/statfs.h> +#include <sys/stat.h> + +#include <stdint.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <assert.h> +#include <errno.h> +#include <debug.h> + +#include <nuttx/arch.h> +#include <nuttx/sched.h> +#include <nuttx/kmalloc.h> +#include <nuttx/fs/fs.h> +#include <nuttx/fs/procfs.h> +#include <nuttx/fs/dirent.h> + +#include <arch/irq.h> + +#if !defined(CONFIG_DISABLE_MOUNTPOINT) && defined(CONFIG_FS_PROCFS) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ +/* This enumeration identifies all of the thread attributes that can be + * accessed via the procfs file system. + */ + +/* This structure describes one open "file" */ + +struct skel_file_s +{ + struct procfs_file_s base; /* Base open file structure */ + + /* Add context specific data types for managing an open file here */ +}; + +/* Level 1 is the directory of attributes */ + +struct skel_level1_s +{ + struct procfs_dir_priv_s base; /* Base directory private data */ + + /* Add context specific data types here for managing the directory + * open / read / stat, etc. + */ + +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ +/* File system methods */ + +static int skel_open(FAR struct file *filep, FAR const char *relpath, + int oflags, mode_t mode); +static int skel_close(FAR struct file *filep); +static ssize_t skel_read(FAR struct file *filep, FAR char *buffer, + size_t buflen); + +static int skel_dup(FAR const struct file *oldp, + FAR struct file *newp); + +static int skel_opendir(const char *relpath, FAR struct fs_dirent_s *dir); +static int skel_closedir(FAR struct fs_dirent_s *dir); +static int skel_readdir(FAR struct fs_dirent_s *dir); +static int skel_rewinddir(FAR struct fs_dirent_s *dir); + +static int skel_stat(FAR const char *relpath, FAR struct stat *buf); + +/**************************************************************************** + * Private Variables + ****************************************************************************/ + +/**************************************************************************** + * Public Variables + ****************************************************************************/ + +/* See include/nutts/fs/procfs.h + * We use the old-fashioned kind of initializers so that this will compile + * with any compiler. + */ + +const struct procfs_operations skel_procfsoperations = +{ + skel_open, /* open */ + skel_close, /* close */ + skel_read, /* read */ + + /* TODO: Decide if this driver supports write */ + NULL, /* write */ + + skel_dup, /* dup */ + + skel_opendir, /* opendir */ + skel_closedir, /* closedir */ + skel_readdir, /* readdir */ + skel_rewinddir, /* rewinddir */ + + skel_stat /* stat */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: skel_open + ****************************************************************************/ + +static int skel_open(FAR struct file *filep, FAR const char *relpath, + int oflags, mode_t mode) +{ + FAR struct skel_file_s *priv; + + fvdbg("Open '%s'\n", relpath); + + /* PROCFS is read-only. Any attempt to open with any kind of write + * access is not permitted. + * + * REVISIT: Write-able proc files could be quite useful. + */ + + if (((oflags & O_WRONLY) != 0 || (oflags & O_RDONLY) == 0) && + (skel_procfsoperations.write == NULL)) + { + fdbg("ERROR: Only O_RDONLY supported\n"); + return -EACCES; + } + + /* Allocate a container to hold the task and attribute selection */ + + priv = (FAR struct skel_file_s *)kzalloc(sizeof(struct skel_file_s)); + if (!priv) + { + fdbg("ERROR: Failed to allocate file attributes\n"); + return -ENOMEM; + } + + /* TODO: Initialize the context specific data here */ + + + /* Save the index as the open-specific state in filep->f_priv */ + + filep->f_priv = (FAR void *)priv; + return OK; +} + +/**************************************************************************** + * Name: skel_close + ****************************************************************************/ + +static int skel_close(FAR struct file *filep) +{ + FAR struct skel_file_s *priv; + + /* Recover our private data from the struct file instance */ + + priv = (FAR struct skel_file_s *)filep->f_priv; + DEBUGASSERT(priv); + + /* Release the file attributes structure */ + + kfree(priv); + filep->f_priv = NULL; + return OK; +} + +/**************************************************************************** + * Name: skel_read + ****************************************************************************/ + +static ssize_t skel_read(FAR struct file *filep, FAR char *buffer, + size_t buflen) +{ + FAR struct skel_file_s *priv; + ssize_t ret; + + fvdbg("buffer=%p buflen=%d\n", buffer, (int)buflen); + + /* Recover our private data from the struct file instance */ + + priv = (FAR struct skel_file_s *)filep->f_priv; + DEBUGASSERT(priv); + + /* TODO: Provide the requested data */ + + ret = 0; + + /* Update the file offset */ + + if (ret > 0) + { + filep->f_pos += ret; + } + + return ret; +} + +/**************************************************************************** + * Name: skel_dup + * + * Description: + * Duplicate open file data in the new file structure. + * + ****************************************************************************/ + +static int skel_dup(FAR const struct file *oldp, FAR struct file *newp) +{ + FAR struct skel_file_s *oldpriv; + FAR struct skel_file_s *newpriv; + + fvdbg("Dup %p->%p\n", oldp, newp); + + /* Recover our private data from the old struct file instance */ + + oldpriv = (FAR struct skel_file_s *)oldp->f_priv; + DEBUGASSERT(oldpriv); + + /* Allocate a new container to hold the task and attribute selection */ + + newpriv = (FAR struct skel_file_s *)kzalloc(sizeof(struct skel_file_s)); + if (!newpriv) + { + fdbg("ERROR: Failed to allocate file attributes\n"); + return -ENOMEM; + } + + /* The copy the file attribtes from the old attributes to the new */ + + memcpy(newpriv, oldpriv, sizeof(struct skel_file_s)); + + /* Save the new attributes in the new file structure */ + + newp->f_priv = (FAR void *)newpriv; + return OK; +} + +/**************************************************************************** + * Name: skel_opendir + * + * Description: + * Open a directory for read access + * + ****************************************************************************/ + +static int skel_opendir(FAR const char *relpath, FAR struct fs_dirent_s *dir) +{ + FAR struct skel_level1_s *level1; + + fvdbg("relpath: \"%s\"\n", relpath ? relpath : "NULL"); + DEBUGASSERT(relpath && dir && !dir->u.procfs); + + /* The path refers to the 1st level sbdirectory. Allocate the level1 + * dirent structure. + */ + + level1 = (FAR struct skel_level1_s *) + kzalloc(sizeof(struct skel_level1_s)); + + if (!level1) + { + fdbg("ERROR: Failed to allocate the level1 directory structure\n"); + return -ENOMEM; + } + + /* TODO: Initialze context specific data */ + + + /* Initialze base structure components */ + + level1->base.level = 1; + level1->base.nentries = 0; + level1->base.index = 0; + + dir->u.procfs = (FAR void *) level1; + return OK; +} + +/**************************************************************************** + * Name: skel_closedir + * + * Description: Close the directory listing + * + ****************************************************************************/ + +static int skel_closedir(FAR struct fs_dirent_s *dir) +{ + FAR struct skel_level1_s *priv; + + DEBUGASSERT(dir && dir->u.procfs); + priv = dir->u.procfs; + + if (priv) + { + kfree(priv); + } + + dir->u.procfs = NULL; + return OK; +} + +/**************************************************************************** + * Name: skel_readdir + * + * Description: Read the next directory entry + * + ****************************************************************************/ + +static int skel_readdir(struct fs_dirent_s *dir) +{ + FAR struct skel_level1_s *level1; + char filename[16]; + int ret, index; + + DEBUGASSERT(dir && dir->u.procfs); + level1 = dir->u.procfs; + + /* TODO: Perform device specific readdir function here. This may + * or may not involve validating the nentries variable + * in the base depending on the implementation. + */ + + /* Have we reached the end of the directory */ + + index = level1->base.index; + if (index >= level1->base.nentries) + { + /* We signal the end of the directory by returning the special + * error -ENOENT + */ + + fvdbg("Entry %d: End of directory\n", index); + ret = -ENOENT; + } + + /* We are tranversing a subdirectory of task attributes */ + + else + { + DEBUGASSERT(level1->base.level == 1); + + /* TODO: Add device specific entries */ + + strcpy(filename, "dummy"); + + /* TODO: Specify the type of entry */ + + dir->fd_dir.d_type = DTYPE_FILE; + strncpy(dir->fd_dir.d_name, filename, NAME_MAX+1); + + /* Set up the next directory entry offset. NOTE that we could use the + * standard f_pos instead of our own private index. + */ + + ret = OK; + } + + return ret; +} + +/**************************************************************************** + * Name: skel_rewindir + * + * Description: Reset directory read to the first entry + * + ****************************************************************************/ + +static int skel_rewinddir(struct fs_dirent_s *dir) +{ + FAR struct skel_level1_s *priv; + + DEBUGASSERT(dir && dir->u.procfs); + priv = dir->u.procfs; + + priv->base.index = 0; + return OK; +} + +/**************************************************************************** + * Name: skel_stat + * + * Description: Return information about a file or directory + * + ****************************************************************************/ + +static int skel_stat(const char *relpath, struct stat *buf) +{ + int ret = -ENOENT; + + /* TODO: Decide if the relpath is valid and if it is a file + * or a directory and set it's permissions. + */ + + buf->st_mode = S_IFDIR|S_IROTH|S_IRGRP|S_IRUSR; + ret = OK; + + /* File/directory size, access block size */ + + buf->st_size = 0; + buf->st_blksize = 0; + buf->st_blocks = 0; + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +#endif /* !CONFIG_DISABLE_MOUNTPOINT && CONFIG_FS_PROCFS */ diff --git a/nuttx/fs/smartfs/Make.defs b/nuttx/fs/smartfs/Make.defs index 9604d546f..b77e1cfd3 100644 --- a/nuttx/fs/smartfs/Make.defs +++ b/nuttx/fs/smartfs/Make.defs @@ -38,7 +38,7 @@ ifeq ($(CONFIG_FS_SMARTFS),y) # Files required for SmartFS file system support ASRCS += -CSRCS += smartfs_smart.c smartfs_utils.c +CSRCS += smartfs_smart.c smartfs_utils.c smartfs_procfs.c # Files required for mksmartfs utility function diff --git a/nuttx/fs/smartfs/smartfs_procfs.c b/nuttx/fs/smartfs/smartfs_procfs.c new file mode 100644 index 000000000..64f7101d4 --- /dev/null +++ b/nuttx/fs/smartfs/smartfs_procfs.c @@ -0,0 +1,460 @@ +/**************************************************************************** + * fs/smartfs/smartfs_procfs.c + * + * Copyright (C) 2013 Ken Pettit. All rights reserved. + * Author: Ken Pettit <pettitkd@gmail.com> + * + * 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/statfs.h> +#include <sys/stat.h> + +#include <stdint.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <assert.h> +#include <errno.h> +#include <debug.h> + +#include <nuttx/arch.h> +#include <nuttx/sched.h> +#include <nuttx/kmalloc.h> +#include <nuttx/fs/fs.h> +#include <nuttx/fs/procfs.h> +#include <nuttx/fs/dirent.h> + +#include <arch/irq.h> + +#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_EXCLUDE_SMARTFS) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ +/* This enumeration identifies all of the thread attributes that can be + * accessed via the procfs file system. + */ + +/* This structure describes one open "file" */ + +struct smartfs_file_s +{ + struct procfs_file_s base; /* Base open file structure */ + + /* Add context specific data types for managing an open file here */ +}; + +/* Level 1 is the directory of attributes */ + +struct smartfs_level1_s +{ + struct procfs_dir_priv_s base; /* Base directory private data */ + + /* Add context specific data types here for managing the directory + * open / read / stat, etc. + */ + +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ +/* File system methods */ + +static int smartfs_open(FAR struct file *filep, FAR const char *relpath, + int oflags, mode_t mode); +static int smartfs_close(FAR struct file *filep); +static ssize_t smartfs_read(FAR struct file *filep, FAR char *buffer, + size_t buflen); + +static int smartfs_dup(FAR const struct file *oldp, + FAR struct file *newp); + +static int smartfs_opendir(const char *relpath, FAR struct fs_dirent_s *dir); +static int smartfs_closedir(FAR struct fs_dirent_s *dir); +static int smartfs_readdir(FAR struct fs_dirent_s *dir); +static int smartfs_rewinddir(FAR struct fs_dirent_s *dir); + +static int smartfs_stat(FAR const char *relpath, FAR struct stat *buf); + +/**************************************************************************** + * Private Variables + ****************************************************************************/ + +/**************************************************************************** + * Public Variables + ****************************************************************************/ + +/* See include/nutts/fs/procfs.h + * We use the old-fashioned kind of initializers so that this will compile + * with any compiler. + */ + +const struct procfs_operations smartfs_procfsoperations = +{ + smartfs_open, /* open */ + smartfs_close, /* close */ + smartfs_read, /* read */ + + /* TODO: Decide if this deiver supports write */ + NULL, /* write */ + + smartfs_dup, /* dup */ + + smartfs_opendir, /* opendir */ + smartfs_closedir, /* closedir */ + smartfs_readdir, /* readdir */ + smartfs_rewinddir, /* rewinddir */ + + smartfs_stat /* stat */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: smartfs_open + ****************************************************************************/ + +static int smartfs_open(FAR struct file *filep, FAR const char *relpath, + int oflags, mode_t mode) +{ + FAR struct smartfs_file_s *priv; + + fvdbg("Open '%s'\n", relpath); + + /* PROCFS is read-only. Any attempt to open with any kind of write + * access is not permitted. + * + * REVISIT: Write-able proc files could be quite useful. + */ + + if (((oflags & O_WRONLY) != 0 || (oflags & O_RDONLY) == 0) && + (smartfs_procfsoperations.write == NULL)) + { + fdbg("ERROR: Only O_RDONLY supported\n"); + return -EACCES; + } + + /* Allocate a container to hold the task and attribute selection */ + + priv = (FAR struct smartfs_file_s *)kzalloc(sizeof(struct smartfs_file_s)); + if (!priv) + { + fdbg("ERROR: Failed to allocate file attributes\n"); + return -ENOMEM; + } + + /* TODO: Initialize the context specific data here */ + + + /* Save the index as the open-specific state in filep->f_priv */ + + filep->f_priv = (FAR void *)priv; + return OK; +} + +/**************************************************************************** + * Name: smartfs_close + ****************************************************************************/ + +static int smartfs_close(FAR struct file *filep) +{ + FAR struct smartfs_file_s *priv; + + /* Recover our private data from the struct file instance */ + + priv = (FAR struct smartfs_file_s *)filep->f_priv; + DEBUGASSERT(priv); + + /* Release the file attributes structure */ + + kfree(priv); + filep->f_priv = NULL; + return OK; +} + +/**************************************************************************** + * Name: smartfs_read + ****************************************************************************/ + +static ssize_t smartfs_read(FAR struct file *filep, FAR char *buffer, + size_t buflen) +{ + FAR struct smartfs_file_s *priv; + ssize_t ret; + + fvdbg("buffer=%p buflen=%d\n", buffer, (int)buflen); + + /* Recover our private data from the struct file instance */ + + priv = (FAR struct smartfs_file_s *)filep->f_priv; + DEBUGASSERT(priv); + + /* TODO: Provide the requested data */ + + ret = 0; + + /* Update the file offset */ + + if (ret > 0) + { + filep->f_pos += ret; + } + + return ret; +} + +/**************************************************************************** + * Name: smartfs_dup + * + * Description: + * Duplicate open file data in the new file structure. + * + ****************************************************************************/ + +static int smartfs_dup(FAR const struct file *oldp, FAR struct file *newp) +{ + FAR struct smartfs_file_s *oldpriv; + FAR struct smartfs_file_s *newpriv; + + fvdbg("Dup %p->%p\n", oldp, newp); + + /* Recover our private data from the old struct file instance */ + + oldpriv = (FAR struct smartfs_file_s *)oldp->f_priv; + DEBUGASSERT(oldpriv); + + /* Allocate a new container to hold the task and attribute selection */ + + newpriv = (FAR struct smartfs_file_s *)kzalloc(sizeof(struct smartfs_file_s)); + if (!newpriv) + { + fdbg("ERROR: Failed to allocate file attributes\n"); + return -ENOMEM; + } + + /* The copy the file attribtes from the old attributes to the new */ + + memcpy(newpriv, oldpriv, sizeof(struct smartfs_file_s)); + + /* Save the new attributes in the new file structure */ + + newp->f_priv = (FAR void *)newpriv; + return OK; +} + +/**************************************************************************** + * Name: smartfs_opendir + * + * Description: + * Open a directory for read access + * + ****************************************************************************/ + +static int smartfs_opendir(FAR const char *relpath, FAR struct fs_dirent_s *dir) +{ + FAR struct smartfs_level1_s *level1; + + fvdbg("relpath: \"%s\"\n", relpath ? relpath : "NULL"); + DEBUGASSERT(relpath && dir && !dir->u.procfs); + + /* The path refers to the 1st level sbdirectory. Allocate the level1 + * dirent structure. + */ + + level1 = (FAR struct smartfs_level1_s *) + kzalloc(sizeof(struct smartfs_level1_s)); + + if (!level1) + { + fdbg("ERROR: Failed to allocate the level1 directory structure\n"); + return -ENOMEM; + } + + /* TODO: Initialze context specific data */ + + + /* Initialze base structure components */ + + level1->base.level = 1; + level1->base.nentries = 0; + level1->base.index = 0; + + dir->u.procfs = (FAR void *) level1; + return OK; +} + +/**************************************************************************** + * Name: smartfs_closedir + * + * Description: Close the directory listing + * + ****************************************************************************/ + +static int smartfs_closedir(FAR struct fs_dirent_s *dir) +{ + FAR struct smartfs_level1_s *priv; + + DEBUGASSERT(dir && dir->u.procfs); + priv = dir->u.procfs; + + if (priv) + { + kfree(priv); + } + + dir->u.procfs = NULL; + return OK; +} + +/**************************************************************************** + * Name: smartfs_readdir + * + * Description: Read the next directory entry + * + ****************************************************************************/ + +static int smartfs_readdir(struct fs_dirent_s *dir) +{ + FAR struct smartfs_level1_s *level1; + char filename[16]; + int ret, index; + + DEBUGASSERT(dir && dir->u.procfs); + level1 = dir->u.procfs; + + /* TODO: Perform device specific readdir function here. This may + * or may not involve validating the nentries variable + * in the base depending on the implementation. + */ + + /* Have we reached the end of the directory */ + + index = level1->base.index; + if (index >= level1->base.nentries) + { + /* We signal the end of the directory by returning the special + * error -ENOENT + */ + + fvdbg("Entry %d: End of directory\n", index); + ret = -ENOENT; + } + + /* We are tranversing a subdirectory of task attributes */ + + else + { + DEBUGASSERT(level1->base.level == 1); + + /* TODO: Add device specific entries */ + + strcpy(filename, "dummy"); + + /* TODO: Specify the type of entry */ + + dir->fd_dir.d_type = DTYPE_FILE; + strncpy(dir->fd_dir.d_name, filename, NAME_MAX+1); + + /* Set up the next directory entry offset. NOTE that we could use the + * standard f_pos instead of our own private index. + */ + + ret = OK; + } + + return ret; +} + +/**************************************************************************** + * Name: smartfs_rewindir + * + * Description: Reset directory read to the first entry + * + ****************************************************************************/ + +static int smartfs_rewinddir(struct fs_dirent_s *dir) +{ + FAR struct smartfs_level1_s *priv; + + DEBUGASSERT(dir && dir->u.procfs); + priv = dir->u.procfs; + + priv->base.index = 0; + return OK; +} + +/**************************************************************************** + * Name: smartfs_stat + * + * Description: Return information about a file or directory + * + ****************************************************************************/ + +static int smartfs_stat(const char *relpath, struct stat *buf) +{ + int ret = -ENOENT; + + /* TODO: Decide if the relpath is valid and if it is a file + * or a directory and set it's permissions. + */ + + buf->st_mode = S_IFDIR|S_IROTH|S_IRGRP|S_IRUSR; + ret = OK; + + /* File/directory size, access block size */ + + buf->st_size = 0; + buf->st_blksize = 0; + buf->st_blocks = 0; + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +#endif /* CONFIG_FS_PROCFS && !CONFIG_FS_PROCFS_EXCLUDE_SMARTFS */ diff --git a/nuttx/include/nuttx/fs/procfs.h b/nuttx/include/nuttx/fs/procfs.h new file mode 100644 index 000000000..cc3e49a40 --- /dev/null +++ b/nuttx/include/nuttx/fs/procfs.h @@ -0,0 +1,144 @@ +/**************************************************************************** + * include/nuttx/fs/procfs.h + * + * Copyright (C) 2013 Gregory Nutt. All rights reserved. + * Author: Ken Pettit <pettitkd@gmail.com> + * + * 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 __INCLUDE_NUTTX_FS_PROCFS_H +#define __INCLUDE_NUTTX_FS_PROCFS_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> +#include <nuttx/fs/fs.h> + +/**************************************************************************** + * Pre-Processor Definitions + ****************************************************************************/ +/* Data entry declaration prototypes ****************************************/ + +/* Procfs operations are a subset of the mountpt_operations */ + +struct procfs_operations +{ + /* The mountpoint open method differs from the driver open method + * because it receives (1) the inode that contains the mountpoint + * private data, (2) the relative path into the mountpoint, and (3) + * information to manage privileges. + */ + + int (*open)(FAR struct file *filep, FAR const char *relpath, + int oflags, mode_t mode); + + /* The following methods must be identical in signature and position because + * the struct file_operations and struct mountp_operations are treated like + * unions. + */ + + int (*close)(FAR struct file *filep); + ssize_t (*read)(FAR struct file *filep, FAR char *buffer, size_t buflen); + ssize_t (*write)(FAR struct file *filep, FAR const char *buffer, size_t buflen); + + /* The two structures need not be common after this point. The following + * are extended methods needed to deal with the unique needs of mounted + * file systems. + * + * Additional open-file-specific mountpoint operations: + */ + + int (*dup)(FAR const struct file *oldp, FAR struct file *newp); + + /* Directory operations */ + + int (*opendir)(FAR const char *relpath, FAR struct fs_dirent_s *dir); + int (*closedir)(FAR struct fs_dirent_s *dir); + int (*readdir)(FAR struct fs_dirent_s *dir); + int (*rewinddir)(FAR struct fs_dirent_s *dir); + + /* Operations on paths */ + + int (*stat)(FAR const char *relpath, FAR struct stat *buf); +}; + +/* Procfs handler prototypes ************************************************/ + +/* This is a procfs entry that each handler should provide to supply + * specific operations for file and directory handling. + */ + +struct procfs_entry_s +{ + FAR const char *pathpattern; + const struct procfs_operations *ops; +}; + +/* Specifies the common elements for an open file in the procfs + * file system. This structure should be sub-classed by handlers + * to add their own specific data elements to the context. + */ + +struct procfs_file_s +{ + const struct procfs_entry_s *pProcfsEntry; +}; + +/* The generic proc/ pseudo directory structure */ + +struct procfs_dir_priv_s +{ + uint8_t level; /* Directory level. Currently 0 or 1 */ + uint16_t index; /* Index to the next directory entry */ + uint16_t nentries; /* Number of directory entries */ + struct procfs_entry_s *pProcfsEntry; /* Pointer to procfs handler entry */ +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" { +#else +#define EXTERN extern +#endif + +/* Nothing here yet */ + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __INCLUDE_NUTTX_FS_PROCFS_H */ diff --git a/nuttx/include/nuttx/mtd/mtd.h b/nuttx/include/nuttx/mtd/mtd.h index 7f18fc4f6..d59ac5fe7 100644 --- a/nuttx/include/nuttx/mtd/mtd.h +++ b/nuttx/include/nuttx/mtd/mtd.h @@ -67,6 +67,10 @@ # define CONFIG_MTD_SUBSECTOR_ERASE 1 #endif +#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_MTD) +#define CONFIG_MTD_REGISTRATION 1 +#endif + /**************************************************************************** * Public Types ****************************************************************************/ @@ -142,6 +146,20 @@ struct mtd_dev_s */ int (*ioctl)(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg); + +#ifdef CONFIG_MTD_REGISTRATION + /* An assigned MTD number for procfs reporting */ + + uint8_t mtdno; + + /* Pointer to the next registered MTD device */ + + FAR struct mtd_dev_s *pnext; + + /* Name of this MTD device */ + + FAR const char *name; +#endif }; /**************************************************************************** @@ -186,6 +204,17 @@ FAR struct mtd_dev_s *mtd_partition(FAR struct mtd_dev_s *mtd, off_t firstblock, off_t nblocks); /**************************************************************************** + * Name: mtd_setpartitionname + * + * Description: + * Sets the name of the specified partition. + * + ****************************************************************************/ +#ifdef CONFIG_MTD_PARTITION_NAMES +int mtd_setpartitionname(FAR struct mtd_dev_s *mtd, FAR const char *name); +#endif + +/**************************************************************************** * Name: ftl_initialize * * Description: @@ -352,6 +381,23 @@ FAR struct mtd_dev_s *w25_initialize(FAR struct spi_dev_s *dev); FAR struct mtd_dev_s *up_flashinitialize(void); +/**************************************************************************** + * Name: mtd_register + * + * Description: + * Registers MTD device with the procfs file system. This assigns a unique + * MTD number and associates the given device name, then add adds it to + * the list of registered devices. + * + * In an embedded system, this all is really unnecessary, but is provided + * in the procfs system simply for information purposes (if desired). + * + ****************************************************************************/ + +#ifdef CONFIG_MTD_REGISTRATION +int mtd_register(FAR struct mtd_dev_s *mtd, FAR const char *name); +#endif + #undef EXTERN #ifdef __cplusplus } |