summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2013-12-12 09:21:55 -0600
committerGregory Nutt <gnutt@nuttx.org>2013-12-12 09:21:55 -0600
commit147415a647658c5d472338d745c5b68942eb0b2c (patch)
tree9a94ea4d00f35742480ecac85db9ef399bf3be0c
parent2977ad09c854e056b26f4ae1d86e751a7acead60 (diff)
downloadnuttx-147415a647658c5d472338d745c5b68942eb0b2c.tar.gz
nuttx-147415a647658c5d472338d745c5b68942eb0b2c.tar.bz2
nuttx-147415a647658c5d472338d745c5b68942eb0b2c.zip
procfs/: Extenstive architectural changes and enhancements by Ken Pettit
-rw-r--r--apps/nshlib/nsh_mntcmds.c9
-rw-r--r--nuttx/ChangeLog4
-rw-r--r--nuttx/drivers/mtd/Kconfig10
-rw-r--r--nuttx/drivers/mtd/Make.defs6
-rw-r--r--nuttx/drivers/mtd/at24xx.c6
-rw-r--r--nuttx/drivers/mtd/at25.c6
-rw-r--r--nuttx/drivers/mtd/at45db.c6
-rw-r--r--nuttx/drivers/mtd/m25px.c6
-rw-r--r--nuttx/drivers/mtd/mtd_partition.c405
-rw-r--r--nuttx/drivers/mtd/mtd_procfs.c352
-rw-r--r--nuttx/drivers/mtd/rammtd.c7
-rw-r--r--nuttx/drivers/mtd/ramtron.c6
-rw-r--r--nuttx/drivers/mtd/sst25.c6
-rw-r--r--nuttx/drivers/mtd/sst25xx.c6
-rw-r--r--nuttx/drivers/mtd/sst39vf.c6
-rw-r--r--nuttx/drivers/mtd/w25.c6
-rw-r--r--nuttx/fs/procfs/Kconfig46
-rw-r--r--nuttx/fs/procfs/Make.defs2
-rw-r--r--nuttx/fs/procfs/fs_procfs.c991
-rw-r--r--nuttx/fs/procfs/fs_procfsproc.c1003
-rw-r--r--nuttx/fs/procfs/fs_skeleton.c460
-rw-r--r--nuttx/fs/smartfs/Make.defs2
-rw-r--r--nuttx/fs/smartfs/smartfs_procfs.c460
-rw-r--r--nuttx/include/nuttx/fs/procfs.h144
-rw-r--r--nuttx/include/nuttx/mtd/mtd.h46
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
}