Add an MTD layer that will add read-ahead or write buffering to any MTD driver (incomplete)

This commit is contained in:
Gregory Nutt 2014-07-11 11:20:11 -06:00
parent b224f08fd7
commit 8516551e54
11 changed files with 1051 additions and 233 deletions

View file

@ -35,6 +35,33 @@ config LOOP
file (or character device) as a block device. See losetup() and
loteardown() in include/nuttx/fs/fs.h.
config DRVR_WRITEBUFFER
bool "Enable write buffer support"
default n
---help---
Enable generic write buffering support that can be used by a variety
of drivers.
if DRVR_WRITEBUFFER
config DRVR_WRDELAY
int "Write flush delay"
default 350
---help---
If there is no write activity for this configured amount of time,
then the contents will be automatically flushed to the media. This
reduces the likelihood that data will be stuck in the write buffer
at the time of power down.
endif # DRVR_WRITEBUFFER
config DRVR_READAHEAD
bool "Enable read-ahead buffer support"
default n
---help---
Enable generic read-ahead buffering support that can be used by a
variety of drivers.
config RAMDISK
bool "RAM Disk Support"
default n

View file

@ -1,7 +1,7 @@
############################################################################
# drivers/Makefile
#
# Copyright (C) 2007-2013 Gregory Nutt. All rights reserved.
# Copyright (C) 2007-2014 Gregory Nutt. All rights reserved.
# Author: Gregory Nutt <gnutt@nuttx.org>
#
# Redistribution and use in source and binary forms, with or without
@ -73,7 +73,14 @@ ifneq ($(CONFIG_NFILE_DESCRIPTORS),0)
CSRCS += dev_null.c dev_zero.c loop.c
ifneq ($(CONFIG_DISABLE_MOUNTPOINT),y)
CSRCS += ramdisk.c rwbuffer.c
CSRCS += ramdisk.c
ifneq ($(CONFIG_DRVR_WRITEBUFFER),y)
CSRCS += rwbuffer.c
else
ifneq ($(CONFIG_DRVR_READAHEAD),y)
CSRCS += rwbuffer.c
endif
endif
endif
ifeq ($(CONFIG_CAN),y)

View file

@ -144,7 +144,7 @@ struct mmcsd_state_s
#endif
/* Read-ahead and write buffering support */
#if defined(CONFIG_FS_WRITEBUFFER) || defined(CONFIG_FS_READAHEAD)
#if defined(CONFIG_DRVR_WRITEBUFFER) || defined(CONFIG_DRVR_READAHEAD)
struct rwbuffer_s rwbuffer;
#endif
};
@ -203,7 +203,7 @@ static ssize_t mmcsd_readsingle(FAR struct mmcsd_state_s *priv,
static ssize_t mmcsd_readmultiple(FAR struct mmcsd_state_s *priv,
FAR uint8_t *buffer, off_t startblock, size_t nblocks);
#endif
#ifdef CONFIG_FS_READAHEAD
#ifdef CONFIG_DRVR_READAHEAD
static ssize_t mmcsd_reload(FAR void *dev, FAR uint8_t *buffer,
off_t startblock, size_t nblocks);
#endif
@ -214,7 +214,7 @@ static ssize_t mmcsd_writesingle(FAR struct mmcsd_state_s *priv,
static ssize_t mmcsd_writemultiple(FAR struct mmcsd_state_s *priv,
FAR const uint8_t *buffer, off_t startblock, size_t nblocks);
#endif
#ifdef CONFIG_FS_WRITEBUFFER
#ifdef CONFIG_DRVR_WRITEBUFFER
static ssize_t mmcsd_flush(FAR void *dev, FAR const uint8_t *buffer,
off_t startblock, size_t nblocks);
#endif
@ -1538,7 +1538,7 @@ static ssize_t mmcsd_readmultiple(FAR struct mmcsd_state_s *priv,
*
****************************************************************************/
#ifdef CONFIG_FS_READAHEAD
#ifdef CONFIG_DRVR_READAHEAD
static ssize_t mmcsd_reload(FAR void *dev, FAR uint8_t *buffer,
off_t startblock, size_t nblocks)
{
@ -1911,12 +1911,12 @@ static ssize_t mmcsd_writemultiple(FAR struct mmcsd_state_s *priv,
*
****************************************************************************/
#if defined(CONFIG_FS_WRITABLE) && defined(CONFIG_FS_WRITEBUFFER)
#if defined(CONFIG_FS_WRITABLE) && defined(CONFIG_DRVR_WRITEBUFFER)
static ssize_t mmcsd_flush(FAR void *dev, FAR const uint8_t *buffer,
off_t startblock, size_t nblocks)
{
FAR struct mmcsd_state_s *priv = (FAR struct mmcsd_state_s *)dev;
#ifndef CONFIG_MMCSD_MULTIBLOCK_DISABLE
#ifdef CONFIG_MMCSD_MULTIBLOCK_DISABLE
size_t block;
size_t endblock;
#endif
@ -2028,7 +2028,7 @@ static ssize_t mmcsd_read(FAR struct inode *inode, unsigned char *buffer,
size_t startsector, unsigned int nsectors)
{
FAR struct mmcsd_state_s *priv;
#if !defined(CONFIG_FS_READAHEAD) && defined(CONFIG_MMCSD_MULTIBLOCK_DISABLE)
#if !defined(CONFIG_DRVR_READAHEAD) && defined(CONFIG_MMCSD_MULTIBLOCK_DISABLE)
size_t sector;
size_t endsector;
#endif
@ -2043,7 +2043,7 @@ static ssize_t mmcsd_read(FAR struct inode *inode, unsigned char *buffer,
{
mmcsd_takesem(priv);
#if defined(CONFIG_FS_READAHEAD)
#if defined(CONFIG_DRVR_READAHEAD)
/* Get the data from the read-ahead buffer */
ret = rwb_read(&priv->rwbuffer, startsector, nsectors, buffer);
@ -2103,7 +2103,7 @@ static ssize_t mmcsd_write(FAR struct inode *inode, FAR const unsigned char *buf
size_t startsector, unsigned int nsectors)
{
FAR struct mmcsd_state_s *priv;
#if !defined(CONFIG_FS_WRITEBUFFER) && defined(CONFIG_MMCSD_MULTIBLOCK_DISABLE)
#if defined(CONFIG_MMCSD_MULTIBLOCK_DISABLE)
size_t sector;
size_t endsector;
#endif
@ -2115,7 +2115,7 @@ static ssize_t mmcsd_write(FAR struct inode *inode, FAR const unsigned char *buf
mmcsd_takesem(priv);
#if defined(CONFIG_FS_WRITEBUFFER)
#if defined(CONFIG_DRVR_WRITEBUFFER)
/* Write the data to the write buffer */
ret = rwb_write(&priv->rwbuffer, startsector, nsectors, buffer);
@ -3271,9 +3271,10 @@ int mmcsd_slotinitialize(int minor, FAR struct sdio_dev_s *dev)
}
}
#if defined(CONFIG_FS_WRITEBUFFER) || defined(CONFIG_FS_READAHEAD)
#if defined(CONFIG_DRVR_WRITEBUFFER) || defined(CONFIG_DRVR_READAHEAD)
/* Initialize buffering */
#warning "Missing setup of rwbuffer"
ret = rwb_initialize(&priv->rwbuffer);
if (ret < 0)
{
@ -3298,7 +3299,7 @@ int mmcsd_slotinitialize(int minor, FAR struct sdio_dev_s *dev)
return OK;
errout_with_buffers:
#if defined(CONFIG_FS_WRITEBUFFER) || defined(CONFIG_FS_READAHEAD)
#if defined(CONFIG_DRVR_WRITEBUFFER) || defined(CONFIG_DRVR_READAHEAD)
rwb_uninitialize(&priv->rwbuffer);
errout_with_hwinit:
#endif

View file

@ -66,6 +66,40 @@ config MTD_BYTE_WRITE
support such writes. The SMART file system can take advantage of
this option if it is enabled.
config MTD_WRBUFFER
bool "Enable MTD write buffering
default n
depends on DRVR_WRITEBUFFER
---help---
Build the mtd_rwbuffer layer and enable support for write buffering.
if MTD_WRBUFFER
config MTD_NWRBLOCKS
int "MTD write buffer size"
default 4
---help---
The size of the MTD write buffer (in blocks)
endif # MTD_WRBUFFER
config MTD_READAHEAD
bool "Enable MTD read-ahead buffering
default n
depends on DRVR_READAHEAD
---help---
Build the mtd_rwbuffer layer and enable support for read-ahead buffering.
if MTD_READAHEAD
config MTD_NRDBLOCKS
int "MTD read-head buffer size"
default 4
---help---
The size of the MTD read-ahead buffer (in blocks)
endif # MTD_READAHEAD
config MTD_CONFIG
bool "Enable Dev Config (MTD based) device"
default n
@ -73,6 +107,8 @@ config MTD_CONFIG
Provides a /dev/config device for saving / restoring application
configuration data to a standard MTD device or partition.
if MTD_CONFIG
config MTD_CONFIG_RAM_CONSOLIDATE
bool "Always use RAM consolidation method (work in progress)"
default n
@ -97,13 +133,14 @@ config MTD_CONFIG_RAM_CONSOLIDATE
config MTD_CONFIG_ERASEDVALUE
hex "Erased value of bytes on the MTD device"
depends on MTD_CONFIG
default 0xff
---help---
Specifies the value of the erased state of the MTD FLASH. For
most FLASH parts, this is 0xff, but could also be zero depending
on the device.
endif # MTD_CONFIG
comment "MTD Device Drivers"
menuconfig MTD_NAND

View file

@ -49,6 +49,14 @@ ifeq ($(CONFIG_MTD_SECT512),y)
CSRCS += sector512.c
endif
ifeq ($(CONFIG_MTD_WRBUFFER),y)
CSRCS += mtd_rwbuffer.c
else
ifeq ($(CONFIG_MTD_READAHEAD),y)
CSRCS += mtd_rwbuffer.c
endif
endif
ifeq ($(CONFIG_MTD_NAND),y)
CSRCS += mtd_nand.c mtd_onfi.c mtd_nandscheme.c mtd_nandmodel.c mtd_modeltab.c
ifeq ($(CONFIG_MTD_NAND_SWECC),y)

View file

@ -56,11 +56,11 @@
#include <nuttx/rwbuffer.h>
/****************************************************************************
* Private Definitions
* Pre-processor Definitions
****************************************************************************/
#if defined(CONFIG_FS_READAHEAD) || (defined(CONFIG_FS_WRITABLE) && defined(CONFIG_FS_WRITEBUFFER))
# defined CONFIG_FTL_RWBUFFER 1
#if defined(CONFIG_DRVR_READAHEAD) || defined(CONFIG_DRVR_WRITEBUFFER)
# define CONFIG_FTL_RWBUFFER 1
#endif
/****************************************************************************
@ -111,7 +111,7 @@ static const struct block_operations g_bops =
#ifdef CONFIG_FS_WRITABLE
ftl_write, /* write */
#else
NULL, /* write */
NULL, /* write */
#endif
ftl_geometry, /* geometry */
ftl_ioctl /* ioctl */
@ -181,13 +181,14 @@ static ssize_t ftl_reload(FAR void *priv, FAR uint8_t *buffer,
static ssize_t ftl_read(FAR struct inode *inode, unsigned char *buffer,
size_t start_sector, unsigned int nsectors)
{
struct ftl_struct_s *dev;
FAR struct ftl_struct_s *dev;
fvdbg("sector: %d nsectors: %d\n", start_sector, nsectors);
DEBUGASSERT(inode && inode->i_private);
dev = (struct ftl_struct_s *)inode->i_private;
#ifdef CONFIG_FS_READAHEAD
dev = (FAR struct ftl_struct_s *)inode->i_private;
#ifdef CONFIG_DRVR_READAHEAD
return rwb_read(&dev->rwb, start_sector, nsectors, buffer);
#else
return ftl_reload(dev, buffer, start_sector, nsectors);
@ -388,7 +389,7 @@ static ssize_t ftl_write(FAR struct inode *inode, const unsigned char *buffer,
DEBUGASSERT(inode && inode->i_private);
dev = (struct ftl_struct_s *)inode->i_private;
#ifdef CONFIG_FS_WRITEBUFFER
#ifdef CONFIG_DRVR_WRITEBUFFER
return rwb_write(&dev->rwb, start_sector, nsectors, buffer);
#else
return ftl_flush(dev, buffer, start_sector, nsectors);
@ -566,15 +567,16 @@ int ftl_initialize(int minor, FAR struct mtd_dev_s *mtd)
dev->rwb.nblocks = dev->geo.neraseblocks * dev->blkper;
dev->rwb.dev = (FAR void *)dev;
#if defined(CONFIG_FS_WRITABLE) && defined(CONFIG_FS_WRITEBUFFER)
#if defined(CONFIG_FS_WRITABLE) && defined(CONFIG_DRVR_WRITEBUFFER)
dev->rwb.wrmaxblocks = dev->blkper;
dev->rwb.wrflush = ftl_flush;
#endif
#ifdef CONFIG_FS_READAHEAD
#ifdef CONFIG_DRVR_READAHEAD
dev->rwb.rhmaxblocks = dev->blkper;
dev->rwb.rhreload = ftl_reload;
#endif
ret = rwb_initialize(&dev->rwb);
if (ret < 0)
{

View file

@ -327,6 +327,7 @@ static int mtd_stat(const char *relpath, struct stat *buf)
* in the procfs system simply for information purposes (if desired).
*
****************************************************************************/
int mtd_register(FAR struct mtd_dev_s *mtd, FAR const char *name)
{
FAR struct mtd_dev_s *plast;
@ -359,6 +360,8 @@ int mtd_register(FAR struct mtd_dev_s *mtd, FAR const char *name)
plast->pnext = mtd;
}
return OK;
}
#endif /* !CONFIG_DISABLE_MOUNTPOINT && CONFIG_FS_PROCFS */

417
drivers/mtd/mtd_rwbuffer.c Normal file
View file

@ -0,0 +1,417 @@
/************************************************************************************
* drivers/mtd/mtd_rwbuffer.c
* MTD driver that contains another MTD driver and provides read-ahead and/or write
* buffering.
*
* Copyright (C) 2014 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* 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 <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/kmalloc.h>
#include <nuttx/rwbuffer.h>
#include <nuttx/fs/ioctl.h>
#include <nuttx/mtd/mtd.h>
#if defined(CONFIG_DRVR_WRITEBUFFER) || defined(CONFIG_DRVR_READAHEAD)
/************************************************************************************
* Pre-processor Definitions
************************************************************************************/
#ifndef CONFIG_MTD_NWRBLOCKS
# define CONFIG_MTD_NWRBLOCKS 4
#endif
#ifndef CONFIG_MTD_NRDBLOCKS
# define CONFIG_MTD_NRDBLOCKS 4
#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 mtd_rwbuffer_s.
*/
struct mtd_rwbuffer_s
{
struct mtd_dev_s mtd; /* Our exported MTD interface */
FAR struct mtd_dev_s *dev; /* Saved lower level MTD interface instance */
struct rwbuffer_s rwb; /* The rwbuffer state structure */
uint16_t spb; /* Number of sectors per block */
};
/************************************************************************************
* Private Function Prototypes
************************************************************************************/
/* rwbuffer callouts */
static ssize_t mtd_reload(FAR void *dev, FAR uint8_t *buffer, off_t startblock,
size_t nblocks);
static ssize_t mtd_flush(FAR void *dev, FAR const uint8_t *buffer, off_t startblock,
size_t nblocks);
/* MTD driver methods */
static int mtd_erase(FAR struct mtd_dev_s *dev, off_t block, size_t nsectors);
static ssize_t mtd_bread(FAR struct mtd_dev_s *dev, off_t block,
size_t nsectors, FAR uint8_t *buf);
static ssize_t mtd_bwrite(FAR struct mtd_dev_s *dev, off_t block,
size_t nsectors, FAR const uint8_t *buf);
static ssize_t mtd_read(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes,
FAR uint8_t *buffer);
static int mtd_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg);
/************************************************************************************
* Private Data
************************************************************************************/
/************************************************************************************
* Private Functions
************************************************************************************/
/************************************************************************************
* Name: mtd_reload
*
* Description:
* Reload the read-ahead buffer
*
************************************************************************************/
static ssize_t mtd_reload(FAR void *dev, FAR uint8_t *buffer, off_t startblock,
size_t nblocks)
{
FAR struct mtd_rwbuffer_s *priv = (FAR struct mtd_rwbuffer_s *)dev;
DEBUGASSERT(priv && priv->dev);
/* This is just a pass-through to the contained MTD */
return priv->dev->bread(priv->dev, startblock, nblocks, buffer);
}
/************************************************************************************
* Name: mtd_flush
*
* Description:
* Flush the write buffer to hardware
*
************************************************************************************/
static ssize_t mtd_flush(FAR void *dev, FAR const uint8_t *buffer, off_t startblock,
size_t nblocks)
{
FAR struct mtd_rwbuffer_s *priv = (FAR struct mtd_rwbuffer_s *)dev;
DEBUGASSERT(priv && priv->dev);
/* This is just a pass-through to the contained MTD */
return priv->dev->bwrite(priv->dev, startblock, nblocks, buffer);
}
/************************************************************************************
* Name: mtd_erase
************************************************************************************/
static int mtd_erase(FAR struct mtd_dev_s *dev, off_t block, size_t nblocks)
{
FAR struct mtd_rwbuffer_s *priv = (FAR struct mtd_rwbuffer_s *)dev;
off_t sector;
size_t nsectors;
int ret;
fvdbg("block: %08lx nsectors: %lu\n",
(unsigned long)block, (unsigned int)nsectors);
/* Convert to logical sectors and sector numbers */
sector = block * priv->spb;
nsectors = nblocks * priv->spb;
/* Then invalidate in cached data */
ret = rwb_invalidate(&priv->rwb, sector, nsectors);
if (ret < 0)
{
fdbg("ERROR: rwb_invalidate failed: %d\n", ret);
return ret;
}
/* Then let the lower level MTD driver do the real erase */
return priv->dev->erase(priv->dev, block, nblocks);
}
/************************************************************************************
* Name: mtd_bread
************************************************************************************/
static ssize_t mtd_bread(FAR struct mtd_dev_s *dev, off_t sector,
size_t nsectors, FAR uint8_t *buffer)
{
FAR struct mtd_rwbuffer_s *priv = (FAR struct mtd_rwbuffer_s *)dev;
/* Let the rwbuffer logic do it real work. It will call out to mtd_reload if is
* needs to read any data.
*/
return rwb_read(&priv->rwb, sector, nsectors, buffer);
}
/************************************************************************************
* Name: mtd_bwrite
************************************************************************************/
static ssize_t mtd_bwrite(FAR struct mtd_dev_s *dev, off_t block, size_t nsectors,
FAR const uint8_t *buffer)
{
FAR struct mtd_rwbuffer_s *priv = (FAR struct mtd_rwbuffer_s *)dev;
/* Let the rwbuffer logic do it real work. It will call out to wrb_reload it is
* needs to read any data.
*/
return rwb_write(&priv->rwb, block, nsectors, buffer);
}
/************************************************************************************
* Name: mtd_read
************************************************************************************/
static ssize_t mtd_read(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes,
FAR uint8_t *buffer)
{
FAR struct mtd_rwbuffer_s *priv = (FAR struct mtd_rwbuffer_s *)dev;
/* Let the rwbuffer logic do it real work. It will call out to mtd_reload it is
* needs to read any data.
*/
return mtd_readbytes(&priv->rwb, offset, nbytes, buffer);
}
/************************************************************************************
* Name: mtd_ioctl
************************************************************************************/
static int mtd_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg)
{
FAR struct mtd_rwbuffer_s *priv = (FAR struct mtd_rwbuffer_s *)dev;
int ret = -EINVAL; /* Assume good command with bad parameters */
fvdbg("cmd: %d \n", cmd);
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.
*
* NOTE: that the device is treated as though it where just an array
* of fixed size blocks. That is most likely not true, but the client
* will expect the device logic to do whatever is necessary to make it
* appear so.
*/
geo->blocksize = priv->rwb.blocksize;
geo->erasesize = priv->rwb.blocksize* priv->spb;
geo->neraseblocks = priv->rwb.nblocks * priv->spb;
ret = OK;
fvdbg("blocksize: %d erasesize: %d neraseblocks: %d\n",
geo->blocksize, geo->erasesize, geo->neraseblocks);
}
}
break;
case MTDIOC_BULKERASE:
{
/* Erase the entire device */
ret = priv->dev->ioctl(priv->dev, MTDIOC_BULKERASE, 0);
if (ret >= 0)
{
fdbg("ERROR: Device ioctl failed: %d\n", ret);
break;
}
/* Then invalidate in cached data */
ret = rwb_invalidate(&priv->rwb,0, priv->rwb.nblocks);
if (ret < 0)
{
fdbg("ERROR: rwb_invalidate failed: %d\n", ret);
}
}
break;
case MTDIOC_XIPBASE:
default:
ret = -ENOTTY; /* Bad command */
break;
}
fvdbg("return %d\n", ret);
return ret;
}
/************************************************************************************
* Public Functions
************************************************************************************/
/************************************************************************************
* Name: mtd_rwb_initialize
*
* Description:
* Create an initialized MTD device instance. This MTD driver contains another
* MTD driver and converts a larger sector size to a standard 512 byte sector
* size.
*
* MTD devices are not registered in the file system, but are created as instances
* that can be bound to other functions (such as a block or character driver front
* end).
*
************************************************************************************/
FAR struct mtd_dev_s *mtd_rwb_initialize(FAR struct mtd_dev_s *mtd)
{
FAR struct mtd_rwbuffer_s *priv;
struct mtd_geometry_s geo;
int ret;
fvdbg("mtd: %p\n", mtd);
DEBUGASSERT(mtd && mtd->ioctl);
/* Get the device geometry */
ret = mtd->ioctl(mtd, MTDIOC_GEOMETRY, (unsigned long)((uintptr_t)&geo));
if (ret < 0)
{
fdbg("ERROR: MTDIOC_GEOMETRY ioctl failed: %d\n", ret);
return NULL;
}
/* Allocate a state structure (we allocate the structure instead of using
* a fixed, static allocation so that we can handle multiple FLASH devices.
* The current implementation would handle only one FLASH part per SPI
* device (only because of the SPIDEV_FLASH definition) and so would have
* to be extended to handle multiple FLASH parts on the same SPI bus.
*/
priv = (FAR struct mtd_rwbuffer_s *)kzalloc(sizeof(struct mtd_rwbuffer_s));
if (priv)
{
/* Initialize the allocated structure. (unsupported methods/fields
* were already nullified by kzalloc).
*/
priv->mtd.erase = mtd_erase; /* Our MTD erase method */
priv->mtd.bread = mtd_bread; /* Our MTD bread method */
priv->mtd.bwrite = mtd_bwrite; /* Our MTD bwrite method */
priv->mtd.read = mtd_read; /* Our MTD read method */
priv->mtd.ioctl = mtd_ioctl; /* Our MTD ioctl method */
priv->dev = mtd; /* The contained MTD instance */
/* Sectors per block. The erase block size must be an even multiple
* of the sector size.
*/
priv->spb = geo.erasesize / geo.blocksize;
DEBUGASSERT((size_t)priv->spb * geo_blocksize = geo.erasesize);
/* Values must be provided to rwb_initialize() */
/* Supported geometry */
priv->rwb.blocksize = geo.blocksize;
priv->rwb.nblocks = geo.neraseblocks * priv->spb;
/* Buffer setup */
#ifdef CONFIG_DRVR_WRITEBUFFER
priv->rwb.wrmaxblocks = CONFIG_MTD_NWRBLOCKS;
#endif
#ifdef CONFIG_DRVR_READAHEAD
priv->rwb.rhmaxblocks = CONFIG_MTD_NRDBLOCKS;
#endif
/* Callouts */
priv->rwb.dev = priv; /* Device state passed to callouts */
priv->rwb.wrflush = mtd_flush; /* Callout to flush buffer */
priv->rwb.rhreload = mtd_reload; /* Callout to reload buffer */
/* Initialize read-ahead/write buffering */
ret = rwb_initialize(&priv->rwb);
if (ret < 0)
{
fdbg("ERROR: rwb_initialize failed: %d\n", ret);
kfree(priv);
return NULL;
}
}
/* Register the MTD with the procfs system if enabled */
#ifdef CONFIG_MTD_REGISTRATION
mtd_register(&priv->mtd, "rwbuffer");
#endif
/* Return the implementation-specific state structure as the MTD device */
fvdbg("Return %p\n", priv);
return &priv->mtd;
}
#endif /* CONFIG_DRVR_WRITEBUFFER || CONFIG_DRVR_READAHEAD */

View file

@ -105,7 +105,8 @@
* other for our use, such as format
* sector, etc. */
#if defined(CONFIG_FS_READAHEAD) || (defined(CONFIG_FS_WRITABLE) && defined(CONFIG_FS_WRITEBUFFER))
#if defined(CONFIG_DRVR_READAHEAD) || (defined(CONFIG_DRVR_WRITABLE) && \
defined(CONFIG_DRVR_WRITEBUFFER))
# define CONFIG_SMART_RWBUFFER 1
#endif

View file

@ -1,7 +1,7 @@
/****************************************************************************
* drivers/rwbuffer.c
*
* Copyright (C) 2009, 2011, 2013 Gregory Nutt. All rights reserved.
* Copyright (C) 2009, 2011, 2013-2014 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@ -54,7 +54,7 @@
#include <nuttx/wqueue.h>
#include <nuttx/rwbuffer.h>
#if defined(CONFIG_FS_WRITEBUFFER) || defined(CONFIG_FS_READAHEAD)
#if defined(CONFIG_DRVR_WRITEBUFFER) || defined(CONFIG_DRVR_READAHEAD)
/****************************************************************************
* Preprocessor Definitions
@ -66,8 +66,8 @@
# error "Worker thread support is required (CONFIG_SCHED_WORKQUEUE)"
#endif
#ifndef CONFIG_FS_WRDELAY
# define CONFIG_FS_WRDELAY 350
#ifndef CONFIG_DRVR_WRDELAY
# define CONFIG_DRVR_WRDELAY 350
#endif
/****************************************************************************
@ -137,7 +137,7 @@ static inline bool rwb_overlap(off_t blockstart1, size_t nblocks1,
* Name: rwb_resetwrbuffer
****************************************************************************/
#ifdef CONFIG_FS_WRITEBUFFER
#ifdef CONFIG_DRVR_WRITEBUFFER
static inline void rwb_resetwrbuffer(struct rwbuffer_s *rwb)
{
/* We assume that the caller holds the wrsem */
@ -152,7 +152,7 @@ static inline void rwb_resetwrbuffer(struct rwbuffer_s *rwb)
* Name: rwb_wrflush
****************************************************************************/
#ifdef CONFIG_FS_WRITEBUFFER
#ifdef CONFIG_DRVR_WRITEBUFFER
static void rwb_wrflush(struct rwbuffer_s *rwb)
{
int ret;
@ -160,7 +160,7 @@ static void rwb_wrflush(struct rwbuffer_s *rwb)
fvdbg("Timeout!\n");
rwb_semtake(&rwb->wrsem);
if (rwb->wrnblocks)
if (rwb->wrnblocks > 0)
{
fvdbg("Flushing: blockstart=0x%08lx nblocks=%d from buffer=%p\n",
(long)rwb->wrblockstart, rwb->wrnblocks, rwb->wrbuffer);
@ -208,11 +208,11 @@ static void rwb_wrtimeout(FAR void *arg)
static void rwb_wrstarttimeout(FAR struct rwbuffer_s *rwb)
{
/* CONFIG_FS_WRDELAY provides the delay period in milliseconds. CLK_TCK
/* CONFIG_DRVR_WRDELAY provides the delay period in milliseconds. CLK_TCK
* provides the clock tick of the system (frequency in Hz).
*/
int ticks = (CONFIG_FS_WRDELAY + CLK_TCK/2) / CLK_TCK;
int ticks = (CONFIG_DRVR_WRDELAY + CLK_TCK/2) / CLK_TCK;
(void)work_queue(LPWORK, &rwb->work, rwb_wrtimeout, (FAR void *)rwb, ticks);
}
@ -229,7 +229,7 @@ static inline void rwb_wrcanceltimeout(struct rwbuffer_s *rwb)
* Name: rwb_writebuffer
****************************************************************************/
#ifdef CONFIG_FS_WRITEBUFFER
#ifdef CONFIG_DRVR_WRITEBUFFER
static ssize_t rwb_writebuffer(FAR struct rwbuffer_s *rwb,
off_t startblock, uint32_t nblocks,
FAR const uint8_t *wrbuffer)
@ -265,7 +265,7 @@ static ssize_t rwb_writebuffer(FAR struct rwbuffer_s *rwb,
/* writebuffer is empty? Then initialize it */
if (!rwb->wrnblocks)
if (rwb->wrnblocks == 0)
{
fvdbg("Fresh cache starting at block: 0x%08x\n", startblock);
rwb->wrblockstart = startblock;
@ -290,7 +290,7 @@ static ssize_t rwb_writebuffer(FAR struct rwbuffer_s *rwb,
* Name: rwb_resetrhbuffer
****************************************************************************/
#ifdef CONFIG_FS_READAHEAD
#ifdef CONFIG_DRVR_READAHEAD
static inline void rwb_resetrhbuffer(struct rwbuffer_s *rwb)
{
/* We assume that the caller holds the readAheadBufferSemphore */
@ -304,7 +304,7 @@ static inline void rwb_resetrhbuffer(struct rwbuffer_s *rwb)
* Name: rwb_bufferread
****************************************************************************/
#ifdef CONFIG_FS_READAHEAD
#ifdef CONFIG_DRVR_READAHEAD
static inline void
rwb_bufferread(struct rwbuffer_s *rwb, off_t startblock,
size_t nblocks, uint8_t **rdbuffer)
@ -338,7 +338,7 @@ rwb_bufferread(struct rwbuffer_s *rwb, off_t startblock,
* Name: rwb_rhreload
****************************************************************************/
#ifdef CONFIG_FS_READAHEAD
#ifdef CONFIG_DRVR_READAHEAD
static int rwb_rhreload(struct rwbuffer_s *rwb, off_t startblock)
{
/* Get the block number +1 of the last block that will fit in the
@ -381,6 +381,231 @@ static int rwb_rhreload(struct rwbuffer_s *rwb, off_t startblock)
}
#endif
/****************************************************************************
* Name: rwb_invalidate_writebuffer
*
* Description:
* Invalidate a region of the write buffer
*
****************************************************************************/
#ifdef CONFIG_DRVR_WRITEBUFFER
int rwb_invalidate_writebuffer(FAR struct rwbuffer_s *rwb,
off_t startblock, size_t blockcount)
{
int ret;
if (rwb->wrmaxblocks > 0 && wrnblocks > 0)
{
off_t wrbend;
off_t invend;
fvdbg("startblock=%d blockcount=%p\n", startblock, blockcount);
rwb_semtake(&rwb->wrsem);
/* Now there are five cases:
*
* 1. We invalidate nothing
*/
wrbend = rwb->wrblockstart + rwb->wrnblocks;
invend = startblock + blockcount;
if (rwb->wrblockstart > invend || wrbend < startblock)
{
ret = OK;
}
/* 2. We invalidate the entire write buffer. */
else if (rwb->wrblockstart >= startblock && wrbend <= invend)
{
rwb->wrnblocks = 0;
ret = OK;
}
/* We are going to invalidate a subset of the write buffer. Three
* more cases to consider:
*
* 2. We invalidate a portion in the middle of the write buffer
*/
else if (rwb->wrblockstart < startblock && wrbend > invend)
{
uint8_t *src;
off_t block;
off_t offset;
size_t nblocks;
/* Write the blocks at the end of the media to hardware */
nblocks = wrbend - invend;
block = invend;
offset = block - rwb->wrblockstart;
src = rwb->wrbuffer + offset * rwb->blocksize;
ret = rwb->wrflush(rwb->dev, block, nblocks, src);
if (ret < 0)
{
fdbg("ERROR: wrflush failed: %d\n", ret);
}
/* Keep the blocks at the beginning of the buffer up the
* start of the invalidated region.
*/
else
{
rwb->wrnblocks = startblock - rwb->wrblockstart;
ret = OK;
}
}
/* 3. We invalidate a portion at the end of the write buffer */
else if (wrbend > startblock && wrbend <= invend)
{
rwb->wrnblocks = wrbend - startblock;
ret = OK;
}
/* 4. We invalidate a portion at the beginning of the write buffer */
else /* if (rwb->wrblockstart >= startblock && wrbend < invend) */
{
uint8_t *src;
size_t ninval;
size_t nkeep;
DEBUGASSERT(rwb->wrblockstart >= startblock && wrbend < invend);
/* Copy the data from the uninvalidated region to the beginning
* of the write buffer.
*
* First calculate the source and destination of the transfer.
*/
ninval = invend - rwb->wrblockstart;
src = rwb->wrbuffer + ninval * rwb->blocksize;
/* Calculate the number of blocks we are keeping. We keep
* the ones that we don't invalidate.
*/
nkeep = rwb->wrnblocks - ninval;
/* Then move the data that we are keeping to the beginning
* the write buffer.
*/
memcpy(rwb->wrbuffer, src, nkeep * rwb->blocksize);
/* Update the block info. The first block is now the one just
* after the invalidation region and the number buffered blocks
* is the number that we kept.
*/
rwb->wrblockstart = invend;
rwb->wrnblocks = nkeep;
ret = OK;
}
rwb_semgive(&rwb->wrsem);
}
return ret;
}
#endif
/****************************************************************************
* Name: rwb_invalidate_readahead
*
* Description:
* Invalidate a region of the read-ahead buffer
*
****************************************************************************/
#ifdef CONFIG_DRVR_READAHEAD
int rwb_invalidate_readahead(FAR struct rwbuffer_s *rwb,
off_t startblock, size_t blockcount)
{
int ret;
if (rwb->rhmaxblocks > 0 && rhnblocks > 0)
{
off_t rhbend;
off_t invend;
fvdbg("startblock=%d blockcount=%p\n", startblock, blockcount);
rwb_semtake(&rwb->rhsem);
/* Now there are five cases:
*
* 1. We invalidate nothing
*/
rhbend = rwb->rhblockstart + rwb->rhnblocks;
invend = startblock + blockcount;
if (rwb->rhblockstart > invend || rhbend < startblock)
{
ret = OK;
}
/* 2. We invalidate the entire read-ahead buffer. */
else if (rwb->rhblockstart >= startblock && rhbend <= invend)
{
rwb->rhnblocks = 0;
ret = OK;
}
/* We are going to invalidate a subset of the read-ahead buffer.
* Three more cases to consider:
*
* 2. We invalidate a portion in the middle of the write buffer
*/
else if (rwb->rhblockstart < startblock && rhbend > invend)
{
/* Keep the blocks at the beginning of the buffer up the
* start of the invalidated region.
*/
rwb->rhnblocks = startblock - rwb->rhblockstart;
ret = OK;
}
/* 3. We invalidate a portion at the end of the read-ahead buffer */
else if (rhbend > startblock && rhbend <= invend)
{
rwb->rhnblocks = rhbend - startblock;
ret = OK;
}
/* 4. We invalidate a portion at the beginning of the write buffer */
else /* if (rwb->rhblockstart >= startblock && rhbend < invend) */
{
/* Let's just force the whole read-ahead buffer to be reloaded.
* That might cost s small amount of performance, but well worth
* the lower complexity.
*/
DEBUGASSERT(rwb->rhblockstart >= startblock && rhbend < invend);
rwb->rhnblocks = 0;
ret = OK;
}
rwb_semgive(&rwb->rhsem);
}
return ret;
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
@ -401,71 +626,78 @@ int rwb_initialize(FAR struct rwbuffer_s *rwb)
/* Setup so that rwb_uninitialize can handle a failure */
#ifdef CONFIG_FS_WRITEBUFFER
#ifdef CONFIG_DRVR_WRITEBUFFER
DEBUGASSERT(rwb->wrflush!= NULL);
rwb->wrbuffer = NULL;
#endif
#ifdef CONFIG_FS_READAHEAD
#ifdef CONFIG_DRVR_READAHEAD
DEBUGASSERT(rwb->rhreload != NULL);
rwb->rhbuffer = NULL;
#endif
#ifdef CONFIG_FS_WRITEBUFFER
fvdbg("Initialize the write buffer\n");
/* Initialize the write buffer access semaphore */
sem_init(&rwb->wrsem, 0, 1);
/* Initialize write buffer parameters */
rwb_resetwrbuffer(rwb);
/* Allocate the write buffer */
rwb->wrbuffer = NULL;
#ifdef CONFIG_DRVR_WRITEBUFFER
if (rwb->wrmaxblocks > 0)
{
allocsize = rwb->wrmaxblocks * rwb->blocksize;
rwb->wrbuffer = kmalloc(allocsize);
if (!rwb->wrbuffer)
fvdbg("Initialize the write buffer\n");
/* Initialize the write buffer access semaphore */
sem_init(&rwb->wrsem, 0, 1);
/* Initialize write buffer parameters */
rwb_resetwrbuffer(rwb);
/* Allocate the write buffer */
rwb->wrbuffer = NULL;
if (rwb->wrmaxblocks > 0)
{
fdbg("Write buffer kmalloc(%d) failed\n", allocsize);
return -ENOMEM;
allocsize = rwb->wrmaxblocks * rwb->blocksize;
rwb->wrbuffer = kmalloc(allocsize);
if (!rwb->wrbuffer)
{
fdbg("Write buffer kmalloc(%d) failed\n", allocsize);
return -ENOMEM;
}
}
fvdbg("Write buffer size: %d bytes\n", allocsize);
}
#endif /* CONFIG_DRVR_WRITEBUFFER */
fvdbg("Write buffer size: %d bytes\n", allocsize);
#endif /* CONFIG_FS_WRITEBUFFER */
#ifdef CONFIG_FS_READAHEAD
fvdbg("Initialize the read-ahead buffer\n");
/* Initialize the read-ahead buffer access semaphore */
sem_init(&rwb->rhsem, 0, 1);
/* Initialize read-ahead buffer parameters */
rwb_resetrhbuffer(rwb);
/* Allocate the read-ahead buffer */
rwb->rhbuffer = NULL;
if (rwb->rhmaxblocks > 0)
#ifdef CONFIG_DRVR_READAHEAD
if (rhmaxblocks > 0)
{
allocsize = rwb->rhmaxblocks * rwb->blocksize;
rwb->rhbuffer = kmalloc(allocsize);
if (!rwb->rhbuffer)
{
fdbg("Read-ahead buffer kmalloc(%d) failed\n", allocsize);
return -ENOMEM;
}
}
fvdbg("Initialize the read-ahead buffer\n");
fvdbg("Read-ahead buffer size: %d bytes\n", allocsize);
#endif /* CONFIG_FS_READAHEAD */
return 0;
/* Initialize the read-ahead buffer access semaphore */
sem_init(&rwb->rhsem, 0, 1);
/* Initialize read-ahead buffer parameters */
rwb_resetrhbuffer(rwb);
/* Allocate the read-ahead buffer */
rwb->rhbuffer = NULL;
if (rwb->rhmaxblocks > 0)
{
allocsize = rwb->rhmaxblocks * rwb->blocksize;
rwb->rhbuffer = kmalloc(allocsize);
if (!rwb->rhbuffer)
{
fdbg("Read-ahead buffer kmalloc(%d) failed\n", allocsize);
return -ENOMEM;
}
}
fvdbg("Read-ahead buffer size: %d bytes\n", allocsize);
}
#endif /* CONFIG_DRVR_READAHEAD */
return OK;
}
/****************************************************************************
@ -474,20 +706,26 @@ int rwb_initialize(FAR struct rwbuffer_s *rwb)
void rwb_uninitialize(FAR struct rwbuffer_s *rwb)
{
#ifdef CONFIG_FS_WRITEBUFFER
rwb_wrcanceltimeout(rwb);
sem_destroy(&rwb->wrsem);
if (rwb->wrbuffer)
#ifdef CONFIG_DRVR_WRITEBUFFER
if (rwb->wrmaxblocks > 0)
{
kfree(rwb->wrbuffer);
rwb_wrcanceltimeout(rwb);
sem_destroy(&rwb->wrsem);
if (rwb->wrbuffer)
{
kfree(rwb->wrbuffer);
}
}
#endif
#ifdef CONFIG_FS_READAHEAD
sem_destroy(&rwb->rhsem);
if (rwb->rhbuffer)
#ifdef CONFIG_DRVR_READAHEAD
if (rhmaxblocks > 0)
{
kfree(rwb->rhbuffer);
sem_destroy(&rwb->rhsem);
if (rwb->rhbuffer)
{
kfree(rwb->rhbuffer);
}
}
#endif
}
@ -504,7 +742,7 @@ int rwb_read(FAR struct rwbuffer_s *rwb, off_t startblock, uint32_t nblocks,
fvdbg("startblock=%ld nblocks=%ld rdbuffer=%p\n",
(long)startblock, (long)nblocks, rdbuffer);
#ifdef CONFIG_FS_WRITEBUFFER
#ifdef CONFIG_DRVR_WRITEBUFFER
/* If the new read data overlaps any part of the write buffer, then
* flush the write data onto the physical media before reading. We
* could attempt some more exotic handling -- but this simple logic
@ -522,74 +760,86 @@ int rwb_read(FAR struct rwbuffer_s *rwb, off_t startblock, uint32_t nblocks,
{
rwb_wrflush(rwb);
}
rwb_semgive(&rwb->wrsem);
}
#endif
#ifdef CONFIG_FS_READAHEAD
/* Loop until we have read all of the requested blocks */
rwb_semtake(&rwb->rhsem);
for (remaining = nblocks; remaining > 0;)
#ifdef CONFIG_DRVR_READAHEAD
if (rhmaxblocks > 0)
{
/* Is there anything in the read-ahead buffer? */
/* Loop until we have read all of the requested blocks */
if (rwb->rhnblocks > 0)
rwb_semtake(&rwb->rhsem);
for (remaining = nblocks; remaining > 0;)
{
off_t startblock = startblock;
size_t nbufblocks = 0;
off_t bufferend;
/* Is there anything in the read-ahead buffer? */
/* Loop for each block we find in the read-head buffer. Count the
* number of buffers that we can read from read-ahead buffer.
if (rwb->rhnblocks > 0)
{
off_t startblock = startblock;
size_t nbufblocks = 0;
off_t bufferend;
/* Loop for each block we find in the read-head buffer. Count
* the number of buffers that we can read from read-ahead
* buffer.
*/
bufferend = rwb->rhblockstart + rwb->rhnblocks;
while ((startblock >= rwb->rhblockstart) &&
(startblock < bufferend) &&
(remaining > 0))
{
/* This is one more that we will read from the read ahead
* buffer.
*/
nbufblocks++;
/* And one less that we will read from the media */
startblock++;
remaining--;
}
/* Then read the data from the read-ahead buffer */
rwb_bufferread(rwb, startblock, nbufblocks, &rdbuffer);
}
/* If we did not get all of the data from the buffer, then we have
* to refill the buffer and try again.
*/
bufferend = rwb->rhblockstart + rwb->rhnblocks;
while ((startblock >= rwb->rhblockstart) &&
(startblock < bufferend) &&
(remaining > 0))
if (remaining > 0)
{
/* This is one more that we will read from the read ahead buffer */
nbufblocks++;
/* And one less that we will read from the media */
startblock++;
remaining--;
int ret = rwb_rhreload(rwb, startblock);
if (ret < 0)
{
fdbg("ERROR: Failed to fill the read-ahead buffer: %d\n", ret);
return ret;
}
}
/* Then read the data from the read-ahead buffer */
rwb_bufferread(rwb, startblock, nbufblocks, &rdbuffer);
}
/* If we did not get all of the data from the buffer, then we have to refill
* the buffer and try again.
/* On success, return the number of blocks that we were requested to
* read. This is for compatibility with the normal return of a block
* driver read method
*/
if (remaining > 0)
{
int ret = rwb_rhreload(rwb, startblock);
if (ret < 0)
{
fdbg("ERROR: Failed to fill the read-ahead buffer: %d\n", -ret);
return ret;
}
}
rwb_semgive(&rwb->rhsem);
ret = nblocks;
}
/* On success, return the number of blocks that we were requested to read.
* This is for compatibility with the normal return of a block driver read
* method
*/
rwb_semgive(&rwb->rhsem);
return 0;
else
#else
return rwb->rhreload(rwb->dev, startblock, nblocks, rdbuffer);
{
ret = rwb->rhreload(rwb->dev, startblock, nblocks, rdbuffer);
}
#endif
return ret;
}
/****************************************************************************
@ -601,57 +851,69 @@ int rwb_write(FAR struct rwbuffer_s *rwb, off_t startblock,
{
int ret;
#ifdef CONFIG_FS_READAHEAD
/* If the new write data overlaps any part of the read buffer, then
* flush the data from the read buffer. We could attempt some more
* exotic handling -- but this simple logic is well-suited for simple
* streaming applications.
*/
rwb_semtake(&rwb->rhsem);
if (rwb_overlap(rwb->rhblockstart, rwb->rhnblocks, startblock, nblocks))
#ifdef CONFIG_DRVR_READAHEAD
if (rhmaxblocks > 0)
{
rwb_resetrhbuffer(rwb);
/* If the new write data overlaps any part of the read buffer, then
* flush the data from the read buffer. We could attempt some more
* exotic handling -- but this simple logic is well-suited for simple
* streaming applications.
*/
rwb_semtake(&rwb->rhsem);
if (rwb_overlap(rwb->rhblockstart, rwb->rhnblocks, startblock, nblocks))
{
rwb_resetrhbuffer(rwb);
}
rwb_semgive(&rwb->rhsem);
}
rwb_semgive(&rwb->rhsem);
#endif
#ifdef CONFIG_FS_WRITEBUFFER
fvdbg("startblock=%d wrbuffer=%p\n", startblock, wrbuffer);
/* Use the block cache unless the buffer size is bigger than block cache */
if (nblocks > rwb->wrmaxblocks)
#ifdef CONFIG_DRVR_WRITEBUFFER
if (rwb->wrmaxblocks > 0)
{
/* First flush the cache */
fvdbg("startblock=%d wrbuffer=%p\n", startblock, wrbuffer);
rwb_semtake(&rwb->wrsem);
rwb_wrflush(rwb);
rwb_semgive(&rwb->wrsem);
/* Use the block cache unless the buffer size is bigger than block cache */
/* Then transfer the data directly to the media */
if (nblocks > rwb->wrmaxblocks)
{
/* First flush the cache */
rwb_semtake(&rwb->wrsem);
rwb_wrflush(rwb);
rwb_semgive(&rwb->wrsem);
/* Then transfer the data directly to the media */
ret = rwb->wrflush(rwb->dev, startblock, nblocks, wrbuffer);
}
else
{
/* Buffer the data in the write buffer */
ret = rwb_writebuffer(rwb, startblock, nblocks, wrbuffer);
}
/* On success, return the number of blocks that we were requested to
* write. This is for compatibility with the normal return of a block
* driver write method
*/
}
else
#else
{
/* No write buffer.. just pass the write operation through via the
* flush callback.
*/
ret = rwb->wrflush(rwb->dev, startblock, nblocks, wrbuffer);
}
else
{
/* Buffer the data in the write buffer */
ret = rwb_writebuffer(rwb, startblock, nblocks, wrbuffer);
}
/* On success, return the number of blocks that we were requested to write.
* This is for compatibility with the normal return of a block driver write
* method
*/
return ret;
#else
return rwb->wrflush(rwb->dev, startblock, nblocks, wrbuffer);
#endif
return ret;
}
/****************************************************************************
@ -664,19 +926,60 @@ int rwb_write(FAR struct rwbuffer_s *rwb, off_t startblock,
int rwb_mediaremoved(FAR struct rwbuffer_s *rwb)
{
#ifdef CONFIG_FS_WRITEBUFFER
rwb_semtake(&rwb->wrsem);
rwb_resetwrbuffer(rwb);
rwb_semgive(&rwb->wrsem);
#ifdef CONFIG_DRVR_WRITEBUFFER
if (rwb->wrmaxblocks > 0)
{
rwb_semtake(&rwb->wrsem);
rwb_resetwrbuffer(rwb);
rwb_semgive(&rwb->wrsem);
}
#endif
#ifdef CONFIG_FS_READAHEAD
rwb_semtake(&rwb->rhsem);
rwb_resetrhbuffer(rwb);
rwb_semgive(&rwb->rhsem);
#ifdef CONFIG_DRVR_READAHEAD
if (rhmaxblocks > 0)
{
rwb_semtake(&rwb->rhsem);
rwb_resetrhbuffer(rwb);
rwb_semgive(&rwb->rhsem);
}
#endif
return 0;
return OK;
}
#endif /* CONFIG_FS_WRITEBUFFER || CONFIG_FS_READAHEAD */
/****************************************************************************
* Name: rwb_invalidate
*
* Description:
* Invalidate a region of the caches
*
****************************************************************************/
int rwb_invalidate(FAR struct rwbuffer_s *rwb,
off_t startblock, size_t blockcount)
{
int ret;
#ifdef CONFIG_DRVR_WRITEBUFFER
ret = rwb_invalidate_writebuffer(rwb, startblock, blockcount);
if (ret < 0)
{
fdbg("ERROR: rwb_invalidate_writebuffer failed: %d\n", ret);
return ret;
}
#endif
#ifdef CONFIG_DRVR_READAHEAD
ret = rwb_invalidate_readahead(rwb, startblock, blockcount);
if (ret < 0)
{
fdbg("ERROR: rwb_invalidate_readahead failed: %d\n", ret);
return ret;
}
#endif
return OK;
}
#endif /* CONFIG_DRVR_WRITEBUFFER || CONFIG_DRVR_READAHEAD */

82
include/nuttx/rwbuffer.h Normal file → Executable file
View file

@ -1,7 +1,7 @@
/****************************************************************************
* include/nuttx/rwbuffer.h
*
* Copyright (C) 2009 Gregory Nutt. All rights reserved.
* Copyright (C) 2009, 2014 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@ -33,10 +33,6 @@
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#ifndef __INCLUDE_NUTTX_RWBUFFER_H
#define __INCLUDE_NUTTX_RWBUFFER_H
@ -51,7 +47,7 @@
#include <semaphore.h>
#include <nuttx/wqueue.h>
#if defined(CONFIG_FS_WRITEBUFFER) || defined(CONFIG_FS_READAHEAD)
#if defined(CONFIG_DRVR_WRITEBUFFER) || defined(CONFIG_DRVR_READAHEAD)
/**********************************************************************
* Pre-processor Definitions
@ -107,45 +103,50 @@ struct rwbuffer_s
uint16_t blocksize; /* The size of one block */
size_t nblocks; /* The total number blocks supported */
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
* not use rwb_write().
/* Read-ahead/Write buffer sizes. Buffering can be disabled (even if it
* is enabled in the configuration) by setting the buffer size to zero
* blocks.
*/
#ifdef CONFIG_FS_WRITEBUFFER
#ifdef CONFIG_DRVR_WRITEBUFFER
uint16_t wrmaxblocks; /* The number of blocks to buffer in memory */
rwbflush_t wrflush; /* Callout to flush the write buffer */
#endif
#ifdef CONFIG_DRVR_READAHEAD
uint16_t rhmaxblocks; /* The number of blocks to buffer in memory */
#endif
/* Read-ahead buffer setup. If CONFIG_FS_READAHEAD is defined but you
* want write-buffer-only operation, then (1) set rhmaxblocks to zero and
* do not use rwb_read().
/* Callback functions.
*
* wrflush. This callback is normally used to flush the contents of
* the write buffer. If write buffering is disabled, then this
* function will instead be used to perform unbuffered writes.
* rhrelad. This callback is normally used to read new data into the
* read-ahead buffer. If read-ahead buffering is disabled, then this
* function will instead be used to perform unbuffered reads.
*/
#ifdef CONFIG_FS_READAHEAD
uint16_t rhmaxblocks; /* The number of blocks to buffer in memory */
FAR void *dev; /* Device state passed to callout functions */
rwbflush_t wrflush; /* Callout to flush the write buffer */
rwbreload_t rhreload; /* Callout to reload the read-ahead buffer */
#endif
/********************************************************************/
/* The user should never modify any of the remaing fields */
/* The user should never modify any of the remaining fields */
/* This is the state of the write buffer */
/* This is the state of the write buffering */
#ifdef CONFIG_FS_WRITEBUFFER
#ifdef CONFIG_DRVR_WRITEBUFFER
sem_t wrsem; /* Enforces exclusive access to the write buffer */
struct work_s work; /* Delayed work to flush buffer after adelay with no activity */
struct work_s work; /* Delayed work to flush buffer after a delay with no activity */
uint8_t *wrbuffer; /* Allocated write buffer */
uint16_t wrnblocks; /* Number of blocks in write buffer */
off_t wrblockstart; /* First block in write buffer */
off_t wrexpectedblock; /* Next block expected */
#endif
/* This is the state of the read-ahead buffer */
/* This is the state of the read-ahead buffering */
#ifdef CONFIG_FS_READAHEAD
#ifdef CONFIG_DRVR_READAHEAD
sem_t rhsem; /* Enforces exclusive access to the write buffer */
uint8_t *rhbuffer; /* Allocated read-ahead buffer */
uint16_t rhnblocks; /* Number of blocks in read-ahead buffer */
@ -160,7 +161,8 @@ struct rwbuffer_s
#undef EXTERN
#if defined(__cplusplus)
#define EXTERN EXTERN "C"
EXTERN "C" {
EXTERN "C"
{
#else
#define EXTERN extern
#endif
@ -171,22 +173,32 @@ EXTERN "C" {
/* Buffer initialization */
EXTERN int rwb_initialize(FAR struct rwbuffer_s *rwb);
EXTERN void rwb_uninitialize(FAR struct rwbuffer_s *rwb);
int rwb_initialize(FAR struct rwbuffer_s *rwb);
void rwb_uninitialize(FAR struct rwbuffer_s *rwb);
/* Buffer transfers */
/* Block oriented transfers */
EXTERN ssize_t rwb_read(FAR struct rwbuffer_s *rwb, off_t startblock,
size_t blockcount, FAR uint8_t *rdbuffer);
EXTERN ssize_t rwb_write(FAR struct rwbuffer_s *rwb,
off_t startblock, size_t blockcount,
FAR const uint8_t *wrbuffer);
EXTERN int rwb_mediaremoved(FAR struct rwbuffer_s *rwb);
ssize_t rwb_read(FAR struct rwbuffer_s *rwb, off_t startblock,
size_t blockcount, FAR uint8_t *rdbuffer);
ssize_t rwb_write(FAR struct rwbuffer_s *rwb,
off_t startblock, size_t blockcount,
FAR const uint8_t *wrbuffer);
/* Character oriented transfers */
ssize_t mtd_readbytes(FAR struct rwbuffer_s *dev, off_t offset,
size_t nbytes, FAR uint8_t *buffer);
/* Media events */
int rwb_mediaremoved(FAR struct rwbuffer_s *rwb);
int rwb_invalidate(FAR struct rwbuffer_s *rwb,
off_t startblock, size_t blockcount);
#undef EXTERN
#if defined(__cplusplus)
}
#endif
#endif /* CONFIG_FS_WRITEBUFFER || CONFIG_READAHEAD_BUFFER */
#endif /* CONFIG_DRVR_WRITEBUFFER || CONFIG_DRVR_READAHEAD */
#endif /* __INCLUDE_NUTTX_RWBUFFER_H */