summaryrefslogtreecommitdiff
path: root/nuttx
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2013-11-01 07:39:20 -0600
committerGregory Nutt <gnutt@nuttx.org>2013-11-01 07:39:20 -0600
commit8d05d097e7089f3a4ce15e29cf3b7701580665b2 (patch)
tree8f7778c1ca1dce0a46989010091fac4c9425e5a2 /nuttx
parent8148ce2957824b783b7ec81919b891f220c0237f (diff)
downloadnuttx-8d05d097e7089f3a4ce15e29cf3b7701580665b2.tar.gz
nuttx-8d05d097e7089f3a4ce15e29cf3b7701580665b2.tar.bz2
nuttx-8d05d097e7089f3a4ce15e29cf3b7701580665b2.zip
Add an MTD device wrapper that can used to provide a /dev/config that can be used for retaining configuration data. From Ken Pettit
Diffstat (limited to 'nuttx')
-rw-r--r--nuttx/ChangeLog5
-rw-r--r--nuttx/drivers/mtd/Kconfig7
-rw-r--r--nuttx/drivers/mtd/Make.defs2
-rw-r--r--nuttx/drivers/mtd/mtd_config.c995
-rw-r--r--nuttx/include/nuttx/configdata.h143
-rw-r--r--nuttx/include/nuttx/fs/ioctl.h17
6 files changed, 1162 insertions, 7 deletions
diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog
index 4e1a5c574..f88f6a9be 100644
--- a/nuttx/ChangeLog
+++ b/nuttx/ChangeLog
@@ -5924,4 +5924,7 @@
caused an exception in usbdev_reset() later. The driver reference
will be nullified later usbdev_unregister when the caller gets the
error. From David Sidrane (2013-10-31).
-
+ * drivers/mtd_config.c and include/nuttx/configdata.h: Add a container
+ for an MTD device that can be used to provide a simple, lightweight
+ interface to configation data storage that resides on some storage
+ media that is wrapped as an MTD device. From Ken Pettit (2013-11-1).
diff --git a/nuttx/drivers/mtd/Kconfig b/nuttx/drivers/mtd/Kconfig
index ffe7ac06e..f41eb8626 100644
--- a/nuttx/drivers/mtd/Kconfig
+++ b/nuttx/drivers/mtd/Kconfig
@@ -35,6 +35,13 @@ config MTD_BYTE_WRITE
support such writes. The SMART file system can take advantage of
this option if it is enabled.
+config MTD_CONFIG
+ bool "Enable Dev Config (MTD based) device"
+ default n
+ ---help---
+ Provides a /dev/config device for saving / restoring application
+ configuration data to a standard MTD device or partition.
+
comment "MTD Device Drivers"
config RAMMTD
diff --git a/nuttx/drivers/mtd/Make.defs b/nuttx/drivers/mtd/Make.defs
index 3f6c8b6fb..f37f2c136 100644
--- a/nuttx/drivers/mtd/Make.defs
+++ b/nuttx/drivers/mtd/Make.defs
@@ -39,7 +39,7 @@
ifeq ($(CONFIG_MTD),y)
-CSRCS += at45db.c flash_eraseall.c ftl.c m25px.c ramtron.c
+CSRCS += at45db.c flash_eraseall.c ftl.c m25px.c ramtron.c mtd_config.c
ifeq ($(CONFIG_MTD_PARTITION),y)
CSRCS += mtd_partition.c
diff --git a/nuttx/drivers/mtd/mtd_config.c b/nuttx/drivers/mtd/mtd_config.c
new file mode 100644
index 000000000..0e1c57430
--- /dev/null
+++ b/nuttx/drivers/mtd/mtd_config.c
@@ -0,0 +1,995 @@
+/****************************************************************************
+ * drivers/mtd/mtd_config.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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * dev_config provides an interface for saving and retrieving
+ * application configuration data to a standard MTD partition.
+ * It accepts an MTD pointer (presumable a partition) and
+ * publishes a /dev/config device file for accessing via
+ * defined ioctl calls to set and get config data.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdbool.h>
+#include <string.h>
+#include <poll.h>
+#include <errno.h>
+#include <debug.h>
+#include <nuttx/fs/fs.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/mtd.h>
+#include <nuttx/configdata.h>
+
+#ifdef CONFIG_MTD_CONFIG
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct mtdconfig_struct_s
+{
+ FAR struct mtd_dev_s *mtd; /* Contained MTD interface */
+ sem_t exclsem; /* Supports mutual exclusion */
+ uint16_t blocksize; /* Size of blocks in contained MTD */
+ uint16_t erasesize; /* Size of erase block in contained MTD */
+ size_t nblocks; /* Number of blocks available */
+ size_t neraseblocks; /* Number of erase blocks available */
+ off_t readoff; /* Read offset (for hexdump) */
+ FAR uint8_t *buffer; /* Temp block read buffer */
+ uint8_t erasedvalue; /* Value of and erased byte on the MTD */
+};
+
+struct mtdconfig_header_s
+{
+ uint8_t flags; /* Entry control flags */
+ uint16_t id; /* ID of the config data item */
+ int instance; /* Instance of the item */
+ size_t len; /* Length of the data block */
+} packed_struct;
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int mtdconfig_open(FAR struct file *filep);
+static int mtdconfig_close(FAR struct file *filep);
+static ssize_t mtdconfig_read(FAR struct file *, FAR char *, size_t);
+static ssize_t mtdconfig_ioctl(FAR struct file *, int, unsigned long);
+#ifndef CONFIG_DISABLE_POLL
+static int mtdconfig_poll(FAR struct file *filep, FAR struct pollfd *fds,
+ bool setup);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations mtdconfig_fops =
+{
+ mtdconfig_open, /* open */
+ mtdconfig_close, /* close */
+ mtdconfig_read, /* read */
+ 0, /* write */
+ 0, /* seek */
+ mtdconfig_ioctl /* ioctl */
+#ifndef CONFIG_DISABLE_POLL
+ , mtdconfig_poll /* poll */
+#endif
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mtdconfig_readbytes
+ *
+ * Reads bytes from the contained MTD device. This will either usee
+ * the read function or if that is not available, the bread with a copy.
+ *
+ ****************************************************************************/
+
+static int mtdconfig_readbytes(FAR struct mtdconfig_struct_s *dev, int offset,
+ FAR uint8_t *pdata, int readlen)
+{
+ off_t bytestoread = readlen;
+ off_t bytesthisblock, firstbyte;
+ off_t block, index;
+ int ret = OK;
+ size_t bytes;
+
+ /* Test if read interface supported. If it is, use it directly */
+
+ if ((dev->mtd->read == NULL) && (readlen < dev->blocksize))
+ {
+ /* Read interface available. Read directly to buffer */
+
+ bytes = MTD_READ(dev->mtd, offset, readlen, pdata);
+ if (bytes != readlen)
+ {
+ /* Error reading data! */
+
+ ret = -EIO;
+ }
+ }
+ else
+ {
+ /* Read interface not available, do a block read into our buffer */
+
+ block = offset / dev->blocksize;
+ firstbyte = offset - (block * dev->blocksize);
+ bytesthisblock = dev->blocksize - firstbyte;
+ if (bytesthisblock > readlen)
+ {
+ bytesthisblock = readlen;
+ }
+
+ index = 0;
+
+ while (bytestoread > 0)
+ {
+ if (bytesthisblock < dev->blocksize || bytestoread < dev->blocksize)
+ {
+ /* Copy to temp buffer first...don't need the whole block */
+
+ bytes = MTD_BREAD(dev->mtd, block, 1, dev->buffer);
+ if (bytes != 1)
+ {
+ /* Error reading data! */
+
+ ret = -EIO;
+ goto errout;
+ }
+
+ /* Copy data to the output buffer */
+
+ memcpy(&pdata[index], &dev->buffer[firstbyte], bytesthisblock);
+ }
+ else
+ {
+ /* We are reading a whole block. Read directly to buffer */
+
+ bytes = MTD_BREAD(dev->mtd, block, 1, &pdata[index]);
+ if (bytes != 1)
+ {
+ /* Error reading data! */
+
+ ret = -EIO;
+ goto errout;
+ }
+ }
+
+ /* Update values for next block read */
+
+ bytestoread -= bytesthisblock;
+ index += bytesthisblock;
+ firstbyte = 0;
+ block++;
+ }
+ }
+
+errout:
+ return ret;
+}
+
+/****************************************************************************
+ * Name: mtdconfig_writebytes
+ *
+ * Writes bytes to the contained MTD device. This will either usee
+ * the byte write function or if that is not available, the bwrite.
+ *
+ ****************************************************************************/
+
+static int mtdconfig_writebytes(FAR struct mtdconfig_struct_s *dev, int offset,
+ FAR const uint8_t *pdata, int writelen)
+{
+ int ret = OK;
+
+#ifdef CONFIG_MTD_BYTE_WRITE
+
+ /* Test if this MTD device supports byte write */
+
+ if (dev->mtd->write != NULL)
+ {
+ ret = MTD_WRITE(dev->mtd, offset, writelen, pdata);
+ }
+ else
+#endif
+
+ /* Perform the write using the block write method of the MTD */
+
+ {
+ uint16_t block, index;
+ off_t bytes_this_block;
+
+ while (writelen)
+ {
+ /* Read existing data from the the block into the buffer */
+
+ block = offset / dev->blocksize;
+ ret = MTD_BREAD(dev->mtd, block, 1, dev->buffer);
+ if (ret != 1)
+ {
+ ret = -EIO;
+ goto errout;
+ }
+
+ bytes_this_block = offset - block * dev->blocksize;
+ index = bytes_this_block;
+ if (bytes_this_block > writelen)
+ {
+ bytes_this_block = writelen;
+ }
+
+ /* Now write data to the block */
+
+ memcpy(&dev->buffer[index], pdata, bytes_this_block);
+ ret = MTD_BWRITE(dev->mtd, block, 1, dev->buffer);
+ if (ret != 1)
+ {
+ ret = -EIO;
+ goto errout;
+ }
+
+ /* Update writelen, etc. */
+
+ writelen -= bytes_this_block;
+ pdata += bytes_this_block;
+ offset += bytes_this_block;
+ }
+ }
+
+errout:
+ return ret;
+}
+
+/****************************************************************************
+ * Name: mtdconfig_findfirstentry
+ *
+ * Locates the first config entry, even if it is empty.
+ *
+ * Returns:
+ * offset to the start of the entry.
+ *
+ ****************************************************************************/
+
+static int mtdconfig_findfirstentry(FAR struct mtdconfig_struct_s *dev,
+ FAR struct mtdconfig_header_s *phdr)
+{
+ off_t offset = 2;
+ uint8_t sig[2];
+ bool found = false;
+ int ret;
+
+ mtdconfig_readbytes(dev, 0, sig, sizeof(sig)); /* Read the signature bytes */
+ if (sig[0] != 'C' || sig[1] != 'D')
+ {
+ /* Config Data partition not formatted. */
+
+ return 0;
+ }
+
+ /* Config is formatted. Now loop until we find the first entry */
+
+ while (!found)
+ {
+ /* Read headers until we find one that hasn't been released.
+ */
+
+ ret = mtdconfig_readbytes(dev, offset, (uint8_t *) phdr,
+ sizeof(struct mtdconfig_header_s));
+ if (ret != OK)
+ {
+ return 0;
+ }
+
+ /* Test if this header has been released */
+
+ if (phdr->flags != dev->erasedvalue)
+ {
+ /* This entry has been released. Advance to next entry */
+
+ offset += sizeof(struct mtdconfig_header_s) + phdr->len;
+ }
+ else
+ {
+ /* We found the first entry! */
+
+ found = true;
+ }
+ }
+
+ /* Return the offset of the first entry */
+
+ return offset;
+}
+
+/****************************************************************************
+ * Name: mtdconfig_findnextentry
+ *
+ * Locates the next config entry starting at offset, even if it is empty.
+ *
+ * Returns:
+ * offset to the start of the next entry.
+ *
+ ****************************************************************************/
+
+static int mtdconfig_findnextentry(FAR struct mtdconfig_struct_s *dev,
+ off_t offset,
+ FAR struct mtdconfig_header_s *phdr)
+{
+ bool found = false;
+ int ret;
+
+ /* Loop until next entry found */
+
+ while (!found)
+ {
+ /* Calculate offset of the next entry */
+
+ offset += sizeof(struct mtdconfig_header_s) + phdr->len;
+
+ /* Read next header */
+
+ ret = mtdconfig_readbytes(dev, offset, (uint8_t *) phdr, sizeof(*phdr));
+ if (ret != OK)
+ {
+ return 0;
+ }
+
+ /* Test if this header has is still active */
+
+ if (phdr->flags == dev->erasedvalue)
+ {
+ found = true;
+ }
+ }
+
+ return offset;
+}
+
+/****************************************************************************
+ * Name: mtdconfig_ramconsolidate
+ *
+ * Attempts to perform a RAM based consolidation of released
+ * items. This requires a large enough free RAM block. This
+ * method of consolidation is used when only a single erase
+ * block is available in the partition.
+ *
+ * Returns:
+ * offset to the next available entry (after consolidation)..
+ *
+ ****************************************************************************/
+
+static off_t mtdconfig_ramconsolidate(FAR struct mtdconfig_struct_s *dev)
+{
+ return 0;
+}
+
+/****************************************************************************
+ * Name: mtdconfig_consolidate
+ *
+ * Performs consolidation by writing erase block zero to the
+ * reserved block at the end, erasing block zero, then
+ * walking through each item and copying them to the newly
+ * erased block. It erases all blocks to the end of the
+ * partition as it goes.
+ *
+ * Returns:
+ * offset to the next available entry (after consolidation)..
+ *
+ ****************************************************************************/
+
+static off_t mtdconfig_consolidate(FAR struct mtdconfig_struct_s *dev)
+{
+ off_t src_block, dst_block;
+ off_t src_offset, dst_offset;
+ uint16_t blkper, x, bytes, bytes_left_in_block;
+ struct mtdconfig_header_s hdr;
+ int ret;
+ uint8_t sig[2];
+
+ /* Prepare to copy block 0 to the last block (erase blocks) */
+
+ src_block = 0;
+ dst_block = dev->neraseblocks - 1;
+
+ /* Ensure the last block is erased */
+
+ MTD_ERASE(dev->mtd, dst_block, 1);
+ blkper = dev->erasesize / dev->blocksize;
+ dst_block *= blkper; /* Convert to read/write blocks */
+
+ /* Now copy block zero to last block */
+
+ for (x = 0; x < blkper; x++)
+ {
+ ret = MTD_BREAD(dev->mtd, src_block, 1, dev->buffer);
+ if (ret < 0)
+ {
+ /* I/O Error! */
+
+ return 0;
+ }
+
+ ret = MTD_BWRITE(dev->mtd, dst_block, 1, dev->buffer);
+ if (ret < 0)
+ {
+ /* I/O Error! */
+
+ return 0;
+ }
+ }
+
+ /* Erase block zero and write a format signature. */
+
+ MTD_ERASE(dev->mtd, 0, 1);
+ sig[0] = 'C';
+ sig[1] = 'D';
+ mtdconfig_writebytes(dev, 0, sig, sizeof(sig));
+
+ /* Now consolidate entries */
+
+ src_block = 1;
+ dst_block = 0;
+ src_offset = src_block * dev->erasesize;
+ dst_offset = 2;
+
+ while (src_block < dev->neraseblocks)
+ {
+ /* Scan all headers and move them to the src_offset */
+
+retry_relocate:
+ MTD_READ(dev->mtd, src_offset, sizeof(hdr), (uint8_t *) &hdr);
+ if (hdr.flags == dev->erasedvalue)
+ {
+ /* Test if this entry will fit in the current destination block */
+
+ bytes_left_in_block = dst_offset - dst_block * dev->erasesize;
+ if (hdr.len + sizeof(hdr) > bytes_left_in_block)
+ {
+ /* We need to release the rest of the bytes in this block */
+
+ hdr.flags = ~dev->erasedvalue;
+ hdr.len = bytes_left_in_block - sizeof(hdr);
+ MTD_WRITE(dev->mtd, dst_offset, sizeof(hdr), (uint8_t *) &hdr);
+
+ /* Update control variables */
+
+ dst_offset += bytes_left_in_block;
+ dst_block++;
+ DEBUGASSERT(dst_block != src_block);
+ DEBUGASSERT(dst_offset == dst_block * dev->erasesize);
+
+ /* Retry the relocate */
+
+ goto retry_relocate;
+ }
+
+ /* Copy this entry to the destination */
+
+ MTD_WRITE(dev->mtd, dst_offset, sizeof(hdr), (uint8_t *) &hdr);
+ src_offset += sizeof(hdr);
+ dst_offset += sizeof(hdr);
+
+ /* Now copy the data */
+
+ while (hdr.len)
+ {
+ bytes = hdr.len;
+ if (bytes > dev->blocksize)
+ {
+ bytes = dev->blocksize;
+ }
+
+ /* Move the data. */
+
+ MTD_READ(dev->mtd, src_offset, bytes, dev->buffer);
+ MTD_WRITE(dev->mtd, dst_offset, bytes, dev->buffer);
+
+ /* Update control variables */
+
+ hdr.len -= bytes;
+ src_offset += bytes;
+ dst_offset += bytes;
+ }
+ }
+ else
+ {
+ /* This item has been released. Skip it! */
+
+ src_offset += sizeof(hdr) + hdr.len;
+ }
+
+ /* Test if we advanced to the next block. If we did, then erase the
+ * old block.
+ */
+
+ if (src_block != src_offset / dev->erasesize)
+ {
+ /* Erase the block ... we have emptied it */
+
+ MTD_ERASE(dev->mtd, src_block, 1);
+ src_block++;
+ }
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: mtdconfig_open
+ ****************************************************************************/
+
+static int mtdconfig_open(FAR struct file *filep)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct mtdconfig_struct_s *dev = inode->i_private;
+ int ret = OK;
+
+ /* Get exclusive access to the device */
+
+ ret = sem_wait(&dev->exclsem);
+ if (ret < 0)
+ {
+ ret = -errno;
+ goto errout;
+ }
+
+ dev->readoff = 0;
+
+errout:
+ return ret;
+}
+
+/****************************************************************************
+ * Name: mtdconfig_close
+ ****************************************************************************/
+
+static int mtdconfig_close(FAR struct file *filep)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct mtdconfig_struct_s *dev = inode->i_private;
+
+ /* Release exclusive access to the device */
+
+ sem_post(&dev->exclsem);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: mtdconfig_read
+ ****************************************************************************/
+
+static ssize_t mtdconfig_read(FAR struct file *filep, FAR char *buffer,
+ size_t len)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct mtdconfig_struct_s *dev = inode->i_private;
+ size_t bytes;
+
+ if (dev->readoff >= dev->neraseblocks * dev->erasesize)
+ {
+ return 0;
+ }
+
+ /* Read data from the file */
+
+ bytes = MTD_READ(dev->mtd, dev->readoff, len, (uint8_t *) buffer);
+ dev->readoff += bytes;
+ return bytes;
+}
+
+/****************************************************************************
+ * Name: mtdconfig_setconfig
+ ****************************************************************************/
+
+static int mtdconfig_setconfig(FAR struct mtdconfig_struct_s *dev,
+ FAR struct config_data_s *pdata)
+{
+ uint8_t sig[2]; /* Format signature bytes ("CD") */
+ char retrycount = 0;
+ int ret = -ENOSYS;
+ off_t offset, bytes_left_in_block, bytes;
+ uint16_t block;
+ struct mtdconfig_header_s hdr;
+
+ /* Allocate a temp block buffer */
+
+ dev->buffer = (FAR uint8_t *) kmalloc(dev->blocksize);
+
+ /* Read and vaidate the signature bytes */
+
+retry:
+ offset = mtdconfig_findfirstentry(dev, &hdr);
+ if (offset == 0)
+ {
+ /* Config Data partition not formatted. */
+
+ if (retrycount)
+ {
+ ret = -ENOSYS;
+ goto errout;
+ }
+
+ /* Try to format the config partition */
+
+ ret = MTD_IOCTL(dev->mtd, MTDIOC_BULKERASE, 0);
+ if (ret < 0)
+ {
+ goto errout;
+ }
+
+ /* Write a format signature */
+
+ sig[0] = 'C';
+ sig[1] = 'D';
+ mtdconfig_writebytes(dev, 0, sig, sizeof(sig));
+
+ /* Now go try to read the signature again (as verification) */
+
+ retrycount++;
+ goto retry;
+ }
+
+ /* Okay, the Config Data partition is formatted. Check if the
+ * config item being written is already in the database. If it
+ * is, we must mark it as obsolete before creating a new entry.
+ */
+
+ while (offset > 0 && (pdata->id != hdr.id || pdata->instance != hdr.instance))
+ {
+ /* This header doesn't match. Test if it is the last hdr */
+
+ if (hdr.id == ((dev->erasedvalue << 8) | dev->erasedvalue))
+ {
+ break;
+ }
+
+ /* Nope, not the last header. Get the next one */
+
+ offset = mtdconfig_findnextentry(dev, offset, &hdr);
+ }
+
+ /* Test if the header was found. */
+
+ if (pdata->id == hdr.id && pdata->instance == hdr.instance)
+ {
+ /* Mark this entry as released */
+
+ hdr.flags = ~dev->erasedvalue;
+ mtdconfig_writebytes(dev, offset, &hdr.flags, sizeof(hdr.flags));
+ }
+
+ /* Now find a new entry for this config data */
+
+ while (hdr.id != ((dev->erasedvalue << 8) | dev->erasedvalue))
+ {
+ /* Read the next entry */
+
+ offset = mtdconfig_findnextentry(dev, offset, &hdr);
+ }
+
+ /* Test if a new entry was found */
+
+ retrycount = 0;
+ if (offset > 0)
+ {
+ /* Try to write the data to this entry. We have to be
+ * sure it will fit in the erase block because we don't
+ * support data spanning erase blocks.
+ */
+
+retry_fit:
+ block = offset / dev->erasesize;
+ bytes_left_in_block = block * dev->erasesize - offset;
+ if (bytes_left_in_block < pdata->len)
+ {
+ /* Data doesn't fit in the block! Our only recourse is
+ * to release the rest of the data in this erase block.
+ */
+
+ hdr.id = 0;
+ hdr.len = bytes_left_in_block - sizeof(hdr);
+ hdr.flags = ~dev->erasedvalue;
+ mtdconfig_writebytes(dev, offset, (uint8_t *) &hdr, sizeof(hdr));
+
+ /* Now we need to advance to the next erase block */
+
+ offset += bytes_left_in_block;
+ block++;
+ if (dev->neraseblocks == 1)
+ {
+ /* If we only have 1 erase block, then we must do a RAM
+ * assisted consolidation of released entries.
+ */
+
+ if (retrycount)
+ {
+ /* Out of space! */
+
+ ret = -ENOMEM;
+ goto errout;
+ }
+
+ offset = mtdconfig_ramconsolidate(dev);
+ retrycount++;
+ goto retry_fit;
+ }
+
+ /* If we have 2 or more erase blocks, then we reserve one
+ * block to perform non-RAM assited consolidation.
+ */
+
+ else if (block + 1 == dev->neraseblocks)
+ {
+ if (retrycount)
+ {
+ /* Out of space! */
+
+ ret = -ENOMEM;
+ goto errout;
+ }
+
+ offset = mtdconfig_consolidate(dev);
+ retrycount++;
+ goto retry_fit;
+ }
+ }
+
+ /* Validate that we have a non-zero offset. We may have done a
+ * consolidation above that resulted
+ */
+
+ if (offset)
+ {
+ /* Save the data at this entry */
+
+ hdr.id = pdata->id;
+ hdr.instance = pdata->instance;
+ hdr.len = pdata->len;
+ hdr.flags = dev->erasedvalue;
+ mtdconfig_writebytes(dev, offset, (uint8_t *) &hdr, sizeof(hdr));
+ bytes = mtdconfig_writebytes(dev, offset + sizeof(hdr), pdata->configdata,
+ pdata->len);
+ if (bytes != pdata->len)
+ {
+ /* Error writing data! */
+
+ hdr.flags = ~dev->erasedvalue;
+ mtdconfig_writebytes(dev, offset, (uint8_t *) &hdr, sizeof(hdr.flags));
+ ret = -EIO;
+ goto errout;
+ }
+
+ ret = OK;
+ }
+ }
+
+errout:
+
+ /* Free the buffer */
+
+ kfree(dev->buffer);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: mtdconfig_getconfig
+ ****************************************************************************/
+
+static int mtdconfig_getconfig(FAR struct mtdconfig_struct_s *dev,
+ FAR struct config_data_s *pdata)
+{
+ int ret = -ENOSYS;
+ off_t offset, bytes_to_read;
+ struct mtdconfig_header_s hdr;
+
+ /* Allocate a temp block buffer */
+
+ dev->buffer = (FAR uint8_t *) kmalloc(dev->blocksize);
+ if (dev->buffer == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ /* Get the offset of the first entry. This will also check
+ * the format signature bytes.
+ */
+
+ offset = mtdconfig_findfirstentry(dev, &hdr);
+ while (offset > 0 && (pdata->id != hdr.id || pdata->instance != hdr.instance))
+ {
+ /* This header doesn't match. Test if it is the last hdr */
+
+ if (hdr.id == ((dev->erasedvalue << 8) | dev->erasedvalue))
+ {
+ break;
+ }
+
+ /* Nope, not the last header. Get the next one */
+
+ offset = mtdconfig_findnextentry(dev, offset, &hdr);
+ }
+
+ /* Test if the header was found. */
+
+ if (pdata->id == hdr.id && pdata->instance == hdr.instance)
+ {
+ /* Entry found. Read the data */
+
+ bytes_to_read = hdr.len;
+ if (bytes_to_read > pdata->len)
+ {
+ bytes_to_read = pdata->len;
+ }
+
+ /* Perform the read */
+
+ ret = mtdconfig_readbytes(dev, offset + sizeof(hdr), pdata->configdata,
+ bytes_to_read);
+ if (ret != OK)
+ {
+ /* Error reading the data */
+
+ ret = -EIO;
+ goto errout;
+ }
+
+ ret = OK;
+ }
+
+errout:
+ /* Free the buffer */
+
+ kfree(dev->buffer);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: mtdconfig_ioctl
+ ****************************************************************************/
+
+static int mtdconfig_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct mtdconfig_struct_s *dev = inode->i_private;
+ FAR struct config_data_s *pdata;
+ int ret = -ENOSYS;
+
+ switch (cmd)
+ {
+ case CFGDIOC_SETCONFIG:
+
+ /* Set the config item */
+
+ pdata = (FAR struct config_data_s *) arg;
+ ret = mtdconfig_setconfig(dev, pdata);
+ break;
+
+ case CFGDIOC_GETCONFIG:
+
+ /* Get the config item */
+
+ pdata = (FAR struct config_data_s *) arg;
+ ret = mtdconfig_getconfig(dev, pdata);
+ break;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: mtdconfig_poll
+ ****************************************************************************/
+
+#ifndef CONFIG_DISABLE_POLL
+static int mtdconfig_poll(FAR struct file *filep, FAR struct pollfd *fds,
+ bool setup)
+{
+ if (setup)
+ {
+ fds->revents |= (fds->events & (POLLIN|POLLOUT));
+ if (fds->revents != 0)
+ {
+ sem_post(fds->sem);
+ }
+ }
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mtdconfig_register
+ *
+ * Description:
+ * Register a /dev/config device backed by an MTD
+ *
+ ****************************************************************************/
+
+int mtdconfig_register(FAR struct mtd_dev_s *mtd)
+{
+ int ret = OK;
+ struct mtdconfig_struct_s *dev;
+ struct mtd_geometry_s geo; /* Device geometry */
+
+ dev = (struct mtdconfig_struct_s *)kmalloc(sizeof(struct mtdconfig_struct_s));
+ if (dev)
+ {
+ /* Initialize the mtdconfig device structure */
+
+ dev->mtd = mtd;
+ sem_init(&dev->exclsem, 0, 1);
+
+ /* Get the device geometry. (casting to uintptr_t first eliminates
+ * complaints on some architectures where the sizeof long is different
+ * from the size of a pointer).
+ */
+
+ ret = MTD_IOCTL(mtd, MTDIOC_GEOMETRY, (unsigned long)((uintptr_t)&geo));
+ if (ret < 0)
+ {
+ fdbg("MTD ioctl(MTDIOC_GEOMETRY) failed: %d\n", ret);
+ kfree(dev);
+ goto errout;
+ }
+
+ dev->blocksize = geo.blocksize;
+ dev->neraseblocks = geo.neraseblocks;
+ dev->erasesize = geo.erasesize;
+ dev->nblocks = geo.neraseblocks * geo.erasesize / geo.blocksize;
+ dev->erasedvalue = 0xFF; /* TODO: fix this */
+
+ (void)register_driver("/dev/config", &mtdconfig_fops, 0666, dev);
+ }
+
+errout:
+ return ret;
+}
+#endif /* CONFIG_MTD_CONFIG */
diff --git a/nuttx/include/nuttx/configdata.h b/nuttx/include/nuttx/configdata.h
new file mode 100644
index 000000000..8b540a49d
--- /dev/null
+++ b/nuttx/include/nuttx/configdata.h
@@ -0,0 +1,143 @@
+/****************************************************************************
+ * include/nuttx/configdata.h
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __INCLUDE_NUTTX_CONFIGDATA_H
+#define __INCLUDE_NUTTX_CONFIGDATA_H
+
+/* The configdata device details kernel level services for providing
+ * application config data from kernel control objects, such as partitions
+ * on shared MTD devices, etc.
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+
+#include <nuttx/fs/ioctl.h>
+
+#ifdef CONFIG_PLATFORM_CONFIGDATA
+
+/****************************************************************************
+ * Pre-Processor Definitions
+ ****************************************************************************/
+/* Configuration ************************************************************/
+/* CONFIG_AUDIO - Enables Audio driver support
+ * CONFIG_DEBUG_AUDIO - If enabled (with CONFIG_DEBUG and, optionally,
+ * CONFIG_DEBUG_VERBOSE), this will generate output that can be used to
+ * debug Audio drivers.
+ */
+
+/* IOCTL Commands ***********************************************************/
+/* The Audio module uses a standard character driver framework. However, a
+ * lot of the Audio driver functionality is configured via a device control
+ * interface, such as sampling rate, volume, data format, etc.
+ * The Audio ioctl commands are lised below:
+ *
+ * CFGDIOC_GETCONFIG - Get a specified Config Data item.
+ *
+ * ioctl argument: Pointer to a config_data_s structure to receive the
+ * config data. All fields of the strucure must be
+ * specified (i.e. id, instance, pointer and len).
+ *
+ * CFGDIOC_SETCONFIG - Set a specified Config Data Item
+ *
+ * ioctl argument: Pointer to a config_data_s structure to receive the
+ * config data. All fields of the strucure must be
+ * specified (i.e. id, instance, pointer and len).
+ */
+
+#define CFGDIOC_GETCONFIG _CFGDIOC(1)
+#define CFGDIOC_SETCONFIG _CFGDIOC(2)
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* This structure is used to get and set config data items */
+
+struct config_data_s
+{
+ uint16_t id; /* ID of the config data item */
+ int instance; /* Instance of the item */
+ FAR uint8_t *configdata; /* Pointer to the config data */
+ size_t len; /* Length of the config data buffer */
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Name: mtdconfig_register
+ *
+ * Description:
+ * This function binds an instance of an MTD device to the /dev/config
+ * device.
+ *
+ * When this function is called, the MTD device pass in should already
+ * be initialized appropriately to access the physical device or partition.
+ *
+ * Input parameters:
+ * mtd - Pointer to the MTD device to bind with the /dev/config device
+ *
+ * Returned Value:
+ * Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int mtdconfig_register(FAR struct mtd_dev_s *mtd);
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CONFIG_PLATFORM_CONFIGDATA */
+#endif /* __INCLUDE_NUTTX_CONFIGDATA_H */
diff --git a/nuttx/include/nuttx/fs/ioctl.h b/nuttx/include/nuttx/fs/ioctl.h
index 5c2fbb67b..7baf2fd49 100644
--- a/nuttx/include/nuttx/fs/ioctl.h
+++ b/nuttx/include/nuttx/fs/ioctl.h
@@ -1,7 +1,7 @@
/****************************************************************************
* include/nuttx/fs/ioctl.h
*
- * Copyright (C) 2008, 2009, 2011-2012 Gregory Nutt. All rights reserved.
+ * Copyright (C) 2008, 2009, 2011-2013 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@@ -68,7 +68,8 @@
#define _QEIOCBASE (0x0f00) /* Quadrature encoder ioctl commands */
#define _AUDIOIOCBASE (0x1000) /* Audio ioctl commands */
#define _SLCDIOCBASE (0x1100) /* Segment LCD ioctl commands */
-#define _WLIOCBASE (0x1100) /* Wireless modules ioctl commands */
+#define _WLIOCBASE (0x1200) /* Wireless modules ioctl commands */
+#define _CFGDIOCBASE (0x1300) /* Config Data device (app config) ioctl commands */
/* Macros used to manage ioctl commands */
@@ -251,24 +252,30 @@
#define _QEIOCVALID(c) (_IOC_TYPE(c)==_QEIOCBASE)
#define _QEIOC(nr) _IOC(_QEIOCBASE,nr)
-/* NuttX Audio driver ioctl definitions ************************************/
+/* NuttX Audio driver ioctl definitions *************************************/
/* (see nuttx/audio/audio.h) */
#define _AUDIOIOCVALID(c) (_IOC_TYPE(c)==_AUDIOIOCBASE)
#define _AUDIOIOC(nr) _IOC(_AUDIOIOCBASE,nr)
-/* Segment LCD driver ioctl definitions ************************************/
+/* Segment LCD driver ioctl definitions *************************************/
/* (see nuttx/include/lcd/slcd_codec.h */
#define _SLCDIOCVALID(c) (_IOC_TYPE(c)==_SLCDIOCBASE)
#define _SLCDIOC(nr) _IOC(_SLCDIOCBASE,nr)
-/* Wireless driver ioctl definitions ************************************/
+/* Wireless driver ioctl definitions ****************************************/
/* (see nuttx/include/wireless/wireless.h */
#define _WLIOCVALID(c) (_IOC_TYPE(c)==_WLIOCBASE)
#define _WLIOC(nr) _IOC(_WLIOCBASE,nr)
+/* Application Config Data driver ioctl definitions *************************/
+/* (see nuttx/include/configdata.h */
+
+#define _CFGDIOCVALID(c) (_IOC_TYPE(c)==_CFGDIOCBASE)
+#define _CFGDIOC(nr) _IOC(_CFGDIOCBASE,nr)
+
/****************************************************************************
* Public Type Definitions
****************************************************************************/