diff --git a/ChangeLog b/ChangeLog index 9c7be4e864940a9f7071c690f76c49902ad76080..4dbe42cd166a22678f8b61b95c9faa133ac96647 100755 --- a/ChangeLog +++ b/ChangeLog @@ -11152,4 +11152,10 @@ * drivers/lcd/ili9432.c: Fixed errors in orientation. Portrait, RPortrait, and RLandscript should work correly now. They were displayed mirrored. From Marco Krahl (2015-11-25). - + * drivers/mtd/mtd/mtd_procfs/c and include/nuttx/mtd/mtd.h: Add an + interface to un-regiser an MTD procfs entry. From Ken Petit + (2015-11-25). + * drivers/mtd/filemtd.c: New new MTD conversion layer that will + convert a regular file (or driver file) to an MTD device. This is + useful for testing on the simulation using the hostfs. From Ken + Petit (2015-11-25). diff --git a/drivers/mtd/filemtd.c b/drivers/mtd/filemtd.c new file mode 100644 index 0000000000000000000000000000000000000000..ba24f4a02384019cb9eb057c62af8b999d41adca --- /dev/null +++ b/drivers/mtd/filemtd.c @@ -0,0 +1,590 @@ +/**************************************************************************** + * drivers/mtd/filemtd.c + * + * Copyright (C) 2015 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/stat.h> +#include <stdint.h> +#include <fcntl.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#include <debug.h> + +#include <nuttx/kmalloc.h> +#include <nuttx/fs/ioctl.h> +#include <nuttx/mtd/mtd.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* Configuration ************************************************************/ + +#ifndef CONFIG_FILEMTD_BLOCKSIZE +# define CONFIG_FILEMTD_BLOCKSIZE 512 +#endif + +#ifndef CONFIG_FILEMTD_ERASESIZE +# define CONFIG_FILEMTD_ERASESIZE 4096 +#endif + +#ifndef CONFIG_FILEMTD_ERASESTATE +# define CONFIG_FILEMTD_ERASESTATE 0xff +#endif + +#if CONFIG_FILEMTD_ERASESTATE != 0xff && CONFIG_FILEMTD_ERASESTATE != 0x00 +# error "Unsupported value for CONFIG_FILEMTD_ERASESTATE" +#endif + +#if CONFIG_FILEMTD_BLOCKSIZE > CONFIG_FILEMTD_ERASESIZE +# error "Must have CONFIG_FILEMTD_BLOCKSIZE <= CONFIG_FILEMTD_ERASESIZE" +#endif + +#undef FILEMTD_BLKPER +#define FILEMTD_BLKPER (CONFIG_FILEMTD_ERASESIZE/CONFIG_FILEMTD_BLOCKSIZE) + +#if FILEMTD_BLKPER*CONFIG_FILEMTD_BLOCKSIZE != CONFIG_FILEMTD_ERASESIZE +# error "CONFIG_FILEMTD_ERASESIZE must be an even multiple of CONFIG_FILEMTD_BLOCKSIZE" +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This type represents the state of the MTD device. The struct mtd_dev_s + * must appear at the beginning of the definition so that you can freely + * cast between pointers to struct mtd_dev_s and struct file_dev_s. + */ + +struct file_dev_s +{ + struct mtd_dev_s mtd; /* MTD device */ + int fd; /* File descriptor of underlying file */ + size_t nblocks; /* Number of erase blocks */ + size_t offset; /* Offset from start of file */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static ssize_t filemtd_read(FAR struct file_dev_s *priv, unsigned char *buffer, + size_t offsetbytes, unsigned int nbytes); +static ssize_t filemtd_write(FAR struct file_dev_s *priv, size_t offset, FAR const void *src, size_t len); + +/* MTD driver methods */ + +static int file_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks); +static ssize_t file_bread(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks, + FAR uint8_t *buf); +static ssize_t file_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks, + FAR const uint8_t *buf); +static ssize_t file_byteread(FAR struct mtd_dev_s *dev, off_t offset, + size_t nbytes, FAR uint8_t *buf); +#ifdef CONFIG_MTD_BYTE_WRITE +static ssize_t file_bytewrite(FAR struct mtd_dev_s *dev, off_t offset, + size_t nbytes, FAR const uint8_t *buf); +#endif +static int file_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: filemtd_write + ****************************************************************************/ + +static ssize_t filemtd_write(FAR struct file_dev_s *priv, size_t offset, + FAR const void *src, size_t len) +{ + FAR const uint8_t *pin = (FAR const uint8_t *)src; + FAR uint8_t *pout; + char buf[128]; + int buflen = 0; + uint8_t oldvalue; + uint8_t srcvalue; + uint8_t newvalue; + size_t seekpos; + + /* Set the starting location in the file */ + + seekpos = priv->offset + offset; + + while (len-- > 0) + { + if (buflen == 0) + { + /* Read more data from the file */ + + lseek(priv->fd, seekpos, SEEK_SET); + buflen = read(priv->fd, buf, sizeof(buf)); + pout = (FAR uint8_t *) buf; + } + + /* Get the source and destination values */ + + oldvalue = *pout; + srcvalue = *pin++; + + /* Get the new destination value, accounting for bits that cannot be + * changes because they are not in the erased state. + */ + +#if CONFIG_FILEMTD_ERASESTATE == 0xff + newvalue = oldvalue & srcvalue; /* We can only clear bits */ +#else /* CONFIG_FILEMTD_ERASESTATE == 0x00 */ + newvalue = oldvalue | srcvalue; /* We can only set bits */ +#endif + + /* Report any attempt to change the value of bits that are not in the + * erased state. + */ + +#ifdef CONFIG_DEBUG + if (newvalue != srcvalue) + { + dbg("ERROR: Bad write: source=%02x dest=%02x result=%02x\n", + srcvalue, oldvalue, newvalue); + } +#endif + + /* Write the modified value to simulated FLASH */ + + *pout++ = newvalue; + buflen--; + + /* If our buffer is full, then seek back to beginning of + * the file and write the buffer contents + */ + + if (buflen == 0) + { + lseek(priv->fd, seekpos, SEEK_SET); + write(priv->fd, buf, sizeof(buf)); + seekpos += sizeof(buf); + } + } + + /* Write remaining bytes */ + + if (buflen != 0) + { + lseek(priv->fd, seekpos, SEEK_SET); + write(priv->fd, buf, sizeof(buf)); + } + + return len; +} + +/**************************************************************************** + * Name: filemtd_read + ****************************************************************************/ + +static ssize_t filemtd_read(FAR struct file_dev_s *priv, unsigned char *buffer, + size_t offsetbytes, unsigned int nbytes) +{ + /* Set the starting location in the file */ + + lseek(priv->fd, priv->offset + offsetbytes, SEEK_SET); + + return read(priv->fd, buffer, nbytes); +} + +/**************************************************************************** + * Name: file_erase + ****************************************************************************/ + +static int file_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks) +{ + FAR struct file_dev_s *priv = (FAR struct file_dev_s *)dev; + size_t nbytes; + size_t offset; + char buffer[128]; + + DEBUGASSERT(dev); + + /* Don't let the erase exceed the original size of the file */ + + if (startblock >= priv->nblocks) + { + return 0; + } + + if (startblock + nblocks > priv->nblocks) + { + nblocks = priv->nblocks - startblock; + } + + /* Convert the erase block to a logical block and the number of blocks + * in logical block numbers + */ + + startblock *= FILEMTD_BLKPER; + nblocks *= FILEMTD_BLKPER; + + /* Get the offset corresponding to the first block and the size + * corresponding to the number of blocks. + */ + + offset = startblock * CONFIG_FILEMTD_BLOCKSIZE; + nbytes = nblocks * CONFIG_FILEMTD_BLOCKSIZE; + + /* Then erase the data in the file */ + + lseek(priv->fd, priv->offset + offset, SEEK_SET); + memset(buffer, CONFIG_FILEMTD_ERASESTATE, sizeof(buffer)); + while (nbytes) + { + write(priv->fd, buffer, sizeof(buffer)); + nbytes -= sizeof(buffer); + } + + return OK; +} + +/**************************************************************************** + * Name: file_bread + ****************************************************************************/ + +static ssize_t file_bread(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks, + FAR uint8_t *buf) +{ + FAR struct file_dev_s *priv = (FAR struct file_dev_s *)dev; + off_t offset; + off_t maxblock; + size_t nbytes; + + DEBUGASSERT(dev && buf); + + /* Don't let the read exceed the original size of the file */ + + maxblock = priv->nblocks * FILEMTD_BLKPER; + if (startblock >= maxblock) + { + return 0; + } + + if (startblock + nblocks > maxblock) + { + nblocks = maxblock - startblock; + } + + /* Get the offset corresponding to the first block and the size + * corresponding to the number of blocks. + */ + + offset = startblock * CONFIG_FILEMTD_BLOCKSIZE; + nbytes = nblocks * CONFIG_FILEMTD_BLOCKSIZE; + + /* Then read the data from the file */ + + filemtd_read(priv, buf, offset, nbytes); + return nblocks; +} + +/**************************************************************************** + * Name: file_bwrite + ****************************************************************************/ + +static ssize_t file_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, + size_t nblocks, FAR const uint8_t *buf) +{ + FAR struct file_dev_s *priv = (FAR struct file_dev_s *)dev; + off_t offset; + off_t maxblock; + size_t nbytes; + + DEBUGASSERT(dev && buf); + + /* Don't let the write exceed the original size of the file */ + + maxblock = priv->nblocks * FILEMTD_BLKPER; + if (startblock >= maxblock) + { + return 0; + } + + if (startblock + nblocks > maxblock) + { + nblocks = maxblock - startblock; + } + + /* Get the offset corresponding to the first block and the size + * corresponding to the number of blocks. + */ + + offset = startblock * CONFIG_FILEMTD_BLOCKSIZE; + nbytes = nblocks * CONFIG_FILEMTD_BLOCKSIZE; + + /* Then write the data to the file */ + + filemtd_write(priv, offset, buf, nbytes); + return nblocks; +} + +/**************************************************************************** + * Name: file_byteread + ****************************************************************************/ + +static ssize_t file_byteread(FAR struct mtd_dev_s *dev, off_t offset, + size_t nbytes, FAR uint8_t *buf) +{ + FAR struct file_dev_s *priv = (FAR struct file_dev_s *)dev; + + DEBUGASSERT(dev && buf); + + /* Don't let read read past end of buffer */ + + if (offset + nbytes > priv->nblocks * CONFIG_FILEMTD_ERASESIZE) + { + return 0; + } + + filemtd_read(priv, buf, offset, nbytes); + return nbytes; +} + +/**************************************************************************** + * Name: file_bytewrite + ****************************************************************************/ + +#ifdef CONFIG_MTD_BYTE_WRITE +static ssize_t file_bytewrite(FAR struct mtd_dev_s *dev, off_t offset, + size_t nbytes, FAR const uint8_t *buf) +{ + FAR struct file_dev_s *priv = (FAR struct file_dev_s *)dev; + off_t maxoffset; + + DEBUGASSERT(dev && buf); + + /* Don't let the write exceed the original size of the file */ + + maxoffset = priv->nblocks * CONFIG_FILEMTD_ERASESIZE; + if (offset + nbytes > maxoffset) + { + return 0; + } + + /* Then write the data to the file */ + + filemtd_write(priv, offset, buf, nbytes); + return nbytes; +} +#endif + +/**************************************************************************** + * Name: file_ioctl + ****************************************************************************/ + +static int file_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg) +{ + FAR struct file_dev_s *priv = (FAR struct file_dev_s *)dev; + int ret = -EINVAL; /* Assume good command with bad parameters */ + + switch (cmd) + { + case MTDIOC_GEOMETRY: + { + FAR struct mtd_geometry_s *geo = (FAR struct mtd_geometry_s *)((uintptr_t)arg); + if (geo) + { + /* Populate the geometry structure with information need to know + * the capacity and how to access the device. + */ + + geo->blocksize = CONFIG_FILEMTD_BLOCKSIZE; + geo->erasesize = CONFIG_FILEMTD_ERASESIZE; + geo->neraseblocks = priv->nblocks; + ret = OK; + } + } + break; + + case MTDIOC_XIPBASE: + ret = -ENOTTY; /* Bad command */ + break; + + case MTDIOC_BULKERASE: + { + /* Erase the entire device */ + + file_erase(dev, 0, priv->nblocks); + ret = OK; + } + break; + + default: + ret = -ENOTTY; /* Bad command */ + break; + } + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: filemtd_initialize + * + * Description: + * Create and initialize a FILE MTD device instance. + * + * Input Parameters: + * path - Path name of the file backing the MTD device + * + ****************************************************************************/ + +FAR struct mtd_dev_s *filemtd_initialize(FAR const char *path, size_t offset) +{ + FAR struct file_dev_s *priv; + struct stat sb; + size_t nblocks; + size_t filelen; + int mode, ret; + + /* Create an instance of the FILE MTD device state structure */ + + priv = (FAR struct file_dev_s *)kmm_zalloc(sizeof(struct file_dev_s)); + if (!priv) + { + fdbg("Failed to allocate the FILE MTD state structure\n"); + return NULL; + } + + /* Determine the file open mode */ + + mode = O_RDOK; +#ifdef CONFIG_FS_WRITABLE + mode |= O_WROK; +#endif + + /* Stat the file */ + + ret = stat(path, &sb); + if (ret < 0) + { + dbg("Failed to stat %s: %d\n", path, get_errno()); + return NULL; + } + + filelen = sb.st_size; + + /* Try to open the file */ + + priv->fd = open(path, mode); + if (priv->fd == -1) + { + fdbg("Failed to open the FILE MTD file %s\n", path); + kmm_free(priv); + return NULL; + } + + /* Force the size to be an even number of the erase block size */ + + nblocks = (filelen - offset) / CONFIG_FILEMTD_ERASESIZE; + if (nblocks < 3) + { + fdbg("Need to provide at least three full erase block\n"); + kmm_free(priv); + return NULL; + } + + /* Perform initialization as necessary. (unsupported methods were + * nullified by kmm_zalloc). + */ + + priv->mtd.erase = file_erase; + priv->mtd.bread = file_bread; + priv->mtd.bwrite = file_bwrite; + priv->mtd.read = file_byteread; +#ifdef CONFIG_MTD_BYTE_WRITE + priv->mtd.write = file_bytewrite; +#endif + priv->mtd.ioctl = file_ioctl; + priv->offset = offset; + priv->nblocks = nblocks; + + /* Register the MTD with the procfs system if enabled */ + +#ifdef CONFIG_MTD_REGISTRATION + mtd_register(&priv->mtd, "filemtd"); +#endif + + return &priv->mtd; +} + +/**************************************************************************** + * Name: filemtd_teardown + * + * Description: + * Teardown a previously created filemtd device. + * + * Input Parameters: + * path - Path name of the file backing the MTD device + * + ****************************************************************************/ + +void filemtd_teardown(FAR struct mtd_dev_s *dev) +{ + FAR struct file_dev_s *priv; + + /* Close the enclosed file */ + + priv = (FAR struct file_dev_s *) dev; + close(priv->fd); + + /* Register the MTD with the procfs system if enabled */ + +#ifdef CONFIG_MTD_REGISTRATION + mtd_unregister(&priv->mtd); +#endif + + /* Free the memory */ + + kmm_free(priv); +} + diff --git a/drivers/mtd/mtd_procfs.c b/drivers/mtd/mtd_procfs.c index 2f640b1a3f00ce36f39f535367a95c520d00fda8..b410df87565f8b49265f87a39ad4fe5843bd7286 100644 --- a/drivers/mtd/mtd_procfs.c +++ b/drivers/mtd/mtd_procfs.c @@ -363,4 +363,45 @@ int mtd_register(FAR struct mtd_dev_s *mtd, FAR const char *name) return OK; } +/**************************************************************************** + * Name: mtd_unregister + * + * Description: + * Un-Registers an MTD device with the procfs file system. + * + * In an embedded system, this all is really unnecessary, but is provided + * in the procfs system simply for information purposes (if desired). + * + ****************************************************************************/ + +int mtd_unregister(FAR struct mtd_dev_s *mtd) +{ + FAR struct mtd_dev_s *plast; + + /* Remove the MTD from the list of registered devices */ + + if (g_pfirstmtd == mtd) + { + g_pfirstmtd = mtd->pnext; + } + else + { + /* Remove from middle of list */ + + plast = g_pfirstmtd; + while (plast->pnext != mtd) + { + /* Skip to next entry as long as there is one */ + + plast = plast->pnext; + } + + /* Now remove at this location */ + + plast->pnext = mtd->pnext; + } + + return OK; +} + #endif /* !CONFIG_DISABLE_MOUNTPOINT && CONFIG_FS_PROCFS */ diff --git a/include/nuttx/mtd/mtd.h b/include/nuttx/mtd/mtd.h index 4bd25a612f06dd75522e7feb483505f7503b0dc1..367fbad1b8a7dfdc7e5091b3ece1e3d1a0109577 100644 --- a/include/nuttx/mtd/mtd.h +++ b/include/nuttx/mtd/mtd.h @@ -524,6 +524,18 @@ FAR struct mtd_dev_s *s25fl1_initialize(FAR struct qspi_dev_s *qspi, FAR struct mtd_dev_s *up_flashinitialize(void); +/**************************************************************************** + * Name: up_flashinitialize + * + * Description: + * Create a file backed MTD device. + * + ****************************************************************************/ + +FAR struct mtd_dev_s *filemtd_initialize(FAR const char *path, size_t offset); + +void filemtd_teardown(FAR struct mtd_dev_s* mtd); + /**************************************************************************** * Name: mtd_register * @@ -541,6 +553,21 @@ FAR struct mtd_dev_s *up_flashinitialize(void); int mtd_register(FAR struct mtd_dev_s *mtd, FAR const char *name); #endif +/**************************************************************************** + * Name: mtd_unregister + * + * Description: + * Un-registers an MTD device with the procfs file system. + * + * 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_unregister(FAR struct mtd_dev_s *mtd); +#endif + #undef EXTERN #ifdef __cplusplus }