From d4ceac7cb866eed2df9b3b0e60a38ca856921ce0 Mon Sep 17 00:00:00 2001 From: patacongo Date: Sun, 6 Dec 2009 00:19:50 +0000 Subject: [PATCH] Add FTL logic git-svn-id: https://nuttx.svn.sourceforge.net/svnroot/nuttx/trunk@2307 7fd9a85b-ad96-42d3-883c-3090e2eb8679 --- nuttx/ChangeLog | 5 + nuttx/Documentation/NuttX.html | 7 +- nuttx/TODO | 23 +- nuttx/drivers/mtd/Make.defs | 2 +- nuttx/drivers/mtd/ftl.c | 542 +++++++++++++++++++++++++++++++++ nuttx/include/nuttx/mtd.h | 4 +- nuttx/include/nuttx/rwbuffer.h | 2 +- 7 files changed, 578 insertions(+), 7 deletions(-) create mode 100755 nuttx/drivers/mtd/ftl.c diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog index f63acc066..0788d88da 100644 --- a/nuttx/ChangeLog +++ b/nuttx/ChangeLog @@ -985,4 +985,9 @@ * arch/hc: Adding framework to support m68hc11/12 * configs/demo9s12ne64: Configuration to support Freescale DEMO9S12NE64 development board (MC9S12NE64 m68hcs12 processor). + * drivers/mtd/ftl.c - A FLASH translation layer (FTL) has been implemented. + This layer will convert a FLASH MTD interface into a block driver that + can be used with any file system. Good performance of this layer will + depend upon functioning write buffer support! + NOTE: FTL support is untested as of the initial check-in. diff --git a/nuttx/Documentation/NuttX.html b/nuttx/Documentation/NuttX.html index cd2121d84..00b100abb 100644 --- a/nuttx/Documentation/NuttX.html +++ b/nuttx/Documentation/NuttX.html @@ -8,7 +8,7 @@

NuttX RTOS

-

Last Updated: December 3, 2009

+

Last Updated: December 5 2009

@@ -1611,6 +1611,11 @@ nuttx-2010.1 2010-xx-xx Gregory Nutt <spudmonkey@racsa.co.cr> * arch/hc: Adding framework to support m68hc11/12 * configs/demo9s12ne64: Configuration to support Freescale DEMO9S12NE64 development board (MC9S12NE64 m68hcs12 processor). + * drivers/mtd/ftl.c - A FLASH translation layer (FTL) has been implemented. + This layer will convert a FLASH MTD interface into a block driver that + can be used with any file system. Good performance of this layer will + depend upon functioning write buffer support! + NOTE: FTL support is untested as of the initial check-in. pascal-2010.1 2010-xx-xx Gregory Nutt <spudmonkey@racsa.co.cr> diff --git a/nuttx/TODO b/nuttx/TODO index cf890b0f2..9fab01288 100644 --- a/nuttx/TODO +++ b/nuttx/TODO @@ -1,4 +1,4 @@ -NuttX TODO List (Last updated November 29, 2009) +NuttX TODO List (Last updated December 5, 2009) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (5) Task/Scheduler (sched/) @@ -11,7 +11,7 @@ NuttX TODO List (Last updated November 29, 2009) (4) Network Utilities (netutils/) (1) USB (drivers/usbdev) (5) Libraries (lib/) - (8) File system/Generic drivers (fs/, drivers/) + (11) File system/Generic drivers (fs/, drivers/) (2) Graphics subystem (graphics/) (1) Pascal add-on (pcode/) (1) Documentation (Documentation/) @@ -359,6 +359,25 @@ o File system / Generic drivers (fs/, drivers/) Status: Open Priority: Low + Description: Block driver read-ahead buffer and write buffer support is + implemented but not yet tested. + Status: Open + Priority: Low + + Description: The drivers/mmcsd/mmcsd_sdio.c driver has hooks in place to + support read-ahead buffering and write buffering, but the logic + is incomplete and untested. + Status: Open + Priority: Low + + Description: A FLASH translation layer (FTL) has been implemented at + drivers/mtd/ftl.c but has not yet been tested. This layer + will convert a FLASH MTD interface into a block driver that + can be used with any file system. Good performance of this + layer will depend upon functioning write buffer support! + Status: Open + Priority: Low + o Graphics subystem (graphics/) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/nuttx/drivers/mtd/Make.defs b/nuttx/drivers/mtd/Make.defs index ff5b0a225..9fb584658 100644 --- a/nuttx/drivers/mtd/Make.defs +++ b/nuttx/drivers/mtd/Make.defs @@ -34,5 +34,5 @@ ############################################################################ MTD_ASRCS = -MTD_CSRCS = m25px.c +MTD_CSRCS = ftl.c m25px.c diff --git a/nuttx/drivers/mtd/ftl.c b/nuttx/drivers/mtd/ftl.c new file mode 100755 index 000000000..08fa94203 --- /dev/null +++ b/nuttx/drivers/mtd/ftl.c @@ -0,0 +1,542 @@ +/**************************************************************************** + * drivers/mtd/ftl.c + * + * Copyright (C) 2009 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/**************************************************************************** + * Private Definitions + ****************************************************************************/ + +#if defined(CONFIG_FS_READAHEAD) || (defined(CONFIG_FS_WRITABLE) && defined(CONFIG_FS_WRITEBUFFER)) +# defined CONFIG_FTL_RWBUFFER 1 +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct ftl_struct_s +{ + FAR struct mtd_dev_s *mtd; /* Contained MTD interface */ + struct mtd_geometry_s geo; /* Device geometry */ +#ifdef CONFIG_FTL_RWBUFFER + struct rwbuffer_s rwb; /* Read-ahead/write buffer support */ +#endif + uint16 blkper; /* R/W blocks per erase block */ +#ifdef CONFIG_FS_WRITABLE + FAR ubyte *eblock; /* One, in-memory erase block */ +#endif +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int ftl_open(FAR struct inode *inode); +static int ftl_close(FAR struct inode *inode); +static ssize_t ftl_reload(FAR void *priv, FAR ubyte *buffer, + off_t startblock, size_t nblocks); +static ssize_t ftl_read(FAR struct inode *inode, unsigned char *buffer, + size_t start_sector, unsigned int nsectors); +#ifdef CONFIG_FS_WRITABLE +static ssize_t ftl_flush(FAR void *priv, FAR const ubyte *buffer, + off_t startblock, size_t nblocks); +static ssize_t ftl_write(FAR struct inode *inode, const unsigned char *buffer, + size_t start_sector, unsigned int nsectors); +#endif +static int ftl_geometry(FAR struct inode *inode, struct geometry *geometry); +static int ftl_ioctl(FAR struct inode *inode, int cmd, unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct block_operations g_bops = +{ + ftl_open, /* open */ + ftl_close, /* close */ + ftl_read, /* read */ +#ifdef CONFIG_FS_WRITABLE + ftl_write, /* write */ +#else + NULL, /* write */ +#endif + ftl_geometry, /* geometry */ + ftl_ioctl /* ioctl */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ftl_open + * + * Description: Open the block device + * + ****************************************************************************/ + +static int ftl_open(FAR struct inode *inode) +{ + fvdbg("Entry\n"); + return OK; +} + +/**************************************************************************** + * Name: ftl_closel + * + * Description: close the block device + * + ****************************************************************************/ + +static int ftl_close(FAR struct inode *inode) +{ + fvdbg("Entry\n"); + return OK; +} + +/**************************************************************************** + * Name: ftl_reload + * + * Description: Read the specified numer of sectors + * + ****************************************************************************/ + +static ssize_t ftl_reload(FAR void *priv, FAR ubyte *buffer, + off_t startblock, size_t nblocks) +{ + struct ftl_struct_s *dev = (struct ftl_struct_s *)priv; + ssize_t nread; + + /* Read the full erase block into the buffer */ + + nread = MTD_BREAD(dev->mtd, startblock, nblocks, buffer); + if (nread != nblocks) + { + fdbg("Read %d blocks starting at block %d failed: %d\n", + nblocks, startblock, nread); + } + return nread; +} + +/**************************************************************************** + * Name: ftl_read + * + * Description: Read the specified numer of sectors + * + ****************************************************************************/ + +static ssize_t ftl_read(FAR struct inode *inode, unsigned char *buffer, + size_t start_sector, unsigned int nsectors) +{ + struct ftl_struct_s *dev; + + fvdbg("sector: %d nsectors: %d sectorsize: %d\n"); + + DEBUGASSERT(inode && inode->i_private); + dev = (struct ftl_struct_s *)inode->i_private; +#ifdef CONFIG_FS_READAHEAD + return rwb_read(&dev->rwb, start_sector, nsectors, buffer); +#else + return ftl_reload(dev, buffer, start_sector, nsectors); +#endif +} + +/**************************************************************************** + * Name: ftl_write + * + * Description: Write the specified number of sectors + * + ****************************************************************************/ + +#ifdef CONFIG_FS_WRITABLE +static ssize_t ftl_flush(FAR void *priv, FAR const ubyte *buffer, + off_t startblock, size_t nblocks) +{ + struct ftl_struct_s *dev = (struct ftl_struct_s *)priv; + off_t alignedblock; + off_t mask; + off_t rwblock; + off_t eraseblock; + off_t offset; + size_t remaining; + size_t nxfrd; + int nbytes; + int ret; + + /* Get the aligned block. Here is is assumed: (1) The number of R/W blocks + * per erase block is a power of 2, and (2) the erase begins with that same + * alignment. + */ + + mask = dev->blkper - 1; + alignedblock = (startblock + mask) & ~mask; + + /* Handle partial erase blocks before the first unaligned block */ + + remaining = nblocks; + if (alignedblock > startblock) + { + /* Read the full erase block into the buffer */ + + rwblock = startblock & ~mask; + nxfrd = MTD_BREAD(dev->mtd, rwblock, dev->blkper, dev->eblock); + if (nxfrd != dev->blkper) + { + fdbg("Read erase block %d failed: %d\n", rwblock, nxfrd); + return -EIO; + } + + /* Then erase the erase block */ + + eraseblock = rwblock / dev->blkper; + ret = MTD_ERASE(dev->mtd, eraseblock, 1); + if (ret < 0) + { + fdbg("Erase block=%d failed: %d\n", eraseblock, ret); + return ret; + } + + /* Copy the user data at the end of the buffered erase block */ + + offset = (startblock & mask) * dev->geo.blocksize; + nbytes = dev->geo.erasesize - offset; + fvdbg("Copy %d bytes into erase block=%d at offset=%d\n", + nbytes, eraseblock, offset); + memcpy(dev->eblock + offset, buffer, nbytes); + + /* And write the erase back to flash */ + + nxfrd = MTD_BWRITE(dev->mtd, rwblock, dev->blkper, dev->eblock); + if (nxfrd != dev->blkper) + { + fdbg("Write erase block %d failed: %d\n", rwblock, nxfrd); + return -EIO; + } + + /* Then update for amount written */ + + remaining -= dev->blkper - (startblock & mask); + buffer += nbytes; + } + + /* How handle full erase pages in the middle */ + + while (remaining >= dev->blkper) + { + /* Erase the erase block */ + + eraseblock = alignedblock / dev->blkper; + ret = MTD_ERASE(dev->mtd, eraseblock, 1); + if (ret < 0) + { + fdbg("Erase block=%d failed: %d\n", eraseblock, ret); + return ret; + } + + /* Write a full erase back to flash */ + + fvdbg("Write %d bytes into erase block=%d at offset=0\n", + dev->geo.erasesize, alignedblock); + nxfrd = MTD_BWRITE(dev->mtd, alignedblock, dev->blkper, buffer); + if (nxfrd != dev->blkper) + { + fdbg("Write erase block %d failed: %d\n", , alignedblock, nxfrd); + return -EIO; + } + + /* Then update for amount written */ + + alignedblock += dev->blkper; + remaining -= dev->blkper; + buffer += dev->geo.erasesize; + } + + /* Finally, handler any partial blocks after the last full erase block */ + + if (remaining > 0) + { + /* Read the full erase block into the buffer */ + + nxfrd = MTD_BREAD(dev->mtd, alignedblock, dev->blkper, dev->eblock); + if (nxfrd != dev->blkper) + { + fdbg("Read erase block %d failed: %d\n", alignedblock, nxfrd); + return -EIO; + } + + /* Then erase the erase block */ + + eraseblock = alignedblock / dev->blkper; + ret = MTD_ERASE(dev->mtd, eraseblock, 1); + if (ret < 0) + { + fdbg("Erase block=%d failed: %d\n", eraseblock, ret); + return ret; + } + + /* Copy the user data at the beginning the buffered erase block */ + + nbytes = remaining * dev->geo.blocksize; + fvdbg("Copy %d bytes into erase block=%d at offset=0\n", + nbytes, alignedblock); + memcpy(dev->eblock, buffer, nbytes); + + /* And write the erase back to flash */ + + nxfrd = MTD_BWRITE(dev->mtd, alignedblock, dev->blkper, dev->eblock); + if (nxfrd != dev->blkper) + { + fdbg("Write erase block %d failed: %d\n", alignedblock, nxfrd); + return -EIO; + } + } + + return nblocks; +} +#endif + +/**************************************************************************** + * Name: ftl_write + * + * Description: Write (or buffer) the specified number of sectors + * + ****************************************************************************/ + +#ifdef CONFIG_FS_WRITABLE +static ssize_t ftl_write(FAR struct inode *inode, const unsigned char *buffer, + size_t start_sector, unsigned int nsectors) +{ + struct ftl_struct_s *dev; + + fvdbg("sector: %d nsectors: %d sectorsize: %d\n"); + + DEBUGASSERT(inode && inode->i_private); + dev = (struct ftl_struct_s *)inode->i_private; +#ifdef CONFIG_FS_WRITEBUFFER + return rwb_write(&dev->rwb, start_sector, nsectors, buffer); +#else + return ftl_flush(dev, buffer, start_sector, nsectors); +#endif +} +#endif + +/**************************************************************************** + * Name: ftl_geometry + * + * Description: Return device geometry + * + ****************************************************************************/ + +static int ftl_geometry(FAR struct inode *inode, struct geometry *geometry) +{ + struct ftl_struct_s *dev; + + fvdbg("Entry\n"); + + DEBUGASSERT(inode); + if (geometry) + { + dev = (struct ftl_struct_s *)inode->i_private; + geometry->geo_available = TRUE; + geometry->geo_mediachanged = FALSE; +#ifdef CONFIG_FS_WRITABLE + geometry->geo_writeenabled = TRUE; +#else + geometry->geo_writeenabled = FALSE; +#endif + geometry->geo_nsectors = dev->geo.neraseblocks * dev->blkper; + geometry->geo_sectorsize = dev->geo.blocksize; + + fvdbg("available: TRUE mediachanged: FALSE writeenabled: %s\n", + geometry->geo_writeenabled ? "TRUE" : "FALSE"); + fvdbg("nsectors: %d sectorsize: %d\n", + geometry->geo_nsectors, geometry->geo_sectorsize); + + return OK; + } + return -EINVAL; +} + +/**************************************************************************** + * Name: ftl_ioctl + * + * Description: Return device geometry + * + ****************************************************************************/ + +static int ftl_ioctl(FAR struct inode *inode, int cmd, unsigned long arg) +{ + struct ftl_struct_s *dev ; + int ret = -ENOTTY; + + fvdbg("Entry\n"); + + /* Only one ioctl command is supported (and that is just passed on to MTD */ + + DEBUGASSERT(inode && inode->i_private); + if (cmd == BIOC_XIPBASE && arg != 0) + { + dev = (struct ftl_struct_s *)inode->i_private; + ret = MTD_IOCTL(dev->mtd, MTDIOC_XIPBASE, arg); + if (ret < 0) + { + fdbg("MTD ioctl(MTDIOC_XIPBASE) failed: %d\n", ret); + } + } + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ftl_initialize + * + * Description: + * Initialize to provide a block driver wrapper around an MTD interface + * + ****************************************************************************/ + +int ftl_initialize(int minor, ubyte *buffer, FAR struct mtd_dev_s *mtd) +{ + struct ftl_struct_s *dev; + char devname[16]; + int ret = -ENOMEM; + + /* Sanity check */ + +#ifdef CONFIG_DEBUG + if (minor < 0 || minor > 255 || !mtd) + { + return -EINVAL; + } +#endif + + /* Allocate a ramdisk device structure */ + + dev = (struct ftl_struct_s *)malloc(sizeof(struct ftl_struct_s)); + if (dev) + { + /* Initialize the ramdisk device structure */ + + dev->mtd = mtd; + + /* Get the device geometry */ + + ret = MTD_IOCTL(mtd, MTDIOC_GEOMETRY, (unsigned long)&dev->geo); + if (ret < 0) + { + fdbg("MTD ioctl(MTDIOC_GEOMETRY) failed: %d\n", ret); + free(dev); + return ret; + } + + /* Allocate one, in-memory erase block buffer */ + +#ifdef CONFIG_FS_WRITABLE + dev->eblock = (FAR ubyte *)malloc(dev->geo.erasesize); + if (!dev->eblock) + { + fdbg("Failed to allocate an erase block buffer\n"); + free(dev); + return -ENOMEM; + } +#endif + + /* Get the number of R/W blocks per erase block */ + + dev->blkper = dev->geo.erasesize / dev->geo.blocksize; + DEBUGASSERT(dev->blkper * dev->geo.blocksize = dev->geo.erasesize); + + /* Configure read-ahead/write buffering */ + +#ifdef CONFIG_FTL_RWBUFFER + dev->rwb.blocksize = dev->geo.blocksize; + dev->rwb.nblocks = dev->geo.neraseblocks * dev->blkper; + dev->rwb.dev = (FAR void *)dev; + +#ifdef defined(CONFIG_FS_WRITABLE) && defined(CONFIG_FS_WRITEBUFFER) + dev->rwb.wrmaxblocks = dev->blkper; + dev->rwb.wrflush = ftl_flush; +#endif + +#ifdef CONFIG_FS_READAHEAD + dev->rwb.rhmaxblocks = dev->blkper; + dev->rwb.rhreload = ftl_reload; +#endif + ret = rwb_initialize(&dev->rwb); + if (ret < 0) + { + fdbg("rwb_initialize failed: %d\n", ret); + free(dev); + return ret; + } +#endif + + /* Create a MTD block device name */ + + snprintf(devname, 16, "/dev/mtdblock%d", minor); + + /* Inode private data is a reference to the ramdisk device stgructure */ + + ret = register_blockdriver(devname, &g_bops, 0, dev); + if (ret < 0) + { + fdbg("register_blockdriver failed: %d\n", -ret); + free(dev); + } + } + return ret; +} diff --git a/nuttx/include/nuttx/mtd.h b/nuttx/include/nuttx/mtd.h index 494baec67..2e1b137ac 100644 --- a/nuttx/include/nuttx/mtd.h +++ b/nuttx/include/nuttx/mtd.h @@ -71,7 +71,7 @@ struct mtd_geometry_s uint16 blocksize; /* Size of one read/write block */ uint16 erasesize; /* Size of one erase blocks -- must be a multiple * of blocksize. */ - size_t neraseblocks; /* Number of erase blocks */ + size_t neraseblocks; /* Number of erase blocks */ }; /* This structure defines the interface to a simple memory technology device. @@ -83,7 +83,7 @@ struct mtd_dev_s { /* The following methods operate on the MTD: */ - /* Erase the specified erase blocks */ + /* Erase the specified erase blocks (units are erase blocks) */ int (*erase)(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks); diff --git a/nuttx/include/nuttx/rwbuffer.h b/nuttx/include/nuttx/rwbuffer.h index da97ac505..2ea525ce7 100644 --- a/nuttx/include/nuttx/rwbuffer.h +++ b/nuttx/include/nuttx/rwbuffer.h @@ -106,7 +106,7 @@ struct rwbuffer_s uint16 blocksize; /* The size of one block */ size_t nblocks; /* The total number blocks supported */ - void *dev; /* Device state passed to callout functions */ + FAR void *dev; /* Device state passed to callout functions */ /* Write buffer setup. If CONFIG_FS_WRITEBUFFER is defined, but you * want read-ahead-only operation, (1) set wrmaxblocks to zero and do