diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index e42e8d1bff..3c3d79d801 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -316,11 +316,11 @@ config FILEMTD if FILEMTD config FILEMTD_BLOCKSIZE - int "File MTD block size" + int "File MTD default block size" default 512 config FILEMTD_ERASESIZE - int "File MTD erase block size" + int "File MTD default erase block size" default 4096 config FILEMTD_ERASESTATE @@ -329,6 +329,29 @@ config FILEMTD_ERASESTATE endif # FILEMTD +config NULLMTD + bool "MTD null driver" + default n + ---help--- + Build support for a MTD null driver. It simulates an always erased + MTD device. + +if NULLMTD + +config NULLMTD_BLOCKSIZE + int "MTD null default block size" + default 512 + +config NULLMTD_ERASESIZE + int "MTD null detault erase block size" + default 4096 + +config NULLMTD_ERASESTATE + hex "Simulated erase state" + default 0xff + +endif # NULLMTD + config MTD_AT24XX bool "I2C-based AT24xx eeprom" default n diff --git a/drivers/mtd/Make.defs b/drivers/mtd/Make.defs index 32cdc7fbd5..893d8d5035 100644 --- a/drivers/mtd/Make.defs +++ b/drivers/mtd/Make.defs @@ -64,6 +64,10 @@ ifeq ($(CONFIG_FILEMTD),y) CSRCS += filemtd.c endif +ifeq ($(CONFIG_NULLMTD),y) +CSRCS += nullmtd.c +endif + ifeq ($(CONFIG_MTD_AT24XX),y) CSRCS += at24xx.c endif diff --git a/drivers/mtd/filemtd.c b/drivers/mtd/filemtd.c index 0cccae06c0..befcc55699 100644 --- a/drivers/mtd/filemtd.c +++ b/drivers/mtd/filemtd.c @@ -252,8 +252,8 @@ static int filemtd_erase(FAR struct mtd_dev_s *dev, off_t startblock, * in logical block numbers */ - startblock *= FILEMTD_BLKPER; - nblocks *= FILEMTD_BLKPER; + startblock *= (priv->erasesize / priv->blocksize); + nblocks *= (priv->erasesize / priv->blocksize); /* Get the offset corresponding to the first block and the size * corresponding to the number of blocks. @@ -291,7 +291,7 @@ static ssize_t filemtd_bread(FAR struct mtd_dev_s *dev, off_t startblock, /* Don't let the read exceed the original size of the file */ - maxblock = priv->nblocks * FILEMTD_BLKPER; + maxblock = priv->nblocks * (priv->erasesize / priv->blocksize); if (startblock >= maxblock) { return 0; @@ -331,7 +331,7 @@ static ssize_t filemtd_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, /* Don't let the write exceed the original size of the file */ - maxblock = priv->nblocks * FILEMTD_BLKPER; + maxblock = priv->nblocks * (priv->erasesize / priv->blocksize); if (startblock >= maxblock) { return 0; @@ -363,16 +363,23 @@ static ssize_t filemtd_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; + off_t maxoffset; DEBUGASSERT(dev && buf); - /* Don't let read read past end of buffer */ + /* Don't let the read exceed the original size of the file */ - if (offset + nbytes > priv->nblocks * priv->erasesize) + maxoffset = priv->nblocks * priv->erasesize; + if (offset >= maxoffset) { return 0; } + if (offset + nbytes > maxoffset) + { + nbytes = maxoffset - offset; + } + filemtd_read(priv, buf, offset, nbytes); return nbytes; } @@ -398,6 +405,11 @@ static ssize_t file_bytewrite(FAR struct mtd_dev_s *dev, off_t offset, return 0; } + if (offset + nbytes > maxoffset) + { + nbytes = maxoffset - offset; + } + /* Then write the data to the file */ filemtd_write(priv, offset, buf, nbytes); @@ -456,8 +468,7 @@ static int filemtd_ioctl(FAR struct mtd_dev_s *dev, int cmd, { /* Erase the entire device */ - filemtd_erase(dev, 0, priv->nblocks); - ret = OK; + ret = filemtd_erase(dev, 0, priv->nblocks); } break; @@ -550,6 +561,15 @@ FAR struct mtd_dev_s *blockmtd_initialize(FAR const char *path, priv->erasesize = erasesize; } + if ((priv->erasesize / priv->blocksize) * priv->blocksize + != priv->erasesize) + { + ferr("ERROR: erasesize must be an even multiple of sectsize\n"); + file_close(&priv->mtdfile); + kmm_free(priv); + return NULL; + } + /* Force the size to be an even number of the erase block size */ nblocks = mtdlen / priv->erasesize; diff --git a/drivers/mtd/mtd_progmem.c b/drivers/mtd/mtd_progmem.c index bb78b0b0de..3ecb920046 100644 --- a/drivers/mtd/mtd_progmem.c +++ b/drivers/mtd/mtd_progmem.c @@ -371,7 +371,6 @@ static int progmem_ioctl(FAR struct mtd_dev_s *dev, int cmd, case MTDIOC_ERASESTATE: { FAR uint8_t *result = (FAR uint8_t *)arg; - *result = up_progmem_erasestate(); ret = OK; diff --git a/drivers/mtd/nullmtd.c b/drivers/mtd/nullmtd.c new file mode 100644 index 0000000000..e8547e9070 --- /dev/null +++ b/drivers/mtd/nullmtd.c @@ -0,0 +1,411 @@ +/**************************************************************************** + * drivers/mtd/nullmtd.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +#ifndef CONFIG_NULLMTD_BLOCKSIZE +# define CONFIG_NULLMTD_BLOCKSIZE 512 +#endif + +#ifndef CONFIG_NULLMTD_ERASESIZE +# define CONFIG_NULLMTD_ERASESIZE 4096 +#endif + +#ifndef CONFIG_NULLMTD_ERASESTATE +# define CONFIG_NULLMTD_ERASESTATE 0xff +#endif + +#if CONFIG_NULLMTD_ERASESTATE != 0xff && CONFIG_NULLMTD_ERASESTATE != 0x00 +# error "Unsupported value for CONFIG_NULLMTD_ERASESTATE" +#endif + +#if CONFIG_NULLMTD_BLOCKSIZE > CONFIG_NULLMTD_ERASESIZE +# error "Must have CONFIG_NULLMTD_BLOCKSIZE <= CONFIG_NULLMTD_ERASESIZE" +#endif + +#undef NULLMTD_BLKPER +#define NULLMTD_BLKPER (CONFIG_NULLMTD_ERASESIZE/CONFIG_NULLMTD_BLOCKSIZE) + +#if NULLMTD_BLKPER*CONFIG_NULLMTD_BLOCKSIZE != CONFIG_NULLMTD_ERASESIZE +# error "CONFIG_NULLMTD_ERASESIZE must be an even multiple of CONFIG_NULLMTD_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 null_dev_s. + */ + +struct null_dev_s +{ + struct mtd_dev_s mtd; /* MTD device */ + size_t nblocks; /* Number of erase blocks */ + size_t erasesize; /* Offset from start of file */ + size_t blocksize; /* Offset from start of file */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +#define nullmtd_read(dest, len) memset(dest, CONFIG_NULLMTD_ERASESTATE, len) + +/* MTD driver methods */ + +static int nullmtd_erase(FAR struct mtd_dev_s *dev, off_t startblock, + size_t nblocks); +static ssize_t nullmtd_bread(FAR struct mtd_dev_s *dev, off_t startblock, + size_t nblocks, FAR uint8_t *buf); +static ssize_t nullmtd_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, + size_t nblocks, FAR const uint8_t *buf); +static ssize_t nullmtd_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 nullmtd_bytewrite(FAR struct mtd_dev_s *dev, off_t offset, + size_t nbytes, FAR const uint8_t *buf); +#endif +static int nullmtd_ioctl(FAR struct mtd_dev_s *dev, int cmd, + unsigned long arg); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nullmtd_erase + ****************************************************************************/ + +static int nullmtd_erase(FAR struct mtd_dev_s *dev, off_t startblock, + size_t nblocks) +{ + FAR struct null_dev_s *priv = (FAR struct null_dev_s *)dev; + + DEBUGASSERT(dev); + + /* Don't let the erase exceed the configured size of the device */ + + if (startblock >= priv->nblocks) + { + return 0; + } + + return OK; +} + +/**************************************************************************** + * Name: nullmtd_bread + ****************************************************************************/ + +static ssize_t nullmtd_bread(FAR struct mtd_dev_s *dev, off_t startblock, + size_t nblocks, FAR uint8_t *buf) +{ + FAR struct null_dev_s *priv = (FAR struct null_dev_s *)dev; + off_t maxblock; + size_t nbytes; + + DEBUGASSERT(dev && buf); + + /* Don't let the read exceed the configured size of the device */ + + maxblock = priv->nblocks * (priv->erasesize / priv->blocksize); + if (startblock >= maxblock) + { + return 0; + } + + if (startblock + nblocks > maxblock) + { + nblocks = maxblock - startblock; + } + + /* Get the size corresponding to the number of blocks. + */ + + nbytes = nblocks * priv->blocksize; + + /* Then read the data from the file */ + + nullmtd_read(buf, nbytes); + return nblocks; +} + +/**************************************************************************** + * Name: nullmtd_bwrite + ****************************************************************************/ + +static ssize_t nullmtd_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, + size_t nblocks, FAR const uint8_t *buf) +{ + FAR struct null_dev_s *priv = (FAR struct null_dev_s *)dev; + off_t maxblock; + + DEBUGASSERT(dev && buf); + + /* Don't let the write exceed the configured size of the device */ + + maxblock = priv->nblocks * (priv->erasesize / priv->blocksize); + if (startblock >= maxblock) + { + return 0; + } + + if (startblock + nblocks > maxblock) + { + nblocks = maxblock - startblock; + } + + return nblocks; +} + +/**************************************************************************** + * Name: nullmtd_byteread + ****************************************************************************/ + +static ssize_t nullmtd_byteread(FAR struct mtd_dev_s *dev, off_t offset, + size_t nbytes, FAR uint8_t *buf) +{ + FAR struct null_dev_s *priv = (FAR struct null_dev_s *)dev; + off_t maxoffset; + + DEBUGASSERT(dev && buf); + + /* Don't let the read exceed the configured size of the device */ + + maxoffset = priv->nblocks * priv->erasesize; + if (offset >= maxoffset) + { + return 0; + } + + if (offset + nbytes > maxoffset) + { + nbytes = maxoffset - offset; + } + + nullmtd_read(buf, nbytes); + return nbytes; +} + +/**************************************************************************** + * Name: nullmtd_bytewrite + ****************************************************************************/ + +#ifdef CONFIG_MTD_BYTE_WRITE +static ssize_t nullmtd_bytewrite(FAR struct mtd_dev_s *dev, off_t offset, + size_t nbytes, FAR const uint8_t *buf) +{ + FAR struct null_dev_s *priv = (FAR struct null_dev_s *)dev; + off_t maxoffset; + + DEBUGASSERT(dev && buf); + + /* Don't let the write exceed the configured size of the device */ + + maxoffset = priv->nblocks * priv->erasesize; + if (offset >= maxoffset) + { + return 0; + } + + if (offset + nbytes > maxoffset) + { + nbytes = maxoffset - offset; + } + + return nbytes; +} +#endif + +/**************************************************************************** + * Name: nullmtd_ioctl + ****************************************************************************/ + +static int nullmtd_ioctl(FAR struct mtd_dev_s *dev, int cmd, + unsigned long arg) +{ + FAR struct null_dev_s *priv = (FAR struct null_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 = priv->blocksize; + geo->erasesize = priv->erasesize; + geo->neraseblocks = priv->nblocks; + ret = OK; + } + } + break; + + case BIOC_PARTINFO: + { + FAR struct partition_info_s *info = + (FAR struct partition_info_s *)arg; + if (info != NULL) + { + info->numsectors = priv->nblocks * + priv->erasesize / priv->blocksize; + info->sectorsize = priv->blocksize; + info->startsector = 0; + info->parent[0] = '\0'; + ret = OK; + } + } + break; + + case MTDIOC_BULKERASE: + { + /* Erase the entire device */ + + ret = nullmtd_erase(dev, 0, priv->nblocks); + } + break; + + case MTDIOC_ERASESTATE: + { + FAR uint8_t *result = (FAR uint8_t *)arg; + *result = CONFIG_NULLMTD_ERASESTATE; + + ret = OK; + } + break; + + default: + ret = -ENOTTY; /* Bad command */ + break; + } + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nullmtd_initialize + * + * Description: + * Create and initialize a MTD null device instance. + * + * Input Parameters: + * mtdlen - total size of MTD device + * sectsize + * + ****************************************************************************/ + +FAR struct mtd_dev_s *nullmtd_initialize(size_t mtdlen, int16_t sectsize, + int32_t erasesize) +{ + FAR struct null_dev_s *priv; + size_t nblocks; + + /* Create an instance of the RAM MTD device state structure */ + + priv = (FAR struct null_dev_s *)kmm_zalloc(sizeof(struct null_dev_s)); + if (!priv) + { + ferr("ERROR: Failed to allocate the RAM MTD state structure\n"); + return NULL; + } + + /* Set the block size based on the provided sectsize parameter */ + + if (sectsize <= 0) + { + priv->blocksize = CONFIG_NULLMTD_BLOCKSIZE; + } + else + { + priv->blocksize = sectsize; + } + + /* Set the erase size based on the provided erasesize parameter */ + + if (erasesize <= 0) + { + priv->erasesize = CONFIG_NULLMTD_ERASESIZE; + } + else + { + priv->erasesize = erasesize; + } + + /* Force the size to be an even number of the erase block size */ + + nblocks = mtdlen / priv->erasesize; + if (nblocks < 1) + { + ferr("ERROR: Need to provide at least one full erase block\n"); + kmm_free(priv); + return NULL; + } + + /* Perform initialization as necessary. (unsupported methods were + * nullified by kmm_zalloc). + */ + + priv->mtd.erase = nullmtd_erase; + priv->mtd.bread = nullmtd_bread; + priv->mtd.bwrite = nullmtd_bwrite; + priv->mtd.read = nullmtd_byteread; +#ifdef CONFIG_MTD_BYTE_WRITE + priv->mtd.write = nullmtd_bytewrite; +#endif + priv->mtd.ioctl = nullmtd_ioctl; + priv->mtd.name = "nullmtd"; + priv->nblocks = nblocks; + + return &priv->mtd; +} diff --git a/drivers/mtd/rammtd.c b/drivers/mtd/rammtd.c index c3014900a8..0088a51947 100644 --- a/drivers/mtd/rammtd.c +++ b/drivers/mtd/rammtd.c @@ -315,16 +315,23 @@ static ssize_t ram_byteread(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes, FAR uint8_t *buf) { FAR struct ram_dev_s *priv = (FAR struct ram_dev_s *)dev; + off_t maxoffset; DEBUGASSERT(dev && buf); - /* Don't let read read past end of buffer */ + /* Don't let the read exceed the size of the ram buffer */ - if (offset + nbytes > priv->nblocks * CONFIG_RAMMTD_ERASESIZE) + maxoffset = priv->nblocks * CONFIG_RAMMTD_ERASESIZE; + if (offset >= maxoffset) { return 0; } + if (offset + nbytes > maxoffset) + { + nbytes = maxoffset - offset; + } + ram_read(buf, &priv->start[offset], nbytes); return nbytes; } @@ -338,18 +345,23 @@ static ssize_t ram_bytewrite(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes, FAR const uint8_t *buf) { FAR struct ram_dev_s *priv = (FAR struct ram_dev_s *)dev; - off_t maxaddr; + off_t maxoffset; DEBUGASSERT(dev && buf); /* Don't let the write exceed the size of the ram buffer */ - maxaddr = priv->nblocks * CONFIG_RAMMTD_ERASESIZE; - if (offset + nbytes > maxaddr) + maxoffset = priv->nblocks * CONFIG_RAMMTD_ERASESIZE; + if (offset >= maxoffset) { return 0; } + if (offset + nbytes > maxoffset) + { + nbytes = maxoffset - offset; + } + /* Then write the data to RAM */ ram_write(&priv->start[offset], buf, nbytes); @@ -480,6 +492,7 @@ FAR struct mtd_dev_s *rammtd_initialize(FAR uint8_t *start, size_t size) if (nblocks < 1) { ferr("ERROR: Need to provide at least one full erase block\n"); + kmm_free(priv); return NULL; } diff --git a/include/nuttx/mtd/mtd.h b/include/nuttx/mtd/mtd.h index c7b820eccd..3039bf950a 100644 --- a/include/nuttx/mtd/mtd.h +++ b/include/nuttx/mtd/mtd.h @@ -671,6 +671,22 @@ void filemtd_teardown(FAR struct mtd_dev_s *dev); bool filemtd_isfilemtd(FAR struct mtd_dev_s *mtd); +/**************************************************************************** + * Name: nullmtd_initialize + * + * Description: + * Create and initialize a MTD null device instance. + * + * Input Parameters: + * mtdlen - Total length of a size in bytes of the MTD null device + * sectsize - Sector size of the MTD null device + * erasesize - Erase block size of the MTD null device + * + ****************************************************************************/ + +FAR struct mtd_dev_s *nullmtd_initialize(size_t mtdlen, int16_t sectsize, + int32_t erasesize); + #undef EXTERN #ifdef __cplusplus }