You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2021/12/05 09:11:14 UTC
[incubator-nuttx] branch master updated: drivers/mtd: add MTD null driver support
This is an automated email from the ASF dual-hosted git repository.
xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new e71b66c drivers/mtd: add MTD null driver support
e71b66c is described below
commit e71b66c792d64e8a6bcbf23c8160bb25e9253f18
Author: Petro Karashchenko <pe...@gmail.com>
AuthorDate: Sat Dec 4 14:48:15 2021 +0200
drivers/mtd: add MTD null driver support
- fix memory leak during RAM MTD initialization
- fix calculations for FILE MTD device with customized
block and erase sizes
Signed-off-by: Petro Karashchenko <pe...@gmail.com>
---
drivers/mtd/Kconfig | 27 ++-
drivers/mtd/Make.defs | 4 +
drivers/mtd/filemtd.c | 36 +++-
drivers/mtd/mtd_progmem.c | 1 -
drivers/mtd/nullmtd.c | 411 ++++++++++++++++++++++++++++++++++++++++++++++
drivers/mtd/rammtd.c | 23 ++-
include/nuttx/mtd/mtd.h | 16 ++
7 files changed, 502 insertions(+), 16 deletions(-)
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index e42e8d1..3c3d79d 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 32cdc7f..893d8d5 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 0cccae0..befcc55 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 bb78b0b..3ecb920 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 0000000..e8547e9
--- /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 <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/mtd/mtd.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+#ifndef CONFIG_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 c301490..0088a51 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 c7b820e..3039bf9 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
}