Add an MTD device wrapper that can used to provide a /dev/config that can be used for retaining configuration data. From Ken Pettit

This commit is contained in:
Gregory Nutt 2013-11-01 07:39:20 -06:00
parent 532d4c7cbe
commit b01900d7e6
6 changed files with 1162 additions and 7 deletions

View File

@ -5924,4 +5924,7 @@
caused an exception in usbdev_reset() later. The driver reference caused an exception in usbdev_reset() later. The driver reference
will be nullified later usbdev_unregister when the caller gets the will be nullified later usbdev_unregister when the caller gets the
error. From David Sidrane (2013-10-31). 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).

View File

@ -35,6 +35,13 @@ config MTD_BYTE_WRITE
support such writes. The SMART file system can take advantage of support such writes. The SMART file system can take advantage of
this option if it is enabled. 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" comment "MTD Device Drivers"
config RAMMTD config RAMMTD

View File

@ -39,7 +39,7 @@
ifeq ($(CONFIG_MTD),y) 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) ifeq ($(CONFIG_MTD_PARTITION),y)
CSRCS += mtd_partition.c CSRCS += mtd_partition.c

995
drivers/mtd/mtd_config.c Normal file
View File

@ -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 */

143
include/nuttx/configdata.h Normal file
View File

@ -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 */

View File

@ -1,7 +1,7 @@
/**************************************************************************** /****************************************************************************
* include/nuttx/fs/ioctl.h * 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> * Author: Gregory Nutt <gnutt@nuttx.org>
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -68,7 +68,8 @@
#define _QEIOCBASE (0x0f00) /* Quadrature encoder ioctl commands */ #define _QEIOCBASE (0x0f00) /* Quadrature encoder ioctl commands */
#define _AUDIOIOCBASE (0x1000) /* Audio ioctl commands */ #define _AUDIOIOCBASE (0x1000) /* Audio ioctl commands */
#define _SLCDIOCBASE (0x1100) /* Segment LCD 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 */ /* Macros used to manage ioctl commands */
@ -251,24 +252,30 @@
#define _QEIOCVALID(c) (_IOC_TYPE(c)==_QEIOCBASE) #define _QEIOCVALID(c) (_IOC_TYPE(c)==_QEIOCBASE)
#define _QEIOC(nr) _IOC(_QEIOCBASE,nr) #define _QEIOC(nr) _IOC(_QEIOCBASE,nr)
/* NuttX Audio driver ioctl definitions ************************************/ /* NuttX Audio driver ioctl definitions *************************************/
/* (see nuttx/audio/audio.h) */ /* (see nuttx/audio/audio.h) */
#define _AUDIOIOCVALID(c) (_IOC_TYPE(c)==_AUDIOIOCBASE) #define _AUDIOIOCVALID(c) (_IOC_TYPE(c)==_AUDIOIOCBASE)
#define _AUDIOIOC(nr) _IOC(_AUDIOIOCBASE,nr) #define _AUDIOIOC(nr) _IOC(_AUDIOIOCBASE,nr)
/* Segment LCD driver ioctl definitions ************************************/ /* Segment LCD driver ioctl definitions *************************************/
/* (see nuttx/include/lcd/slcd_codec.h */ /* (see nuttx/include/lcd/slcd_codec.h */
#define _SLCDIOCVALID(c) (_IOC_TYPE(c)==_SLCDIOCBASE) #define _SLCDIOCVALID(c) (_IOC_TYPE(c)==_SLCDIOCBASE)
#define _SLCDIOC(nr) _IOC(_SLCDIOCBASE,nr) #define _SLCDIOC(nr) _IOC(_SLCDIOCBASE,nr)
/* Wireless driver ioctl definitions ************************************/ /* Wireless driver ioctl definitions ****************************************/
/* (see nuttx/include/wireless/wireless.h */ /* (see nuttx/include/wireless/wireless.h */
#define _WLIOCVALID(c) (_IOC_TYPE(c)==_WLIOCBASE) #define _WLIOCVALID(c) (_IOC_TYPE(c)==_WLIOCBASE)
#define _WLIOC(nr) _IOC(_WLIOCBASE,nr) #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 * Public Type Definitions
****************************************************************************/ ****************************************************************************/