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
 }