Add FTL logic

git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@2307 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
patacongo 2009-12-06 00:19:50 +00:00
parent b585cbbcd4
commit bea1cb5e0e
7 changed files with 578 additions and 7 deletions

View File

@ -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.

View File

@ -8,7 +8,7 @@
<tr align="center" bgcolor="#e4e4e4">
<td>
<h1><big><font color="#3c34ec"><i>NuttX RTOS</i></font></big></h1>
<p>Last Updated: December 3, 2009</p>
<p>Last Updated: December 5 2009</p>
</td>
</tr>
</table>
@ -1611,6 +1611,11 @@ nuttx-2010.1 2010-xx-xx Gregory Nutt &lt;spudmonkey@racsa.co.cr&gt;
* 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 &lt;spudmonkey@racsa.co.cr&gt;

23
TODO
View File

@ -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/)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -34,5 +34,5 @@
############################################################################
MTD_ASRCS =
MTD_CSRCS = m25px.c
MTD_CSRCS = ftl.c m25px.c

542
drivers/mtd/ftl.c Executable file
View File

@ -0,0 +1,542 @@
/****************************************************************************
* drivers/mtd/ftl.c
*
* Copyright (C) 2009 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <spudmonkey@racsa.co.cr>
*
* 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/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <debug.h>
#include <errno.h>
#include <nuttx/fs.h>
#include <nuttx/ioctl.h>
#include <nuttx/mtd.h>
#include <nuttx/rwbuffer.h>
/****************************************************************************
* 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;
}

View File

@ -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);

View File

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