You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by GitBox <gi...@apache.org> on 2022/09/01 02:06:24 UTC

[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a diff in pull request #6829: drivers/mtd:init commit of power-loss resilient cfg

xiaoxiang781216 commented on code in PR #6829:
URL: https://github.com/apache/incubator-nuttx/pull/6829#discussion_r959936676


##########
drivers/mtd/mtd_config_fs.c:
##########
@@ -0,0 +1,2484 @@
+/****************************************************************************
+ * drivers/mtd/mtd_config_fs.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.
+ *
+ * NVS: non volatile storage in flash
+ *
+ * Copyright (c) 2018 Laczen
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <sys/types.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <debug.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include <nuttx/crc8.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mtd/mtd.h>
+#include <nuttx/mtd/configdata.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define MIN(a, b)                       ((a) > (b) ? (b) : (a))
+
+/* MASKS AND SHIFT FOR ADDRESSES
+ * an address in nvs is an uint32_t where:
+ *   high 2 bytes represent the sector number
+ *   low 2 bytes represent the offset in a sector
+ */
+
+#define ADDR_SECT_MASK                  0xFFFF0000
+#define ADDR_SECT_SHIFT                 16
+#define ADDR_OFFS_MASK                  0x0000FFFF
+
+/* we don't want to store all the read content in stack or heap,
+ * so we make a buffer to do compare or move.
+ */
+
+#define NVS_BUFFER_SIZE                 32
+
+/* if data is written after last ate, and power loss happens,
+ * we need to find a clean offset by skipping dirty data.
+ * This macro defines how many bytes to skip when dirty data
+ * is spotted(may take several skips).
+ * Normally 1 byte is okay, such process only happens when
+ * nvs is started, and it is acceptable to take some time during
+ * starting.
+ */
+
+#define NVS_CORRUPT_DATA_SKIP_STEP      1
+
+#define NVS_LOOKUP_CACHE_NO_ADDR        0xffffffff
+
+/* gc done or close ate has the id of 0xffffffff.
+ * We can tell if the ate is special by looking at its id.
+ */
+
+#define NVS_SPECIAL_ATE_ID              0xffffffff
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Non-volatile Storage File system structure */
+
+struct nvs_fs
+{
+  FAR struct mtd_dev_s  *mtd;          /* mtd device */
+  uint32_t              ate_wra;       /* next alloc table entry
+                                        * write address
+                                        */
+  uint32_t              data_wra;      /* next data write address */
+  struct mtd_geometry_s geo;

Review Comment:
   move after line 94



##########
drivers/mtd/mtd_config_fs.c:
##########
@@ -0,0 +1,2484 @@
+/****************************************************************************
+ * drivers/mtd/mtd_config_fs.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.
+ *
+ * NVS: non volatile storage in flash
+ *
+ * Copyright (c) 2018 Laczen
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <sys/types.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <debug.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include <nuttx/crc8.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mtd/mtd.h>
+#include <nuttx/mtd/configdata.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define MIN(a, b)                       ((a) > (b) ? (b) : (a))
+
+/* MASKS AND SHIFT FOR ADDRESSES
+ * an address in nvs is an uint32_t where:
+ *   high 2 bytes represent the sector number
+ *   low 2 bytes represent the offset in a sector
+ */
+
+#define ADDR_SECT_MASK                  0xFFFF0000
+#define ADDR_SECT_SHIFT                 16
+#define ADDR_OFFS_MASK                  0x0000FFFF
+
+/* we don't want to store all the read content in stack or heap,
+ * so we make a buffer to do compare or move.
+ */
+
+#define NVS_BUFFER_SIZE                 32
+
+/* if data is written after last ate, and power loss happens,
+ * we need to find a clean offset by skipping dirty data.
+ * This macro defines how many bytes to skip when dirty data
+ * is spotted(may take several skips).
+ * Normally 1 byte is okay, such process only happens when
+ * nvs is started, and it is acceptable to take some time during
+ * starting.
+ */
+
+#define NVS_CORRUPT_DATA_SKIP_STEP      1
+
+#define NVS_LOOKUP_CACHE_NO_ADDR        0xffffffff
+
+/* gc done or close ate has the id of 0xffffffff.
+ * We can tell if the ate is special by looking at its id.
+ */
+
+#define NVS_SPECIAL_ATE_ID              0xffffffff

Review Comment:
   uthis id need same as erase value?



##########
drivers/mtd/mtd_config_fs.c:
##########
@@ -0,0 +1,2484 @@
+/****************************************************************************
+ * drivers/mtd/mtd_config_fs.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.
+ *
+ * NVS: non volatile storage in flash
+ *
+ * Copyright (c) 2018 Laczen
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <sys/types.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <debug.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include <nuttx/crc8.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mtd/mtd.h>
+#include <nuttx/mtd/configdata.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define MIN(a, b)                       ((a) > (b) ? (b) : (a))
+
+/* MASKS AND SHIFT FOR ADDRESSES
+ * an address in nvs is an uint32_t where:
+ *   high 2 bytes represent the sector number
+ *   low 2 bytes represent the offset in a sector
+ */
+
+#define ADDR_SECT_MASK                  0xFFFF0000

Review Comment:
   all SECT/sector to BLOCK/block?



##########
drivers/mtd/mtd_config_fs.c:
##########
@@ -0,0 +1,2484 @@
+/****************************************************************************
+ * drivers/mtd/mtd_config_fs.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.
+ *
+ * NVS: non volatile storage in flash
+ *
+ * Copyright (c) 2018 Laczen
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <sys/types.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <debug.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include <nuttx/crc8.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mtd/mtd.h>
+#include <nuttx/mtd/configdata.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define MIN(a, b)                       ((a) > (b) ? (b) : (a))
+
+/* MASKS AND SHIFT FOR ADDRESSES
+ * an address in nvs is an uint32_t where:
+ *   high 2 bytes represent the sector number
+ *   low 2 bytes represent the offset in a sector
+ */
+
+#define ADDR_SECT_MASK                  0xFFFF0000
+#define ADDR_SECT_SHIFT                 16
+#define ADDR_OFFS_MASK                  0x0000FFFF
+
+/* we don't want to store all the read content in stack or heap,
+ * so we make a buffer to do compare or move.
+ */
+
+#define NVS_BUFFER_SIZE                 32
+
+/* if data is written after last ate, and power loss happens,
+ * we need to find a clean offset by skipping dirty data.
+ * This macro defines how many bytes to skip when dirty data
+ * is spotted(may take several skips).
+ * Normally 1 byte is okay, such process only happens when
+ * nvs is started, and it is acceptable to take some time during
+ * starting.
+ */
+
+#define NVS_CORRUPT_DATA_SKIP_STEP      1
+
+#define NVS_LOOKUP_CACHE_NO_ADDR        0xffffffff
+
+/* gc done or close ate has the id of 0xffffffff.
+ * We can tell if the ate is special by looking at its id.
+ */
+
+#define NVS_SPECIAL_ATE_ID              0xffffffff
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Non-volatile Storage File system structure */
+
+struct nvs_fs
+{
+  FAR struct mtd_dev_s  *mtd;          /* mtd device */
+  uint32_t              ate_wra;       /* next alloc table entry
+                                        * write address
+                                        */
+  uint32_t              data_wra;      /* next data write address */
+  struct mtd_geometry_s geo;
+  uint32_t              step_addr;     /* for traverse */
+  mutex_t               nvs_lock;
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  uint32_t lookup_cache[CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE];
+#endif
+};
+
+/* Allocation Table Entry */
+
+begin_packed_struct struct nvs_ate
+{
+  uint32_t id;           /* data id */
+  uint16_t offset;       /* data offset within sector */
+  uint16_t len;          /* data len within sector */
+  uint16_t key_len;      /* key string len */
+  uint8_t  part;         /* part of a multipart data - future extension */
+  uint8_t  crc8;         /* crc8 check of the ate entry */
+  uint8_t  expired;      /* 0xFF-newest entry, others-old entry */
+  uint8_t  reserved[3];  /* for future extension */
+} end_packed_struct;
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* MTD NVS opeation api */
+
+static int     mtdconfig_open(FAR struct file *filep);
+static int     mtdconfig_close(FAR struct file *filep);
+static ssize_t mtdconfig_read(FAR struct file *filep, FAR char *buffer,
+                              size_t buflen);
+static int     mtdconfig_ioctl(FAR struct file *filep, int cmd,
+                               unsigned long arg);
+static int     mtdconfig_poll(FAR struct file *filep, FAR struct pollfd *fds,
+                              bool setup);
+
+/* Basic flash operation api */
+
+static ssize_t flash_read(FAR struct mtd_dev_s *mtd, off_t offset,
+                          FAR void *data, size_t len);
+static ssize_t flash_write(FAR struct mtd_dev_s *mtd, off_t offset,
+                           FAR const uint8_t *data, size_t len);
+static int flash_erase(FAR struct mtd_dev_s *mtd, off_t offset, size_t len);
+
+/* NVS operations needed by lookup cache */
+
+static int nvs_prev_ate(FAR struct nvs_fs *fs, FAR uint32_t *addr,
+                        FAR struct nvs_ate *ate);
+static int nvs_ate_valid(FAR struct nvs_fs *fs,
+                         FAR const struct nvs_ate *entry);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_mtdnvs_fops =
+{
+  mtdconfig_open,  /* open */
+  mtdconfig_close, /* close */
+  mtdconfig_read,  /* read */
+  NULL,            /* write */
+  NULL,            /* seek */
+  mtdconfig_ioctl, /* ioctl */
+  mtdconfig_poll   /* poll */
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+  , NULL           /* unlink */
+#endif
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nvs_fnv_hash
+ ****************************************************************************/
+
+static uint32_t nvs_fnv_hash(FAR const uint8_t *input, uint32_t len)
+{
+  uint32_t i = 0;
+  uint32_t hval = 2166136261;
+
+  /* FNV-1 hash each octet in the buffer */
+
+  while (i++ < len)
+    {
+      /* multiply by the 32 bit FNV magic prime mod 2^32 */
+
+      hval *= 0x01000193;
+
+      /* xor the bottom with the current octet */
+
+      hval ^= *input++;
+    }
+
+  return hval;
+}
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_pos
+ ****************************************************************************/
+
+static inline size_t nvs_lookup_cache_pos(uint32_t id)
+{
+  return id % CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_rebuild
+ ****************************************************************************/
+
+static int nvs_lookup_cache_rebuild(FAR struct nvs_fs *fs)
+{
+  int rc;
+  uint32_t addr;
+  uint32_t ate_addr;
+  FAR uint32_t *cache_entry;
+  struct nvs_ate ate;
+
+  memset(fs->lookup_cache, CONFIG_MTD_CONFIG_ERASEDVALUE,
+         sizeof(fs->lookup_cache));
+  addr = fs->ate_wra;
+
+  while (true)
+    {
+      /* Make a copy of 'addr' as it will be advanced by nvs_prev_ate() */
+
+      ate_addr = addr;
+      rc = nvs_prev_ate(fs, &addr, &ate);
+
+      if (rc)
+        {
+          return rc;
+        }
+
+      cache_entry = &fs->lookup_cache[nvs_lookup_cache_pos(ate.id)];
+
+      if (ate.id != NVS_SPECIAL_ATE_ID
+          && *cache_entry == NVS_LOOKUP_CACHE_NO_ADDR
+          && nvs_ate_valid(fs, &ate))
+        {
+          *cache_entry = ate_addr;
+        }
+
+      if (addr == fs->ate_wra)
+        {
+          break;
+        }
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_invalidate
+ ****************************************************************************/
+
+static void nvs_lookup_cache_invalidate(FAR struct nvs_fs *fs,
+                                        uint32_t sector)
+{
+  FAR uint32_t *cache_entry = fs->lookup_cache;
+  FAR uint32_t *const cache_end =
+    &fs->lookup_cache[CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE];
+
+  for (; cache_entry < cache_end; ++cache_entry)
+    {
+      if ((*cache_entry >> ADDR_SECT_SHIFT) == sector)
+        {
+          *cache_entry = NVS_LOOKUP_CACHE_NO_ADDR;
+        }
+    }
+}
+
+#endif /* CONFIG_MTD_NVS_LOOKUP_CACHE */
+
+/****************************************************************************
+ * Name: flash_read
+ ****************************************************************************/
+
+static ssize_t flash_read(FAR struct mtd_dev_s *mtd, off_t offset,
+                          FAR void *data, size_t len)
+{
+  return MTD_READ(mtd, offset, len, data);
+}
+
+/****************************************************************************
+ * Name: flash_write
+ ****************************************************************************/
+
+static ssize_t flash_write(FAR struct mtd_dev_s *mtd, off_t offset,
+                           FAR const uint8_t *data, size_t len)
+{
+  return MTD_WRITE(mtd, offset, len, data);
+}
+
+/****************************************************************************
+ * Name: flash_erase
+ ****************************************************************************/
+
+static int flash_erase(FAR struct mtd_dev_s *mtd, off_t startblock,
+                       size_t nblocks)
+{
+  return MTD_ERASE(mtd, startblock, nblocks);
+}
+
+/****************************************************************************
+ * Name: nvs_flash_wrt
+ *
+ * Description:
+ *   flash routines, process offset then write.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_wrt(FAR struct nvs_fs *fs, uint32_t addr,
+                            FAR const void *data, size_t len)
+{
+  off_t offset;
+  int ret;
+
+  offset = fs->geo.erasesize * (addr >> ADDR_SECT_SHIFT);
+  offset += addr & ADDR_OFFS_MASK;
+
+  ret = flash_write(fs->mtd, offset, data, len);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_rd
+ *
+ * Description:
+ *   basic flash read from nvs address.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_rd(FAR struct nvs_fs *fs, uint32_t addr,
+                        FAR void *data, size_t len)
+{
+  off_t offset;
+  int ret;
+
+  offset = fs->geo.erasesize * (addr >> ADDR_SECT_SHIFT);
+  offset += addr & ADDR_OFFS_MASK;
+
+  ret = flash_read(fs->mtd, offset, data, len);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_ate_wrt
+ *
+ * Description:
+ *   allocation entry write.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_ate_wrt(FAR struct nvs_fs *fs,
+                             FAR const struct nvs_ate *entry)
+{
+  int rc;
+
+  rc = nvs_flash_wrt(fs, fs->ate_wra, entry, sizeof(struct nvs_ate));
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  /* 0xffffffff is a special-purpose identifier. Exclude it from the cache */
+
+  if (entry->id != NVS_SPECIAL_ATE_ID)
+    {
+      fs->lookup_cache[nvs_lookup_cache_pos(entry->id)] = fs->ate_wra;
+    }
+#endif
+
+  fs->ate_wra -= sizeof(struct nvs_ate);
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_data_wrt
+ ****************************************************************************/
+
+static int nvs_flash_data_wrt(FAR struct nvs_fs *fs,
+                              FAR const void *data, size_t len)
+{
+  int rc;
+
+  rc = nvs_flash_wrt(fs, fs->data_wra, data, len);
+  fs->data_wra += len;
+  finfo("write data done, data_wra=0x%lx\n",
+        fs->data_wra);
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_ate_rd
+ ****************************************************************************/
+
+static int nvs_flash_ate_rd(FAR struct nvs_fs *fs, uint32_t addr,
+                            FAR struct nvs_ate *entry)
+{
+  return nvs_flash_rd(fs, addr, entry, sizeof(struct nvs_ate));
+}
+
+/****************************************************************************
+ * Name: nvs_flash_block_cmp
+ *
+ * Description:
+ *   nvs_flash_block_cmp compares the data in flash at addr to data
+ *   in blocks of size NVS_BLOCK_SIZE aligned to fs->write_block_size
+ *   returns 0 if equal, 1 if not equal, errcode if error
+ *
+ ****************************************************************************/
+
+static int nvs_flash_block_cmp(FAR struct nvs_fs *fs, uint32_t addr,
+                               FAR const void *data, size_t len)
+{
+  FAR const uint8_t *data8 = (FAR const uint8_t *)data;
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t buf[NVS_BUFFER_SIZE];
+
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr, buf, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = memcmp(data8, buf, bytes_to_cmp);
+      if (rc)
+        {
+          return 1;
+        }
+
+      len -= bytes_to_cmp;
+      addr += bytes_to_cmp;
+      data8 += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_direct_cmp
+ *
+ * Description:
+ *   nvs_flash_direct_cmp compares the data in flash at addr1 and addr2
+ *   of len in blocks of size NVS_BLOCK_SIZE aligned to fs->write_block_size
+ *   returns 0 if equal, 1 if not equal, errcode if error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_direct_cmp(FAR struct nvs_fs *fs, uint32_t addr1,
+                                uint32_t addr2, size_t len)
+{
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t buf1[NVS_BUFFER_SIZE];
+  uint8_t buf2[NVS_BUFFER_SIZE];
+
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr1, buf1, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = nvs_flash_rd(fs, addr2, buf2, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = memcmp(buf1, buf2, bytes_to_cmp);
+      if (rc)
+        {
+          return 1;
+        }
+
+      len -= bytes_to_cmp;
+      addr1 += bytes_to_cmp;
+      addr2 += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_cmp_const
+ *
+ * Description:
+ *   nvs_flash_cmp_const compares the data in flash at addr to a constant
+ *   value. returns 0 if all data in flash is equal to value, 1 if not equal,
+ *   errcode if error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_cmp_const(FAR struct nvs_fs *fs, uint32_t addr,
+                               uint8_t value, size_t len)
+{
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t cmp[NVS_BUFFER_SIZE];
+
+  memset(cmp, value, NVS_BUFFER_SIZE);
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_block_cmp(fs, addr, cmp, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      len -= bytes_to_cmp;
+      addr += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_block_move
+ *
+ * Description:
+ *   flash block move: move a block at addr to the current data write
+ *   location and updates the data write location.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_block_move(FAR struct nvs_fs *fs, uint32_t addr,
+                                size_t len)
+{
+  int rc;
+  size_t bytes_to_copy;
+  uint8_t buf[NVS_BUFFER_SIZE];
+
+  while (len)
+    {
+      bytes_to_copy = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr, buf, bytes_to_copy);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = nvs_flash_data_wrt(fs, buf, bytes_to_copy);
+      if (rc)
+        {
+          return rc;
+        }
+
+      len -= bytes_to_copy;
+      addr += bytes_to_copy;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_erase_sector
+ *
+ * Description:
+ *   erase a sector by first checking it is used and then erasing if required
+ *   return 0 if OK, errorcode on error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_erase_sector(FAR struct nvs_fs *fs, uint32_t addr)
+{
+  int rc;
+
+  ferr("Erasing addr %lx\n", addr);
+  addr &= ADDR_SECT_MASK;
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  nvs_lookup_cache_invalidate(fs, addr >> ADDR_SECT_SHIFT);
+#endif
+  rc = flash_erase(fs->mtd, addr >> ADDR_SECT_SHIFT, 1);
+
+  if (rc < 0)
+    {
+      ferr("Erasing failed %d\n", rc);
+      return rc;
+    }
+  else
+    {
+      rc = OK;
+    }
+
+  if (nvs_flash_cmp_const(fs, addr, CONFIG_MTD_CONFIG_ERASEDVALUE,
+                          fs->geo.erasesize))
+    {
+      ferr("Erasing not complete\n");
+      rc = -ENXIO;
+    }
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_crc8_update
+ *
+ * Description:
+ *   crc update on allocation entry.
+ *
+ ****************************************************************************/
+
+static void nvs_ate_crc8_update(FAR struct nvs_ate *entry)
+{
+  uint8_t ate_crc;
+
+  ate_crc = crc8part((FAR const uint8_t *)entry,
+                     offsetof(struct nvs_ate, crc8), 0xff);
+  entry->crc8 = ate_crc;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_crc8_check
+ *
+ * Description:
+ *   crc check on allocation entry
+ *   returns 0 if OK, 1 on crc fail
+ *
+ ****************************************************************************/
+
+static int nvs_ate_crc8_check(FAR const struct nvs_ate *entry)
+{
+  uint8_t ate_crc;
+
+  ate_crc = crc8part((FAR const uint8_t *)entry,
+                     offsetof(struct nvs_ate, crc8), 0xff);
+  if (ate_crc == entry->crc8)
+    {
+      return 0;
+    }
+
+  return 1;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_cmp_const
+ *
+ * Description:
+ *   nvs_ate_cmp_const compares an ATE to a constant value. returns 0 if
+ *   the whole ATE is equal to value, 1 if not equal.
+ *
+ ****************************************************************************/
+
+static int nvs_ate_cmp_const(FAR const struct nvs_ate *entry,
+                             uint8_t value)
+{
+  FAR const uint8_t *data8 = (FAR const uint8_t *)entry;
+  int i;
+
+  for (i = 0; i < sizeof(struct nvs_ate); i++)
+    {
+      if (data8[i] != value)
+        {
+          return 1;
+        }
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_valid
+ *
+ * Description:
+ *   return 1 if crc8 and offset valid, 0 otherwise
+ *
+ ****************************************************************************/
+
+static int nvs_ate_valid(FAR struct nvs_fs *fs,
+                         FAR const struct nvs_ate *entry)
+{
+  if ((nvs_ate_crc8_check(entry)) ||
+      (entry->offset >= (fs->geo.erasesize - sizeof(struct nvs_ate))))

Review Comment:
   ```suggestion
         (entry->offset > (fs->geo.erasesize - sizeof(struct nvs_ate))))
   ```



##########
drivers/mtd/mtd_config_fs.c:
##########
@@ -0,0 +1,2484 @@
+/****************************************************************************
+ * drivers/mtd/mtd_config_fs.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.
+ *
+ * NVS: non volatile storage in flash
+ *
+ * Copyright (c) 2018 Laczen
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <sys/types.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <debug.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include <nuttx/crc8.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mtd/mtd.h>
+#include <nuttx/mtd/configdata.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define MIN(a, b)                       ((a) > (b) ? (b) : (a))
+
+/* MASKS AND SHIFT FOR ADDRESSES
+ * an address in nvs is an uint32_t where:
+ *   high 2 bytes represent the sector number
+ *   low 2 bytes represent the offset in a sector
+ */
+
+#define ADDR_SECT_MASK                  0xFFFF0000
+#define ADDR_SECT_SHIFT                 16
+#define ADDR_OFFS_MASK                  0x0000FFFF
+
+/* we don't want to store all the read content in stack or heap,
+ * so we make a buffer to do compare or move.
+ */
+
+#define NVS_BUFFER_SIZE                 32
+
+/* if data is written after last ate, and power loss happens,
+ * we need to find a clean offset by skipping dirty data.
+ * This macro defines how many bytes to skip when dirty data
+ * is spotted(may take several skips).
+ * Normally 1 byte is okay, such process only happens when
+ * nvs is started, and it is acceptable to take some time during
+ * starting.
+ */
+
+#define NVS_CORRUPT_DATA_SKIP_STEP      1
+
+#define NVS_LOOKUP_CACHE_NO_ADDR        0xffffffff
+
+/* gc done or close ate has the id of 0xffffffff.
+ * We can tell if the ate is special by looking at its id.
+ */
+
+#define NVS_SPECIAL_ATE_ID              0xffffffff
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Non-volatile Storage File system structure */
+
+struct nvs_fs
+{
+  FAR struct mtd_dev_s  *mtd;          /* mtd device */
+  uint32_t              ate_wra;       /* next alloc table entry
+                                        * write address
+                                        */
+  uint32_t              data_wra;      /* next data write address */
+  struct mtd_geometry_s geo;
+  uint32_t              step_addr;     /* for traverse */
+  mutex_t               nvs_lock;
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  uint32_t lookup_cache[CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE];
+#endif
+};
+
+/* Allocation Table Entry */
+
+begin_packed_struct struct nvs_ate
+{
+  uint32_t id;           /* data id */
+  uint16_t offset;       /* data offset within sector */
+  uint16_t len;          /* data len within sector */
+  uint16_t key_len;      /* key string len */
+  uint8_t  part;         /* part of a multipart data - future extension */
+  uint8_t  crc8;         /* crc8 check of the ate entry */
+  uint8_t  expired;      /* 0xFF-newest entry, others-old entry */
+  uint8_t  reserved[3];  /* for future extension */
+} end_packed_struct;
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* MTD NVS opeation api */
+
+static int     mtdconfig_open(FAR struct file *filep);
+static int     mtdconfig_close(FAR struct file *filep);
+static ssize_t mtdconfig_read(FAR struct file *filep, FAR char *buffer,
+                              size_t buflen);
+static int     mtdconfig_ioctl(FAR struct file *filep, int cmd,
+                               unsigned long arg);
+static int     mtdconfig_poll(FAR struct file *filep, FAR struct pollfd *fds,
+                              bool setup);
+
+/* Basic flash operation api */
+
+static ssize_t flash_read(FAR struct mtd_dev_s *mtd, off_t offset,
+                          FAR void *data, size_t len);
+static ssize_t flash_write(FAR struct mtd_dev_s *mtd, off_t offset,
+                           FAR const uint8_t *data, size_t len);
+static int flash_erase(FAR struct mtd_dev_s *mtd, off_t offset, size_t len);
+
+/* NVS operations needed by lookup cache */
+
+static int nvs_prev_ate(FAR struct nvs_fs *fs, FAR uint32_t *addr,
+                        FAR struct nvs_ate *ate);
+static int nvs_ate_valid(FAR struct nvs_fs *fs,
+                         FAR const struct nvs_ate *entry);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_mtdnvs_fops =
+{
+  mtdconfig_open,  /* open */
+  mtdconfig_close, /* close */
+  mtdconfig_read,  /* read */
+  NULL,            /* write */
+  NULL,            /* seek */
+  mtdconfig_ioctl, /* ioctl */
+  mtdconfig_poll   /* poll */
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+  , NULL           /* unlink */
+#endif
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nvs_fnv_hash
+ ****************************************************************************/
+
+static uint32_t nvs_fnv_hash(FAR const uint8_t *input, uint32_t len)
+{
+  uint32_t i = 0;
+  uint32_t hval = 2166136261;
+
+  /* FNV-1 hash each octet in the buffer */
+
+  while (i++ < len)
+    {
+      /* multiply by the 32 bit FNV magic prime mod 2^32 */
+
+      hval *= 0x01000193;
+
+      /* xor the bottom with the current octet */
+
+      hval ^= *input++;
+    }
+
+  return hval;
+}
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_pos
+ ****************************************************************************/
+
+static inline size_t nvs_lookup_cache_pos(uint32_t id)
+{
+  return id % CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_rebuild
+ ****************************************************************************/
+
+static int nvs_lookup_cache_rebuild(FAR struct nvs_fs *fs)
+{
+  int rc;
+  uint32_t addr;
+  uint32_t ate_addr;
+  FAR uint32_t *cache_entry;
+  struct nvs_ate ate;
+
+  memset(fs->lookup_cache, CONFIG_MTD_CONFIG_ERASEDVALUE,
+         sizeof(fs->lookup_cache));
+  addr = fs->ate_wra;
+
+  while (true)
+    {
+      /* Make a copy of 'addr' as it will be advanced by nvs_prev_ate() */
+
+      ate_addr = addr;
+      rc = nvs_prev_ate(fs, &addr, &ate);
+
+      if (rc)
+        {
+          return rc;
+        }
+
+      cache_entry = &fs->lookup_cache[nvs_lookup_cache_pos(ate.id)];
+
+      if (ate.id != NVS_SPECIAL_ATE_ID
+          && *cache_entry == NVS_LOOKUP_CACHE_NO_ADDR
+          && nvs_ate_valid(fs, &ate))
+        {
+          *cache_entry = ate_addr;
+        }
+
+      if (addr == fs->ate_wra)
+        {
+          break;
+        }
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_invalidate
+ ****************************************************************************/
+
+static void nvs_lookup_cache_invalidate(FAR struct nvs_fs *fs,
+                                        uint32_t sector)
+{
+  FAR uint32_t *cache_entry = fs->lookup_cache;
+  FAR uint32_t *const cache_end =
+    &fs->lookup_cache[CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE];
+
+  for (; cache_entry < cache_end; ++cache_entry)
+    {
+      if ((*cache_entry >> ADDR_SECT_SHIFT) == sector)
+        {
+          *cache_entry = NVS_LOOKUP_CACHE_NO_ADDR;
+        }
+    }
+}
+
+#endif /* CONFIG_MTD_NVS_LOOKUP_CACHE */
+
+/****************************************************************************
+ * Name: flash_read
+ ****************************************************************************/
+
+static ssize_t flash_read(FAR struct mtd_dev_s *mtd, off_t offset,
+                          FAR void *data, size_t len)
+{
+  return MTD_READ(mtd, offset, len, data);
+}
+
+/****************************************************************************
+ * Name: flash_write
+ ****************************************************************************/
+
+static ssize_t flash_write(FAR struct mtd_dev_s *mtd, off_t offset,
+                           FAR const uint8_t *data, size_t len)
+{
+  return MTD_WRITE(mtd, offset, len, data);
+}
+
+/****************************************************************************
+ * Name: flash_erase
+ ****************************************************************************/
+
+static int flash_erase(FAR struct mtd_dev_s *mtd, off_t startblock,
+                       size_t nblocks)
+{
+  return MTD_ERASE(mtd, startblock, nblocks);
+}
+
+/****************************************************************************
+ * Name: nvs_flash_wrt
+ *
+ * Description:
+ *   flash routines, process offset then write.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_wrt(FAR struct nvs_fs *fs, uint32_t addr,
+                            FAR const void *data, size_t len)
+{
+  off_t offset;
+  int ret;
+
+  offset = fs->geo.erasesize * (addr >> ADDR_SECT_SHIFT);
+  offset += addr & ADDR_OFFS_MASK;
+
+  ret = flash_write(fs->mtd, offset, data, len);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_rd
+ *
+ * Description:
+ *   basic flash read from nvs address.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_rd(FAR struct nvs_fs *fs, uint32_t addr,
+                        FAR void *data, size_t len)
+{
+  off_t offset;
+  int ret;
+
+  offset = fs->geo.erasesize * (addr >> ADDR_SECT_SHIFT);
+  offset += addr & ADDR_OFFS_MASK;
+
+  ret = flash_read(fs->mtd, offset, data, len);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_ate_wrt
+ *
+ * Description:
+ *   allocation entry write.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_ate_wrt(FAR struct nvs_fs *fs,
+                             FAR const struct nvs_ate *entry)
+{
+  int rc;
+
+  rc = nvs_flash_wrt(fs, fs->ate_wra, entry, sizeof(struct nvs_ate));
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  /* 0xffffffff is a special-purpose identifier. Exclude it from the cache */
+
+  if (entry->id != NVS_SPECIAL_ATE_ID)
+    {
+      fs->lookup_cache[nvs_lookup_cache_pos(entry->id)] = fs->ate_wra;
+    }
+#endif
+
+  fs->ate_wra -= sizeof(struct nvs_ate);
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_data_wrt
+ ****************************************************************************/
+
+static int nvs_flash_data_wrt(FAR struct nvs_fs *fs,
+                              FAR const void *data, size_t len)
+{
+  int rc;
+
+  rc = nvs_flash_wrt(fs, fs->data_wra, data, len);
+  fs->data_wra += len;
+  finfo("write data done, data_wra=0x%lx\n",
+        fs->data_wra);
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_ate_rd
+ ****************************************************************************/
+
+static int nvs_flash_ate_rd(FAR struct nvs_fs *fs, uint32_t addr,
+                            FAR struct nvs_ate *entry)
+{
+  return nvs_flash_rd(fs, addr, entry, sizeof(struct nvs_ate));
+}
+
+/****************************************************************************
+ * Name: nvs_flash_block_cmp
+ *
+ * Description:
+ *   nvs_flash_block_cmp compares the data in flash at addr to data
+ *   in blocks of size NVS_BLOCK_SIZE aligned to fs->write_block_size
+ *   returns 0 if equal, 1 if not equal, errcode if error
+ *
+ ****************************************************************************/
+
+static int nvs_flash_block_cmp(FAR struct nvs_fs *fs, uint32_t addr,
+                               FAR const void *data, size_t len)
+{
+  FAR const uint8_t *data8 = (FAR const uint8_t *)data;
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t buf[NVS_BUFFER_SIZE];
+
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr, buf, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = memcmp(data8, buf, bytes_to_cmp);
+      if (rc)
+        {
+          return 1;
+        }
+
+      len -= bytes_to_cmp;
+      addr += bytes_to_cmp;
+      data8 += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_direct_cmp
+ *
+ * Description:
+ *   nvs_flash_direct_cmp compares the data in flash at addr1 and addr2
+ *   of len in blocks of size NVS_BLOCK_SIZE aligned to fs->write_block_size
+ *   returns 0 if equal, 1 if not equal, errcode if error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_direct_cmp(FAR struct nvs_fs *fs, uint32_t addr1,
+                                uint32_t addr2, size_t len)
+{
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t buf1[NVS_BUFFER_SIZE];
+  uint8_t buf2[NVS_BUFFER_SIZE];
+
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr1, buf1, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = nvs_flash_rd(fs, addr2, buf2, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = memcmp(buf1, buf2, bytes_to_cmp);
+      if (rc)
+        {
+          return 1;
+        }
+
+      len -= bytes_to_cmp;
+      addr1 += bytes_to_cmp;
+      addr2 += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_cmp_const
+ *
+ * Description:
+ *   nvs_flash_cmp_const compares the data in flash at addr to a constant
+ *   value. returns 0 if all data in flash is equal to value, 1 if not equal,
+ *   errcode if error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_cmp_const(FAR struct nvs_fs *fs, uint32_t addr,
+                               uint8_t value, size_t len)
+{
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t cmp[NVS_BUFFER_SIZE];
+
+  memset(cmp, value, NVS_BUFFER_SIZE);
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_block_cmp(fs, addr, cmp, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      len -= bytes_to_cmp;
+      addr += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_block_move
+ *
+ * Description:
+ *   flash block move: move a block at addr to the current data write
+ *   location and updates the data write location.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_block_move(FAR struct nvs_fs *fs, uint32_t addr,
+                                size_t len)
+{
+  int rc;
+  size_t bytes_to_copy;
+  uint8_t buf[NVS_BUFFER_SIZE];
+
+  while (len)
+    {
+      bytes_to_copy = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr, buf, bytes_to_copy);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = nvs_flash_data_wrt(fs, buf, bytes_to_copy);
+      if (rc)
+        {
+          return rc;
+        }
+
+      len -= bytes_to_copy;
+      addr += bytes_to_copy;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_erase_sector
+ *
+ * Description:
+ *   erase a sector by first checking it is used and then erasing if required
+ *   return 0 if OK, errorcode on error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_erase_sector(FAR struct nvs_fs *fs, uint32_t addr)
+{
+  int rc;
+
+  ferr("Erasing addr %lx\n", addr);
+  addr &= ADDR_SECT_MASK;
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  nvs_lookup_cache_invalidate(fs, addr >> ADDR_SECT_SHIFT);
+#endif
+  rc = flash_erase(fs->mtd, addr >> ADDR_SECT_SHIFT, 1);
+
+  if (rc < 0)
+    {
+      ferr("Erasing failed %d\n", rc);
+      return rc;
+    }
+  else
+    {
+      rc = OK;
+    }
+
+  if (nvs_flash_cmp_const(fs, addr, CONFIG_MTD_CONFIG_ERASEDVALUE,
+                          fs->geo.erasesize))
+    {
+      ferr("Erasing not complete\n");
+      rc = -ENXIO;
+    }
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_crc8_update
+ *
+ * Description:
+ *   crc update on allocation entry.
+ *
+ ****************************************************************************/
+
+static void nvs_ate_crc8_update(FAR struct nvs_ate *entry)
+{
+  uint8_t ate_crc;
+
+  ate_crc = crc8part((FAR const uint8_t *)entry,
+                     offsetof(struct nvs_ate, crc8), 0xff);
+  entry->crc8 = ate_crc;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_crc8_check
+ *
+ * Description:
+ *   crc check on allocation entry
+ *   returns 0 if OK, 1 on crc fail
+ *
+ ****************************************************************************/
+
+static int nvs_ate_crc8_check(FAR const struct nvs_ate *entry)
+{
+  uint8_t ate_crc;
+
+  ate_crc = crc8part((FAR const uint8_t *)entry,
+                     offsetof(struct nvs_ate, crc8), 0xff);
+  if (ate_crc == entry->crc8)
+    {
+      return 0;
+    }
+
+  return 1;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_cmp_const
+ *
+ * Description:
+ *   nvs_ate_cmp_const compares an ATE to a constant value. returns 0 if
+ *   the whole ATE is equal to value, 1 if not equal.
+ *
+ ****************************************************************************/
+
+static int nvs_ate_cmp_const(FAR const struct nvs_ate *entry,
+                             uint8_t value)
+{
+  FAR const uint8_t *data8 = (FAR const uint8_t *)entry;
+  int i;
+
+  for (i = 0; i < sizeof(struct nvs_ate); i++)
+    {
+      if (data8[i] != value)
+        {
+          return 1;
+        }
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_valid
+ *
+ * Description:
+ *   return 1 if crc8 and offset valid, 0 otherwise
+ *
+ ****************************************************************************/
+
+static int nvs_ate_valid(FAR struct nvs_fs *fs,
+                         FAR const struct nvs_ate *entry)
+{
+  if ((nvs_ate_crc8_check(entry)) ||
+      (entry->offset >= (fs->geo.erasesize - sizeof(struct nvs_ate))))
+    {
+      return 0;
+    }
+
+  return 1;
+}
+
+/****************************************************************************
+ * Name: nvs_close_ate_valid
+ *
+ * Description:
+ *   nvs_close_ate_valid validates an sector close ate:
+ *   a valid sector close ate:
+ *   - valid ate
+ *   - len = 0 and id = 0xFFFFFFFF
+ *   - offset points to location at ate multiple from sector size
+ *   return 1 if valid, 0 otherwise
+ *
+ ****************************************************************************/
+
+static int nvs_close_ate_valid(FAR struct nvs_fs *fs,
+                               FAR const struct nvs_ate *entry)
+{
+  if ((!nvs_ate_valid(fs, entry)) || (entry->len != 0U) ||
+      (entry->id != NVS_SPECIAL_ATE_ID))
+    {
+      return 0;
+    }
+
+  if ((fs->geo.erasesize - entry->offset) % sizeof(struct nvs_ate))
+    {
+      return 0;
+    }
+
+  return 1;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_wrt_entry
+ *
+ * Description:
+ *   store an entry in flash
+ *
+ ****************************************************************************/
+
+static int nvs_flash_wrt_entry(FAR struct nvs_fs *fs, uint32_t id,
+            FAR const uint8_t *key, size_t key_size, FAR const void *data,
+            size_t len)
+{
+  int rc;
+  struct nvs_ate entry;
+
+  entry.id = id;
+  entry.offset = (uint16_t)(fs->data_wra & ADDR_OFFS_MASK);
+  entry.len = (uint16_t)len;
+  entry.key_len = (uint16_t)key_size;
+  entry.part = 0xff;
+
+  nvs_ate_crc8_update(&entry);
+
+  entry.expired = 0xff;
+  entry.reserved[0] = 0xff;
+  entry.reserved[1] = 0xff;
+  entry.reserved[2] = 0xff;
+
+  /* let's sew key and data into one, key comes first, then data */
+
+  rc = nvs_flash_data_wrt(fs, key, key_size);
+  if (rc)
+    {
+      ferr("Write key failed, rc=%d\n", rc);
+      return rc;
+    }
+
+  rc = nvs_flash_data_wrt(fs, data, len);
+  if (rc)
+    {
+      ferr("Write value failed, rc=%d\n", rc);
+      return rc;
+    }
+
+  rc = nvs_flash_ate_wrt(fs, &entry);
+  if (rc)
+    {
+      ferr("Write ate failed, rc=%d\n", rc);
+      return rc;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_recover_last_ate
+ *
+ * Description:
+ *   If the closing ate is invalid, its offset cannot be trusted and
+ *   the last valid ate of the sector should instead try to be recovered
+ *   by going through all ate's.
+ *
+ *   addr should point to the faulty closing ate and will be updated to
+ *   the last valid ate. If no valid ate is found it will be left untouched.
+ *
+ ****************************************************************************/
+
+static int nvs_recover_last_ate(FAR struct nvs_fs *fs,
+                                FAR uint32_t *addr)
+{
+  uint32_t data_end_addr;
+  uint32_t ate_end_addr;
+  struct nvs_ate end_ate;
+  int rc;
+
+  finfo("Recovering last ate from sector %" PRIu32 "\n",
+        (*addr >> ADDR_SECT_SHIFT));
+
+  *addr -= sizeof(struct nvs_ate);
+  ate_end_addr = *addr;
+  data_end_addr = *addr & ADDR_SECT_MASK;
+  while (ate_end_addr > data_end_addr)
+    {
+      rc = nvs_flash_ate_rd(fs, ate_end_addr, &end_ate);
+      if (rc)
+        {
+          return rc;
+        }
+
+      if (nvs_ate_valid(fs, &end_ate))
+        {
+          /* Found a valid ate, update data_end_addr and *addr */
+
+          data_end_addr &= ADDR_SECT_MASK;
+          data_end_addr += end_ate.offset + end_ate.len + end_ate.key_len;

Review Comment:
   ```suggestion
             data_end_addr += end_ate.offset + end_ate.key_len + end_ate.len;
   ```



##########
drivers/mtd/mtd_config_fs.c:
##########
@@ -0,0 +1,2484 @@
+/****************************************************************************
+ * drivers/mtd/mtd_config_fs.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.
+ *
+ * NVS: non volatile storage in flash
+ *
+ * Copyright (c) 2018 Laczen
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <sys/types.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <debug.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include <nuttx/crc8.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mtd/mtd.h>
+#include <nuttx/mtd/configdata.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define MIN(a, b)                       ((a) > (b) ? (b) : (a))
+
+/* MASKS AND SHIFT FOR ADDRESSES
+ * an address in nvs is an uint32_t where:
+ *   high 2 bytes represent the sector number
+ *   low 2 bytes represent the offset in a sector
+ */
+
+#define ADDR_SECT_MASK                  0xFFFF0000
+#define ADDR_SECT_SHIFT                 16
+#define ADDR_OFFS_MASK                  0x0000FFFF
+
+/* we don't want to store all the read content in stack or heap,
+ * so we make a buffer to do compare or move.
+ */
+
+#define NVS_BUFFER_SIZE                 32
+
+/* if data is written after last ate, and power loss happens,
+ * we need to find a clean offset by skipping dirty data.
+ * This macro defines how many bytes to skip when dirty data
+ * is spotted(may take several skips).
+ * Normally 1 byte is okay, such process only happens when
+ * nvs is started, and it is acceptable to take some time during
+ * starting.
+ */
+
+#define NVS_CORRUPT_DATA_SKIP_STEP      1
+
+#define NVS_LOOKUP_CACHE_NO_ADDR        0xffffffff
+
+/* gc done or close ate has the id of 0xffffffff.
+ * We can tell if the ate is special by looking at its id.
+ */
+
+#define NVS_SPECIAL_ATE_ID              0xffffffff
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Non-volatile Storage File system structure */
+
+struct nvs_fs
+{
+  FAR struct mtd_dev_s  *mtd;          /* mtd device */
+  uint32_t              ate_wra;       /* next alloc table entry
+                                        * write address
+                                        */
+  uint32_t              data_wra;      /* next data write address */
+  struct mtd_geometry_s geo;
+  uint32_t              step_addr;     /* for traverse */
+  mutex_t               nvs_lock;
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  uint32_t lookup_cache[CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE];
+#endif
+};
+
+/* Allocation Table Entry */
+
+begin_packed_struct struct nvs_ate
+{
+  uint32_t id;           /* data id */
+  uint16_t offset;       /* data offset within sector */
+  uint16_t len;          /* data len within sector */
+  uint16_t key_len;      /* key string len */
+  uint8_t  part;         /* part of a multipart data - future extension */
+  uint8_t  crc8;         /* crc8 check of the ate entry */
+  uint8_t  expired;      /* 0xFF-newest entry, others-old entry */
+  uint8_t  reserved[3];  /* for future extension */
+} end_packed_struct;
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* MTD NVS opeation api */
+
+static int     mtdconfig_open(FAR struct file *filep);
+static int     mtdconfig_close(FAR struct file *filep);
+static ssize_t mtdconfig_read(FAR struct file *filep, FAR char *buffer,
+                              size_t buflen);
+static int     mtdconfig_ioctl(FAR struct file *filep, int cmd,
+                               unsigned long arg);
+static int     mtdconfig_poll(FAR struct file *filep, FAR struct pollfd *fds,
+                              bool setup);
+
+/* Basic flash operation api */
+
+static ssize_t flash_read(FAR struct mtd_dev_s *mtd, off_t offset,
+                          FAR void *data, size_t len);
+static ssize_t flash_write(FAR struct mtd_dev_s *mtd, off_t offset,
+                           FAR const uint8_t *data, size_t len);
+static int flash_erase(FAR struct mtd_dev_s *mtd, off_t offset, size_t len);
+
+/* NVS operations needed by lookup cache */
+
+static int nvs_prev_ate(FAR struct nvs_fs *fs, FAR uint32_t *addr,
+                        FAR struct nvs_ate *ate);
+static int nvs_ate_valid(FAR struct nvs_fs *fs,
+                         FAR const struct nvs_ate *entry);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_mtdnvs_fops =
+{
+  mtdconfig_open,  /* open */
+  mtdconfig_close, /* close */
+  mtdconfig_read,  /* read */
+  NULL,            /* write */
+  NULL,            /* seek */
+  mtdconfig_ioctl, /* ioctl */
+  mtdconfig_poll   /* poll */
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+  , NULL           /* unlink */
+#endif
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nvs_fnv_hash
+ ****************************************************************************/
+
+static uint32_t nvs_fnv_hash(FAR const uint8_t *input, uint32_t len)
+{
+  uint32_t i = 0;
+  uint32_t hval = 2166136261;
+
+  /* FNV-1 hash each octet in the buffer */
+
+  while (i++ < len)
+    {
+      /* multiply by the 32 bit FNV magic prime mod 2^32 */
+
+      hval *= 0x01000193;
+
+      /* xor the bottom with the current octet */
+
+      hval ^= *input++;
+    }
+
+  return hval;
+}
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_pos
+ ****************************************************************************/
+
+static inline size_t nvs_lookup_cache_pos(uint32_t id)
+{
+  return id % CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_rebuild
+ ****************************************************************************/
+
+static int nvs_lookup_cache_rebuild(FAR struct nvs_fs *fs)
+{
+  int rc;
+  uint32_t addr;
+  uint32_t ate_addr;
+  FAR uint32_t *cache_entry;
+  struct nvs_ate ate;
+
+  memset(fs->lookup_cache, CONFIG_MTD_CONFIG_ERASEDVALUE,
+         sizeof(fs->lookup_cache));
+  addr = fs->ate_wra;
+
+  while (true)
+    {
+      /* Make a copy of 'addr' as it will be advanced by nvs_prev_ate() */
+
+      ate_addr = addr;
+      rc = nvs_prev_ate(fs, &addr, &ate);
+
+      if (rc)
+        {
+          return rc;
+        }
+
+      cache_entry = &fs->lookup_cache[nvs_lookup_cache_pos(ate.id)];
+
+      if (ate.id != NVS_SPECIAL_ATE_ID
+          && *cache_entry == NVS_LOOKUP_CACHE_NO_ADDR
+          && nvs_ate_valid(fs, &ate))
+        {
+          *cache_entry = ate_addr;
+        }
+
+      if (addr == fs->ate_wra)
+        {
+          break;
+        }
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_invalidate
+ ****************************************************************************/
+
+static void nvs_lookup_cache_invalidate(FAR struct nvs_fs *fs,
+                                        uint32_t sector)
+{
+  FAR uint32_t *cache_entry = fs->lookup_cache;
+  FAR uint32_t *const cache_end =
+    &fs->lookup_cache[CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE];
+
+  for (; cache_entry < cache_end; ++cache_entry)
+    {
+      if ((*cache_entry >> ADDR_SECT_SHIFT) == sector)
+        {
+          *cache_entry = NVS_LOOKUP_CACHE_NO_ADDR;
+        }
+    }
+}
+
+#endif /* CONFIG_MTD_NVS_LOOKUP_CACHE */
+
+/****************************************************************************
+ * Name: flash_read
+ ****************************************************************************/
+
+static ssize_t flash_read(FAR struct mtd_dev_s *mtd, off_t offset,
+                          FAR void *data, size_t len)
+{
+  return MTD_READ(mtd, offset, len, data);
+}
+
+/****************************************************************************
+ * Name: flash_write
+ ****************************************************************************/
+
+static ssize_t flash_write(FAR struct mtd_dev_s *mtd, off_t offset,
+                           FAR const uint8_t *data, size_t len)
+{
+  return MTD_WRITE(mtd, offset, len, data);
+}
+
+/****************************************************************************
+ * Name: flash_erase
+ ****************************************************************************/
+
+static int flash_erase(FAR struct mtd_dev_s *mtd, off_t startblock,
+                       size_t nblocks)
+{
+  return MTD_ERASE(mtd, startblock, nblocks);
+}
+
+/****************************************************************************
+ * Name: nvs_flash_wrt
+ *
+ * Description:
+ *   flash routines, process offset then write.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_wrt(FAR struct nvs_fs *fs, uint32_t addr,
+                            FAR const void *data, size_t len)
+{
+  off_t offset;
+  int ret;
+
+  offset = fs->geo.erasesize * (addr >> ADDR_SECT_SHIFT);
+  offset += addr & ADDR_OFFS_MASK;
+
+  ret = flash_write(fs->mtd, offset, data, len);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_rd
+ *
+ * Description:
+ *   basic flash read from nvs address.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_rd(FAR struct nvs_fs *fs, uint32_t addr,
+                        FAR void *data, size_t len)
+{
+  off_t offset;
+  int ret;
+
+  offset = fs->geo.erasesize * (addr >> ADDR_SECT_SHIFT);
+  offset += addr & ADDR_OFFS_MASK;
+
+  ret = flash_read(fs->mtd, offset, data, len);

Review Comment:
   ditto



##########
drivers/mtd/mtd_config_fs.c:
##########
@@ -0,0 +1,2484 @@
+/****************************************************************************
+ * drivers/mtd/mtd_config_fs.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.
+ *
+ * NVS: non volatile storage in flash
+ *
+ * Copyright (c) 2018 Laczen
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <sys/types.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <debug.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include <nuttx/crc8.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mtd/mtd.h>
+#include <nuttx/mtd/configdata.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define MIN(a, b)                       ((a) > (b) ? (b) : (a))
+
+/* MASKS AND SHIFT FOR ADDRESSES
+ * an address in nvs is an uint32_t where:
+ *   high 2 bytes represent the sector number
+ *   low 2 bytes represent the offset in a sector
+ */
+
+#define ADDR_SECT_MASK                  0xFFFF0000
+#define ADDR_SECT_SHIFT                 16
+#define ADDR_OFFS_MASK                  0x0000FFFF
+
+/* we don't want to store all the read content in stack or heap,
+ * so we make a buffer to do compare or move.
+ */
+
+#define NVS_BUFFER_SIZE                 32
+
+/* if data is written after last ate, and power loss happens,
+ * we need to find a clean offset by skipping dirty data.
+ * This macro defines how many bytes to skip when dirty data
+ * is spotted(may take several skips).
+ * Normally 1 byte is okay, such process only happens when
+ * nvs is started, and it is acceptable to take some time during
+ * starting.
+ */
+
+#define NVS_CORRUPT_DATA_SKIP_STEP      1
+
+#define NVS_LOOKUP_CACHE_NO_ADDR        0xffffffff
+
+/* gc done or close ate has the id of 0xffffffff.
+ * We can tell if the ate is special by looking at its id.
+ */
+
+#define NVS_SPECIAL_ATE_ID              0xffffffff
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Non-volatile Storage File system structure */
+
+struct nvs_fs
+{
+  FAR struct mtd_dev_s  *mtd;          /* mtd device */
+  uint32_t              ate_wra;       /* next alloc table entry
+                                        * write address
+                                        */
+  uint32_t              data_wra;      /* next data write address */
+  struct mtd_geometry_s geo;
+  uint32_t              step_addr;     /* for traverse */
+  mutex_t               nvs_lock;
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  uint32_t lookup_cache[CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE];
+#endif
+};
+
+/* Allocation Table Entry */
+
+begin_packed_struct struct nvs_ate
+{
+  uint32_t id;           /* data id */
+  uint16_t offset;       /* data offset within sector */
+  uint16_t len;          /* data len within sector */
+  uint16_t key_len;      /* key string len */
+  uint8_t  part;         /* part of a multipart data - future extension */
+  uint8_t  crc8;         /* crc8 check of the ate entry */
+  uint8_t  expired;      /* 0xFF-newest entry, others-old entry */
+  uint8_t  reserved[3];  /* for future extension */
+} end_packed_struct;
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* MTD NVS opeation api */
+
+static int     mtdconfig_open(FAR struct file *filep);
+static int     mtdconfig_close(FAR struct file *filep);
+static ssize_t mtdconfig_read(FAR struct file *filep, FAR char *buffer,
+                              size_t buflen);
+static int     mtdconfig_ioctl(FAR struct file *filep, int cmd,
+                               unsigned long arg);
+static int     mtdconfig_poll(FAR struct file *filep, FAR struct pollfd *fds,
+                              bool setup);
+
+/* Basic flash operation api */
+
+static ssize_t flash_read(FAR struct mtd_dev_s *mtd, off_t offset,
+                          FAR void *data, size_t len);
+static ssize_t flash_write(FAR struct mtd_dev_s *mtd, off_t offset,
+                           FAR const uint8_t *data, size_t len);
+static int flash_erase(FAR struct mtd_dev_s *mtd, off_t offset, size_t len);
+
+/* NVS operations needed by lookup cache */
+
+static int nvs_prev_ate(FAR struct nvs_fs *fs, FAR uint32_t *addr,
+                        FAR struct nvs_ate *ate);
+static int nvs_ate_valid(FAR struct nvs_fs *fs,
+                         FAR const struct nvs_ate *entry);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_mtdnvs_fops =
+{
+  mtdconfig_open,  /* open */
+  mtdconfig_close, /* close */
+  mtdconfig_read,  /* read */
+  NULL,            /* write */
+  NULL,            /* seek */
+  mtdconfig_ioctl, /* ioctl */
+  mtdconfig_poll   /* poll */
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+  , NULL           /* unlink */
+#endif
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nvs_fnv_hash
+ ****************************************************************************/
+
+static uint32_t nvs_fnv_hash(FAR const uint8_t *input, uint32_t len)
+{
+  uint32_t i = 0;
+  uint32_t hval = 2166136261;
+
+  /* FNV-1 hash each octet in the buffer */
+
+  while (i++ < len)
+    {
+      /* multiply by the 32 bit FNV magic prime mod 2^32 */
+
+      hval *= 0x01000193;
+
+      /* xor the bottom with the current octet */
+
+      hval ^= *input++;
+    }
+
+  return hval;
+}
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_pos
+ ****************************************************************************/
+
+static inline size_t nvs_lookup_cache_pos(uint32_t id)
+{
+  return id % CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_rebuild
+ ****************************************************************************/
+
+static int nvs_lookup_cache_rebuild(FAR struct nvs_fs *fs)
+{
+  int rc;
+  uint32_t addr;
+  uint32_t ate_addr;
+  FAR uint32_t *cache_entry;
+  struct nvs_ate ate;
+
+  memset(fs->lookup_cache, CONFIG_MTD_CONFIG_ERASEDVALUE,
+         sizeof(fs->lookup_cache));
+  addr = fs->ate_wra;
+
+  while (true)
+    {
+      /* Make a copy of 'addr' as it will be advanced by nvs_prev_ate() */
+
+      ate_addr = addr;
+      rc = nvs_prev_ate(fs, &addr, &ate);
+
+      if (rc)
+        {
+          return rc;
+        }
+
+      cache_entry = &fs->lookup_cache[nvs_lookup_cache_pos(ate.id)];
+
+      if (ate.id != NVS_SPECIAL_ATE_ID
+          && *cache_entry == NVS_LOOKUP_CACHE_NO_ADDR
+          && nvs_ate_valid(fs, &ate))
+        {
+          *cache_entry = ate_addr;
+        }
+
+      if (addr == fs->ate_wra)
+        {
+          break;
+        }
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_invalidate
+ ****************************************************************************/
+
+static void nvs_lookup_cache_invalidate(FAR struct nvs_fs *fs,
+                                        uint32_t sector)
+{
+  FAR uint32_t *cache_entry = fs->lookup_cache;
+  FAR uint32_t *const cache_end =
+    &fs->lookup_cache[CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE];
+
+  for (; cache_entry < cache_end; ++cache_entry)
+    {
+      if ((*cache_entry >> ADDR_SECT_SHIFT) == sector)
+        {
+          *cache_entry = NVS_LOOKUP_CACHE_NO_ADDR;
+        }
+    }
+}
+
+#endif /* CONFIG_MTD_NVS_LOOKUP_CACHE */
+
+/****************************************************************************
+ * Name: flash_read
+ ****************************************************************************/
+
+static ssize_t flash_read(FAR struct mtd_dev_s *mtd, off_t offset,
+                          FAR void *data, size_t len)
+{
+  return MTD_READ(mtd, offset, len, data);
+}
+
+/****************************************************************************
+ * Name: flash_write
+ ****************************************************************************/
+
+static ssize_t flash_write(FAR struct mtd_dev_s *mtd, off_t offset,
+                           FAR const uint8_t *data, size_t len)
+{
+  return MTD_WRITE(mtd, offset, len, data);
+}
+
+/****************************************************************************
+ * Name: flash_erase
+ ****************************************************************************/
+
+static int flash_erase(FAR struct mtd_dev_s *mtd, off_t startblock,
+                       size_t nblocks)
+{
+  return MTD_ERASE(mtd, startblock, nblocks);
+}
+
+/****************************************************************************
+ * Name: nvs_flash_wrt
+ *
+ * Description:
+ *   flash routines, process offset then write.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_wrt(FAR struct nvs_fs *fs, uint32_t addr,
+                            FAR const void *data, size_t len)
+{
+  off_t offset;
+  int ret;
+
+  offset = fs->geo.erasesize * (addr >> ADDR_SECT_SHIFT);
+  offset += addr & ADDR_OFFS_MASK;
+
+  ret = flash_write(fs->mtd, offset, data, len);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_rd
+ *
+ * Description:
+ *   basic flash read from nvs address.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_rd(FAR struct nvs_fs *fs, uint32_t addr,
+                        FAR void *data, size_t len)
+{
+  off_t offset;
+  int ret;
+
+  offset = fs->geo.erasesize * (addr >> ADDR_SECT_SHIFT);
+  offset += addr & ADDR_OFFS_MASK;
+
+  ret = flash_read(fs->mtd, offset, data, len);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_ate_wrt
+ *
+ * Description:
+ *   allocation entry write.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_ate_wrt(FAR struct nvs_fs *fs,
+                             FAR const struct nvs_ate *entry)
+{
+  int rc;
+
+  rc = nvs_flash_wrt(fs, fs->ate_wra, entry, sizeof(struct nvs_ate));
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  /* 0xffffffff is a special-purpose identifier. Exclude it from the cache */
+
+  if (entry->id != NVS_SPECIAL_ATE_ID)
+    {
+      fs->lookup_cache[nvs_lookup_cache_pos(entry->id)] = fs->ate_wra;
+    }
+#endif
+
+  fs->ate_wra -= sizeof(struct nvs_ate);
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_data_wrt
+ ****************************************************************************/
+
+static int nvs_flash_data_wrt(FAR struct nvs_fs *fs,
+                              FAR const void *data, size_t len)
+{
+  int rc;
+
+  rc = nvs_flash_wrt(fs, fs->data_wra, data, len);
+  fs->data_wra += len;
+  finfo("write data done, data_wra=0x%lx\n",
+        fs->data_wra);

Review Comment:
   change lx to PRIxxx



##########
drivers/mtd/mtd_config_fs.c:
##########
@@ -0,0 +1,2484 @@
+/****************************************************************************
+ * drivers/mtd/mtd_config_fs.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.
+ *
+ * NVS: non volatile storage in flash
+ *
+ * Copyright (c) 2018 Laczen
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <sys/types.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <debug.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include <nuttx/crc8.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mtd/mtd.h>
+#include <nuttx/mtd/configdata.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define MIN(a, b)                       ((a) > (b) ? (b) : (a))
+
+/* MASKS AND SHIFT FOR ADDRESSES
+ * an address in nvs is an uint32_t where:
+ *   high 2 bytes represent the sector number
+ *   low 2 bytes represent the offset in a sector
+ */
+
+#define ADDR_SECT_MASK                  0xFFFF0000
+#define ADDR_SECT_SHIFT                 16
+#define ADDR_OFFS_MASK                  0x0000FFFF
+
+/* we don't want to store all the read content in stack or heap,
+ * so we make a buffer to do compare or move.
+ */
+
+#define NVS_BUFFER_SIZE                 32
+
+/* if data is written after last ate, and power loss happens,
+ * we need to find a clean offset by skipping dirty data.
+ * This macro defines how many bytes to skip when dirty data
+ * is spotted(may take several skips).
+ * Normally 1 byte is okay, such process only happens when
+ * nvs is started, and it is acceptable to take some time during
+ * starting.
+ */
+
+#define NVS_CORRUPT_DATA_SKIP_STEP      1
+
+#define NVS_LOOKUP_CACHE_NO_ADDR        0xffffffff
+
+/* gc done or close ate has the id of 0xffffffff.
+ * We can tell if the ate is special by looking at its id.
+ */
+
+#define NVS_SPECIAL_ATE_ID              0xffffffff
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Non-volatile Storage File system structure */
+
+struct nvs_fs
+{
+  FAR struct mtd_dev_s  *mtd;          /* mtd device */
+  uint32_t              ate_wra;       /* next alloc table entry
+                                        * write address
+                                        */
+  uint32_t              data_wra;      /* next data write address */
+  struct mtd_geometry_s geo;
+  uint32_t              step_addr;     /* for traverse */
+  mutex_t               nvs_lock;
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  uint32_t lookup_cache[CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE];
+#endif
+};
+
+/* Allocation Table Entry */
+
+begin_packed_struct struct nvs_ate
+{
+  uint32_t id;           /* data id */
+  uint16_t offset;       /* data offset within sector */
+  uint16_t len;          /* data len within sector */
+  uint16_t key_len;      /* key string len */
+  uint8_t  part;         /* part of a multipart data - future extension */
+  uint8_t  crc8;         /* crc8 check of the ate entry */
+  uint8_t  expired;      /* 0xFF-newest entry, others-old entry */
+  uint8_t  reserved[3];  /* for future extension */
+} end_packed_struct;
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* MTD NVS opeation api */
+
+static int     mtdconfig_open(FAR struct file *filep);
+static int     mtdconfig_close(FAR struct file *filep);
+static ssize_t mtdconfig_read(FAR struct file *filep, FAR char *buffer,
+                              size_t buflen);
+static int     mtdconfig_ioctl(FAR struct file *filep, int cmd,
+                               unsigned long arg);
+static int     mtdconfig_poll(FAR struct file *filep, FAR struct pollfd *fds,
+                              bool setup);
+
+/* Basic flash operation api */
+
+static ssize_t flash_read(FAR struct mtd_dev_s *mtd, off_t offset,
+                          FAR void *data, size_t len);
+static ssize_t flash_write(FAR struct mtd_dev_s *mtd, off_t offset,
+                           FAR const uint8_t *data, size_t len);
+static int flash_erase(FAR struct mtd_dev_s *mtd, off_t offset, size_t len);
+
+/* NVS operations needed by lookup cache */
+
+static int nvs_prev_ate(FAR struct nvs_fs *fs, FAR uint32_t *addr,
+                        FAR struct nvs_ate *ate);
+static int nvs_ate_valid(FAR struct nvs_fs *fs,
+                         FAR const struct nvs_ate *entry);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_mtdnvs_fops =
+{
+  mtdconfig_open,  /* open */
+  mtdconfig_close, /* close */
+  mtdconfig_read,  /* read */
+  NULL,            /* write */
+  NULL,            /* seek */
+  mtdconfig_ioctl, /* ioctl */
+  mtdconfig_poll   /* poll */
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+  , NULL           /* unlink */
+#endif
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nvs_fnv_hash
+ ****************************************************************************/
+
+static uint32_t nvs_fnv_hash(FAR const uint8_t *input, uint32_t len)
+{
+  uint32_t i = 0;
+  uint32_t hval = 2166136261;
+
+  /* FNV-1 hash each octet in the buffer */
+
+  while (i++ < len)
+    {
+      /* multiply by the 32 bit FNV magic prime mod 2^32 */
+
+      hval *= 0x01000193;
+
+      /* xor the bottom with the current octet */
+
+      hval ^= *input++;
+    }
+
+  return hval;
+}
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_pos
+ ****************************************************************************/
+
+static inline size_t nvs_lookup_cache_pos(uint32_t id)
+{
+  return id % CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_rebuild
+ ****************************************************************************/
+
+static int nvs_lookup_cache_rebuild(FAR struct nvs_fs *fs)
+{
+  int rc;
+  uint32_t addr;
+  uint32_t ate_addr;
+  FAR uint32_t *cache_entry;
+  struct nvs_ate ate;
+
+  memset(fs->lookup_cache, CONFIG_MTD_CONFIG_ERASEDVALUE,
+         sizeof(fs->lookup_cache));
+  addr = fs->ate_wra;
+
+  while (true)
+    {
+      /* Make a copy of 'addr' as it will be advanced by nvs_prev_ate() */
+
+      ate_addr = addr;
+      rc = nvs_prev_ate(fs, &addr, &ate);
+
+      if (rc)
+        {
+          return rc;
+        }
+
+      cache_entry = &fs->lookup_cache[nvs_lookup_cache_pos(ate.id)];
+
+      if (ate.id != NVS_SPECIAL_ATE_ID
+          && *cache_entry == NVS_LOOKUP_CACHE_NO_ADDR
+          && nvs_ate_valid(fs, &ate))
+        {
+          *cache_entry = ate_addr;
+        }
+
+      if (addr == fs->ate_wra)
+        {
+          break;
+        }
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_invalidate
+ ****************************************************************************/
+
+static void nvs_lookup_cache_invalidate(FAR struct nvs_fs *fs,
+                                        uint32_t sector)
+{
+  FAR uint32_t *cache_entry = fs->lookup_cache;
+  FAR uint32_t *const cache_end =
+    &fs->lookup_cache[CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE];
+
+  for (; cache_entry < cache_end; ++cache_entry)
+    {
+      if ((*cache_entry >> ADDR_SECT_SHIFT) == sector)
+        {
+          *cache_entry = NVS_LOOKUP_CACHE_NO_ADDR;
+        }
+    }
+}
+
+#endif /* CONFIG_MTD_NVS_LOOKUP_CACHE */
+
+/****************************************************************************
+ * Name: flash_read
+ ****************************************************************************/
+
+static ssize_t flash_read(FAR struct mtd_dev_s *mtd, off_t offset,
+                          FAR void *data, size_t len)
+{
+  return MTD_READ(mtd, offset, len, data);
+}
+
+/****************************************************************************
+ * Name: flash_write
+ ****************************************************************************/
+
+static ssize_t flash_write(FAR struct mtd_dev_s *mtd, off_t offset,
+                           FAR const uint8_t *data, size_t len)
+{
+  return MTD_WRITE(mtd, offset, len, data);
+}
+
+/****************************************************************************
+ * Name: flash_erase
+ ****************************************************************************/
+
+static int flash_erase(FAR struct mtd_dev_s *mtd, off_t startblock,
+                       size_t nblocks)
+{
+  return MTD_ERASE(mtd, startblock, nblocks);
+}
+
+/****************************************************************************
+ * Name: nvs_flash_wrt
+ *
+ * Description:
+ *   flash routines, process offset then write.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_wrt(FAR struct nvs_fs *fs, uint32_t addr,
+                            FAR const void *data, size_t len)
+{
+  off_t offset;
+  int ret;
+
+  offset = fs->geo.erasesize * (addr >> ADDR_SECT_SHIFT);
+  offset += addr & ADDR_OFFS_MASK;
+
+  ret = flash_write(fs->mtd, offset, data, len);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_rd
+ *
+ * Description:
+ *   basic flash read from nvs address.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_rd(FAR struct nvs_fs *fs, uint32_t addr,
+                        FAR void *data, size_t len)
+{
+  off_t offset;
+  int ret;
+
+  offset = fs->geo.erasesize * (addr >> ADDR_SECT_SHIFT);
+  offset += addr & ADDR_OFFS_MASK;
+
+  ret = flash_read(fs->mtd, offset, data, len);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_ate_wrt
+ *
+ * Description:
+ *   allocation entry write.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_ate_wrt(FAR struct nvs_fs *fs,
+                             FAR const struct nvs_ate *entry)
+{
+  int rc;
+
+  rc = nvs_flash_wrt(fs, fs->ate_wra, entry, sizeof(struct nvs_ate));
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  /* 0xffffffff is a special-purpose identifier. Exclude it from the cache */
+
+  if (entry->id != NVS_SPECIAL_ATE_ID)
+    {
+      fs->lookup_cache[nvs_lookup_cache_pos(entry->id)] = fs->ate_wra;
+    }
+#endif
+
+  fs->ate_wra -= sizeof(struct nvs_ate);
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_data_wrt
+ ****************************************************************************/
+
+static int nvs_flash_data_wrt(FAR struct nvs_fs *fs,
+                              FAR const void *data, size_t len)
+{
+  int rc;
+
+  rc = nvs_flash_wrt(fs, fs->data_wra, data, len);
+  fs->data_wra += len;
+  finfo("write data done, data_wra=0x%lx\n",
+        fs->data_wra);
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_ate_rd
+ ****************************************************************************/
+
+static int nvs_flash_ate_rd(FAR struct nvs_fs *fs, uint32_t addr,
+                            FAR struct nvs_ate *entry)
+{
+  return nvs_flash_rd(fs, addr, entry, sizeof(struct nvs_ate));
+}
+
+/****************************************************************************
+ * Name: nvs_flash_block_cmp
+ *
+ * Description:
+ *   nvs_flash_block_cmp compares the data in flash at addr to data
+ *   in blocks of size NVS_BLOCK_SIZE aligned to fs->write_block_size
+ *   returns 0 if equal, 1 if not equal, errcode if error
+ *
+ ****************************************************************************/
+
+static int nvs_flash_block_cmp(FAR struct nvs_fs *fs, uint32_t addr,
+                               FAR const void *data, size_t len)
+{
+  FAR const uint8_t *data8 = (FAR const uint8_t *)data;
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t buf[NVS_BUFFER_SIZE];
+
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr, buf, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = memcmp(data8, buf, bytes_to_cmp);
+      if (rc)
+        {
+          return 1;
+        }
+
+      len -= bytes_to_cmp;
+      addr += bytes_to_cmp;
+      data8 += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_direct_cmp
+ *
+ * Description:
+ *   nvs_flash_direct_cmp compares the data in flash at addr1 and addr2
+ *   of len in blocks of size NVS_BLOCK_SIZE aligned to fs->write_block_size
+ *   returns 0 if equal, 1 if not equal, errcode if error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_direct_cmp(FAR struct nvs_fs *fs, uint32_t addr1,
+                                uint32_t addr2, size_t len)
+{
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t buf1[NVS_BUFFER_SIZE];
+  uint8_t buf2[NVS_BUFFER_SIZE];
+
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr1, buf1, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = nvs_flash_rd(fs, addr2, buf2, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = memcmp(buf1, buf2, bytes_to_cmp);
+      if (rc)
+        {
+          return 1;
+        }
+
+      len -= bytes_to_cmp;
+      addr1 += bytes_to_cmp;
+      addr2 += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_cmp_const
+ *
+ * Description:
+ *   nvs_flash_cmp_const compares the data in flash at addr to a constant
+ *   value. returns 0 if all data in flash is equal to value, 1 if not equal,
+ *   errcode if error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_cmp_const(FAR struct nvs_fs *fs, uint32_t addr,
+                               uint8_t value, size_t len)
+{
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t cmp[NVS_BUFFER_SIZE];
+
+  memset(cmp, value, NVS_BUFFER_SIZE);
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_block_cmp(fs, addr, cmp, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      len -= bytes_to_cmp;
+      addr += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_block_move
+ *
+ * Description:
+ *   flash block move: move a block at addr to the current data write
+ *   location and updates the data write location.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_block_move(FAR struct nvs_fs *fs, uint32_t addr,
+                                size_t len)
+{
+  int rc;
+  size_t bytes_to_copy;
+  uint8_t buf[NVS_BUFFER_SIZE];
+
+  while (len)
+    {
+      bytes_to_copy = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr, buf, bytes_to_copy);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = nvs_flash_data_wrt(fs, buf, bytes_to_copy);
+      if (rc)
+        {
+          return rc;
+        }
+
+      len -= bytes_to_copy;
+      addr += bytes_to_copy;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_erase_sector
+ *
+ * Description:
+ *   erase a sector by first checking it is used and then erasing if required
+ *   return 0 if OK, errorcode on error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_erase_sector(FAR struct nvs_fs *fs, uint32_t addr)
+{
+  int rc;
+
+  ferr("Erasing addr %lx\n", addr);
+  addr &= ADDR_SECT_MASK;
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  nvs_lookup_cache_invalidate(fs, addr >> ADDR_SECT_SHIFT);
+#endif
+  rc = flash_erase(fs->mtd, addr >> ADDR_SECT_SHIFT, 1);
+
+  if (rc < 0)
+    {
+      ferr("Erasing failed %d\n", rc);
+      return rc;
+    }
+  else
+    {
+      rc = OK;

Review Comment:
   move to flash_erase



##########
drivers/mtd/mtd_config_fs.c:
##########
@@ -0,0 +1,2484 @@
+/****************************************************************************
+ * drivers/mtd/mtd_config_fs.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.
+ *
+ * NVS: non volatile storage in flash
+ *
+ * Copyright (c) 2018 Laczen
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <sys/types.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <debug.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include <nuttx/crc8.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mtd/mtd.h>
+#include <nuttx/mtd/configdata.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define MIN(a, b)                       ((a) > (b) ? (b) : (a))
+
+/* MASKS AND SHIFT FOR ADDRESSES
+ * an address in nvs is an uint32_t where:
+ *   high 2 bytes represent the sector number
+ *   low 2 bytes represent the offset in a sector
+ */
+
+#define ADDR_SECT_MASK                  0xFFFF0000
+#define ADDR_SECT_SHIFT                 16
+#define ADDR_OFFS_MASK                  0x0000FFFF
+
+/* we don't want to store all the read content in stack or heap,
+ * so we make a buffer to do compare or move.
+ */
+
+#define NVS_BUFFER_SIZE                 32
+
+/* if data is written after last ate, and power loss happens,
+ * we need to find a clean offset by skipping dirty data.
+ * This macro defines how many bytes to skip when dirty data
+ * is spotted(may take several skips).
+ * Normally 1 byte is okay, such process only happens when
+ * nvs is started, and it is acceptable to take some time during
+ * starting.
+ */
+
+#define NVS_CORRUPT_DATA_SKIP_STEP      1
+
+#define NVS_LOOKUP_CACHE_NO_ADDR        0xffffffff
+
+/* gc done or close ate has the id of 0xffffffff.
+ * We can tell if the ate is special by looking at its id.
+ */
+
+#define NVS_SPECIAL_ATE_ID              0xffffffff
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Non-volatile Storage File system structure */
+
+struct nvs_fs
+{
+  FAR struct mtd_dev_s  *mtd;          /* mtd device */
+  uint32_t              ate_wra;       /* next alloc table entry
+                                        * write address
+                                        */
+  uint32_t              data_wra;      /* next data write address */
+  struct mtd_geometry_s geo;
+  uint32_t              step_addr;     /* for traverse */
+  mutex_t               nvs_lock;
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  uint32_t lookup_cache[CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE];
+#endif
+};
+
+/* Allocation Table Entry */
+
+begin_packed_struct struct nvs_ate
+{
+  uint32_t id;           /* data id */
+  uint16_t offset;       /* data offset within sector */
+  uint16_t len;          /* data len within sector */
+  uint16_t key_len;      /* key string len */
+  uint8_t  part;         /* part of a multipart data - future extension */
+  uint8_t  crc8;         /* crc8 check of the ate entry */
+  uint8_t  expired;      /* 0xFF-newest entry, others-old entry */
+  uint8_t  reserved[3];  /* for future extension */
+} end_packed_struct;
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* MTD NVS opeation api */
+
+static int     mtdconfig_open(FAR struct file *filep);
+static int     mtdconfig_close(FAR struct file *filep);
+static ssize_t mtdconfig_read(FAR struct file *filep, FAR char *buffer,
+                              size_t buflen);
+static int     mtdconfig_ioctl(FAR struct file *filep, int cmd,
+                               unsigned long arg);
+static int     mtdconfig_poll(FAR struct file *filep, FAR struct pollfd *fds,
+                              bool setup);
+
+/* Basic flash operation api */
+
+static ssize_t flash_read(FAR struct mtd_dev_s *mtd, off_t offset,
+                          FAR void *data, size_t len);
+static ssize_t flash_write(FAR struct mtd_dev_s *mtd, off_t offset,
+                           FAR const uint8_t *data, size_t len);
+static int flash_erase(FAR struct mtd_dev_s *mtd, off_t offset, size_t len);
+
+/* NVS operations needed by lookup cache */
+
+static int nvs_prev_ate(FAR struct nvs_fs *fs, FAR uint32_t *addr,
+                        FAR struct nvs_ate *ate);
+static int nvs_ate_valid(FAR struct nvs_fs *fs,
+                         FAR const struct nvs_ate *entry);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_mtdnvs_fops =
+{
+  mtdconfig_open,  /* open */
+  mtdconfig_close, /* close */
+  mtdconfig_read,  /* read */
+  NULL,            /* write */
+  NULL,            /* seek */
+  mtdconfig_ioctl, /* ioctl */
+  mtdconfig_poll   /* poll */
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+  , NULL           /* unlink */
+#endif
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nvs_fnv_hash
+ ****************************************************************************/
+
+static uint32_t nvs_fnv_hash(FAR const uint8_t *input, uint32_t len)
+{
+  uint32_t i = 0;
+  uint32_t hval = 2166136261;
+
+  /* FNV-1 hash each octet in the buffer */
+
+  while (i++ < len)
+    {
+      /* multiply by the 32 bit FNV magic prime mod 2^32 */
+
+      hval *= 0x01000193;
+
+      /* xor the bottom with the current octet */
+
+      hval ^= *input++;
+    }
+
+  return hval;
+}
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_pos
+ ****************************************************************************/
+
+static inline size_t nvs_lookup_cache_pos(uint32_t id)
+{
+  return id % CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_rebuild
+ ****************************************************************************/
+
+static int nvs_lookup_cache_rebuild(FAR struct nvs_fs *fs)
+{
+  int rc;
+  uint32_t addr;
+  uint32_t ate_addr;
+  FAR uint32_t *cache_entry;
+  struct nvs_ate ate;
+
+  memset(fs->lookup_cache, CONFIG_MTD_CONFIG_ERASEDVALUE,
+         sizeof(fs->lookup_cache));
+  addr = fs->ate_wra;
+
+  while (true)
+    {
+      /* Make a copy of 'addr' as it will be advanced by nvs_prev_ate() */
+
+      ate_addr = addr;
+      rc = nvs_prev_ate(fs, &addr, &ate);
+
+      if (rc)
+        {
+          return rc;
+        }
+
+      cache_entry = &fs->lookup_cache[nvs_lookup_cache_pos(ate.id)];
+
+      if (ate.id != NVS_SPECIAL_ATE_ID
+          && *cache_entry == NVS_LOOKUP_CACHE_NO_ADDR
+          && nvs_ate_valid(fs, &ate))
+        {
+          *cache_entry = ate_addr;
+        }
+
+      if (addr == fs->ate_wra)
+        {
+          break;
+        }
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_invalidate
+ ****************************************************************************/
+
+static void nvs_lookup_cache_invalidate(FAR struct nvs_fs *fs,
+                                        uint32_t sector)
+{
+  FAR uint32_t *cache_entry = fs->lookup_cache;
+  FAR uint32_t *const cache_end =
+    &fs->lookup_cache[CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE];
+
+  for (; cache_entry < cache_end; ++cache_entry)
+    {
+      if ((*cache_entry >> ADDR_SECT_SHIFT) == sector)
+        {
+          *cache_entry = NVS_LOOKUP_CACHE_NO_ADDR;
+        }
+    }
+}
+
+#endif /* CONFIG_MTD_NVS_LOOKUP_CACHE */
+
+/****************************************************************************
+ * Name: flash_read
+ ****************************************************************************/
+
+static ssize_t flash_read(FAR struct mtd_dev_s *mtd, off_t offset,
+                          FAR void *data, size_t len)
+{
+  return MTD_READ(mtd, offset, len, data);
+}
+
+/****************************************************************************
+ * Name: flash_write
+ ****************************************************************************/
+
+static ssize_t flash_write(FAR struct mtd_dev_s *mtd, off_t offset,
+                           FAR const uint8_t *data, size_t len)
+{
+  return MTD_WRITE(mtd, offset, len, data);
+}
+
+/****************************************************************************
+ * Name: flash_erase
+ ****************************************************************************/
+
+static int flash_erase(FAR struct mtd_dev_s *mtd, off_t startblock,
+                       size_t nblocks)
+{
+  return MTD_ERASE(mtd, startblock, nblocks);
+}
+
+/****************************************************************************
+ * Name: nvs_flash_wrt
+ *
+ * Description:
+ *   flash routines, process offset then write.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_wrt(FAR struct nvs_fs *fs, uint32_t addr,
+                            FAR const void *data, size_t len)
+{
+  off_t offset;
+  int ret;
+
+  offset = fs->geo.erasesize * (addr >> ADDR_SECT_SHIFT);
+  offset += addr & ADDR_OFFS_MASK;
+
+  ret = flash_write(fs->mtd, offset, data, len);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_rd
+ *
+ * Description:
+ *   basic flash read from nvs address.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_rd(FAR struct nvs_fs *fs, uint32_t addr,
+                        FAR void *data, size_t len)
+{
+  off_t offset;
+  int ret;
+
+  offset = fs->geo.erasesize * (addr >> ADDR_SECT_SHIFT);
+  offset += addr & ADDR_OFFS_MASK;
+
+  ret = flash_read(fs->mtd, offset, data, len);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_ate_wrt
+ *
+ * Description:
+ *   allocation entry write.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_ate_wrt(FAR struct nvs_fs *fs,
+                             FAR const struct nvs_ate *entry)
+{
+  int rc;
+
+  rc = nvs_flash_wrt(fs, fs->ate_wra, entry, sizeof(struct nvs_ate));
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  /* 0xffffffff is a special-purpose identifier. Exclude it from the cache */
+
+  if (entry->id != NVS_SPECIAL_ATE_ID)
+    {
+      fs->lookup_cache[nvs_lookup_cache_pos(entry->id)] = fs->ate_wra;
+    }
+#endif
+
+  fs->ate_wra -= sizeof(struct nvs_ate);
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_data_wrt
+ ****************************************************************************/
+
+static int nvs_flash_data_wrt(FAR struct nvs_fs *fs,
+                              FAR const void *data, size_t len)
+{
+  int rc;
+
+  rc = nvs_flash_wrt(fs, fs->data_wra, data, len);
+  fs->data_wra += len;
+  finfo("write data done, data_wra=0x%lx\n",
+        fs->data_wra);
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_ate_rd
+ ****************************************************************************/
+
+static int nvs_flash_ate_rd(FAR struct nvs_fs *fs, uint32_t addr,
+                            FAR struct nvs_ate *entry)
+{
+  return nvs_flash_rd(fs, addr, entry, sizeof(struct nvs_ate));
+}
+
+/****************************************************************************
+ * Name: nvs_flash_block_cmp
+ *
+ * Description:
+ *   nvs_flash_block_cmp compares the data in flash at addr to data
+ *   in blocks of size NVS_BLOCK_SIZE aligned to fs->write_block_size
+ *   returns 0 if equal, 1 if not equal, errcode if error
+ *
+ ****************************************************************************/
+
+static int nvs_flash_block_cmp(FAR struct nvs_fs *fs, uint32_t addr,
+                               FAR const void *data, size_t len)
+{
+  FAR const uint8_t *data8 = (FAR const uint8_t *)data;
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t buf[NVS_BUFFER_SIZE];
+
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr, buf, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = memcmp(data8, buf, bytes_to_cmp);
+      if (rc)
+        {
+          return 1;
+        }
+
+      len -= bytes_to_cmp;
+      addr += bytes_to_cmp;
+      data8 += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_direct_cmp
+ *
+ * Description:
+ *   nvs_flash_direct_cmp compares the data in flash at addr1 and addr2
+ *   of len in blocks of size NVS_BLOCK_SIZE aligned to fs->write_block_size
+ *   returns 0 if equal, 1 if not equal, errcode if error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_direct_cmp(FAR struct nvs_fs *fs, uint32_t addr1,
+                                uint32_t addr2, size_t len)
+{
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t buf1[NVS_BUFFER_SIZE];
+  uint8_t buf2[NVS_BUFFER_SIZE];
+
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr1, buf1, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = nvs_flash_rd(fs, addr2, buf2, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = memcmp(buf1, buf2, bytes_to_cmp);
+      if (rc)
+        {
+          return 1;
+        }
+
+      len -= bytes_to_cmp;
+      addr1 += bytes_to_cmp;
+      addr2 += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_cmp_const
+ *
+ * Description:
+ *   nvs_flash_cmp_const compares the data in flash at addr to a constant
+ *   value. returns 0 if all data in flash is equal to value, 1 if not equal,
+ *   errcode if error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_cmp_const(FAR struct nvs_fs *fs, uint32_t addr,
+                               uint8_t value, size_t len)
+{
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t cmp[NVS_BUFFER_SIZE];
+
+  memset(cmp, value, NVS_BUFFER_SIZE);
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_block_cmp(fs, addr, cmp, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      len -= bytes_to_cmp;
+      addr += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_block_move
+ *
+ * Description:
+ *   flash block move: move a block at addr to the current data write
+ *   location and updates the data write location.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_block_move(FAR struct nvs_fs *fs, uint32_t addr,
+                                size_t len)
+{
+  int rc;
+  size_t bytes_to_copy;
+  uint8_t buf[NVS_BUFFER_SIZE];
+
+  while (len)
+    {
+      bytes_to_copy = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr, buf, bytes_to_copy);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = nvs_flash_data_wrt(fs, buf, bytes_to_copy);
+      if (rc)
+        {
+          return rc;
+        }
+
+      len -= bytes_to_copy;
+      addr += bytes_to_copy;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_erase_sector
+ *
+ * Description:
+ *   erase a sector by first checking it is used and then erasing if required
+ *   return 0 if OK, errorcode on error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_erase_sector(FAR struct nvs_fs *fs, uint32_t addr)

Review Comment:
   ```suggestion
   static int nvs_flash_erase_block(FAR struct nvs_fs *fs, uint32_t addr)
   ```



##########
drivers/mtd/mtd_config_fs.c:
##########
@@ -0,0 +1,2484 @@
+/****************************************************************************
+ * drivers/mtd/mtd_config_fs.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.
+ *
+ * NVS: non volatile storage in flash
+ *
+ * Copyright (c) 2018 Laczen
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <sys/types.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <debug.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include <nuttx/crc8.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mtd/mtd.h>
+#include <nuttx/mtd/configdata.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define MIN(a, b)                       ((a) > (b) ? (b) : (a))
+
+/* MASKS AND SHIFT FOR ADDRESSES
+ * an address in nvs is an uint32_t where:
+ *   high 2 bytes represent the sector number
+ *   low 2 bytes represent the offset in a sector
+ */
+
+#define ADDR_SECT_MASK                  0xFFFF0000
+#define ADDR_SECT_SHIFT                 16
+#define ADDR_OFFS_MASK                  0x0000FFFF
+
+/* we don't want to store all the read content in stack or heap,
+ * so we make a buffer to do compare or move.
+ */
+
+#define NVS_BUFFER_SIZE                 32
+
+/* if data is written after last ate, and power loss happens,
+ * we need to find a clean offset by skipping dirty data.
+ * This macro defines how many bytes to skip when dirty data
+ * is spotted(may take several skips).
+ * Normally 1 byte is okay, such process only happens when
+ * nvs is started, and it is acceptable to take some time during
+ * starting.
+ */
+
+#define NVS_CORRUPT_DATA_SKIP_STEP      1
+
+#define NVS_LOOKUP_CACHE_NO_ADDR        0xffffffff
+
+/* gc done or close ate has the id of 0xffffffff.
+ * We can tell if the ate is special by looking at its id.
+ */
+
+#define NVS_SPECIAL_ATE_ID              0xffffffff
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Non-volatile Storage File system structure */
+
+struct nvs_fs
+{
+  FAR struct mtd_dev_s  *mtd;          /* mtd device */
+  uint32_t              ate_wra;       /* next alloc table entry
+                                        * write address
+                                        */
+  uint32_t              data_wra;      /* next data write address */
+  struct mtd_geometry_s geo;
+  uint32_t              step_addr;     /* for traverse */
+  mutex_t               nvs_lock;
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  uint32_t lookup_cache[CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE];
+#endif
+};
+
+/* Allocation Table Entry */
+
+begin_packed_struct struct nvs_ate
+{
+  uint32_t id;           /* data id */
+  uint16_t offset;       /* data offset within sector */
+  uint16_t len;          /* data len within sector */
+  uint16_t key_len;      /* key string len */
+  uint8_t  part;         /* part of a multipart data - future extension */
+  uint8_t  crc8;         /* crc8 check of the ate entry */
+  uint8_t  expired;      /* 0xFF-newest entry, others-old entry */
+  uint8_t  reserved[3];  /* for future extension */
+} end_packed_struct;
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* MTD NVS opeation api */
+
+static int     mtdconfig_open(FAR struct file *filep);
+static int     mtdconfig_close(FAR struct file *filep);
+static ssize_t mtdconfig_read(FAR struct file *filep, FAR char *buffer,
+                              size_t buflen);
+static int     mtdconfig_ioctl(FAR struct file *filep, int cmd,
+                               unsigned long arg);
+static int     mtdconfig_poll(FAR struct file *filep, FAR struct pollfd *fds,
+                              bool setup);
+
+/* Basic flash operation api */
+
+static ssize_t flash_read(FAR struct mtd_dev_s *mtd, off_t offset,
+                          FAR void *data, size_t len);
+static ssize_t flash_write(FAR struct mtd_dev_s *mtd, off_t offset,
+                           FAR const uint8_t *data, size_t len);
+static int flash_erase(FAR struct mtd_dev_s *mtd, off_t offset, size_t len);
+
+/* NVS operations needed by lookup cache */
+
+static int nvs_prev_ate(FAR struct nvs_fs *fs, FAR uint32_t *addr,
+                        FAR struct nvs_ate *ate);
+static int nvs_ate_valid(FAR struct nvs_fs *fs,
+                         FAR const struct nvs_ate *entry);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_mtdnvs_fops =
+{
+  mtdconfig_open,  /* open */
+  mtdconfig_close, /* close */
+  mtdconfig_read,  /* read */
+  NULL,            /* write */
+  NULL,            /* seek */
+  mtdconfig_ioctl, /* ioctl */
+  mtdconfig_poll   /* poll */
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+  , NULL           /* unlink */
+#endif
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nvs_fnv_hash
+ ****************************************************************************/
+
+static uint32_t nvs_fnv_hash(FAR const uint8_t *input, uint32_t len)
+{
+  uint32_t i = 0;
+  uint32_t hval = 2166136261;
+
+  /* FNV-1 hash each octet in the buffer */
+
+  while (i++ < len)
+    {
+      /* multiply by the 32 bit FNV magic prime mod 2^32 */
+
+      hval *= 0x01000193;
+
+      /* xor the bottom with the current octet */
+
+      hval ^= *input++;
+    }
+
+  return hval;
+}
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_pos
+ ****************************************************************************/
+
+static inline size_t nvs_lookup_cache_pos(uint32_t id)
+{
+  return id % CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_rebuild
+ ****************************************************************************/
+
+static int nvs_lookup_cache_rebuild(FAR struct nvs_fs *fs)
+{
+  int rc;
+  uint32_t addr;
+  uint32_t ate_addr;
+  FAR uint32_t *cache_entry;
+  struct nvs_ate ate;
+
+  memset(fs->lookup_cache, CONFIG_MTD_CONFIG_ERASEDVALUE,
+         sizeof(fs->lookup_cache));
+  addr = fs->ate_wra;
+
+  while (true)
+    {
+      /* Make a copy of 'addr' as it will be advanced by nvs_prev_ate() */
+
+      ate_addr = addr;
+      rc = nvs_prev_ate(fs, &addr, &ate);
+
+      if (rc)
+        {
+          return rc;
+        }
+
+      cache_entry = &fs->lookup_cache[nvs_lookup_cache_pos(ate.id)];
+
+      if (ate.id != NVS_SPECIAL_ATE_ID
+          && *cache_entry == NVS_LOOKUP_CACHE_NO_ADDR
+          && nvs_ate_valid(fs, &ate))
+        {
+          *cache_entry = ate_addr;
+        }
+
+      if (addr == fs->ate_wra)
+        {
+          break;
+        }
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_invalidate
+ ****************************************************************************/
+
+static void nvs_lookup_cache_invalidate(FAR struct nvs_fs *fs,
+                                        uint32_t sector)
+{
+  FAR uint32_t *cache_entry = fs->lookup_cache;
+  FAR uint32_t *const cache_end =
+    &fs->lookup_cache[CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE];
+
+  for (; cache_entry < cache_end; ++cache_entry)
+    {
+      if ((*cache_entry >> ADDR_SECT_SHIFT) == sector)
+        {
+          *cache_entry = NVS_LOOKUP_CACHE_NO_ADDR;
+        }
+    }
+}
+
+#endif /* CONFIG_MTD_NVS_LOOKUP_CACHE */
+
+/****************************************************************************
+ * Name: flash_read
+ ****************************************************************************/
+
+static ssize_t flash_read(FAR struct mtd_dev_s *mtd, off_t offset,
+                          FAR void *data, size_t len)
+{
+  return MTD_READ(mtd, offset, len, data);
+}
+
+/****************************************************************************
+ * Name: flash_write
+ ****************************************************************************/
+
+static ssize_t flash_write(FAR struct mtd_dev_s *mtd, off_t offset,
+                           FAR const uint8_t *data, size_t len)
+{
+  return MTD_WRITE(mtd, offset, len, data);
+}
+
+/****************************************************************************
+ * Name: flash_erase
+ ****************************************************************************/
+
+static int flash_erase(FAR struct mtd_dev_s *mtd, off_t startblock,
+                       size_t nblocks)
+{
+  return MTD_ERASE(mtd, startblock, nblocks);
+}
+
+/****************************************************************************
+ * Name: nvs_flash_wrt
+ *
+ * Description:
+ *   flash routines, process offset then write.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_wrt(FAR struct nvs_fs *fs, uint32_t addr,
+                            FAR const void *data, size_t len)
+{
+  off_t offset;
+  int ret;
+
+  offset = fs->geo.erasesize * (addr >> ADDR_SECT_SHIFT);
+  offset += addr & ADDR_OFFS_MASK;
+
+  ret = flash_write(fs->mtd, offset, data, len);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_rd
+ *
+ * Description:
+ *   basic flash read from nvs address.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_rd(FAR struct nvs_fs *fs, uint32_t addr,
+                        FAR void *data, size_t len)
+{
+  off_t offset;
+  int ret;
+
+  offset = fs->geo.erasesize * (addr >> ADDR_SECT_SHIFT);
+  offset += addr & ADDR_OFFS_MASK;
+
+  ret = flash_read(fs->mtd, offset, data, len);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_ate_wrt
+ *
+ * Description:
+ *   allocation entry write.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_ate_wrt(FAR struct nvs_fs *fs,
+                             FAR const struct nvs_ate *entry)
+{
+  int rc;
+
+  rc = nvs_flash_wrt(fs, fs->ate_wra, entry, sizeof(struct nvs_ate));
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  /* 0xffffffff is a special-purpose identifier. Exclude it from the cache */
+
+  if (entry->id != NVS_SPECIAL_ATE_ID)
+    {
+      fs->lookup_cache[nvs_lookup_cache_pos(entry->id)] = fs->ate_wra;
+    }
+#endif
+
+  fs->ate_wra -= sizeof(struct nvs_ate);
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_data_wrt
+ ****************************************************************************/
+
+static int nvs_flash_data_wrt(FAR struct nvs_fs *fs,
+                              FAR const void *data, size_t len)
+{
+  int rc;
+
+  rc = nvs_flash_wrt(fs, fs->data_wra, data, len);
+  fs->data_wra += len;
+  finfo("write data done, data_wra=0x%lx\n",
+        fs->data_wra);
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_ate_rd
+ ****************************************************************************/
+
+static int nvs_flash_ate_rd(FAR struct nvs_fs *fs, uint32_t addr,
+                            FAR struct nvs_ate *entry)
+{
+  return nvs_flash_rd(fs, addr, entry, sizeof(struct nvs_ate));
+}
+
+/****************************************************************************
+ * Name: nvs_flash_block_cmp
+ *
+ * Description:
+ *   nvs_flash_block_cmp compares the data in flash at addr to data
+ *   in blocks of size NVS_BLOCK_SIZE aligned to fs->write_block_size
+ *   returns 0 if equal, 1 if not equal, errcode if error
+ *
+ ****************************************************************************/
+
+static int nvs_flash_block_cmp(FAR struct nvs_fs *fs, uint32_t addr,
+                               FAR const void *data, size_t len)
+{
+  FAR const uint8_t *data8 = (FAR const uint8_t *)data;
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t buf[NVS_BUFFER_SIZE];
+
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr, buf, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = memcmp(data8, buf, bytes_to_cmp);
+      if (rc)
+        {
+          return 1;
+        }
+
+      len -= bytes_to_cmp;
+      addr += bytes_to_cmp;
+      data8 += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_direct_cmp
+ *
+ * Description:
+ *   nvs_flash_direct_cmp compares the data in flash at addr1 and addr2
+ *   of len in blocks of size NVS_BLOCK_SIZE aligned to fs->write_block_size
+ *   returns 0 if equal, 1 if not equal, errcode if error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_direct_cmp(FAR struct nvs_fs *fs, uint32_t addr1,
+                                uint32_t addr2, size_t len)
+{
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t buf1[NVS_BUFFER_SIZE];
+  uint8_t buf2[NVS_BUFFER_SIZE];
+
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr1, buf1, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = nvs_flash_rd(fs, addr2, buf2, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = memcmp(buf1, buf2, bytes_to_cmp);
+      if (rc)
+        {
+          return 1;
+        }
+
+      len -= bytes_to_cmp;
+      addr1 += bytes_to_cmp;
+      addr2 += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_cmp_const
+ *
+ * Description:
+ *   nvs_flash_cmp_const compares the data in flash at addr to a constant
+ *   value. returns 0 if all data in flash is equal to value, 1 if not equal,
+ *   errcode if error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_cmp_const(FAR struct nvs_fs *fs, uint32_t addr,
+                               uint8_t value, size_t len)
+{
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t cmp[NVS_BUFFER_SIZE];
+
+  memset(cmp, value, NVS_BUFFER_SIZE);
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_block_cmp(fs, addr, cmp, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      len -= bytes_to_cmp;
+      addr += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_block_move
+ *
+ * Description:
+ *   flash block move: move a block at addr to the current data write
+ *   location and updates the data write location.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_block_move(FAR struct nvs_fs *fs, uint32_t addr,
+                                size_t len)
+{
+  int rc;
+  size_t bytes_to_copy;
+  uint8_t buf[NVS_BUFFER_SIZE];
+
+  while (len)
+    {
+      bytes_to_copy = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr, buf, bytes_to_copy);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = nvs_flash_data_wrt(fs, buf, bytes_to_copy);
+      if (rc)
+        {
+          return rc;
+        }
+
+      len -= bytes_to_copy;
+      addr += bytes_to_copy;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_erase_sector
+ *
+ * Description:
+ *   erase a sector by first checking it is used and then erasing if required
+ *   return 0 if OK, errorcode on error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_erase_sector(FAR struct nvs_fs *fs, uint32_t addr)
+{
+  int rc;
+
+  ferr("Erasing addr %lx\n", addr);
+  addr &= ADDR_SECT_MASK;
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  nvs_lookup_cache_invalidate(fs, addr >> ADDR_SECT_SHIFT);
+#endif
+  rc = flash_erase(fs->mtd, addr >> ADDR_SECT_SHIFT, 1);
+
+  if (rc < 0)
+    {
+      ferr("Erasing failed %d\n", rc);
+      return rc;
+    }
+  else
+    {
+      rc = OK;
+    }
+
+  if (nvs_flash_cmp_const(fs, addr, CONFIG_MTD_CONFIG_ERASEDVALUE,
+                          fs->geo.erasesize))
+    {
+      ferr("Erasing not complete\n");
+      rc = -ENXIO;
+    }
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_crc8_update
+ *
+ * Description:
+ *   crc update on allocation entry.
+ *
+ ****************************************************************************/
+
+static void nvs_ate_crc8_update(FAR struct nvs_ate *entry)
+{
+  uint8_t ate_crc;
+
+  ate_crc = crc8part((FAR const uint8_t *)entry,
+                     offsetof(struct nvs_ate, crc8), 0xff);
+  entry->crc8 = ate_crc;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_crc8_check
+ *
+ * Description:
+ *   crc check on allocation entry
+ *   returns 0 if OK, 1 on crc fail
+ *
+ ****************************************************************************/
+
+static int nvs_ate_crc8_check(FAR const struct nvs_ate *entry)
+{
+  uint8_t ate_crc;
+
+  ate_crc = crc8part((FAR const uint8_t *)entry,
+                     offsetof(struct nvs_ate, crc8), 0xff);
+  if (ate_crc == entry->crc8)
+    {
+      return 0;
+    }
+
+  return 1;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_cmp_const
+ *
+ * Description:
+ *   nvs_ate_cmp_const compares an ATE to a constant value. returns 0 if
+ *   the whole ATE is equal to value, 1 if not equal.
+ *
+ ****************************************************************************/
+
+static int nvs_ate_cmp_const(FAR const struct nvs_ate *entry,
+                             uint8_t value)
+{
+  FAR const uint8_t *data8 = (FAR const uint8_t *)entry;
+  int i;
+
+  for (i = 0; i < sizeof(struct nvs_ate); i++)
+    {
+      if (data8[i] != value)
+        {
+          return 1;
+        }
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_valid
+ *
+ * Description:
+ *   return 1 if crc8 and offset valid, 0 otherwise
+ *
+ ****************************************************************************/
+
+static int nvs_ate_valid(FAR struct nvs_fs *fs,
+                         FAR const struct nvs_ate *entry)
+{
+  if ((nvs_ate_crc8_check(entry)) ||
+      (entry->offset >= (fs->geo.erasesize - sizeof(struct nvs_ate))))
+    {
+      return 0;
+    }
+
+  return 1;
+}
+
+/****************************************************************************
+ * Name: nvs_close_ate_valid
+ *
+ * Description:
+ *   nvs_close_ate_valid validates an sector close ate:
+ *   a valid sector close ate:
+ *   - valid ate
+ *   - len = 0 and id = 0xFFFFFFFF
+ *   - offset points to location at ate multiple from sector size
+ *   return 1 if valid, 0 otherwise
+ *
+ ****************************************************************************/
+
+static int nvs_close_ate_valid(FAR struct nvs_fs *fs,
+                               FAR const struct nvs_ate *entry)
+{
+  if ((!nvs_ate_valid(fs, entry)) || (entry->len != 0U) ||
+      (entry->id != NVS_SPECIAL_ATE_ID))
+    {
+      return 0;
+    }
+
+  if ((fs->geo.erasesize - entry->offset) % sizeof(struct nvs_ate))
+    {
+      return 0;
+    }
+
+  return 1;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_wrt_entry
+ *
+ * Description:
+ *   store an entry in flash
+ *
+ ****************************************************************************/
+
+static int nvs_flash_wrt_entry(FAR struct nvs_fs *fs, uint32_t id,
+            FAR const uint8_t *key, size_t key_size, FAR const void *data,
+            size_t len)
+{
+  int rc;
+  struct nvs_ate entry;
+
+  entry.id = id;
+  entry.offset = (uint16_t)(fs->data_wra & ADDR_OFFS_MASK);
+  entry.len = (uint16_t)len;
+  entry.key_len = (uint16_t)key_size;
+  entry.part = 0xff;
+
+  nvs_ate_crc8_update(&entry);
+
+  entry.expired = 0xff;
+  entry.reserved[0] = 0xff;
+  entry.reserved[1] = 0xff;
+  entry.reserved[2] = 0xff;
+
+  /* let's sew key and data into one, key comes first, then data */
+
+  rc = nvs_flash_data_wrt(fs, key, key_size);
+  if (rc)
+    {
+      ferr("Write key failed, rc=%d\n", rc);
+      return rc;
+    }
+
+  rc = nvs_flash_data_wrt(fs, data, len);
+  if (rc)
+    {
+      ferr("Write value failed, rc=%d\n", rc);
+      return rc;
+    }
+
+  rc = nvs_flash_ate_wrt(fs, &entry);
+  if (rc)
+    {
+      ferr("Write ate failed, rc=%d\n", rc);
+      return rc;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_recover_last_ate
+ *
+ * Description:
+ *   If the closing ate is invalid, its offset cannot be trusted and
+ *   the last valid ate of the sector should instead try to be recovered
+ *   by going through all ate's.
+ *
+ *   addr should point to the faulty closing ate and will be updated to
+ *   the last valid ate. If no valid ate is found it will be left untouched.
+ *
+ ****************************************************************************/
+
+static int nvs_recover_last_ate(FAR struct nvs_fs *fs,
+                                FAR uint32_t *addr)
+{
+  uint32_t data_end_addr;
+  uint32_t ate_end_addr;
+  struct nvs_ate end_ate;
+  int rc;
+
+  finfo("Recovering last ate from sector %" PRIu32 "\n",
+        (*addr >> ADDR_SECT_SHIFT));
+
+  *addr -= sizeof(struct nvs_ate);
+  ate_end_addr = *addr;
+  data_end_addr = *addr & ADDR_SECT_MASK;
+  while (ate_end_addr > data_end_addr)
+    {
+      rc = nvs_flash_ate_rd(fs, ate_end_addr, &end_ate);
+      if (rc)
+        {
+          return rc;
+        }
+
+      if (nvs_ate_valid(fs, &end_ate))
+        {
+          /* Found a valid ate, update data_end_addr and *addr */
+
+          data_end_addr &= ADDR_SECT_MASK;
+          data_end_addr += end_ate.offset + end_ate.len + end_ate.key_len;
+          *addr = ate_end_addr;
+        }
+
+      ate_end_addr -= sizeof(struct nvs_ate);
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_prev_ate
+ *
+ * Description:
+ *   walking through allocation entry list, from newest to oldest entries
+ *   read ate from addr, modify addr to the previous ate.
+ *
+ ****************************************************************************/
+
+static int nvs_prev_ate(FAR struct nvs_fs *fs, FAR uint32_t *addr,
+                        FAR struct nvs_ate *ate)
+{
+  int rc;
+  struct nvs_ate close_ate;
+
+  rc = nvs_flash_ate_rd(fs, *addr, ate);
+  if (rc)
+    {
+      return rc;
+    }
+
+  *addr += sizeof(struct nvs_ate);
+  if (((*addr) & ADDR_OFFS_MASK) !=
+      (fs->geo.erasesize - sizeof(struct nvs_ate)))
+    {
+      return 0;
+    }
+
+  /* last ate in sector, do jump to previous sector */
+
+  if (((*addr) >> ADDR_SECT_SHIFT) == 0U)
+    {
+      *addr += ((fs->geo.neraseblocks - 1) << ADDR_SECT_SHIFT);
+    }
+  else
+    {
+      *addr -= (1 << ADDR_SECT_SHIFT);
+    }
+
+  rc = nvs_flash_ate_rd(fs, *addr, &close_ate);
+  if (rc)
+    {
+      return rc;
+    }
+
+  rc = nvs_ate_cmp_const(&close_ate, CONFIG_MTD_CONFIG_ERASEDVALUE);
+
+  /* at the end of filesystem */
+
+  if (!rc)
+    {
+      *addr = fs->ate_wra;
+      return 0;
+    }
+
+  /* Update the address if the close ate is valid. */
+
+  if (nvs_close_ate_valid(fs, &close_ate))
+    {
+      (*addr) &= ADDR_SECT_MASK;
+      (*addr) += close_ate.offset;
+      return 0;
+    }
+
+  /* The close_ate was invalid, `lets find out the last valid ate
+   * and point the address to this found ate.
+   *
+   * remark: if there was absolutely no valid data in the sector *addr
+   * is kept at sector_end - 2*ate_size, the next read will contain
+   * invalid data and continue with a sector jump
+   */
+
+  return nvs_recover_last_ate(fs, addr);
+}
+
+/****************************************************************************
+ * Name: nvs_sector_advance
+ ****************************************************************************/
+
+static void nvs_sector_advance(FAR struct nvs_fs *fs, FAR uint32_t *addr)
+{
+  *addr += (1 << ADDR_SECT_SHIFT);
+  if ((*addr >> ADDR_SECT_SHIFT) == fs->geo.neraseblocks)
+    {
+      *addr -= (fs->geo.neraseblocks << ADDR_SECT_SHIFT);
+    }
+}
+
+/****************************************************************************
+ * Name: nvs_sector_close
+ *
+ * Description:
+ *   allocation entry close (this closes the current sector) by writing
+ *   offset of last ate to the sector end.
+ *
+ ****************************************************************************/
+
+static int nvs_sector_close(FAR struct nvs_fs *fs)
+{
+  int rc;
+  struct nvs_ate close_ate;
+
+  close_ate.id = NVS_SPECIAL_ATE_ID;
+  close_ate.len = 0U;
+  close_ate.key_len = 0U;
+  close_ate.offset =
+    (uint16_t)((fs->ate_wra + sizeof(struct nvs_ate)) & ADDR_OFFS_MASK);
+
+  fs->ate_wra &= ADDR_SECT_MASK;
+  fs->ate_wra += (fs->geo.erasesize - sizeof(struct nvs_ate));
+
+  nvs_ate_crc8_update(&close_ate);
+
+  rc = nvs_flash_ate_wrt(fs, &close_ate);
+  if (rc < 0)
+    {
+      ferr("Write ate failed, rc=%d\n", rc);
+    }
+
+  nvs_sector_advance(fs, &fs->ate_wra);
+
+  fs->data_wra = fs->ate_wra & ADDR_SECT_MASK;
+  finfo("sector close, data_wra=0x%lx\n", fs->data_wra);
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_add_gc_done_ate
+ ****************************************************************************/
+
+static int nvs_add_gc_done_ate(FAR struct nvs_fs *fs)
+{
+  struct nvs_ate gc_done_ate;
+
+  finfo("Adding gc done ate at %lx\n", fs->ate_wra & ADDR_OFFS_MASK);
+  gc_done_ate.id = NVS_SPECIAL_ATE_ID;
+  gc_done_ate.len = 0U;
+  gc_done_ate.key_len = 0U;
+  gc_done_ate.offset = (uint16_t)(fs->data_wra & ADDR_OFFS_MASK);

Review Comment:
   ditto



##########
drivers/mtd/mtd_config_fs.c:
##########
@@ -0,0 +1,2484 @@
+/****************************************************************************
+ * drivers/mtd/mtd_config_fs.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.
+ *
+ * NVS: non volatile storage in flash
+ *
+ * Copyright (c) 2018 Laczen
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <sys/types.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <debug.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include <nuttx/crc8.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mtd/mtd.h>
+#include <nuttx/mtd/configdata.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define MIN(a, b)                       ((a) > (b) ? (b) : (a))
+
+/* MASKS AND SHIFT FOR ADDRESSES
+ * an address in nvs is an uint32_t where:
+ *   high 2 bytes represent the sector number
+ *   low 2 bytes represent the offset in a sector
+ */
+
+#define ADDR_SECT_MASK                  0xFFFF0000
+#define ADDR_SECT_SHIFT                 16
+#define ADDR_OFFS_MASK                  0x0000FFFF
+
+/* we don't want to store all the read content in stack or heap,
+ * so we make a buffer to do compare or move.
+ */
+
+#define NVS_BUFFER_SIZE                 32
+
+/* if data is written after last ate, and power loss happens,
+ * we need to find a clean offset by skipping dirty data.
+ * This macro defines how many bytes to skip when dirty data
+ * is spotted(may take several skips).
+ * Normally 1 byte is okay, such process only happens when
+ * nvs is started, and it is acceptable to take some time during
+ * starting.
+ */
+
+#define NVS_CORRUPT_DATA_SKIP_STEP      1
+
+#define NVS_LOOKUP_CACHE_NO_ADDR        0xffffffff
+
+/* gc done or close ate has the id of 0xffffffff.
+ * We can tell if the ate is special by looking at its id.
+ */
+
+#define NVS_SPECIAL_ATE_ID              0xffffffff
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Non-volatile Storage File system structure */
+
+struct nvs_fs
+{
+  FAR struct mtd_dev_s  *mtd;          /* mtd device */
+  uint32_t              ate_wra;       /* next alloc table entry
+                                        * write address
+                                        */
+  uint32_t              data_wra;      /* next data write address */
+  struct mtd_geometry_s geo;
+  uint32_t              step_addr;     /* for traverse */
+  mutex_t               nvs_lock;
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  uint32_t lookup_cache[CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE];
+#endif
+};
+
+/* Allocation Table Entry */
+
+begin_packed_struct struct nvs_ate
+{
+  uint32_t id;           /* data id */
+  uint16_t offset;       /* data offset within sector */
+  uint16_t len;          /* data len within sector */
+  uint16_t key_len;      /* key string len */
+  uint8_t  part;         /* part of a multipart data - future extension */
+  uint8_t  crc8;         /* crc8 check of the ate entry */
+  uint8_t  expired;      /* 0xFF-newest entry, others-old entry */
+  uint8_t  reserved[3];  /* for future extension */
+} end_packed_struct;
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* MTD NVS opeation api */
+
+static int     mtdconfig_open(FAR struct file *filep);
+static int     mtdconfig_close(FAR struct file *filep);
+static ssize_t mtdconfig_read(FAR struct file *filep, FAR char *buffer,
+                              size_t buflen);
+static int     mtdconfig_ioctl(FAR struct file *filep, int cmd,
+                               unsigned long arg);
+static int     mtdconfig_poll(FAR struct file *filep, FAR struct pollfd *fds,
+                              bool setup);
+
+/* Basic flash operation api */
+
+static ssize_t flash_read(FAR struct mtd_dev_s *mtd, off_t offset,
+                          FAR void *data, size_t len);
+static ssize_t flash_write(FAR struct mtd_dev_s *mtd, off_t offset,
+                           FAR const uint8_t *data, size_t len);
+static int flash_erase(FAR struct mtd_dev_s *mtd, off_t offset, size_t len);
+
+/* NVS operations needed by lookup cache */
+
+static int nvs_prev_ate(FAR struct nvs_fs *fs, FAR uint32_t *addr,
+                        FAR struct nvs_ate *ate);
+static int nvs_ate_valid(FAR struct nvs_fs *fs,
+                         FAR const struct nvs_ate *entry);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_mtdnvs_fops =
+{
+  mtdconfig_open,  /* open */
+  mtdconfig_close, /* close */
+  mtdconfig_read,  /* read */
+  NULL,            /* write */
+  NULL,            /* seek */
+  mtdconfig_ioctl, /* ioctl */
+  mtdconfig_poll   /* poll */
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+  , NULL           /* unlink */
+#endif
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nvs_fnv_hash
+ ****************************************************************************/
+
+static uint32_t nvs_fnv_hash(FAR const uint8_t *input, uint32_t len)
+{
+  uint32_t i = 0;
+  uint32_t hval = 2166136261;
+
+  /* FNV-1 hash each octet in the buffer */
+
+  while (i++ < len)
+    {
+      /* multiply by the 32 bit FNV magic prime mod 2^32 */
+
+      hval *= 0x01000193;
+
+      /* xor the bottom with the current octet */
+
+      hval ^= *input++;
+    }
+
+  return hval;
+}
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_pos
+ ****************************************************************************/
+
+static inline size_t nvs_lookup_cache_pos(uint32_t id)
+{
+  return id % CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_rebuild
+ ****************************************************************************/
+
+static int nvs_lookup_cache_rebuild(FAR struct nvs_fs *fs)
+{
+  int rc;
+  uint32_t addr;
+  uint32_t ate_addr;
+  FAR uint32_t *cache_entry;
+  struct nvs_ate ate;
+
+  memset(fs->lookup_cache, CONFIG_MTD_CONFIG_ERASEDVALUE,
+         sizeof(fs->lookup_cache));
+  addr = fs->ate_wra;
+
+  while (true)
+    {
+      /* Make a copy of 'addr' as it will be advanced by nvs_prev_ate() */
+
+      ate_addr = addr;
+      rc = nvs_prev_ate(fs, &addr, &ate);
+
+      if (rc)
+        {
+          return rc;
+        }
+
+      cache_entry = &fs->lookup_cache[nvs_lookup_cache_pos(ate.id)];
+
+      if (ate.id != NVS_SPECIAL_ATE_ID
+          && *cache_entry == NVS_LOOKUP_CACHE_NO_ADDR
+          && nvs_ate_valid(fs, &ate))
+        {
+          *cache_entry = ate_addr;
+        }
+
+      if (addr == fs->ate_wra)
+        {
+          break;
+        }
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_invalidate
+ ****************************************************************************/
+
+static void nvs_lookup_cache_invalidate(FAR struct nvs_fs *fs,
+                                        uint32_t sector)
+{
+  FAR uint32_t *cache_entry = fs->lookup_cache;
+  FAR uint32_t *const cache_end =
+    &fs->lookup_cache[CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE];
+
+  for (; cache_entry < cache_end; ++cache_entry)
+    {
+      if ((*cache_entry >> ADDR_SECT_SHIFT) == sector)
+        {
+          *cache_entry = NVS_LOOKUP_CACHE_NO_ADDR;
+        }
+    }
+}
+
+#endif /* CONFIG_MTD_NVS_LOOKUP_CACHE */
+
+/****************************************************************************
+ * Name: flash_read
+ ****************************************************************************/
+
+static ssize_t flash_read(FAR struct mtd_dev_s *mtd, off_t offset,
+                          FAR void *data, size_t len)
+{
+  return MTD_READ(mtd, offset, len, data);
+}
+
+/****************************************************************************
+ * Name: flash_write
+ ****************************************************************************/
+
+static ssize_t flash_write(FAR struct mtd_dev_s *mtd, off_t offset,
+                           FAR const uint8_t *data, size_t len)
+{
+  return MTD_WRITE(mtd, offset, len, data);
+}
+
+/****************************************************************************
+ * Name: flash_erase
+ ****************************************************************************/
+
+static int flash_erase(FAR struct mtd_dev_s *mtd, off_t startblock,
+                       size_t nblocks)
+{
+  return MTD_ERASE(mtd, startblock, nblocks);
+}
+
+/****************************************************************************
+ * Name: nvs_flash_wrt
+ *
+ * Description:
+ *   flash routines, process offset then write.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_wrt(FAR struct nvs_fs *fs, uint32_t addr,
+                            FAR const void *data, size_t len)
+{
+  off_t offset;
+  int ret;
+
+  offset = fs->geo.erasesize * (addr >> ADDR_SECT_SHIFT);
+  offset += addr & ADDR_OFFS_MASK;
+
+  ret = flash_write(fs->mtd, offset, data, len);
+  if (ret < 0)

Review Comment:
   let move ret > 0 => 0 to flash_write, flash_read and flash_erase.



##########
drivers/mtd/mtd_config_fs.c:
##########
@@ -0,0 +1,2484 @@
+/****************************************************************************
+ * drivers/mtd/mtd_config_fs.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.
+ *
+ * NVS: non volatile storage in flash
+ *
+ * Copyright (c) 2018 Laczen
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <sys/types.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <debug.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include <nuttx/crc8.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mtd/mtd.h>
+#include <nuttx/mtd/configdata.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define MIN(a, b)                       ((a) > (b) ? (b) : (a))
+
+/* MASKS AND SHIFT FOR ADDRESSES
+ * an address in nvs is an uint32_t where:
+ *   high 2 bytes represent the sector number
+ *   low 2 bytes represent the offset in a sector
+ */
+
+#define ADDR_SECT_MASK                  0xFFFF0000
+#define ADDR_SECT_SHIFT                 16
+#define ADDR_OFFS_MASK                  0x0000FFFF
+
+/* we don't want to store all the read content in stack or heap,
+ * so we make a buffer to do compare or move.
+ */
+
+#define NVS_BUFFER_SIZE                 32
+
+/* if data is written after last ate, and power loss happens,
+ * we need to find a clean offset by skipping dirty data.
+ * This macro defines how many bytes to skip when dirty data
+ * is spotted(may take several skips).
+ * Normally 1 byte is okay, such process only happens when
+ * nvs is started, and it is acceptable to take some time during
+ * starting.
+ */
+
+#define NVS_CORRUPT_DATA_SKIP_STEP      1
+
+#define NVS_LOOKUP_CACHE_NO_ADDR        0xffffffff
+
+/* gc done or close ate has the id of 0xffffffff.
+ * We can tell if the ate is special by looking at its id.
+ */
+
+#define NVS_SPECIAL_ATE_ID              0xffffffff
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Non-volatile Storage File system structure */
+
+struct nvs_fs
+{
+  FAR struct mtd_dev_s  *mtd;          /* mtd device */
+  uint32_t              ate_wra;       /* next alloc table entry
+                                        * write address
+                                        */
+  uint32_t              data_wra;      /* next data write address */
+  struct mtd_geometry_s geo;
+  uint32_t              step_addr;     /* for traverse */
+  mutex_t               nvs_lock;
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  uint32_t lookup_cache[CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE];
+#endif
+};
+
+/* Allocation Table Entry */
+
+begin_packed_struct struct nvs_ate
+{
+  uint32_t id;           /* data id */
+  uint16_t offset;       /* data offset within sector */
+  uint16_t len;          /* data len within sector */
+  uint16_t key_len;      /* key string len */
+  uint8_t  part;         /* part of a multipart data - future extension */
+  uint8_t  crc8;         /* crc8 check of the ate entry */
+  uint8_t  expired;      /* 0xFF-newest entry, others-old entry */
+  uint8_t  reserved[3];  /* for future extension */
+} end_packed_struct;
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* MTD NVS opeation api */
+
+static int     mtdconfig_open(FAR struct file *filep);
+static int     mtdconfig_close(FAR struct file *filep);
+static ssize_t mtdconfig_read(FAR struct file *filep, FAR char *buffer,
+                              size_t buflen);
+static int     mtdconfig_ioctl(FAR struct file *filep, int cmd,
+                               unsigned long arg);
+static int     mtdconfig_poll(FAR struct file *filep, FAR struct pollfd *fds,
+                              bool setup);
+
+/* Basic flash operation api */
+
+static ssize_t flash_read(FAR struct mtd_dev_s *mtd, off_t offset,
+                          FAR void *data, size_t len);
+static ssize_t flash_write(FAR struct mtd_dev_s *mtd, off_t offset,
+                           FAR const uint8_t *data, size_t len);
+static int flash_erase(FAR struct mtd_dev_s *mtd, off_t offset, size_t len);
+
+/* NVS operations needed by lookup cache */
+
+static int nvs_prev_ate(FAR struct nvs_fs *fs, FAR uint32_t *addr,
+                        FAR struct nvs_ate *ate);
+static int nvs_ate_valid(FAR struct nvs_fs *fs,
+                         FAR const struct nvs_ate *entry);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_mtdnvs_fops =
+{
+  mtdconfig_open,  /* open */
+  mtdconfig_close, /* close */
+  mtdconfig_read,  /* read */
+  NULL,            /* write */
+  NULL,            /* seek */
+  mtdconfig_ioctl, /* ioctl */
+  mtdconfig_poll   /* poll */
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+  , NULL           /* unlink */
+#endif
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nvs_fnv_hash
+ ****************************************************************************/
+
+static uint32_t nvs_fnv_hash(FAR const uint8_t *input, uint32_t len)
+{
+  uint32_t i = 0;
+  uint32_t hval = 2166136261;
+
+  /* FNV-1 hash each octet in the buffer */
+
+  while (i++ < len)
+    {
+      /* multiply by the 32 bit FNV magic prime mod 2^32 */
+
+      hval *= 0x01000193;
+
+      /* xor the bottom with the current octet */
+
+      hval ^= *input++;
+    }
+
+  return hval;
+}
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_pos
+ ****************************************************************************/
+
+static inline size_t nvs_lookup_cache_pos(uint32_t id)
+{
+  return id % CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_rebuild
+ ****************************************************************************/
+
+static int nvs_lookup_cache_rebuild(FAR struct nvs_fs *fs)
+{
+  int rc;
+  uint32_t addr;
+  uint32_t ate_addr;
+  FAR uint32_t *cache_entry;
+  struct nvs_ate ate;
+
+  memset(fs->lookup_cache, CONFIG_MTD_CONFIG_ERASEDVALUE,
+         sizeof(fs->lookup_cache));
+  addr = fs->ate_wra;
+
+  while (true)
+    {
+      /* Make a copy of 'addr' as it will be advanced by nvs_prev_ate() */
+
+      ate_addr = addr;
+      rc = nvs_prev_ate(fs, &addr, &ate);
+
+      if (rc)
+        {
+          return rc;
+        }
+
+      cache_entry = &fs->lookup_cache[nvs_lookup_cache_pos(ate.id)];
+
+      if (ate.id != NVS_SPECIAL_ATE_ID
+          && *cache_entry == NVS_LOOKUP_CACHE_NO_ADDR
+          && nvs_ate_valid(fs, &ate))
+        {
+          *cache_entry = ate_addr;
+        }
+
+      if (addr == fs->ate_wra)
+        {
+          break;
+        }
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_invalidate
+ ****************************************************************************/
+
+static void nvs_lookup_cache_invalidate(FAR struct nvs_fs *fs,
+                                        uint32_t sector)
+{
+  FAR uint32_t *cache_entry = fs->lookup_cache;
+  FAR uint32_t *const cache_end =
+    &fs->lookup_cache[CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE];
+
+  for (; cache_entry < cache_end; ++cache_entry)
+    {
+      if ((*cache_entry >> ADDR_SECT_SHIFT) == sector)
+        {
+          *cache_entry = NVS_LOOKUP_CACHE_NO_ADDR;
+        }
+    }
+}
+
+#endif /* CONFIG_MTD_NVS_LOOKUP_CACHE */
+
+/****************************************************************************
+ * Name: flash_read
+ ****************************************************************************/
+
+static ssize_t flash_read(FAR struct mtd_dev_s *mtd, off_t offset,
+                          FAR void *data, size_t len)
+{
+  return MTD_READ(mtd, offset, len, data);
+}
+
+/****************************************************************************
+ * Name: flash_write
+ ****************************************************************************/
+
+static ssize_t flash_write(FAR struct mtd_dev_s *mtd, off_t offset,
+                           FAR const uint8_t *data, size_t len)
+{
+  return MTD_WRITE(mtd, offset, len, data);
+}
+
+/****************************************************************************
+ * Name: flash_erase
+ ****************************************************************************/
+
+static int flash_erase(FAR struct mtd_dev_s *mtd, off_t startblock,
+                       size_t nblocks)
+{
+  return MTD_ERASE(mtd, startblock, nblocks);
+}
+
+/****************************************************************************
+ * Name: nvs_flash_wrt
+ *
+ * Description:
+ *   flash routines, process offset then write.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_wrt(FAR struct nvs_fs *fs, uint32_t addr,
+                            FAR const void *data, size_t len)
+{
+  off_t offset;
+  int ret;
+
+  offset = fs->geo.erasesize * (addr >> ADDR_SECT_SHIFT);
+  offset += addr & ADDR_OFFS_MASK;
+
+  ret = flash_write(fs->mtd, offset, data, len);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_rd
+ *
+ * Description:
+ *   basic flash read from nvs address.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_rd(FAR struct nvs_fs *fs, uint32_t addr,
+                        FAR void *data, size_t len)
+{
+  off_t offset;
+  int ret;
+
+  offset = fs->geo.erasesize * (addr >> ADDR_SECT_SHIFT);
+  offset += addr & ADDR_OFFS_MASK;
+
+  ret = flash_read(fs->mtd, offset, data, len);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_ate_wrt
+ *
+ * Description:
+ *   allocation entry write.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_ate_wrt(FAR struct nvs_fs *fs,
+                             FAR const struct nvs_ate *entry)
+{
+  int rc;
+
+  rc = nvs_flash_wrt(fs, fs->ate_wra, entry, sizeof(struct nvs_ate));
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  /* 0xffffffff is a special-purpose identifier. Exclude it from the cache */
+
+  if (entry->id != NVS_SPECIAL_ATE_ID)
+    {
+      fs->lookup_cache[nvs_lookup_cache_pos(entry->id)] = fs->ate_wra;
+    }
+#endif
+
+  fs->ate_wra -= sizeof(struct nvs_ate);
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_data_wrt
+ ****************************************************************************/
+
+static int nvs_flash_data_wrt(FAR struct nvs_fs *fs,
+                              FAR const void *data, size_t len)
+{
+  int rc;
+
+  rc = nvs_flash_wrt(fs, fs->data_wra, data, len);
+  fs->data_wra += len;
+  finfo("write data done, data_wra=0x%lx\n",
+        fs->data_wra);
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_ate_rd
+ ****************************************************************************/
+
+static int nvs_flash_ate_rd(FAR struct nvs_fs *fs, uint32_t addr,
+                            FAR struct nvs_ate *entry)
+{
+  return nvs_flash_rd(fs, addr, entry, sizeof(struct nvs_ate));
+}
+
+/****************************************************************************
+ * Name: nvs_flash_block_cmp
+ *
+ * Description:
+ *   nvs_flash_block_cmp compares the data in flash at addr to data
+ *   in blocks of size NVS_BLOCK_SIZE aligned to fs->write_block_size
+ *   returns 0 if equal, 1 if not equal, errcode if error
+ *
+ ****************************************************************************/
+
+static int nvs_flash_block_cmp(FAR struct nvs_fs *fs, uint32_t addr,
+                               FAR const void *data, size_t len)
+{
+  FAR const uint8_t *data8 = (FAR const uint8_t *)data;
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t buf[NVS_BUFFER_SIZE];
+
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr, buf, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = memcmp(data8, buf, bytes_to_cmp);
+      if (rc)
+        {
+          return 1;
+        }
+
+      len -= bytes_to_cmp;
+      addr += bytes_to_cmp;
+      data8 += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_direct_cmp
+ *
+ * Description:
+ *   nvs_flash_direct_cmp compares the data in flash at addr1 and addr2
+ *   of len in blocks of size NVS_BLOCK_SIZE aligned to fs->write_block_size
+ *   returns 0 if equal, 1 if not equal, errcode if error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_direct_cmp(FAR struct nvs_fs *fs, uint32_t addr1,
+                                uint32_t addr2, size_t len)
+{
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t buf1[NVS_BUFFER_SIZE];
+  uint8_t buf2[NVS_BUFFER_SIZE];
+
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr1, buf1, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = nvs_flash_rd(fs, addr2, buf2, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = memcmp(buf1, buf2, bytes_to_cmp);
+      if (rc)
+        {
+          return 1;
+        }
+
+      len -= bytes_to_cmp;
+      addr1 += bytes_to_cmp;
+      addr2 += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_cmp_const
+ *
+ * Description:
+ *   nvs_flash_cmp_const compares the data in flash at addr to a constant
+ *   value. returns 0 if all data in flash is equal to value, 1 if not equal,
+ *   errcode if error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_cmp_const(FAR struct nvs_fs *fs, uint32_t addr,
+                               uint8_t value, size_t len)
+{
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t cmp[NVS_BUFFER_SIZE];
+
+  memset(cmp, value, NVS_BUFFER_SIZE);
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_block_cmp(fs, addr, cmp, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      len -= bytes_to_cmp;
+      addr += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_block_move
+ *
+ * Description:
+ *   flash block move: move a block at addr to the current data write
+ *   location and updates the data write location.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_block_move(FAR struct nvs_fs *fs, uint32_t addr,
+                                size_t len)
+{
+  int rc;
+  size_t bytes_to_copy;
+  uint8_t buf[NVS_BUFFER_SIZE];
+
+  while (len)
+    {
+      bytes_to_copy = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr, buf, bytes_to_copy);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = nvs_flash_data_wrt(fs, buf, bytes_to_copy);
+      if (rc)
+        {
+          return rc;
+        }
+
+      len -= bytes_to_copy;
+      addr += bytes_to_copy;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_erase_sector
+ *
+ * Description:
+ *   erase a sector by first checking it is used and then erasing if required
+ *   return 0 if OK, errorcode on error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_erase_sector(FAR struct nvs_fs *fs, uint32_t addr)
+{
+  int rc;
+
+  ferr("Erasing addr %lx\n", addr);
+  addr &= ADDR_SECT_MASK;
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  nvs_lookup_cache_invalidate(fs, addr >> ADDR_SECT_SHIFT);
+#endif
+  rc = flash_erase(fs->mtd, addr >> ADDR_SECT_SHIFT, 1);
+
+  if (rc < 0)
+    {
+      ferr("Erasing failed %d\n", rc);
+      return rc;
+    }
+  else
+    {
+      rc = OK;
+    }
+
+  if (nvs_flash_cmp_const(fs, addr, CONFIG_MTD_CONFIG_ERASEDVALUE,
+                          fs->geo.erasesize))
+    {
+      ferr("Erasing not complete\n");
+      rc = -ENXIO;
+    }
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_crc8_update
+ *
+ * Description:
+ *   crc update on allocation entry.
+ *
+ ****************************************************************************/
+
+static void nvs_ate_crc8_update(FAR struct nvs_ate *entry)
+{
+  uint8_t ate_crc;
+
+  ate_crc = crc8part((FAR const uint8_t *)entry,
+                     offsetof(struct nvs_ate, crc8), 0xff);
+  entry->crc8 = ate_crc;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_crc8_check
+ *
+ * Description:
+ *   crc check on allocation entry
+ *   returns 0 if OK, 1 on crc fail
+ *
+ ****************************************************************************/
+
+static int nvs_ate_crc8_check(FAR const struct nvs_ate *entry)
+{
+  uint8_t ate_crc;
+
+  ate_crc = crc8part((FAR const uint8_t *)entry,
+                     offsetof(struct nvs_ate, crc8), 0xff);
+  if (ate_crc == entry->crc8)
+    {
+      return 0;
+    }
+
+  return 1;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_cmp_const
+ *
+ * Description:
+ *   nvs_ate_cmp_const compares an ATE to a constant value. returns 0 if
+ *   the whole ATE is equal to value, 1 if not equal.
+ *
+ ****************************************************************************/
+
+static int nvs_ate_cmp_const(FAR const struct nvs_ate *entry,
+                             uint8_t value)
+{
+  FAR const uint8_t *data8 = (FAR const uint8_t *)entry;
+  int i;
+
+  for (i = 0; i < sizeof(struct nvs_ate); i++)
+    {
+      if (data8[i] != value)
+        {
+          return 1;
+        }
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_valid
+ *
+ * Description:
+ *   return 1 if crc8 and offset valid, 0 otherwise
+ *
+ ****************************************************************************/
+
+static int nvs_ate_valid(FAR struct nvs_fs *fs,
+                         FAR const struct nvs_ate *entry)
+{
+  if ((nvs_ate_crc8_check(entry)) ||
+      (entry->offset >= (fs->geo.erasesize - sizeof(struct nvs_ate))))
+    {
+      return 0;
+    }
+
+  return 1;
+}
+
+/****************************************************************************
+ * Name: nvs_close_ate_valid
+ *
+ * Description:
+ *   nvs_close_ate_valid validates an sector close ate:
+ *   a valid sector close ate:
+ *   - valid ate
+ *   - len = 0 and id = 0xFFFFFFFF
+ *   - offset points to location at ate multiple from sector size
+ *   return 1 if valid, 0 otherwise
+ *
+ ****************************************************************************/
+
+static int nvs_close_ate_valid(FAR struct nvs_fs *fs,
+                               FAR const struct nvs_ate *entry)
+{
+  if ((!nvs_ate_valid(fs, entry)) || (entry->len != 0U) ||
+      (entry->id != NVS_SPECIAL_ATE_ID))
+    {
+      return 0;
+    }
+
+  if ((fs->geo.erasesize - entry->offset) % sizeof(struct nvs_ate))
+    {
+      return 0;
+    }
+
+  return 1;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_wrt_entry
+ *
+ * Description:
+ *   store an entry in flash
+ *
+ ****************************************************************************/
+
+static int nvs_flash_wrt_entry(FAR struct nvs_fs *fs, uint32_t id,
+            FAR const uint8_t *key, size_t key_size, FAR const void *data,
+            size_t len)
+{
+  int rc;
+  struct nvs_ate entry;
+
+  entry.id = id;
+  entry.offset = (uint16_t)(fs->data_wra & ADDR_OFFS_MASK);
+  entry.len = (uint16_t)len;
+  entry.key_len = (uint16_t)key_size;
+  entry.part = 0xff;
+
+  nvs_ate_crc8_update(&entry);
+
+  entry.expired = 0xff;

Review Comment:
   let call memset with erase value and remove all 0xff



##########
drivers/mtd/mtd_config_fs.c:
##########
@@ -0,0 +1,2484 @@
+/****************************************************************************
+ * drivers/mtd/mtd_config_fs.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.
+ *
+ * NVS: non volatile storage in flash
+ *
+ * Copyright (c) 2018 Laczen
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <sys/types.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <debug.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include <nuttx/crc8.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mtd/mtd.h>
+#include <nuttx/mtd/configdata.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define MIN(a, b)                       ((a) > (b) ? (b) : (a))
+
+/* MASKS AND SHIFT FOR ADDRESSES
+ * an address in nvs is an uint32_t where:
+ *   high 2 bytes represent the sector number
+ *   low 2 bytes represent the offset in a sector
+ */
+
+#define ADDR_SECT_MASK                  0xFFFF0000
+#define ADDR_SECT_SHIFT                 16
+#define ADDR_OFFS_MASK                  0x0000FFFF
+
+/* we don't want to store all the read content in stack or heap,
+ * so we make a buffer to do compare or move.
+ */
+
+#define NVS_BUFFER_SIZE                 32
+
+/* if data is written after last ate, and power loss happens,
+ * we need to find a clean offset by skipping dirty data.
+ * This macro defines how many bytes to skip when dirty data
+ * is spotted(may take several skips).
+ * Normally 1 byte is okay, such process only happens when
+ * nvs is started, and it is acceptable to take some time during
+ * starting.
+ */
+
+#define NVS_CORRUPT_DATA_SKIP_STEP      1
+
+#define NVS_LOOKUP_CACHE_NO_ADDR        0xffffffff
+
+/* gc done or close ate has the id of 0xffffffff.
+ * We can tell if the ate is special by looking at its id.
+ */
+
+#define NVS_SPECIAL_ATE_ID              0xffffffff
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Non-volatile Storage File system structure */
+
+struct nvs_fs
+{
+  FAR struct mtd_dev_s  *mtd;          /* mtd device */
+  uint32_t              ate_wra;       /* next alloc table entry
+                                        * write address
+                                        */
+  uint32_t              data_wra;      /* next data write address */
+  struct mtd_geometry_s geo;
+  uint32_t              step_addr;     /* for traverse */
+  mutex_t               nvs_lock;
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  uint32_t lookup_cache[CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE];
+#endif
+};
+
+/* Allocation Table Entry */
+
+begin_packed_struct struct nvs_ate
+{
+  uint32_t id;           /* data id */
+  uint16_t offset;       /* data offset within sector */
+  uint16_t len;          /* data len within sector */
+  uint16_t key_len;      /* key string len */
+  uint8_t  part;         /* part of a multipart data - future extension */
+  uint8_t  crc8;         /* crc8 check of the ate entry */
+  uint8_t  expired;      /* 0xFF-newest entry, others-old entry */
+  uint8_t  reserved[3];  /* for future extension */
+} end_packed_struct;
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* MTD NVS opeation api */
+
+static int     mtdconfig_open(FAR struct file *filep);
+static int     mtdconfig_close(FAR struct file *filep);
+static ssize_t mtdconfig_read(FAR struct file *filep, FAR char *buffer,
+                              size_t buflen);
+static int     mtdconfig_ioctl(FAR struct file *filep, int cmd,
+                               unsigned long arg);
+static int     mtdconfig_poll(FAR struct file *filep, FAR struct pollfd *fds,
+                              bool setup);
+
+/* Basic flash operation api */
+
+static ssize_t flash_read(FAR struct mtd_dev_s *mtd, off_t offset,
+                          FAR void *data, size_t len);
+static ssize_t flash_write(FAR struct mtd_dev_s *mtd, off_t offset,
+                           FAR const uint8_t *data, size_t len);
+static int flash_erase(FAR struct mtd_dev_s *mtd, off_t offset, size_t len);
+
+/* NVS operations needed by lookup cache */
+
+static int nvs_prev_ate(FAR struct nvs_fs *fs, FAR uint32_t *addr,
+                        FAR struct nvs_ate *ate);
+static int nvs_ate_valid(FAR struct nvs_fs *fs,
+                         FAR const struct nvs_ate *entry);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_mtdnvs_fops =
+{
+  mtdconfig_open,  /* open */
+  mtdconfig_close, /* close */
+  mtdconfig_read,  /* read */
+  NULL,            /* write */
+  NULL,            /* seek */
+  mtdconfig_ioctl, /* ioctl */
+  mtdconfig_poll   /* poll */
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+  , NULL           /* unlink */
+#endif
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nvs_fnv_hash
+ ****************************************************************************/
+
+static uint32_t nvs_fnv_hash(FAR const uint8_t *input, uint32_t len)
+{
+  uint32_t i = 0;
+  uint32_t hval = 2166136261;
+
+  /* FNV-1 hash each octet in the buffer */
+
+  while (i++ < len)
+    {
+      /* multiply by the 32 bit FNV magic prime mod 2^32 */
+
+      hval *= 0x01000193;
+
+      /* xor the bottom with the current octet */
+
+      hval ^= *input++;
+    }
+
+  return hval;
+}
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_pos
+ ****************************************************************************/
+
+static inline size_t nvs_lookup_cache_pos(uint32_t id)
+{
+  return id % CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_rebuild
+ ****************************************************************************/
+
+static int nvs_lookup_cache_rebuild(FAR struct nvs_fs *fs)
+{
+  int rc;
+  uint32_t addr;
+  uint32_t ate_addr;
+  FAR uint32_t *cache_entry;
+  struct nvs_ate ate;
+
+  memset(fs->lookup_cache, CONFIG_MTD_CONFIG_ERASEDVALUE,
+         sizeof(fs->lookup_cache));
+  addr = fs->ate_wra;
+
+  while (true)
+    {
+      /* Make a copy of 'addr' as it will be advanced by nvs_prev_ate() */
+
+      ate_addr = addr;
+      rc = nvs_prev_ate(fs, &addr, &ate);
+
+      if (rc)
+        {
+          return rc;
+        }
+
+      cache_entry = &fs->lookup_cache[nvs_lookup_cache_pos(ate.id)];
+
+      if (ate.id != NVS_SPECIAL_ATE_ID
+          && *cache_entry == NVS_LOOKUP_CACHE_NO_ADDR
+          && nvs_ate_valid(fs, &ate))
+        {
+          *cache_entry = ate_addr;
+        }
+
+      if (addr == fs->ate_wra)
+        {
+          break;
+        }
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_invalidate
+ ****************************************************************************/
+
+static void nvs_lookup_cache_invalidate(FAR struct nvs_fs *fs,
+                                        uint32_t sector)
+{
+  FAR uint32_t *cache_entry = fs->lookup_cache;
+  FAR uint32_t *const cache_end =
+    &fs->lookup_cache[CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE];
+
+  for (; cache_entry < cache_end; ++cache_entry)
+    {
+      if ((*cache_entry >> ADDR_SECT_SHIFT) == sector)
+        {
+          *cache_entry = NVS_LOOKUP_CACHE_NO_ADDR;
+        }
+    }
+}
+
+#endif /* CONFIG_MTD_NVS_LOOKUP_CACHE */
+
+/****************************************************************************
+ * Name: flash_read
+ ****************************************************************************/
+
+static ssize_t flash_read(FAR struct mtd_dev_s *mtd, off_t offset,
+                          FAR void *data, size_t len)
+{
+  return MTD_READ(mtd, offset, len, data);
+}
+
+/****************************************************************************
+ * Name: flash_write
+ ****************************************************************************/
+
+static ssize_t flash_write(FAR struct mtd_dev_s *mtd, off_t offset,
+                           FAR const uint8_t *data, size_t len)
+{
+  return MTD_WRITE(mtd, offset, len, data);
+}
+
+/****************************************************************************
+ * Name: flash_erase
+ ****************************************************************************/
+
+static int flash_erase(FAR struct mtd_dev_s *mtd, off_t startblock,
+                       size_t nblocks)
+{
+  return MTD_ERASE(mtd, startblock, nblocks);
+}
+
+/****************************************************************************
+ * Name: nvs_flash_wrt
+ *
+ * Description:
+ *   flash routines, process offset then write.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_wrt(FAR struct nvs_fs *fs, uint32_t addr,
+                            FAR const void *data, size_t len)
+{
+  off_t offset;
+  int ret;
+
+  offset = fs->geo.erasesize * (addr >> ADDR_SECT_SHIFT);
+  offset += addr & ADDR_OFFS_MASK;
+
+  ret = flash_write(fs->mtd, offset, data, len);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_rd
+ *
+ * Description:
+ *   basic flash read from nvs address.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_rd(FAR struct nvs_fs *fs, uint32_t addr,
+                        FAR void *data, size_t len)
+{
+  off_t offset;
+  int ret;
+
+  offset = fs->geo.erasesize * (addr >> ADDR_SECT_SHIFT);
+  offset += addr & ADDR_OFFS_MASK;
+
+  ret = flash_read(fs->mtd, offset, data, len);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_ate_wrt
+ *
+ * Description:
+ *   allocation entry write.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_ate_wrt(FAR struct nvs_fs *fs,
+                             FAR const struct nvs_ate *entry)
+{
+  int rc;
+
+  rc = nvs_flash_wrt(fs, fs->ate_wra, entry, sizeof(struct nvs_ate));
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  /* 0xffffffff is a special-purpose identifier. Exclude it from the cache */
+
+  if (entry->id != NVS_SPECIAL_ATE_ID)
+    {
+      fs->lookup_cache[nvs_lookup_cache_pos(entry->id)] = fs->ate_wra;
+    }
+#endif
+
+  fs->ate_wra -= sizeof(struct nvs_ate);
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_data_wrt
+ ****************************************************************************/
+
+static int nvs_flash_data_wrt(FAR struct nvs_fs *fs,
+                              FAR const void *data, size_t len)
+{
+  int rc;
+
+  rc = nvs_flash_wrt(fs, fs->data_wra, data, len);
+  fs->data_wra += len;
+  finfo("write data done, data_wra=0x%lx\n",
+        fs->data_wra);
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_ate_rd
+ ****************************************************************************/
+
+static int nvs_flash_ate_rd(FAR struct nvs_fs *fs, uint32_t addr,
+                            FAR struct nvs_ate *entry)
+{
+  return nvs_flash_rd(fs, addr, entry, sizeof(struct nvs_ate));
+}
+
+/****************************************************************************
+ * Name: nvs_flash_block_cmp
+ *
+ * Description:
+ *   nvs_flash_block_cmp compares the data in flash at addr to data
+ *   in blocks of size NVS_BLOCK_SIZE aligned to fs->write_block_size
+ *   returns 0 if equal, 1 if not equal, errcode if error
+ *
+ ****************************************************************************/
+
+static int nvs_flash_block_cmp(FAR struct nvs_fs *fs, uint32_t addr,
+                               FAR const void *data, size_t len)
+{
+  FAR const uint8_t *data8 = (FAR const uint8_t *)data;
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t buf[NVS_BUFFER_SIZE];
+
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr, buf, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = memcmp(data8, buf, bytes_to_cmp);
+      if (rc)
+        {
+          return 1;
+        }
+
+      len -= bytes_to_cmp;
+      addr += bytes_to_cmp;
+      data8 += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_direct_cmp
+ *
+ * Description:
+ *   nvs_flash_direct_cmp compares the data in flash at addr1 and addr2
+ *   of len in blocks of size NVS_BLOCK_SIZE aligned to fs->write_block_size
+ *   returns 0 if equal, 1 if not equal, errcode if error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_direct_cmp(FAR struct nvs_fs *fs, uint32_t addr1,
+                                uint32_t addr2, size_t len)
+{
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t buf1[NVS_BUFFER_SIZE];
+  uint8_t buf2[NVS_BUFFER_SIZE];
+
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr1, buf1, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = nvs_flash_rd(fs, addr2, buf2, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = memcmp(buf1, buf2, bytes_to_cmp);
+      if (rc)
+        {
+          return 1;
+        }
+
+      len -= bytes_to_cmp;
+      addr1 += bytes_to_cmp;
+      addr2 += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_cmp_const
+ *
+ * Description:
+ *   nvs_flash_cmp_const compares the data in flash at addr to a constant
+ *   value. returns 0 if all data in flash is equal to value, 1 if not equal,
+ *   errcode if error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_cmp_const(FAR struct nvs_fs *fs, uint32_t addr,
+                               uint8_t value, size_t len)
+{
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t cmp[NVS_BUFFER_SIZE];
+
+  memset(cmp, value, NVS_BUFFER_SIZE);
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_block_cmp(fs, addr, cmp, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      len -= bytes_to_cmp;
+      addr += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_block_move
+ *
+ * Description:
+ *   flash block move: move a block at addr to the current data write
+ *   location and updates the data write location.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_block_move(FAR struct nvs_fs *fs, uint32_t addr,
+                                size_t len)
+{
+  int rc;
+  size_t bytes_to_copy;
+  uint8_t buf[NVS_BUFFER_SIZE];
+
+  while (len)
+    {
+      bytes_to_copy = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr, buf, bytes_to_copy);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = nvs_flash_data_wrt(fs, buf, bytes_to_copy);
+      if (rc)
+        {
+          return rc;
+        }
+
+      len -= bytes_to_copy;
+      addr += bytes_to_copy;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_erase_sector
+ *
+ * Description:
+ *   erase a sector by first checking it is used and then erasing if required
+ *   return 0 if OK, errorcode on error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_erase_sector(FAR struct nvs_fs *fs, uint32_t addr)
+{
+  int rc;
+
+  ferr("Erasing addr %lx\n", addr);
+  addr &= ADDR_SECT_MASK;
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  nvs_lookup_cache_invalidate(fs, addr >> ADDR_SECT_SHIFT);
+#endif
+  rc = flash_erase(fs->mtd, addr >> ADDR_SECT_SHIFT, 1);
+
+  if (rc < 0)
+    {
+      ferr("Erasing failed %d\n", rc);
+      return rc;
+    }
+  else
+    {
+      rc = OK;
+    }
+
+  if (nvs_flash_cmp_const(fs, addr, CONFIG_MTD_CONFIG_ERASEDVALUE,
+                          fs->geo.erasesize))
+    {
+      ferr("Erasing not complete\n");
+      rc = -ENXIO;
+    }
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_crc8_update
+ *
+ * Description:
+ *   crc update on allocation entry.
+ *
+ ****************************************************************************/
+
+static void nvs_ate_crc8_update(FAR struct nvs_ate *entry)
+{
+  uint8_t ate_crc;
+
+  ate_crc = crc8part((FAR const uint8_t *)entry,
+                     offsetof(struct nvs_ate, crc8), 0xff);
+  entry->crc8 = ate_crc;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_crc8_check
+ *
+ * Description:
+ *   crc check on allocation entry
+ *   returns 0 if OK, 1 on crc fail
+ *
+ ****************************************************************************/
+
+static int nvs_ate_crc8_check(FAR const struct nvs_ate *entry)
+{
+  uint8_t ate_crc;
+
+  ate_crc = crc8part((FAR const uint8_t *)entry,
+                     offsetof(struct nvs_ate, crc8), 0xff);
+  if (ate_crc == entry->crc8)
+    {
+      return 0;
+    }
+
+  return 1;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_cmp_const
+ *
+ * Description:
+ *   nvs_ate_cmp_const compares an ATE to a constant value. returns 0 if
+ *   the whole ATE is equal to value, 1 if not equal.
+ *
+ ****************************************************************************/
+
+static int nvs_ate_cmp_const(FAR const struct nvs_ate *entry,
+                             uint8_t value)
+{
+  FAR const uint8_t *data8 = (FAR const uint8_t *)entry;
+  int i;
+
+  for (i = 0; i < sizeof(struct nvs_ate); i++)
+    {
+      if (data8[i] != value)
+        {
+          return 1;
+        }
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_valid
+ *
+ * Description:
+ *   return 1 if crc8 and offset valid, 0 otherwise
+ *
+ ****************************************************************************/
+
+static int nvs_ate_valid(FAR struct nvs_fs *fs,
+                         FAR const struct nvs_ate *entry)
+{
+  if ((nvs_ate_crc8_check(entry)) ||
+      (entry->offset >= (fs->geo.erasesize - sizeof(struct nvs_ate))))
+    {
+      return 0;
+    }
+
+  return 1;
+}
+
+/****************************************************************************
+ * Name: nvs_close_ate_valid
+ *
+ * Description:
+ *   nvs_close_ate_valid validates an sector close ate:
+ *   a valid sector close ate:
+ *   - valid ate
+ *   - len = 0 and id = 0xFFFFFFFF
+ *   - offset points to location at ate multiple from sector size
+ *   return 1 if valid, 0 otherwise
+ *
+ ****************************************************************************/
+
+static int nvs_close_ate_valid(FAR struct nvs_fs *fs,
+                               FAR const struct nvs_ate *entry)
+{
+  if ((!nvs_ate_valid(fs, entry)) || (entry->len != 0U) ||
+      (entry->id != NVS_SPECIAL_ATE_ID))
+    {
+      return 0;
+    }
+
+  if ((fs->geo.erasesize - entry->offset) % sizeof(struct nvs_ate))
+    {
+      return 0;
+    }
+
+  return 1;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_wrt_entry
+ *
+ * Description:
+ *   store an entry in flash
+ *
+ ****************************************************************************/
+
+static int nvs_flash_wrt_entry(FAR struct nvs_fs *fs, uint32_t id,
+            FAR const uint8_t *key, size_t key_size, FAR const void *data,
+            size_t len)
+{
+  int rc;
+  struct nvs_ate entry;
+
+  entry.id = id;
+  entry.offset = (uint16_t)(fs->data_wra & ADDR_OFFS_MASK);
+  entry.len = (uint16_t)len;
+  entry.key_len = (uint16_t)key_size;
+  entry.part = 0xff;
+
+  nvs_ate_crc8_update(&entry);
+
+  entry.expired = 0xff;
+  entry.reserved[0] = 0xff;
+  entry.reserved[1] = 0xff;
+  entry.reserved[2] = 0xff;
+
+  /* let's sew key and data into one, key comes first, then data */
+
+  rc = nvs_flash_data_wrt(fs, key, key_size);
+  if (rc)
+    {
+      ferr("Write key failed, rc=%d\n", rc);
+      return rc;
+    }
+
+  rc = nvs_flash_data_wrt(fs, data, len);
+  if (rc)
+    {
+      ferr("Write value failed, rc=%d\n", rc);
+      return rc;
+    }
+
+  rc = nvs_flash_ate_wrt(fs, &entry);
+  if (rc)
+    {
+      ferr("Write ate failed, rc=%d\n", rc);
+      return rc;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_recover_last_ate
+ *
+ * Description:
+ *   If the closing ate is invalid, its offset cannot be trusted and
+ *   the last valid ate of the sector should instead try to be recovered
+ *   by going through all ate's.
+ *
+ *   addr should point to the faulty closing ate and will be updated to
+ *   the last valid ate. If no valid ate is found it will be left untouched.
+ *
+ ****************************************************************************/
+
+static int nvs_recover_last_ate(FAR struct nvs_fs *fs,
+                                FAR uint32_t *addr)
+{
+  uint32_t data_end_addr;
+  uint32_t ate_end_addr;
+  struct nvs_ate end_ate;
+  int rc;
+
+  finfo("Recovering last ate from sector %" PRIu32 "\n",
+        (*addr >> ADDR_SECT_SHIFT));
+
+  *addr -= sizeof(struct nvs_ate);
+  ate_end_addr = *addr;
+  data_end_addr = *addr & ADDR_SECT_MASK;
+  while (ate_end_addr > data_end_addr)
+    {
+      rc = nvs_flash_ate_rd(fs, ate_end_addr, &end_ate);
+      if (rc)
+        {
+          return rc;
+        }
+
+      if (nvs_ate_valid(fs, &end_ate))
+        {
+          /* Found a valid ate, update data_end_addr and *addr */
+
+          data_end_addr &= ADDR_SECT_MASK;
+          data_end_addr += end_ate.offset + end_ate.len + end_ate.key_len;
+          *addr = ate_end_addr;
+        }
+
+      ate_end_addr -= sizeof(struct nvs_ate);
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_prev_ate
+ *
+ * Description:
+ *   walking through allocation entry list, from newest to oldest entries
+ *   read ate from addr, modify addr to the previous ate.
+ *
+ ****************************************************************************/
+
+static int nvs_prev_ate(FAR struct nvs_fs *fs, FAR uint32_t *addr,
+                        FAR struct nvs_ate *ate)
+{
+  int rc;
+  struct nvs_ate close_ate;
+
+  rc = nvs_flash_ate_rd(fs, *addr, ate);
+  if (rc)
+    {
+      return rc;
+    }
+
+  *addr += sizeof(struct nvs_ate);
+  if (((*addr) & ADDR_OFFS_MASK) !=
+      (fs->geo.erasesize - sizeof(struct nvs_ate)))
+    {
+      return 0;
+    }
+
+  /* last ate in sector, do jump to previous sector */
+
+  if (((*addr) >> ADDR_SECT_SHIFT) == 0U)
+    {
+      *addr += ((fs->geo.neraseblocks - 1) << ADDR_SECT_SHIFT);
+    }
+  else
+    {
+      *addr -= (1 << ADDR_SECT_SHIFT);
+    }
+
+  rc = nvs_flash_ate_rd(fs, *addr, &close_ate);
+  if (rc)
+    {
+      return rc;
+    }
+
+  rc = nvs_ate_cmp_const(&close_ate, CONFIG_MTD_CONFIG_ERASEDVALUE);
+
+  /* at the end of filesystem */

Review Comment:
   why can identify the file system end here?



##########
drivers/mtd/mtd_config_fs.c:
##########
@@ -0,0 +1,2484 @@
+/****************************************************************************
+ * drivers/mtd/mtd_config_fs.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.
+ *
+ * NVS: non volatile storage in flash
+ *
+ * Copyright (c) 2018 Laczen
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <sys/types.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <debug.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include <nuttx/crc8.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mtd/mtd.h>
+#include <nuttx/mtd/configdata.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define MIN(a, b)                       ((a) > (b) ? (b) : (a))
+
+/* MASKS AND SHIFT FOR ADDRESSES
+ * an address in nvs is an uint32_t where:
+ *   high 2 bytes represent the sector number
+ *   low 2 bytes represent the offset in a sector
+ */
+
+#define ADDR_SECT_MASK                  0xFFFF0000
+#define ADDR_SECT_SHIFT                 16
+#define ADDR_OFFS_MASK                  0x0000FFFF
+
+/* we don't want to store all the read content in stack or heap,
+ * so we make a buffer to do compare or move.
+ */
+
+#define NVS_BUFFER_SIZE                 32
+
+/* if data is written after last ate, and power loss happens,
+ * we need to find a clean offset by skipping dirty data.
+ * This macro defines how many bytes to skip when dirty data
+ * is spotted(may take several skips).
+ * Normally 1 byte is okay, such process only happens when
+ * nvs is started, and it is acceptable to take some time during
+ * starting.
+ */
+
+#define NVS_CORRUPT_DATA_SKIP_STEP      1
+
+#define NVS_LOOKUP_CACHE_NO_ADDR        0xffffffff
+
+/* gc done or close ate has the id of 0xffffffff.
+ * We can tell if the ate is special by looking at its id.
+ */
+
+#define NVS_SPECIAL_ATE_ID              0xffffffff
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Non-volatile Storage File system structure */
+
+struct nvs_fs
+{
+  FAR struct mtd_dev_s  *mtd;          /* mtd device */
+  uint32_t              ate_wra;       /* next alloc table entry
+                                        * write address
+                                        */
+  uint32_t              data_wra;      /* next data write address */
+  struct mtd_geometry_s geo;
+  uint32_t              step_addr;     /* for traverse */
+  mutex_t               nvs_lock;
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  uint32_t lookup_cache[CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE];
+#endif
+};
+
+/* Allocation Table Entry */
+
+begin_packed_struct struct nvs_ate
+{
+  uint32_t id;           /* data id */
+  uint16_t offset;       /* data offset within sector */
+  uint16_t len;          /* data len within sector */
+  uint16_t key_len;      /* key string len */
+  uint8_t  part;         /* part of a multipart data - future extension */
+  uint8_t  crc8;         /* crc8 check of the ate entry */
+  uint8_t  expired;      /* 0xFF-newest entry, others-old entry */
+  uint8_t  reserved[3];  /* for future extension */
+} end_packed_struct;
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* MTD NVS opeation api */
+
+static int     mtdconfig_open(FAR struct file *filep);
+static int     mtdconfig_close(FAR struct file *filep);
+static ssize_t mtdconfig_read(FAR struct file *filep, FAR char *buffer,
+                              size_t buflen);
+static int     mtdconfig_ioctl(FAR struct file *filep, int cmd,
+                               unsigned long arg);
+static int     mtdconfig_poll(FAR struct file *filep, FAR struct pollfd *fds,
+                              bool setup);
+
+/* Basic flash operation api */
+
+static ssize_t flash_read(FAR struct mtd_dev_s *mtd, off_t offset,
+                          FAR void *data, size_t len);
+static ssize_t flash_write(FAR struct mtd_dev_s *mtd, off_t offset,
+                           FAR const uint8_t *data, size_t len);
+static int flash_erase(FAR struct mtd_dev_s *mtd, off_t offset, size_t len);
+
+/* NVS operations needed by lookup cache */
+
+static int nvs_prev_ate(FAR struct nvs_fs *fs, FAR uint32_t *addr,
+                        FAR struct nvs_ate *ate);
+static int nvs_ate_valid(FAR struct nvs_fs *fs,
+                         FAR const struct nvs_ate *entry);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_mtdnvs_fops =
+{
+  mtdconfig_open,  /* open */
+  mtdconfig_close, /* close */
+  mtdconfig_read,  /* read */
+  NULL,            /* write */
+  NULL,            /* seek */
+  mtdconfig_ioctl, /* ioctl */
+  mtdconfig_poll   /* poll */
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+  , NULL           /* unlink */
+#endif
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nvs_fnv_hash
+ ****************************************************************************/
+
+static uint32_t nvs_fnv_hash(FAR const uint8_t *input, uint32_t len)
+{
+  uint32_t i = 0;
+  uint32_t hval = 2166136261;
+
+  /* FNV-1 hash each octet in the buffer */
+
+  while (i++ < len)
+    {
+      /* multiply by the 32 bit FNV magic prime mod 2^32 */
+
+      hval *= 0x01000193;
+
+      /* xor the bottom with the current octet */
+
+      hval ^= *input++;
+    }
+
+  return hval;
+}
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_pos
+ ****************************************************************************/
+
+static inline size_t nvs_lookup_cache_pos(uint32_t id)
+{
+  return id % CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_rebuild
+ ****************************************************************************/
+
+static int nvs_lookup_cache_rebuild(FAR struct nvs_fs *fs)
+{
+  int rc;
+  uint32_t addr;
+  uint32_t ate_addr;
+  FAR uint32_t *cache_entry;
+  struct nvs_ate ate;
+
+  memset(fs->lookup_cache, CONFIG_MTD_CONFIG_ERASEDVALUE,
+         sizeof(fs->lookup_cache));
+  addr = fs->ate_wra;
+
+  while (true)
+    {
+      /* Make a copy of 'addr' as it will be advanced by nvs_prev_ate() */
+
+      ate_addr = addr;
+      rc = nvs_prev_ate(fs, &addr, &ate);
+
+      if (rc)
+        {
+          return rc;
+        }
+
+      cache_entry = &fs->lookup_cache[nvs_lookup_cache_pos(ate.id)];
+
+      if (ate.id != NVS_SPECIAL_ATE_ID
+          && *cache_entry == NVS_LOOKUP_CACHE_NO_ADDR
+          && nvs_ate_valid(fs, &ate))
+        {
+          *cache_entry = ate_addr;
+        }
+
+      if (addr == fs->ate_wra)
+        {
+          break;
+        }
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_invalidate
+ ****************************************************************************/
+
+static void nvs_lookup_cache_invalidate(FAR struct nvs_fs *fs,
+                                        uint32_t sector)

Review Comment:
   sector->block? nuttx mtd don't have sector concept



##########
drivers/mtd/mtd_config_fs.c:
##########
@@ -0,0 +1,2484 @@
+/****************************************************************************
+ * drivers/mtd/mtd_config_fs.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.
+ *
+ * NVS: non volatile storage in flash
+ *
+ * Copyright (c) 2018 Laczen
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <sys/types.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <debug.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include <nuttx/crc8.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mtd/mtd.h>
+#include <nuttx/mtd/configdata.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define MIN(a, b)                       ((a) > (b) ? (b) : (a))
+
+/* MASKS AND SHIFT FOR ADDRESSES
+ * an address in nvs is an uint32_t where:
+ *   high 2 bytes represent the sector number
+ *   low 2 bytes represent the offset in a sector
+ */
+
+#define ADDR_SECT_MASK                  0xFFFF0000
+#define ADDR_SECT_SHIFT                 16
+#define ADDR_OFFS_MASK                  0x0000FFFF
+
+/* we don't want to store all the read content in stack or heap,
+ * so we make a buffer to do compare or move.
+ */
+
+#define NVS_BUFFER_SIZE                 32
+
+/* if data is written after last ate, and power loss happens,
+ * we need to find a clean offset by skipping dirty data.
+ * This macro defines how many bytes to skip when dirty data
+ * is spotted(may take several skips).
+ * Normally 1 byte is okay, such process only happens when
+ * nvs is started, and it is acceptable to take some time during
+ * starting.
+ */
+
+#define NVS_CORRUPT_DATA_SKIP_STEP      1
+
+#define NVS_LOOKUP_CACHE_NO_ADDR        0xffffffff
+
+/* gc done or close ate has the id of 0xffffffff.
+ * We can tell if the ate is special by looking at its id.
+ */
+
+#define NVS_SPECIAL_ATE_ID              0xffffffff
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Non-volatile Storage File system structure */
+
+struct nvs_fs
+{
+  FAR struct mtd_dev_s  *mtd;          /* mtd device */
+  uint32_t              ate_wra;       /* next alloc table entry
+                                        * write address
+                                        */
+  uint32_t              data_wra;      /* next data write address */
+  struct mtd_geometry_s geo;
+  uint32_t              step_addr;     /* for traverse */
+  mutex_t               nvs_lock;
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  uint32_t lookup_cache[CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE];
+#endif
+};
+
+/* Allocation Table Entry */
+
+begin_packed_struct struct nvs_ate
+{
+  uint32_t id;           /* data id */
+  uint16_t offset;       /* data offset within sector */
+  uint16_t len;          /* data len within sector */
+  uint16_t key_len;      /* key string len */
+  uint8_t  part;         /* part of a multipart data - future extension */
+  uint8_t  crc8;         /* crc8 check of the ate entry */
+  uint8_t  expired;      /* 0xFF-newest entry, others-old entry */
+  uint8_t  reserved[3];  /* for future extension */
+} end_packed_struct;
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* MTD NVS opeation api */
+
+static int     mtdconfig_open(FAR struct file *filep);
+static int     mtdconfig_close(FAR struct file *filep);
+static ssize_t mtdconfig_read(FAR struct file *filep, FAR char *buffer,
+                              size_t buflen);
+static int     mtdconfig_ioctl(FAR struct file *filep, int cmd,
+                               unsigned long arg);
+static int     mtdconfig_poll(FAR struct file *filep, FAR struct pollfd *fds,
+                              bool setup);
+
+/* Basic flash operation api */
+
+static ssize_t flash_read(FAR struct mtd_dev_s *mtd, off_t offset,
+                          FAR void *data, size_t len);
+static ssize_t flash_write(FAR struct mtd_dev_s *mtd, off_t offset,
+                           FAR const uint8_t *data, size_t len);
+static int flash_erase(FAR struct mtd_dev_s *mtd, off_t offset, size_t len);
+
+/* NVS operations needed by lookup cache */
+
+static int nvs_prev_ate(FAR struct nvs_fs *fs, FAR uint32_t *addr,
+                        FAR struct nvs_ate *ate);
+static int nvs_ate_valid(FAR struct nvs_fs *fs,
+                         FAR const struct nvs_ate *entry);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_mtdnvs_fops =
+{
+  mtdconfig_open,  /* open */
+  mtdconfig_close, /* close */
+  mtdconfig_read,  /* read */
+  NULL,            /* write */
+  NULL,            /* seek */
+  mtdconfig_ioctl, /* ioctl */
+  mtdconfig_poll   /* poll */
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+  , NULL           /* unlink */
+#endif
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nvs_fnv_hash
+ ****************************************************************************/
+
+static uint32_t nvs_fnv_hash(FAR const uint8_t *input, uint32_t len)
+{
+  uint32_t i = 0;
+  uint32_t hval = 2166136261;
+
+  /* FNV-1 hash each octet in the buffer */
+
+  while (i++ < len)
+    {
+      /* multiply by the 32 bit FNV magic prime mod 2^32 */
+
+      hval *= 0x01000193;
+
+      /* xor the bottom with the current octet */
+
+      hval ^= *input++;
+    }
+
+  return hval;
+}
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_pos
+ ****************************************************************************/
+
+static inline size_t nvs_lookup_cache_pos(uint32_t id)
+{
+  return id % CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_rebuild
+ ****************************************************************************/
+
+static int nvs_lookup_cache_rebuild(FAR struct nvs_fs *fs)
+{
+  int rc;
+  uint32_t addr;
+  uint32_t ate_addr;
+  FAR uint32_t *cache_entry;
+  struct nvs_ate ate;
+
+  memset(fs->lookup_cache, CONFIG_MTD_CONFIG_ERASEDVALUE,
+         sizeof(fs->lookup_cache));
+  addr = fs->ate_wra;
+
+  while (true)
+    {
+      /* Make a copy of 'addr' as it will be advanced by nvs_prev_ate() */
+
+      ate_addr = addr;
+      rc = nvs_prev_ate(fs, &addr, &ate);
+
+      if (rc)
+        {
+          return rc;
+        }
+
+      cache_entry = &fs->lookup_cache[nvs_lookup_cache_pos(ate.id)];
+
+      if (ate.id != NVS_SPECIAL_ATE_ID
+          && *cache_entry == NVS_LOOKUP_CACHE_NO_ADDR
+          && nvs_ate_valid(fs, &ate))
+        {
+          *cache_entry = ate_addr;
+        }
+
+      if (addr == fs->ate_wra)
+        {
+          break;
+        }
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_invalidate
+ ****************************************************************************/
+
+static void nvs_lookup_cache_invalidate(FAR struct nvs_fs *fs,
+                                        uint32_t sector)
+{
+  FAR uint32_t *cache_entry = fs->lookup_cache;
+  FAR uint32_t *const cache_end =
+    &fs->lookup_cache[CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE];
+
+  for (; cache_entry < cache_end; ++cache_entry)
+    {
+      if ((*cache_entry >> ADDR_SECT_SHIFT) == sector)
+        {
+          *cache_entry = NVS_LOOKUP_CACHE_NO_ADDR;
+        }
+    }
+}
+
+#endif /* CONFIG_MTD_NVS_LOOKUP_CACHE */
+
+/****************************************************************************
+ * Name: flash_read
+ ****************************************************************************/
+
+static ssize_t flash_read(FAR struct mtd_dev_s *mtd, off_t offset,
+                          FAR void *data, size_t len)
+{
+  return MTD_READ(mtd, offset, len, data);
+}
+
+/****************************************************************************
+ * Name: flash_write
+ ****************************************************************************/
+
+static ssize_t flash_write(FAR struct mtd_dev_s *mtd, off_t offset,
+                           FAR const uint8_t *data, size_t len)
+{
+  return MTD_WRITE(mtd, offset, len, data);
+}
+
+/****************************************************************************
+ * Name: flash_erase
+ ****************************************************************************/
+
+static int flash_erase(FAR struct mtd_dev_s *mtd, off_t startblock,
+                       size_t nblocks)
+{
+  return MTD_ERASE(mtd, startblock, nblocks);
+}
+
+/****************************************************************************
+ * Name: nvs_flash_wrt
+ *
+ * Description:
+ *   flash routines, process offset then write.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_wrt(FAR struct nvs_fs *fs, uint32_t addr,
+                            FAR const void *data, size_t len)
+{
+  off_t offset;
+  int ret;
+
+  offset = fs->geo.erasesize * (addr >> ADDR_SECT_SHIFT);
+  offset += addr & ADDR_OFFS_MASK;
+
+  ret = flash_write(fs->mtd, offset, data, len);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_rd
+ *
+ * Description:
+ *   basic flash read from nvs address.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_rd(FAR struct nvs_fs *fs, uint32_t addr,
+                        FAR void *data, size_t len)
+{
+  off_t offset;
+  int ret;
+
+  offset = fs->geo.erasesize * (addr >> ADDR_SECT_SHIFT);
+  offset += addr & ADDR_OFFS_MASK;
+
+  ret = flash_read(fs->mtd, offset, data, len);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_ate_wrt
+ *
+ * Description:
+ *   allocation entry write.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_ate_wrt(FAR struct nvs_fs *fs,
+                             FAR const struct nvs_ate *entry)
+{
+  int rc;
+
+  rc = nvs_flash_wrt(fs, fs->ate_wra, entry, sizeof(struct nvs_ate));
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  /* 0xffffffff is a special-purpose identifier. Exclude it from the cache */
+
+  if (entry->id != NVS_SPECIAL_ATE_ID)
+    {
+      fs->lookup_cache[nvs_lookup_cache_pos(entry->id)] = fs->ate_wra;
+    }
+#endif
+
+  fs->ate_wra -= sizeof(struct nvs_ate);
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_data_wrt
+ ****************************************************************************/
+
+static int nvs_flash_data_wrt(FAR struct nvs_fs *fs,
+                              FAR const void *data, size_t len)
+{
+  int rc;
+
+  rc = nvs_flash_wrt(fs, fs->data_wra, data, len);
+  fs->data_wra += len;
+  finfo("write data done, data_wra=0x%lx\n",
+        fs->data_wra);
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_ate_rd
+ ****************************************************************************/
+
+static int nvs_flash_ate_rd(FAR struct nvs_fs *fs, uint32_t addr,
+                            FAR struct nvs_ate *entry)
+{
+  return nvs_flash_rd(fs, addr, entry, sizeof(struct nvs_ate));
+}
+
+/****************************************************************************
+ * Name: nvs_flash_block_cmp
+ *
+ * Description:
+ *   nvs_flash_block_cmp compares the data in flash at addr to data
+ *   in blocks of size NVS_BLOCK_SIZE aligned to fs->write_block_size
+ *   returns 0 if equal, 1 if not equal, errcode if error
+ *
+ ****************************************************************************/
+
+static int nvs_flash_block_cmp(FAR struct nvs_fs *fs, uint32_t addr,
+                               FAR const void *data, size_t len)
+{
+  FAR const uint8_t *data8 = (FAR const uint8_t *)data;
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t buf[NVS_BUFFER_SIZE];
+
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr, buf, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = memcmp(data8, buf, bytes_to_cmp);
+      if (rc)
+        {
+          return 1;
+        }
+
+      len -= bytes_to_cmp;
+      addr += bytes_to_cmp;
+      data8 += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_direct_cmp
+ *
+ * Description:
+ *   nvs_flash_direct_cmp compares the data in flash at addr1 and addr2
+ *   of len in blocks of size NVS_BLOCK_SIZE aligned to fs->write_block_size
+ *   returns 0 if equal, 1 if not equal, errcode if error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_direct_cmp(FAR struct nvs_fs *fs, uint32_t addr1,
+                                uint32_t addr2, size_t len)
+{
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t buf1[NVS_BUFFER_SIZE];
+  uint8_t buf2[NVS_BUFFER_SIZE];
+
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr1, buf1, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = nvs_flash_rd(fs, addr2, buf2, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = memcmp(buf1, buf2, bytes_to_cmp);
+      if (rc)
+        {
+          return 1;
+        }
+
+      len -= bytes_to_cmp;
+      addr1 += bytes_to_cmp;
+      addr2 += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_cmp_const
+ *
+ * Description:
+ *   nvs_flash_cmp_const compares the data in flash at addr to a constant
+ *   value. returns 0 if all data in flash is equal to value, 1 if not equal,
+ *   errcode if error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_cmp_const(FAR struct nvs_fs *fs, uint32_t addr,
+                               uint8_t value, size_t len)
+{
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t cmp[NVS_BUFFER_SIZE];
+
+  memset(cmp, value, NVS_BUFFER_SIZE);
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_block_cmp(fs, addr, cmp, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      len -= bytes_to_cmp;
+      addr += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_block_move
+ *
+ * Description:
+ *   flash block move: move a block at addr to the current data write
+ *   location and updates the data write location.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_block_move(FAR struct nvs_fs *fs, uint32_t addr,
+                                size_t len)
+{
+  int rc;
+  size_t bytes_to_copy;
+  uint8_t buf[NVS_BUFFER_SIZE];
+
+  while (len)
+    {
+      bytes_to_copy = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr, buf, bytes_to_copy);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = nvs_flash_data_wrt(fs, buf, bytes_to_copy);
+      if (rc)
+        {
+          return rc;
+        }
+
+      len -= bytes_to_copy;
+      addr += bytes_to_copy;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_erase_sector
+ *
+ * Description:
+ *   erase a sector by first checking it is used and then erasing if required
+ *   return 0 if OK, errorcode on error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_erase_sector(FAR struct nvs_fs *fs, uint32_t addr)
+{
+  int rc;
+
+  ferr("Erasing addr %lx\n", addr);
+  addr &= ADDR_SECT_MASK;
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  nvs_lookup_cache_invalidate(fs, addr >> ADDR_SECT_SHIFT);
+#endif
+  rc = flash_erase(fs->mtd, addr >> ADDR_SECT_SHIFT, 1);
+
+  if (rc < 0)
+    {
+      ferr("Erasing failed %d\n", rc);
+      return rc;
+    }
+  else
+    {
+      rc = OK;
+    }
+
+  if (nvs_flash_cmp_const(fs, addr, CONFIG_MTD_CONFIG_ERASEDVALUE,
+                          fs->geo.erasesize))
+    {
+      ferr("Erasing not complete\n");
+      rc = -ENXIO;
+    }
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_crc8_update
+ *
+ * Description:
+ *   crc update on allocation entry.
+ *
+ ****************************************************************************/
+
+static void nvs_ate_crc8_update(FAR struct nvs_ate *entry)
+{
+  uint8_t ate_crc;
+
+  ate_crc = crc8part((FAR const uint8_t *)entry,
+                     offsetof(struct nvs_ate, crc8), 0xff);
+  entry->crc8 = ate_crc;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_crc8_check
+ *
+ * Description:
+ *   crc check on allocation entry
+ *   returns 0 if OK, 1 on crc fail
+ *
+ ****************************************************************************/
+
+static int nvs_ate_crc8_check(FAR const struct nvs_ate *entry)
+{
+  uint8_t ate_crc;
+
+  ate_crc = crc8part((FAR const uint8_t *)entry,
+                     offsetof(struct nvs_ate, crc8), 0xff);
+  if (ate_crc == entry->crc8)
+    {
+      return 0;
+    }
+
+  return 1;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_cmp_const
+ *
+ * Description:
+ *   nvs_ate_cmp_const compares an ATE to a constant value. returns 0 if
+ *   the whole ATE is equal to value, 1 if not equal.
+ *
+ ****************************************************************************/
+
+static int nvs_ate_cmp_const(FAR const struct nvs_ate *entry,
+                             uint8_t value)
+{
+  FAR const uint8_t *data8 = (FAR const uint8_t *)entry;
+  int i;
+
+  for (i = 0; i < sizeof(struct nvs_ate); i++)
+    {
+      if (data8[i] != value)
+        {
+          return 1;
+        }
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_valid
+ *
+ * Description:
+ *   return 1 if crc8 and offset valid, 0 otherwise
+ *
+ ****************************************************************************/
+
+static int nvs_ate_valid(FAR struct nvs_fs *fs,
+                         FAR const struct nvs_ate *entry)
+{
+  if ((nvs_ate_crc8_check(entry)) ||
+      (entry->offset >= (fs->geo.erasesize - sizeof(struct nvs_ate))))
+    {
+      return 0;
+    }
+
+  return 1;
+}
+
+/****************************************************************************
+ * Name: nvs_close_ate_valid
+ *
+ * Description:
+ *   nvs_close_ate_valid validates an sector close ate:
+ *   a valid sector close ate:
+ *   - valid ate
+ *   - len = 0 and id = 0xFFFFFFFF
+ *   - offset points to location at ate multiple from sector size
+ *   return 1 if valid, 0 otherwise
+ *
+ ****************************************************************************/
+
+static int nvs_close_ate_valid(FAR struct nvs_fs *fs,
+                               FAR const struct nvs_ate *entry)
+{
+  if ((!nvs_ate_valid(fs, entry)) || (entry->len != 0U) ||
+      (entry->id != NVS_SPECIAL_ATE_ID))
+    {
+      return 0;
+    }
+
+  if ((fs->geo.erasesize - entry->offset) % sizeof(struct nvs_ate))
+    {
+      return 0;
+    }
+
+  return 1;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_wrt_entry
+ *
+ * Description:
+ *   store an entry in flash
+ *
+ ****************************************************************************/
+
+static int nvs_flash_wrt_entry(FAR struct nvs_fs *fs, uint32_t id,
+            FAR const uint8_t *key, size_t key_size, FAR const void *data,
+            size_t len)
+{
+  int rc;
+  struct nvs_ate entry;
+
+  entry.id = id;
+  entry.offset = (uint16_t)(fs->data_wra & ADDR_OFFS_MASK);
+  entry.len = (uint16_t)len;
+  entry.key_len = (uint16_t)key_size;
+  entry.part = 0xff;
+
+  nvs_ate_crc8_update(&entry);
+
+  entry.expired = 0xff;
+  entry.reserved[0] = 0xff;
+  entry.reserved[1] = 0xff;
+  entry.reserved[2] = 0xff;
+
+  /* let's sew key and data into one, key comes first, then data */
+
+  rc = nvs_flash_data_wrt(fs, key, key_size);
+  if (rc)
+    {
+      ferr("Write key failed, rc=%d\n", rc);
+      return rc;
+    }
+
+  rc = nvs_flash_data_wrt(fs, data, len);
+  if (rc)
+    {
+      ferr("Write value failed, rc=%d\n", rc);
+      return rc;
+    }
+
+  rc = nvs_flash_ate_wrt(fs, &entry);
+  if (rc)
+    {
+      ferr("Write ate failed, rc=%d\n", rc);
+      return rc;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_recover_last_ate
+ *
+ * Description:
+ *   If the closing ate is invalid, its offset cannot be trusted and
+ *   the last valid ate of the sector should instead try to be recovered
+ *   by going through all ate's.
+ *
+ *   addr should point to the faulty closing ate and will be updated to
+ *   the last valid ate. If no valid ate is found it will be left untouched.
+ *
+ ****************************************************************************/
+
+static int nvs_recover_last_ate(FAR struct nvs_fs *fs,
+                                FAR uint32_t *addr)
+{
+  uint32_t data_end_addr;
+  uint32_t ate_end_addr;
+  struct nvs_ate end_ate;
+  int rc;
+
+  finfo("Recovering last ate from sector %" PRIu32 "\n",
+        (*addr >> ADDR_SECT_SHIFT));
+
+  *addr -= sizeof(struct nvs_ate);
+  ate_end_addr = *addr;
+  data_end_addr = *addr & ADDR_SECT_MASK;
+  while (ate_end_addr > data_end_addr)
+    {
+      rc = nvs_flash_ate_rd(fs, ate_end_addr, &end_ate);
+      if (rc)
+        {
+          return rc;
+        }
+
+      if (nvs_ate_valid(fs, &end_ate))
+        {
+          /* Found a valid ate, update data_end_addr and *addr */
+
+          data_end_addr &= ADDR_SECT_MASK;
+          data_end_addr += end_ate.offset + end_ate.len + end_ate.key_len;
+          *addr = ate_end_addr;
+        }
+
+      ate_end_addr -= sizeof(struct nvs_ate);
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_prev_ate
+ *
+ * Description:
+ *   walking through allocation entry list, from newest to oldest entries
+ *   read ate from addr, modify addr to the previous ate.
+ *
+ ****************************************************************************/
+
+static int nvs_prev_ate(FAR struct nvs_fs *fs, FAR uint32_t *addr,
+                        FAR struct nvs_ate *ate)
+{
+  int rc;
+  struct nvs_ate close_ate;
+
+  rc = nvs_flash_ate_rd(fs, *addr, ate);
+  if (rc)
+    {
+      return rc;
+    }
+
+  *addr += sizeof(struct nvs_ate);
+  if (((*addr) & ADDR_OFFS_MASK) !=
+      (fs->geo.erasesize - sizeof(struct nvs_ate)))
+    {
+      return 0;
+    }
+
+  /* last ate in sector, do jump to previous sector */
+
+  if (((*addr) >> ADDR_SECT_SHIFT) == 0U)
+    {
+      *addr += ((fs->geo.neraseblocks - 1) << ADDR_SECT_SHIFT);
+    }
+  else
+    {
+      *addr -= (1 << ADDR_SECT_SHIFT);
+    }
+
+  rc = nvs_flash_ate_rd(fs, *addr, &close_ate);
+  if (rc)
+    {
+      return rc;
+    }
+
+  rc = nvs_ate_cmp_const(&close_ate, CONFIG_MTD_CONFIG_ERASEDVALUE);
+
+  /* at the end of filesystem */
+
+  if (!rc)
+    {
+      *addr = fs->ate_wra;
+      return 0;
+    }
+
+  /* Update the address if the close ate is valid. */
+
+  if (nvs_close_ate_valid(fs, &close_ate))
+    {
+      (*addr) &= ADDR_SECT_MASK;
+      (*addr) += close_ate.offset;
+      return 0;
+    }
+
+  /* The close_ate was invalid, `lets find out the last valid ate
+   * and point the address to this found ate.
+   *
+   * remark: if there was absolutely no valid data in the sector *addr
+   * is kept at sector_end - 2*ate_size, the next read will contain
+   * invalid data and continue with a sector jump
+   */
+
+  return nvs_recover_last_ate(fs, addr);
+}
+
+/****************************************************************************
+ * Name: nvs_sector_advance
+ ****************************************************************************/
+
+static void nvs_sector_advance(FAR struct nvs_fs *fs, FAR uint32_t *addr)
+{
+  *addr += (1 << ADDR_SECT_SHIFT);
+  if ((*addr >> ADDR_SECT_SHIFT) == fs->geo.neraseblocks)
+    {
+      *addr -= (fs->geo.neraseblocks << ADDR_SECT_SHIFT);
+    }
+}
+
+/****************************************************************************
+ * Name: nvs_sector_close
+ *
+ * Description:
+ *   allocation entry close (this closes the current sector) by writing
+ *   offset of last ate to the sector end.
+ *
+ ****************************************************************************/
+
+static int nvs_sector_close(FAR struct nvs_fs *fs)
+{
+  int rc;
+  struct nvs_ate close_ate;
+
+  close_ate.id = NVS_SPECIAL_ATE_ID;
+  close_ate.len = 0U;
+  close_ate.key_len = 0U;
+  close_ate.offset =
+    (uint16_t)((fs->ate_wra + sizeof(struct nvs_ate)) & ADDR_OFFS_MASK);
+
+  fs->ate_wra &= ADDR_SECT_MASK;
+  fs->ate_wra += (fs->geo.erasesize - sizeof(struct nvs_ate));
+
+  nvs_ate_crc8_update(&close_ate);
+
+  rc = nvs_flash_ate_wrt(fs, &close_ate);
+  if (rc < 0)
+    {
+      ferr("Write ate failed, rc=%d\n", rc);
+    }
+
+  nvs_sector_advance(fs, &fs->ate_wra);
+
+  fs->data_wra = fs->ate_wra & ADDR_SECT_MASK;
+  finfo("sector close, data_wra=0x%lx\n", fs->data_wra);
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_add_gc_done_ate
+ ****************************************************************************/
+
+static int nvs_add_gc_done_ate(FAR struct nvs_fs *fs)
+{
+  struct nvs_ate gc_done_ate;
+
+  finfo("Adding gc done ate at %lx\n", fs->ate_wra & ADDR_OFFS_MASK);
+  gc_done_ate.id = NVS_SPECIAL_ATE_ID;
+  gc_done_ate.len = 0U;
+  gc_done_ate.key_len = 0U;
+  gc_done_ate.offset = (uint16_t)(fs->data_wra & ADDR_OFFS_MASK);
+  nvs_ate_crc8_update(&gc_done_ate);
+
+  return nvs_flash_ate_wrt(fs, &gc_done_ate);
+}
+
+/****************************************************************************
+ * Name: nvs_expire_ate
+ ****************************************************************************/
+
+static int nvs_expire_ate(FAR struct nvs_fs *fs, uint32_t addr)
+{
+  uint8_t expired = 0x7f;

Review Comment:
   ~erase value



##########
drivers/mtd/mtd_config_fs.c:
##########
@@ -0,0 +1,2484 @@
+/****************************************************************************
+ * drivers/mtd/mtd_config_fs.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.
+ *
+ * NVS: non volatile storage in flash
+ *
+ * Copyright (c) 2018 Laczen
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <sys/types.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <debug.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include <nuttx/crc8.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mtd/mtd.h>
+#include <nuttx/mtd/configdata.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define MIN(a, b)                       ((a) > (b) ? (b) : (a))
+
+/* MASKS AND SHIFT FOR ADDRESSES
+ * an address in nvs is an uint32_t where:
+ *   high 2 bytes represent the sector number
+ *   low 2 bytes represent the offset in a sector
+ */
+
+#define ADDR_SECT_MASK                  0xFFFF0000
+#define ADDR_SECT_SHIFT                 16
+#define ADDR_OFFS_MASK                  0x0000FFFF
+
+/* we don't want to store all the read content in stack or heap,
+ * so we make a buffer to do compare or move.
+ */
+
+#define NVS_BUFFER_SIZE                 32
+
+/* if data is written after last ate, and power loss happens,
+ * we need to find a clean offset by skipping dirty data.
+ * This macro defines how many bytes to skip when dirty data
+ * is spotted(may take several skips).
+ * Normally 1 byte is okay, such process only happens when
+ * nvs is started, and it is acceptable to take some time during
+ * starting.
+ */
+
+#define NVS_CORRUPT_DATA_SKIP_STEP      1
+
+#define NVS_LOOKUP_CACHE_NO_ADDR        0xffffffff
+
+/* gc done or close ate has the id of 0xffffffff.
+ * We can tell if the ate is special by looking at its id.
+ */
+
+#define NVS_SPECIAL_ATE_ID              0xffffffff
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Non-volatile Storage File system structure */
+
+struct nvs_fs
+{
+  FAR struct mtd_dev_s  *mtd;          /* mtd device */
+  uint32_t              ate_wra;       /* next alloc table entry
+                                        * write address
+                                        */
+  uint32_t              data_wra;      /* next data write address */
+  struct mtd_geometry_s geo;
+  uint32_t              step_addr;     /* for traverse */
+  mutex_t               nvs_lock;
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  uint32_t lookup_cache[CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE];
+#endif
+};
+
+/* Allocation Table Entry */
+
+begin_packed_struct struct nvs_ate
+{
+  uint32_t id;           /* data id */
+  uint16_t offset;       /* data offset within sector */
+  uint16_t len;          /* data len within sector */
+  uint16_t key_len;      /* key string len */
+  uint8_t  part;         /* part of a multipart data - future extension */
+  uint8_t  crc8;         /* crc8 check of the ate entry */
+  uint8_t  expired;      /* 0xFF-newest entry, others-old entry */
+  uint8_t  reserved[3];  /* for future extension */
+} end_packed_struct;
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* MTD NVS opeation api */
+
+static int     mtdconfig_open(FAR struct file *filep);
+static int     mtdconfig_close(FAR struct file *filep);
+static ssize_t mtdconfig_read(FAR struct file *filep, FAR char *buffer,
+                              size_t buflen);
+static int     mtdconfig_ioctl(FAR struct file *filep, int cmd,
+                               unsigned long arg);
+static int     mtdconfig_poll(FAR struct file *filep, FAR struct pollfd *fds,
+                              bool setup);
+
+/* Basic flash operation api */
+
+static ssize_t flash_read(FAR struct mtd_dev_s *mtd, off_t offset,
+                          FAR void *data, size_t len);
+static ssize_t flash_write(FAR struct mtd_dev_s *mtd, off_t offset,
+                           FAR const uint8_t *data, size_t len);
+static int flash_erase(FAR struct mtd_dev_s *mtd, off_t offset, size_t len);
+
+/* NVS operations needed by lookup cache */
+
+static int nvs_prev_ate(FAR struct nvs_fs *fs, FAR uint32_t *addr,
+                        FAR struct nvs_ate *ate);
+static int nvs_ate_valid(FAR struct nvs_fs *fs,
+                         FAR const struct nvs_ate *entry);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_mtdnvs_fops =
+{
+  mtdconfig_open,  /* open */
+  mtdconfig_close, /* close */
+  mtdconfig_read,  /* read */
+  NULL,            /* write */
+  NULL,            /* seek */
+  mtdconfig_ioctl, /* ioctl */
+  mtdconfig_poll   /* poll */
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+  , NULL           /* unlink */
+#endif
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nvs_fnv_hash
+ ****************************************************************************/
+
+static uint32_t nvs_fnv_hash(FAR const uint8_t *input, uint32_t len)
+{
+  uint32_t i = 0;
+  uint32_t hval = 2166136261;
+
+  /* FNV-1 hash each octet in the buffer */
+
+  while (i++ < len)
+    {
+      /* multiply by the 32 bit FNV magic prime mod 2^32 */
+
+      hval *= 0x01000193;
+
+      /* xor the bottom with the current octet */
+
+      hval ^= *input++;
+    }
+
+  return hval;
+}
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_pos
+ ****************************************************************************/
+
+static inline size_t nvs_lookup_cache_pos(uint32_t id)
+{
+  return id % CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_rebuild
+ ****************************************************************************/
+
+static int nvs_lookup_cache_rebuild(FAR struct nvs_fs *fs)
+{
+  int rc;
+  uint32_t addr;
+  uint32_t ate_addr;
+  FAR uint32_t *cache_entry;
+  struct nvs_ate ate;
+
+  memset(fs->lookup_cache, CONFIG_MTD_CONFIG_ERASEDVALUE,
+         sizeof(fs->lookup_cache));
+  addr = fs->ate_wra;
+
+  while (true)
+    {
+      /* Make a copy of 'addr' as it will be advanced by nvs_prev_ate() */
+
+      ate_addr = addr;
+      rc = nvs_prev_ate(fs, &addr, &ate);
+
+      if (rc)
+        {
+          return rc;
+        }
+
+      cache_entry = &fs->lookup_cache[nvs_lookup_cache_pos(ate.id)];
+
+      if (ate.id != NVS_SPECIAL_ATE_ID
+          && *cache_entry == NVS_LOOKUP_CACHE_NO_ADDR
+          && nvs_ate_valid(fs, &ate))
+        {
+          *cache_entry = ate_addr;
+        }
+
+      if (addr == fs->ate_wra)
+        {
+          break;
+        }
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_invalidate
+ ****************************************************************************/
+
+static void nvs_lookup_cache_invalidate(FAR struct nvs_fs *fs,
+                                        uint32_t sector)
+{
+  FAR uint32_t *cache_entry = fs->lookup_cache;
+  FAR uint32_t *const cache_end =
+    &fs->lookup_cache[CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE];
+
+  for (; cache_entry < cache_end; ++cache_entry)
+    {
+      if ((*cache_entry >> ADDR_SECT_SHIFT) == sector)
+        {
+          *cache_entry = NVS_LOOKUP_CACHE_NO_ADDR;
+        }
+    }
+}
+
+#endif /* CONFIG_MTD_NVS_LOOKUP_CACHE */
+
+/****************************************************************************
+ * Name: flash_read
+ ****************************************************************************/
+
+static ssize_t flash_read(FAR struct mtd_dev_s *mtd, off_t offset,
+                          FAR void *data, size_t len)
+{
+  return MTD_READ(mtd, offset, len, data);
+}
+
+/****************************************************************************
+ * Name: flash_write
+ ****************************************************************************/
+
+static ssize_t flash_write(FAR struct mtd_dev_s *mtd, off_t offset,
+                           FAR const uint8_t *data, size_t len)
+{
+  return MTD_WRITE(mtd, offset, len, data);
+}
+
+/****************************************************************************
+ * Name: flash_erase
+ ****************************************************************************/
+
+static int flash_erase(FAR struct mtd_dev_s *mtd, off_t startblock,
+                       size_t nblocks)
+{
+  return MTD_ERASE(mtd, startblock, nblocks);
+}
+
+/****************************************************************************
+ * Name: nvs_flash_wrt
+ *
+ * Description:
+ *   flash routines, process offset then write.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_wrt(FAR struct nvs_fs *fs, uint32_t addr,
+                            FAR const void *data, size_t len)
+{
+  off_t offset;
+  int ret;
+
+  offset = fs->geo.erasesize * (addr >> ADDR_SECT_SHIFT);
+  offset += addr & ADDR_OFFS_MASK;
+
+  ret = flash_write(fs->mtd, offset, data, len);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_rd
+ *
+ * Description:
+ *   basic flash read from nvs address.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_rd(FAR struct nvs_fs *fs, uint32_t addr,
+                        FAR void *data, size_t len)
+{
+  off_t offset;
+  int ret;
+
+  offset = fs->geo.erasesize * (addr >> ADDR_SECT_SHIFT);
+  offset += addr & ADDR_OFFS_MASK;
+
+  ret = flash_read(fs->mtd, offset, data, len);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_ate_wrt
+ *
+ * Description:
+ *   allocation entry write.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_ate_wrt(FAR struct nvs_fs *fs,
+                             FAR const struct nvs_ate *entry)
+{
+  int rc;
+
+  rc = nvs_flash_wrt(fs, fs->ate_wra, entry, sizeof(struct nvs_ate));
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  /* 0xffffffff is a special-purpose identifier. Exclude it from the cache */
+
+  if (entry->id != NVS_SPECIAL_ATE_ID)
+    {
+      fs->lookup_cache[nvs_lookup_cache_pos(entry->id)] = fs->ate_wra;
+    }
+#endif
+
+  fs->ate_wra -= sizeof(struct nvs_ate);
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_data_wrt
+ ****************************************************************************/
+
+static int nvs_flash_data_wrt(FAR struct nvs_fs *fs,
+                              FAR const void *data, size_t len)
+{
+  int rc;
+
+  rc = nvs_flash_wrt(fs, fs->data_wra, data, len);
+  fs->data_wra += len;
+  finfo("write data done, data_wra=0x%lx\n",
+        fs->data_wra);
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_ate_rd
+ ****************************************************************************/
+
+static int nvs_flash_ate_rd(FAR struct nvs_fs *fs, uint32_t addr,
+                            FAR struct nvs_ate *entry)
+{
+  return nvs_flash_rd(fs, addr, entry, sizeof(struct nvs_ate));
+}
+
+/****************************************************************************
+ * Name: nvs_flash_block_cmp
+ *
+ * Description:
+ *   nvs_flash_block_cmp compares the data in flash at addr to data
+ *   in blocks of size NVS_BLOCK_SIZE aligned to fs->write_block_size
+ *   returns 0 if equal, 1 if not equal, errcode if error
+ *
+ ****************************************************************************/
+
+static int nvs_flash_block_cmp(FAR struct nvs_fs *fs, uint32_t addr,
+                               FAR const void *data, size_t len)
+{
+  FAR const uint8_t *data8 = (FAR const uint8_t *)data;
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t buf[NVS_BUFFER_SIZE];
+
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr, buf, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = memcmp(data8, buf, bytes_to_cmp);
+      if (rc)
+        {
+          return 1;
+        }
+
+      len -= bytes_to_cmp;
+      addr += bytes_to_cmp;
+      data8 += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_direct_cmp
+ *
+ * Description:
+ *   nvs_flash_direct_cmp compares the data in flash at addr1 and addr2
+ *   of len in blocks of size NVS_BLOCK_SIZE aligned to fs->write_block_size
+ *   returns 0 if equal, 1 if not equal, errcode if error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_direct_cmp(FAR struct nvs_fs *fs, uint32_t addr1,
+                                uint32_t addr2, size_t len)
+{
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t buf1[NVS_BUFFER_SIZE];
+  uint8_t buf2[NVS_BUFFER_SIZE];
+
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr1, buf1, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = nvs_flash_rd(fs, addr2, buf2, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = memcmp(buf1, buf2, bytes_to_cmp);
+      if (rc)
+        {
+          return 1;
+        }
+
+      len -= bytes_to_cmp;
+      addr1 += bytes_to_cmp;
+      addr2 += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_cmp_const
+ *
+ * Description:
+ *   nvs_flash_cmp_const compares the data in flash at addr to a constant
+ *   value. returns 0 if all data in flash is equal to value, 1 if not equal,
+ *   errcode if error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_cmp_const(FAR struct nvs_fs *fs, uint32_t addr,
+                               uint8_t value, size_t len)
+{
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t cmp[NVS_BUFFER_SIZE];
+
+  memset(cmp, value, NVS_BUFFER_SIZE);
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_block_cmp(fs, addr, cmp, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      len -= bytes_to_cmp;
+      addr += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_block_move
+ *
+ * Description:
+ *   flash block move: move a block at addr to the current data write
+ *   location and updates the data write location.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_block_move(FAR struct nvs_fs *fs, uint32_t addr,
+                                size_t len)
+{
+  int rc;
+  size_t bytes_to_copy;
+  uint8_t buf[NVS_BUFFER_SIZE];
+
+  while (len)
+    {
+      bytes_to_copy = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr, buf, bytes_to_copy);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = nvs_flash_data_wrt(fs, buf, bytes_to_copy);
+      if (rc)
+        {
+          return rc;
+        }
+
+      len -= bytes_to_copy;
+      addr += bytes_to_copy;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_erase_sector
+ *
+ * Description:
+ *   erase a sector by first checking it is used and then erasing if required
+ *   return 0 if OK, errorcode on error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_erase_sector(FAR struct nvs_fs *fs, uint32_t addr)
+{
+  int rc;
+
+  ferr("Erasing addr %lx\n", addr);
+  addr &= ADDR_SECT_MASK;
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  nvs_lookup_cache_invalidate(fs, addr >> ADDR_SECT_SHIFT);
+#endif
+  rc = flash_erase(fs->mtd, addr >> ADDR_SECT_SHIFT, 1);
+
+  if (rc < 0)
+    {
+      ferr("Erasing failed %d\n", rc);
+      return rc;
+    }
+  else
+    {
+      rc = OK;
+    }
+
+  if (nvs_flash_cmp_const(fs, addr, CONFIG_MTD_CONFIG_ERASEDVALUE,
+                          fs->geo.erasesize))
+    {
+      ferr("Erasing not complete\n");
+      rc = -ENXIO;
+    }
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_crc8_update
+ *
+ * Description:
+ *   crc update on allocation entry.
+ *
+ ****************************************************************************/
+
+static void nvs_ate_crc8_update(FAR struct nvs_ate *entry)
+{
+  uint8_t ate_crc;
+
+  ate_crc = crc8part((FAR const uint8_t *)entry,
+                     offsetof(struct nvs_ate, crc8), 0xff);
+  entry->crc8 = ate_crc;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_crc8_check
+ *
+ * Description:
+ *   crc check on allocation entry
+ *   returns 0 if OK, 1 on crc fail
+ *
+ ****************************************************************************/
+
+static int nvs_ate_crc8_check(FAR const struct nvs_ate *entry)
+{
+  uint8_t ate_crc;
+
+  ate_crc = crc8part((FAR const uint8_t *)entry,
+                     offsetof(struct nvs_ate, crc8), 0xff);
+  if (ate_crc == entry->crc8)
+    {
+      return 0;
+    }
+
+  return 1;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_cmp_const
+ *
+ * Description:
+ *   nvs_ate_cmp_const compares an ATE to a constant value. returns 0 if
+ *   the whole ATE is equal to value, 1 if not equal.
+ *
+ ****************************************************************************/
+
+static int nvs_ate_cmp_const(FAR const struct nvs_ate *entry,
+                             uint8_t value)
+{
+  FAR const uint8_t *data8 = (FAR const uint8_t *)entry;
+  int i;
+
+  for (i = 0; i < sizeof(struct nvs_ate); i++)
+    {
+      if (data8[i] != value)
+        {
+          return 1;
+        }
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_valid
+ *
+ * Description:
+ *   return 1 if crc8 and offset valid, 0 otherwise
+ *
+ ****************************************************************************/
+
+static int nvs_ate_valid(FAR struct nvs_fs *fs,
+                         FAR const struct nvs_ate *entry)
+{
+  if ((nvs_ate_crc8_check(entry)) ||
+      (entry->offset >= (fs->geo.erasesize - sizeof(struct nvs_ate))))
+    {
+      return 0;
+    }
+
+  return 1;
+}
+
+/****************************************************************************
+ * Name: nvs_close_ate_valid
+ *
+ * Description:
+ *   nvs_close_ate_valid validates an sector close ate:
+ *   a valid sector close ate:
+ *   - valid ate
+ *   - len = 0 and id = 0xFFFFFFFF
+ *   - offset points to location at ate multiple from sector size
+ *   return 1 if valid, 0 otherwise
+ *
+ ****************************************************************************/
+
+static int nvs_close_ate_valid(FAR struct nvs_fs *fs,
+                               FAR const struct nvs_ate *entry)
+{
+  if ((!nvs_ate_valid(fs, entry)) || (entry->len != 0U) ||
+      (entry->id != NVS_SPECIAL_ATE_ID))
+    {
+      return 0;
+    }
+
+  if ((fs->geo.erasesize - entry->offset) % sizeof(struct nvs_ate))
+    {
+      return 0;
+    }
+
+  return 1;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_wrt_entry
+ *
+ * Description:
+ *   store an entry in flash
+ *
+ ****************************************************************************/
+
+static int nvs_flash_wrt_entry(FAR struct nvs_fs *fs, uint32_t id,
+            FAR const uint8_t *key, size_t key_size, FAR const void *data,
+            size_t len)
+{
+  int rc;
+  struct nvs_ate entry;
+
+  entry.id = id;
+  entry.offset = (uint16_t)(fs->data_wra & ADDR_OFFS_MASK);
+  entry.len = (uint16_t)len;
+  entry.key_len = (uint16_t)key_size;
+  entry.part = 0xff;
+
+  nvs_ate_crc8_update(&entry);
+
+  entry.expired = 0xff;
+  entry.reserved[0] = 0xff;
+  entry.reserved[1] = 0xff;
+  entry.reserved[2] = 0xff;
+
+  /* let's sew key and data into one, key comes first, then data */
+
+  rc = nvs_flash_data_wrt(fs, key, key_size);
+  if (rc)
+    {
+      ferr("Write key failed, rc=%d\n", rc);
+      return rc;
+    }
+
+  rc = nvs_flash_data_wrt(fs, data, len);
+  if (rc)
+    {
+      ferr("Write value failed, rc=%d\n", rc);
+      return rc;
+    }
+
+  rc = nvs_flash_ate_wrt(fs, &entry);
+  if (rc)
+    {
+      ferr("Write ate failed, rc=%d\n", rc);
+      return rc;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_recover_last_ate
+ *
+ * Description:
+ *   If the closing ate is invalid, its offset cannot be trusted and
+ *   the last valid ate of the sector should instead try to be recovered
+ *   by going through all ate's.
+ *
+ *   addr should point to the faulty closing ate and will be updated to
+ *   the last valid ate. If no valid ate is found it will be left untouched.
+ *
+ ****************************************************************************/
+
+static int nvs_recover_last_ate(FAR struct nvs_fs *fs,
+                                FAR uint32_t *addr)
+{
+  uint32_t data_end_addr;
+  uint32_t ate_end_addr;
+  struct nvs_ate end_ate;
+  int rc;
+
+  finfo("Recovering last ate from sector %" PRIu32 "\n",
+        (*addr >> ADDR_SECT_SHIFT));
+
+  *addr -= sizeof(struct nvs_ate);

Review Comment:
   remove? done by line 832



##########
drivers/mtd/mtd_config_fs.c:
##########
@@ -0,0 +1,2484 @@
+/****************************************************************************
+ * drivers/mtd/mtd_config_fs.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.
+ *
+ * NVS: non volatile storage in flash
+ *
+ * Copyright (c) 2018 Laczen
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <sys/types.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <debug.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include <nuttx/crc8.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mtd/mtd.h>
+#include <nuttx/mtd/configdata.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define MIN(a, b)                       ((a) > (b) ? (b) : (a))
+
+/* MASKS AND SHIFT FOR ADDRESSES
+ * an address in nvs is an uint32_t where:
+ *   high 2 bytes represent the sector number
+ *   low 2 bytes represent the offset in a sector
+ */
+
+#define ADDR_SECT_MASK                  0xFFFF0000
+#define ADDR_SECT_SHIFT                 16
+#define ADDR_OFFS_MASK                  0x0000FFFF
+
+/* we don't want to store all the read content in stack or heap,
+ * so we make a buffer to do compare or move.
+ */
+
+#define NVS_BUFFER_SIZE                 32
+
+/* if data is written after last ate, and power loss happens,
+ * we need to find a clean offset by skipping dirty data.
+ * This macro defines how many bytes to skip when dirty data
+ * is spotted(may take several skips).
+ * Normally 1 byte is okay, such process only happens when
+ * nvs is started, and it is acceptable to take some time during
+ * starting.
+ */
+
+#define NVS_CORRUPT_DATA_SKIP_STEP      1
+
+#define NVS_LOOKUP_CACHE_NO_ADDR        0xffffffff
+
+/* gc done or close ate has the id of 0xffffffff.
+ * We can tell if the ate is special by looking at its id.
+ */
+
+#define NVS_SPECIAL_ATE_ID              0xffffffff
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Non-volatile Storage File system structure */
+
+struct nvs_fs
+{
+  FAR struct mtd_dev_s  *mtd;          /* mtd device */
+  uint32_t              ate_wra;       /* next alloc table entry
+                                        * write address
+                                        */
+  uint32_t              data_wra;      /* next data write address */
+  struct mtd_geometry_s geo;
+  uint32_t              step_addr;     /* for traverse */
+  mutex_t               nvs_lock;
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  uint32_t lookup_cache[CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE];
+#endif
+};
+
+/* Allocation Table Entry */
+
+begin_packed_struct struct nvs_ate
+{
+  uint32_t id;           /* data id */
+  uint16_t offset;       /* data offset within sector */
+  uint16_t len;          /* data len within sector */
+  uint16_t key_len;      /* key string len */
+  uint8_t  part;         /* part of a multipart data - future extension */
+  uint8_t  crc8;         /* crc8 check of the ate entry */
+  uint8_t  expired;      /* 0xFF-newest entry, others-old entry */
+  uint8_t  reserved[3];  /* for future extension */
+} end_packed_struct;
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* MTD NVS opeation api */
+
+static int     mtdconfig_open(FAR struct file *filep);
+static int     mtdconfig_close(FAR struct file *filep);
+static ssize_t mtdconfig_read(FAR struct file *filep, FAR char *buffer,
+                              size_t buflen);
+static int     mtdconfig_ioctl(FAR struct file *filep, int cmd,
+                               unsigned long arg);
+static int     mtdconfig_poll(FAR struct file *filep, FAR struct pollfd *fds,
+                              bool setup);
+
+/* Basic flash operation api */
+
+static ssize_t flash_read(FAR struct mtd_dev_s *mtd, off_t offset,
+                          FAR void *data, size_t len);
+static ssize_t flash_write(FAR struct mtd_dev_s *mtd, off_t offset,
+                           FAR const uint8_t *data, size_t len);
+static int flash_erase(FAR struct mtd_dev_s *mtd, off_t offset, size_t len);
+
+/* NVS operations needed by lookup cache */
+
+static int nvs_prev_ate(FAR struct nvs_fs *fs, FAR uint32_t *addr,
+                        FAR struct nvs_ate *ate);
+static int nvs_ate_valid(FAR struct nvs_fs *fs,
+                         FAR const struct nvs_ate *entry);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_mtdnvs_fops =
+{
+  mtdconfig_open,  /* open */
+  mtdconfig_close, /* close */
+  mtdconfig_read,  /* read */
+  NULL,            /* write */
+  NULL,            /* seek */
+  mtdconfig_ioctl, /* ioctl */
+  mtdconfig_poll   /* poll */
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+  , NULL           /* unlink */
+#endif
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nvs_fnv_hash
+ ****************************************************************************/
+
+static uint32_t nvs_fnv_hash(FAR const uint8_t *input, uint32_t len)
+{
+  uint32_t i = 0;
+  uint32_t hval = 2166136261;
+
+  /* FNV-1 hash each octet in the buffer */
+
+  while (i++ < len)
+    {
+      /* multiply by the 32 bit FNV magic prime mod 2^32 */
+
+      hval *= 0x01000193;
+
+      /* xor the bottom with the current octet */
+
+      hval ^= *input++;
+    }
+
+  return hval;
+}
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_pos
+ ****************************************************************************/
+
+static inline size_t nvs_lookup_cache_pos(uint32_t id)
+{
+  return id % CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_rebuild
+ ****************************************************************************/
+
+static int nvs_lookup_cache_rebuild(FAR struct nvs_fs *fs)
+{
+  int rc;
+  uint32_t addr;
+  uint32_t ate_addr;
+  FAR uint32_t *cache_entry;
+  struct nvs_ate ate;
+
+  memset(fs->lookup_cache, CONFIG_MTD_CONFIG_ERASEDVALUE,
+         sizeof(fs->lookup_cache));
+  addr = fs->ate_wra;
+
+  while (true)
+    {
+      /* Make a copy of 'addr' as it will be advanced by nvs_prev_ate() */
+
+      ate_addr = addr;
+      rc = nvs_prev_ate(fs, &addr, &ate);
+
+      if (rc)
+        {
+          return rc;
+        }
+
+      cache_entry = &fs->lookup_cache[nvs_lookup_cache_pos(ate.id)];
+
+      if (ate.id != NVS_SPECIAL_ATE_ID
+          && *cache_entry == NVS_LOOKUP_CACHE_NO_ADDR
+          && nvs_ate_valid(fs, &ate))
+        {
+          *cache_entry = ate_addr;
+        }
+
+      if (addr == fs->ate_wra)
+        {
+          break;
+        }
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_invalidate
+ ****************************************************************************/
+
+static void nvs_lookup_cache_invalidate(FAR struct nvs_fs *fs,
+                                        uint32_t sector)
+{
+  FAR uint32_t *cache_entry = fs->lookup_cache;
+  FAR uint32_t *const cache_end =
+    &fs->lookup_cache[CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE];
+
+  for (; cache_entry < cache_end; ++cache_entry)
+    {
+      if ((*cache_entry >> ADDR_SECT_SHIFT) == sector)
+        {
+          *cache_entry = NVS_LOOKUP_CACHE_NO_ADDR;
+        }
+    }
+}
+
+#endif /* CONFIG_MTD_NVS_LOOKUP_CACHE */
+
+/****************************************************************************
+ * Name: flash_read
+ ****************************************************************************/
+
+static ssize_t flash_read(FAR struct mtd_dev_s *mtd, off_t offset,
+                          FAR void *data, size_t len)
+{
+  return MTD_READ(mtd, offset, len, data);
+}
+
+/****************************************************************************
+ * Name: flash_write
+ ****************************************************************************/
+
+static ssize_t flash_write(FAR struct mtd_dev_s *mtd, off_t offset,
+                           FAR const uint8_t *data, size_t len)
+{
+  return MTD_WRITE(mtd, offset, len, data);
+}
+
+/****************************************************************************
+ * Name: flash_erase
+ ****************************************************************************/
+
+static int flash_erase(FAR struct mtd_dev_s *mtd, off_t startblock,
+                       size_t nblocks)
+{
+  return MTD_ERASE(mtd, startblock, nblocks);
+}
+
+/****************************************************************************
+ * Name: nvs_flash_wrt
+ *
+ * Description:
+ *   flash routines, process offset then write.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_wrt(FAR struct nvs_fs *fs, uint32_t addr,
+                            FAR const void *data, size_t len)
+{
+  off_t offset;
+  int ret;
+
+  offset = fs->geo.erasesize * (addr >> ADDR_SECT_SHIFT);
+  offset += addr & ADDR_OFFS_MASK;
+
+  ret = flash_write(fs->mtd, offset, data, len);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_rd
+ *
+ * Description:
+ *   basic flash read from nvs address.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_rd(FAR struct nvs_fs *fs, uint32_t addr,
+                        FAR void *data, size_t len)
+{
+  off_t offset;
+  int ret;
+
+  offset = fs->geo.erasesize * (addr >> ADDR_SECT_SHIFT);
+  offset += addr & ADDR_OFFS_MASK;
+
+  ret = flash_read(fs->mtd, offset, data, len);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_ate_wrt
+ *
+ * Description:
+ *   allocation entry write.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_ate_wrt(FAR struct nvs_fs *fs,
+                             FAR const struct nvs_ate *entry)
+{
+  int rc;
+
+  rc = nvs_flash_wrt(fs, fs->ate_wra, entry, sizeof(struct nvs_ate));
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  /* 0xffffffff is a special-purpose identifier. Exclude it from the cache */
+
+  if (entry->id != NVS_SPECIAL_ATE_ID)
+    {
+      fs->lookup_cache[nvs_lookup_cache_pos(entry->id)] = fs->ate_wra;
+    }
+#endif
+
+  fs->ate_wra -= sizeof(struct nvs_ate);
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_data_wrt
+ ****************************************************************************/
+
+static int nvs_flash_data_wrt(FAR struct nvs_fs *fs,
+                              FAR const void *data, size_t len)
+{
+  int rc;
+
+  rc = nvs_flash_wrt(fs, fs->data_wra, data, len);
+  fs->data_wra += len;
+  finfo("write data done, data_wra=0x%lx\n",
+        fs->data_wra);
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_ate_rd
+ ****************************************************************************/
+
+static int nvs_flash_ate_rd(FAR struct nvs_fs *fs, uint32_t addr,
+                            FAR struct nvs_ate *entry)
+{
+  return nvs_flash_rd(fs, addr, entry, sizeof(struct nvs_ate));
+}
+
+/****************************************************************************
+ * Name: nvs_flash_block_cmp
+ *
+ * Description:
+ *   nvs_flash_block_cmp compares the data in flash at addr to data
+ *   in blocks of size NVS_BLOCK_SIZE aligned to fs->write_block_size
+ *   returns 0 if equal, 1 if not equal, errcode if error
+ *
+ ****************************************************************************/
+
+static int nvs_flash_block_cmp(FAR struct nvs_fs *fs, uint32_t addr,
+                               FAR const void *data, size_t len)
+{
+  FAR const uint8_t *data8 = (FAR const uint8_t *)data;
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t buf[NVS_BUFFER_SIZE];
+
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr, buf, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = memcmp(data8, buf, bytes_to_cmp);
+      if (rc)
+        {
+          return 1;
+        }
+
+      len -= bytes_to_cmp;
+      addr += bytes_to_cmp;
+      data8 += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_direct_cmp
+ *
+ * Description:
+ *   nvs_flash_direct_cmp compares the data in flash at addr1 and addr2
+ *   of len in blocks of size NVS_BLOCK_SIZE aligned to fs->write_block_size
+ *   returns 0 if equal, 1 if not equal, errcode if error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_direct_cmp(FAR struct nvs_fs *fs, uint32_t addr1,
+                                uint32_t addr2, size_t len)
+{
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t buf1[NVS_BUFFER_SIZE];
+  uint8_t buf2[NVS_BUFFER_SIZE];
+
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr1, buf1, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = nvs_flash_rd(fs, addr2, buf2, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = memcmp(buf1, buf2, bytes_to_cmp);
+      if (rc)
+        {
+          return 1;
+        }
+
+      len -= bytes_to_cmp;
+      addr1 += bytes_to_cmp;
+      addr2 += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_cmp_const
+ *
+ * Description:
+ *   nvs_flash_cmp_const compares the data in flash at addr to a constant
+ *   value. returns 0 if all data in flash is equal to value, 1 if not equal,
+ *   errcode if error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_cmp_const(FAR struct nvs_fs *fs, uint32_t addr,
+                               uint8_t value, size_t len)
+{
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t cmp[NVS_BUFFER_SIZE];
+
+  memset(cmp, value, NVS_BUFFER_SIZE);
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_block_cmp(fs, addr, cmp, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      len -= bytes_to_cmp;
+      addr += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_block_move
+ *
+ * Description:
+ *   flash block move: move a block at addr to the current data write
+ *   location and updates the data write location.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_block_move(FAR struct nvs_fs *fs, uint32_t addr,
+                                size_t len)
+{
+  int rc;
+  size_t bytes_to_copy;
+  uint8_t buf[NVS_BUFFER_SIZE];
+
+  while (len)
+    {
+      bytes_to_copy = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr, buf, bytes_to_copy);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = nvs_flash_data_wrt(fs, buf, bytes_to_copy);
+      if (rc)
+        {
+          return rc;
+        }
+
+      len -= bytes_to_copy;
+      addr += bytes_to_copy;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_erase_sector
+ *
+ * Description:
+ *   erase a sector by first checking it is used and then erasing if required
+ *   return 0 if OK, errorcode on error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_erase_sector(FAR struct nvs_fs *fs, uint32_t addr)
+{
+  int rc;
+
+  ferr("Erasing addr %lx\n", addr);
+  addr &= ADDR_SECT_MASK;
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  nvs_lookup_cache_invalidate(fs, addr >> ADDR_SECT_SHIFT);
+#endif
+  rc = flash_erase(fs->mtd, addr >> ADDR_SECT_SHIFT, 1);
+
+  if (rc < 0)
+    {
+      ferr("Erasing failed %d\n", rc);
+      return rc;
+    }
+  else
+    {
+      rc = OK;
+    }
+
+  if (nvs_flash_cmp_const(fs, addr, CONFIG_MTD_CONFIG_ERASEDVALUE,
+                          fs->geo.erasesize))
+    {
+      ferr("Erasing not complete\n");
+      rc = -ENXIO;
+    }
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_crc8_update
+ *
+ * Description:
+ *   crc update on allocation entry.
+ *
+ ****************************************************************************/
+
+static void nvs_ate_crc8_update(FAR struct nvs_ate *entry)
+{
+  uint8_t ate_crc;
+
+  ate_crc = crc8part((FAR const uint8_t *)entry,
+                     offsetof(struct nvs_ate, crc8), 0xff);
+  entry->crc8 = ate_crc;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_crc8_check
+ *
+ * Description:
+ *   crc check on allocation entry
+ *   returns 0 if OK, 1 on crc fail
+ *
+ ****************************************************************************/
+
+static int nvs_ate_crc8_check(FAR const struct nvs_ate *entry)
+{
+  uint8_t ate_crc;
+
+  ate_crc = crc8part((FAR const uint8_t *)entry,
+                     offsetof(struct nvs_ate, crc8), 0xff);
+  if (ate_crc == entry->crc8)
+    {
+      return 0;
+    }
+
+  return 1;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_cmp_const
+ *
+ * Description:
+ *   nvs_ate_cmp_const compares an ATE to a constant value. returns 0 if
+ *   the whole ATE is equal to value, 1 if not equal.
+ *
+ ****************************************************************************/
+
+static int nvs_ate_cmp_const(FAR const struct nvs_ate *entry,
+                             uint8_t value)
+{
+  FAR const uint8_t *data8 = (FAR const uint8_t *)entry;
+  int i;
+
+  for (i = 0; i < sizeof(struct nvs_ate); i++)
+    {
+      if (data8[i] != value)
+        {
+          return 1;
+        }
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_valid
+ *
+ * Description:
+ *   return 1 if crc8 and offset valid, 0 otherwise
+ *
+ ****************************************************************************/
+
+static int nvs_ate_valid(FAR struct nvs_fs *fs,
+                         FAR const struct nvs_ate *entry)
+{
+  if ((nvs_ate_crc8_check(entry)) ||
+      (entry->offset >= (fs->geo.erasesize - sizeof(struct nvs_ate))))
+    {
+      return 0;
+    }
+
+  return 1;
+}
+
+/****************************************************************************
+ * Name: nvs_close_ate_valid
+ *
+ * Description:
+ *   nvs_close_ate_valid validates an sector close ate:
+ *   a valid sector close ate:
+ *   - valid ate
+ *   - len = 0 and id = 0xFFFFFFFF
+ *   - offset points to location at ate multiple from sector size
+ *   return 1 if valid, 0 otherwise
+ *
+ ****************************************************************************/
+
+static int nvs_close_ate_valid(FAR struct nvs_fs *fs,
+                               FAR const struct nvs_ate *entry)
+{
+  if ((!nvs_ate_valid(fs, entry)) || (entry->len != 0U) ||
+      (entry->id != NVS_SPECIAL_ATE_ID))
+    {
+      return 0;
+    }
+
+  if ((fs->geo.erasesize - entry->offset) % sizeof(struct nvs_ate))
+    {
+      return 0;
+    }
+
+  return 1;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_wrt_entry
+ *
+ * Description:
+ *   store an entry in flash
+ *
+ ****************************************************************************/
+
+static int nvs_flash_wrt_entry(FAR struct nvs_fs *fs, uint32_t id,
+            FAR const uint8_t *key, size_t key_size, FAR const void *data,
+            size_t len)
+{
+  int rc;
+  struct nvs_ate entry;
+
+  entry.id = id;
+  entry.offset = (uint16_t)(fs->data_wra & ADDR_OFFS_MASK);
+  entry.len = (uint16_t)len;
+  entry.key_len = (uint16_t)key_size;
+  entry.part = 0xff;
+
+  nvs_ate_crc8_update(&entry);
+
+  entry.expired = 0xff;
+  entry.reserved[0] = 0xff;
+  entry.reserved[1] = 0xff;
+  entry.reserved[2] = 0xff;
+
+  /* let's sew key and data into one, key comes first, then data */
+
+  rc = nvs_flash_data_wrt(fs, key, key_size);
+  if (rc)
+    {
+      ferr("Write key failed, rc=%d\n", rc);
+      return rc;
+    }
+
+  rc = nvs_flash_data_wrt(fs, data, len);
+  if (rc)
+    {
+      ferr("Write value failed, rc=%d\n", rc);
+      return rc;
+    }
+
+  rc = nvs_flash_ate_wrt(fs, &entry);
+  if (rc)
+    {
+      ferr("Write ate failed, rc=%d\n", rc);
+      return rc;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_recover_last_ate
+ *
+ * Description:
+ *   If the closing ate is invalid, its offset cannot be trusted and
+ *   the last valid ate of the sector should instead try to be recovered
+ *   by going through all ate's.
+ *
+ *   addr should point to the faulty closing ate and will be updated to
+ *   the last valid ate. If no valid ate is found it will be left untouched.
+ *
+ ****************************************************************************/
+
+static int nvs_recover_last_ate(FAR struct nvs_fs *fs,
+                                FAR uint32_t *addr)
+{
+  uint32_t data_end_addr;
+  uint32_t ate_end_addr;
+  struct nvs_ate end_ate;
+  int rc;
+
+  finfo("Recovering last ate from sector %" PRIu32 "\n",
+        (*addr >> ADDR_SECT_SHIFT));
+
+  *addr -= sizeof(struct nvs_ate);
+  ate_end_addr = *addr;
+  data_end_addr = *addr & ADDR_SECT_MASK;
+  while (ate_end_addr > data_end_addr)
+    {
+      rc = nvs_flash_ate_rd(fs, ate_end_addr, &end_ate);
+      if (rc)
+        {
+          return rc;
+        }
+
+      if (nvs_ate_valid(fs, &end_ate))
+        {
+          /* Found a valid ate, update data_end_addr and *addr */
+
+          data_end_addr &= ADDR_SECT_MASK;
+          data_end_addr += end_ate.offset + end_ate.len + end_ate.key_len;
+          *addr = ate_end_addr;
+        }
+
+      ate_end_addr -= sizeof(struct nvs_ate);
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_prev_ate
+ *
+ * Description:
+ *   walking through allocation entry list, from newest to oldest entries
+ *   read ate from addr, modify addr to the previous ate.
+ *
+ ****************************************************************************/
+
+static int nvs_prev_ate(FAR struct nvs_fs *fs, FAR uint32_t *addr,
+                        FAR struct nvs_ate *ate)
+{
+  int rc;
+  struct nvs_ate close_ate;
+
+  rc = nvs_flash_ate_rd(fs, *addr, ate);
+  if (rc)
+    {
+      return rc;
+    }
+
+  *addr += sizeof(struct nvs_ate);
+  if (((*addr) & ADDR_OFFS_MASK) !=
+      (fs->geo.erasesize - sizeof(struct nvs_ate)))
+    {
+      return 0;
+    }
+
+  /* last ate in sector, do jump to previous sector */
+
+  if (((*addr) >> ADDR_SECT_SHIFT) == 0U)

Review Comment:
   all 0U to 0?



##########
drivers/mtd/mtd_config_fs.c:
##########
@@ -0,0 +1,2484 @@
+/****************************************************************************
+ * drivers/mtd/mtd_config_fs.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.
+ *
+ * NVS: non volatile storage in flash
+ *
+ * Copyright (c) 2018 Laczen
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <sys/types.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <debug.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include <nuttx/crc8.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mtd/mtd.h>
+#include <nuttx/mtd/configdata.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define MIN(a, b)                       ((a) > (b) ? (b) : (a))
+
+/* MASKS AND SHIFT FOR ADDRESSES
+ * an address in nvs is an uint32_t where:
+ *   high 2 bytes represent the sector number
+ *   low 2 bytes represent the offset in a sector
+ */
+
+#define ADDR_SECT_MASK                  0xFFFF0000
+#define ADDR_SECT_SHIFT                 16
+#define ADDR_OFFS_MASK                  0x0000FFFF
+
+/* we don't want to store all the read content in stack or heap,
+ * so we make a buffer to do compare or move.
+ */
+
+#define NVS_BUFFER_SIZE                 32
+
+/* if data is written after last ate, and power loss happens,
+ * we need to find a clean offset by skipping dirty data.
+ * This macro defines how many bytes to skip when dirty data
+ * is spotted(may take several skips).
+ * Normally 1 byte is okay, such process only happens when
+ * nvs is started, and it is acceptable to take some time during
+ * starting.
+ */
+
+#define NVS_CORRUPT_DATA_SKIP_STEP      1
+
+#define NVS_LOOKUP_CACHE_NO_ADDR        0xffffffff
+
+/* gc done or close ate has the id of 0xffffffff.
+ * We can tell if the ate is special by looking at its id.
+ */
+
+#define NVS_SPECIAL_ATE_ID              0xffffffff
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Non-volatile Storage File system structure */
+
+struct nvs_fs
+{
+  FAR struct mtd_dev_s  *mtd;          /* mtd device */
+  uint32_t              ate_wra;       /* next alloc table entry
+                                        * write address
+                                        */
+  uint32_t              data_wra;      /* next data write address */
+  struct mtd_geometry_s geo;
+  uint32_t              step_addr;     /* for traverse */
+  mutex_t               nvs_lock;
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  uint32_t lookup_cache[CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE];
+#endif
+};
+
+/* Allocation Table Entry */
+
+begin_packed_struct struct nvs_ate
+{
+  uint32_t id;           /* data id */
+  uint16_t offset;       /* data offset within sector */
+  uint16_t len;          /* data len within sector */
+  uint16_t key_len;      /* key string len */
+  uint8_t  part;         /* part of a multipart data - future extension */
+  uint8_t  crc8;         /* crc8 check of the ate entry */
+  uint8_t  expired;      /* 0xFF-newest entry, others-old entry */
+  uint8_t  reserved[3];  /* for future extension */
+} end_packed_struct;
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* MTD NVS opeation api */
+
+static int     mtdconfig_open(FAR struct file *filep);
+static int     mtdconfig_close(FAR struct file *filep);
+static ssize_t mtdconfig_read(FAR struct file *filep, FAR char *buffer,
+                              size_t buflen);
+static int     mtdconfig_ioctl(FAR struct file *filep, int cmd,
+                               unsigned long arg);
+static int     mtdconfig_poll(FAR struct file *filep, FAR struct pollfd *fds,
+                              bool setup);
+
+/* Basic flash operation api */
+
+static ssize_t flash_read(FAR struct mtd_dev_s *mtd, off_t offset,
+                          FAR void *data, size_t len);
+static ssize_t flash_write(FAR struct mtd_dev_s *mtd, off_t offset,
+                           FAR const uint8_t *data, size_t len);
+static int flash_erase(FAR struct mtd_dev_s *mtd, off_t offset, size_t len);
+
+/* NVS operations needed by lookup cache */
+
+static int nvs_prev_ate(FAR struct nvs_fs *fs, FAR uint32_t *addr,
+                        FAR struct nvs_ate *ate);
+static int nvs_ate_valid(FAR struct nvs_fs *fs,
+                         FAR const struct nvs_ate *entry);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_mtdnvs_fops =
+{
+  mtdconfig_open,  /* open */
+  mtdconfig_close, /* close */
+  mtdconfig_read,  /* read */
+  NULL,            /* write */
+  NULL,            /* seek */
+  mtdconfig_ioctl, /* ioctl */
+  mtdconfig_poll   /* poll */
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+  , NULL           /* unlink */
+#endif
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nvs_fnv_hash
+ ****************************************************************************/
+
+static uint32_t nvs_fnv_hash(FAR const uint8_t *input, uint32_t len)
+{
+  uint32_t i = 0;
+  uint32_t hval = 2166136261;
+
+  /* FNV-1 hash each octet in the buffer */
+
+  while (i++ < len)
+    {
+      /* multiply by the 32 bit FNV magic prime mod 2^32 */
+
+      hval *= 0x01000193;
+
+      /* xor the bottom with the current octet */
+
+      hval ^= *input++;
+    }
+
+  return hval;
+}
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_pos
+ ****************************************************************************/
+
+static inline size_t nvs_lookup_cache_pos(uint32_t id)
+{
+  return id % CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_rebuild
+ ****************************************************************************/
+
+static int nvs_lookup_cache_rebuild(FAR struct nvs_fs *fs)
+{
+  int rc;
+  uint32_t addr;
+  uint32_t ate_addr;
+  FAR uint32_t *cache_entry;
+  struct nvs_ate ate;
+
+  memset(fs->lookup_cache, CONFIG_MTD_CONFIG_ERASEDVALUE,
+         sizeof(fs->lookup_cache));
+  addr = fs->ate_wra;
+
+  while (true)
+    {
+      /* Make a copy of 'addr' as it will be advanced by nvs_prev_ate() */
+
+      ate_addr = addr;
+      rc = nvs_prev_ate(fs, &addr, &ate);
+
+      if (rc)
+        {
+          return rc;
+        }
+
+      cache_entry = &fs->lookup_cache[nvs_lookup_cache_pos(ate.id)];
+
+      if (ate.id != NVS_SPECIAL_ATE_ID
+          && *cache_entry == NVS_LOOKUP_CACHE_NO_ADDR
+          && nvs_ate_valid(fs, &ate))
+        {
+          *cache_entry = ate_addr;
+        }
+
+      if (addr == fs->ate_wra)
+        {
+          break;
+        }
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_invalidate
+ ****************************************************************************/
+
+static void nvs_lookup_cache_invalidate(FAR struct nvs_fs *fs,
+                                        uint32_t sector)
+{
+  FAR uint32_t *cache_entry = fs->lookup_cache;
+  FAR uint32_t *const cache_end =
+    &fs->lookup_cache[CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE];
+
+  for (; cache_entry < cache_end; ++cache_entry)
+    {
+      if ((*cache_entry >> ADDR_SECT_SHIFT) == sector)
+        {
+          *cache_entry = NVS_LOOKUP_CACHE_NO_ADDR;
+        }
+    }
+}
+
+#endif /* CONFIG_MTD_NVS_LOOKUP_CACHE */
+
+/****************************************************************************
+ * Name: flash_read
+ ****************************************************************************/
+
+static ssize_t flash_read(FAR struct mtd_dev_s *mtd, off_t offset,
+                          FAR void *data, size_t len)
+{
+  return MTD_READ(mtd, offset, len, data);
+}
+
+/****************************************************************************
+ * Name: flash_write
+ ****************************************************************************/
+
+static ssize_t flash_write(FAR struct mtd_dev_s *mtd, off_t offset,
+                           FAR const uint8_t *data, size_t len)
+{
+  return MTD_WRITE(mtd, offset, len, data);
+}
+
+/****************************************************************************
+ * Name: flash_erase
+ ****************************************************************************/
+
+static int flash_erase(FAR struct mtd_dev_s *mtd, off_t startblock,
+                       size_t nblocks)
+{
+  return MTD_ERASE(mtd, startblock, nblocks);
+}
+
+/****************************************************************************
+ * Name: nvs_flash_wrt
+ *
+ * Description:
+ *   flash routines, process offset then write.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_wrt(FAR struct nvs_fs *fs, uint32_t addr,
+                            FAR const void *data, size_t len)
+{
+  off_t offset;
+  int ret;
+
+  offset = fs->geo.erasesize * (addr >> ADDR_SECT_SHIFT);
+  offset += addr & ADDR_OFFS_MASK;
+
+  ret = flash_write(fs->mtd, offset, data, len);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_rd
+ *
+ * Description:
+ *   basic flash read from nvs address.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_rd(FAR struct nvs_fs *fs, uint32_t addr,
+                        FAR void *data, size_t len)
+{
+  off_t offset;
+  int ret;
+
+  offset = fs->geo.erasesize * (addr >> ADDR_SECT_SHIFT);
+  offset += addr & ADDR_OFFS_MASK;
+
+  ret = flash_read(fs->mtd, offset, data, len);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_ate_wrt
+ *
+ * Description:
+ *   allocation entry write.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_ate_wrt(FAR struct nvs_fs *fs,
+                             FAR const struct nvs_ate *entry)
+{
+  int rc;
+
+  rc = nvs_flash_wrt(fs, fs->ate_wra, entry, sizeof(struct nvs_ate));
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  /* 0xffffffff is a special-purpose identifier. Exclude it from the cache */
+
+  if (entry->id != NVS_SPECIAL_ATE_ID)
+    {
+      fs->lookup_cache[nvs_lookup_cache_pos(entry->id)] = fs->ate_wra;
+    }
+#endif
+
+  fs->ate_wra -= sizeof(struct nvs_ate);
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_data_wrt
+ ****************************************************************************/
+
+static int nvs_flash_data_wrt(FAR struct nvs_fs *fs,
+                              FAR const void *data, size_t len)
+{
+  int rc;
+
+  rc = nvs_flash_wrt(fs, fs->data_wra, data, len);
+  fs->data_wra += len;
+  finfo("write data done, data_wra=0x%lx\n",
+        fs->data_wra);
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_ate_rd
+ ****************************************************************************/
+
+static int nvs_flash_ate_rd(FAR struct nvs_fs *fs, uint32_t addr,
+                            FAR struct nvs_ate *entry)
+{
+  return nvs_flash_rd(fs, addr, entry, sizeof(struct nvs_ate));
+}
+
+/****************************************************************************
+ * Name: nvs_flash_block_cmp
+ *
+ * Description:
+ *   nvs_flash_block_cmp compares the data in flash at addr to data
+ *   in blocks of size NVS_BLOCK_SIZE aligned to fs->write_block_size
+ *   returns 0 if equal, 1 if not equal, errcode if error
+ *
+ ****************************************************************************/
+
+static int nvs_flash_block_cmp(FAR struct nvs_fs *fs, uint32_t addr,
+                               FAR const void *data, size_t len)
+{
+  FAR const uint8_t *data8 = (FAR const uint8_t *)data;
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t buf[NVS_BUFFER_SIZE];
+
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr, buf, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = memcmp(data8, buf, bytes_to_cmp);
+      if (rc)
+        {
+          return 1;
+        }
+
+      len -= bytes_to_cmp;
+      addr += bytes_to_cmp;
+      data8 += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_direct_cmp
+ *
+ * Description:
+ *   nvs_flash_direct_cmp compares the data in flash at addr1 and addr2
+ *   of len in blocks of size NVS_BLOCK_SIZE aligned to fs->write_block_size
+ *   returns 0 if equal, 1 if not equal, errcode if error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_direct_cmp(FAR struct nvs_fs *fs, uint32_t addr1,
+                                uint32_t addr2, size_t len)
+{
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t buf1[NVS_BUFFER_SIZE];
+  uint8_t buf2[NVS_BUFFER_SIZE];
+
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr1, buf1, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = nvs_flash_rd(fs, addr2, buf2, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = memcmp(buf1, buf2, bytes_to_cmp);
+      if (rc)
+        {
+          return 1;
+        }
+
+      len -= bytes_to_cmp;
+      addr1 += bytes_to_cmp;
+      addr2 += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_cmp_const
+ *
+ * Description:
+ *   nvs_flash_cmp_const compares the data in flash at addr to a constant
+ *   value. returns 0 if all data in flash is equal to value, 1 if not equal,
+ *   errcode if error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_cmp_const(FAR struct nvs_fs *fs, uint32_t addr,
+                               uint8_t value, size_t len)
+{
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t cmp[NVS_BUFFER_SIZE];
+
+  memset(cmp, value, NVS_BUFFER_SIZE);
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_block_cmp(fs, addr, cmp, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      len -= bytes_to_cmp;
+      addr += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_block_move
+ *
+ * Description:
+ *   flash block move: move a block at addr to the current data write
+ *   location and updates the data write location.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_block_move(FAR struct nvs_fs *fs, uint32_t addr,
+                                size_t len)
+{
+  int rc;
+  size_t bytes_to_copy;
+  uint8_t buf[NVS_BUFFER_SIZE];
+
+  while (len)
+    {
+      bytes_to_copy = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr, buf, bytes_to_copy);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = nvs_flash_data_wrt(fs, buf, bytes_to_copy);
+      if (rc)
+        {
+          return rc;
+        }
+
+      len -= bytes_to_copy;
+      addr += bytes_to_copy;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_erase_sector
+ *
+ * Description:
+ *   erase a sector by first checking it is used and then erasing if required
+ *   return 0 if OK, errorcode on error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_erase_sector(FAR struct nvs_fs *fs, uint32_t addr)
+{
+  int rc;
+
+  ferr("Erasing addr %lx\n", addr);
+  addr &= ADDR_SECT_MASK;
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  nvs_lookup_cache_invalidate(fs, addr >> ADDR_SECT_SHIFT);
+#endif
+  rc = flash_erase(fs->mtd, addr >> ADDR_SECT_SHIFT, 1);
+
+  if (rc < 0)
+    {
+      ferr("Erasing failed %d\n", rc);
+      return rc;
+    }
+  else
+    {
+      rc = OK;
+    }
+
+  if (nvs_flash_cmp_const(fs, addr, CONFIG_MTD_CONFIG_ERASEDVALUE,
+                          fs->geo.erasesize))
+    {
+      ferr("Erasing not complete\n");
+      rc = -ENXIO;
+    }
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_crc8_update
+ *
+ * Description:
+ *   crc update on allocation entry.
+ *
+ ****************************************************************************/
+
+static void nvs_ate_crc8_update(FAR struct nvs_ate *entry)
+{
+  uint8_t ate_crc;
+
+  ate_crc = crc8part((FAR const uint8_t *)entry,
+                     offsetof(struct nvs_ate, crc8), 0xff);
+  entry->crc8 = ate_crc;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_crc8_check
+ *
+ * Description:
+ *   crc check on allocation entry
+ *   returns 0 if OK, 1 on crc fail
+ *
+ ****************************************************************************/
+
+static int nvs_ate_crc8_check(FAR const struct nvs_ate *entry)
+{
+  uint8_t ate_crc;
+
+  ate_crc = crc8part((FAR const uint8_t *)entry,
+                     offsetof(struct nvs_ate, crc8), 0xff);
+  if (ate_crc == entry->crc8)
+    {
+      return 0;
+    }
+
+  return 1;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_cmp_const
+ *
+ * Description:
+ *   nvs_ate_cmp_const compares an ATE to a constant value. returns 0 if
+ *   the whole ATE is equal to value, 1 if not equal.
+ *
+ ****************************************************************************/
+
+static int nvs_ate_cmp_const(FAR const struct nvs_ate *entry,
+                             uint8_t value)
+{
+  FAR const uint8_t *data8 = (FAR const uint8_t *)entry;
+  int i;
+
+  for (i = 0; i < sizeof(struct nvs_ate); i++)
+    {
+      if (data8[i] != value)
+        {
+          return 1;
+        }
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_valid
+ *
+ * Description:
+ *   return 1 if crc8 and offset valid, 0 otherwise
+ *
+ ****************************************************************************/
+
+static int nvs_ate_valid(FAR struct nvs_fs *fs,
+                         FAR const struct nvs_ate *entry)
+{
+  if ((nvs_ate_crc8_check(entry)) ||
+      (entry->offset >= (fs->geo.erasesize - sizeof(struct nvs_ate))))
+    {
+      return 0;
+    }
+
+  return 1;
+}
+
+/****************************************************************************
+ * Name: nvs_close_ate_valid
+ *
+ * Description:
+ *   nvs_close_ate_valid validates an sector close ate:
+ *   a valid sector close ate:
+ *   - valid ate
+ *   - len = 0 and id = 0xFFFFFFFF
+ *   - offset points to location at ate multiple from sector size
+ *   return 1 if valid, 0 otherwise
+ *
+ ****************************************************************************/
+
+static int nvs_close_ate_valid(FAR struct nvs_fs *fs,
+                               FAR const struct nvs_ate *entry)
+{
+  if ((!nvs_ate_valid(fs, entry)) || (entry->len != 0U) ||
+      (entry->id != NVS_SPECIAL_ATE_ID))
+    {
+      return 0;
+    }
+
+  if ((fs->geo.erasesize - entry->offset) % sizeof(struct nvs_ate))
+    {
+      return 0;
+    }
+
+  return 1;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_wrt_entry
+ *
+ * Description:
+ *   store an entry in flash
+ *
+ ****************************************************************************/
+
+static int nvs_flash_wrt_entry(FAR struct nvs_fs *fs, uint32_t id,
+            FAR const uint8_t *key, size_t key_size, FAR const void *data,
+            size_t len)
+{
+  int rc;
+  struct nvs_ate entry;
+
+  entry.id = id;
+  entry.offset = (uint16_t)(fs->data_wra & ADDR_OFFS_MASK);
+  entry.len = (uint16_t)len;
+  entry.key_len = (uint16_t)key_size;
+  entry.part = 0xff;
+
+  nvs_ate_crc8_update(&entry);
+
+  entry.expired = 0xff;
+  entry.reserved[0] = 0xff;
+  entry.reserved[1] = 0xff;
+  entry.reserved[2] = 0xff;
+
+  /* let's sew key and data into one, key comes first, then data */
+
+  rc = nvs_flash_data_wrt(fs, key, key_size);
+  if (rc)
+    {
+      ferr("Write key failed, rc=%d\n", rc);
+      return rc;
+    }
+
+  rc = nvs_flash_data_wrt(fs, data, len);
+  if (rc)
+    {
+      ferr("Write value failed, rc=%d\n", rc);
+      return rc;
+    }
+
+  rc = nvs_flash_ate_wrt(fs, &entry);
+  if (rc)
+    {
+      ferr("Write ate failed, rc=%d\n", rc);
+      return rc;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_recover_last_ate
+ *
+ * Description:
+ *   If the closing ate is invalid, its offset cannot be trusted and
+ *   the last valid ate of the sector should instead try to be recovered
+ *   by going through all ate's.
+ *
+ *   addr should point to the faulty closing ate and will be updated to
+ *   the last valid ate. If no valid ate is found it will be left untouched.
+ *
+ ****************************************************************************/
+
+static int nvs_recover_last_ate(FAR struct nvs_fs *fs,
+                                FAR uint32_t *addr)
+{
+  uint32_t data_end_addr;
+  uint32_t ate_end_addr;
+  struct nvs_ate end_ate;
+  int rc;
+
+  finfo("Recovering last ate from sector %" PRIu32 "\n",
+        (*addr >> ADDR_SECT_SHIFT));
+
+  *addr -= sizeof(struct nvs_ate);
+  ate_end_addr = *addr;
+  data_end_addr = *addr & ADDR_SECT_MASK;
+  while (ate_end_addr > data_end_addr)
+    {
+      rc = nvs_flash_ate_rd(fs, ate_end_addr, &end_ate);
+      if (rc)
+        {
+          return rc;
+        }
+
+      if (nvs_ate_valid(fs, &end_ate))
+        {
+          /* Found a valid ate, update data_end_addr and *addr */
+
+          data_end_addr &= ADDR_SECT_MASK;
+          data_end_addr += end_ate.offset + end_ate.len + end_ate.key_len;
+          *addr = ate_end_addr;
+        }
+
+      ate_end_addr -= sizeof(struct nvs_ate);
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_prev_ate
+ *
+ * Description:
+ *   walking through allocation entry list, from newest to oldest entries
+ *   read ate from addr, modify addr to the previous ate.
+ *
+ ****************************************************************************/
+
+static int nvs_prev_ate(FAR struct nvs_fs *fs, FAR uint32_t *addr,
+                        FAR struct nvs_ate *ate)
+{
+  int rc;
+  struct nvs_ate close_ate;
+
+  rc = nvs_flash_ate_rd(fs, *addr, ate);
+  if (rc)
+    {
+      return rc;
+    }
+
+  *addr += sizeof(struct nvs_ate);
+  if (((*addr) & ADDR_OFFS_MASK) !=
+      (fs->geo.erasesize - sizeof(struct nvs_ate)))
+    {
+      return 0;
+    }
+
+  /* last ate in sector, do jump to previous sector */
+
+  if (((*addr) >> ADDR_SECT_SHIFT) == 0U)
+    {
+      *addr += ((fs->geo.neraseblocks - 1) << ADDR_SECT_SHIFT);
+    }
+  else
+    {
+      *addr -= (1 << ADDR_SECT_SHIFT);
+    }
+
+  rc = nvs_flash_ate_rd(fs, *addr, &close_ate);
+  if (rc)
+    {
+      return rc;
+    }
+
+  rc = nvs_ate_cmp_const(&close_ate, CONFIG_MTD_CONFIG_ERASEDVALUE);
+
+  /* at the end of filesystem */
+
+  if (!rc)
+    {
+      *addr = fs->ate_wra;
+      return 0;
+    }
+
+  /* Update the address if the close ate is valid. */
+
+  if (nvs_close_ate_valid(fs, &close_ate))
+    {
+      (*addr) &= ADDR_SECT_MASK;
+      (*addr) += close_ate.offset;
+      return 0;
+    }
+
+  /* The close_ate was invalid, `lets find out the last valid ate
+   * and point the address to this found ate.
+   *
+   * remark: if there was absolutely no valid data in the sector *addr
+   * is kept at sector_end - 2*ate_size, the next read will contain
+   * invalid data and continue with a sector jump
+   */
+
+  return nvs_recover_last_ate(fs, addr);
+}
+
+/****************************************************************************
+ * Name: nvs_sector_advance
+ ****************************************************************************/
+
+static void nvs_sector_advance(FAR struct nvs_fs *fs, FAR uint32_t *addr)
+{
+  *addr += (1 << ADDR_SECT_SHIFT);
+  if ((*addr >> ADDR_SECT_SHIFT) == fs->geo.neraseblocks)
+    {
+      *addr -= (fs->geo.neraseblocks << ADDR_SECT_SHIFT);
+    }
+}
+
+/****************************************************************************
+ * Name: nvs_sector_close
+ *
+ * Description:
+ *   allocation entry close (this closes the current sector) by writing
+ *   offset of last ate to the sector end.
+ *
+ ****************************************************************************/
+
+static int nvs_sector_close(FAR struct nvs_fs *fs)
+{
+  int rc;
+  struct nvs_ate close_ate;
+
+  close_ate.id = NVS_SPECIAL_ATE_ID;
+  close_ate.len = 0U;
+  close_ate.key_len = 0U;
+  close_ate.offset =
+    (uint16_t)((fs->ate_wra + sizeof(struct nvs_ate)) & ADDR_OFFS_MASK);

Review Comment:
   what about part field?



##########
drivers/mtd/mtd_config_fs.c:
##########
@@ -0,0 +1,2484 @@
+/****************************************************************************
+ * drivers/mtd/mtd_config_fs.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.
+ *
+ * NVS: non volatile storage in flash
+ *
+ * Copyright (c) 2018 Laczen
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <sys/types.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <debug.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include <nuttx/crc8.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mtd/mtd.h>
+#include <nuttx/mtd/configdata.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define MIN(a, b)                       ((a) > (b) ? (b) : (a))
+
+/* MASKS AND SHIFT FOR ADDRESSES
+ * an address in nvs is an uint32_t where:
+ *   high 2 bytes represent the sector number
+ *   low 2 bytes represent the offset in a sector
+ */
+
+#define ADDR_SECT_MASK                  0xFFFF0000
+#define ADDR_SECT_SHIFT                 16
+#define ADDR_OFFS_MASK                  0x0000FFFF
+
+/* we don't want to store all the read content in stack or heap,
+ * so we make a buffer to do compare or move.
+ */
+
+#define NVS_BUFFER_SIZE                 32
+
+/* if data is written after last ate, and power loss happens,
+ * we need to find a clean offset by skipping dirty data.
+ * This macro defines how many bytes to skip when dirty data
+ * is spotted(may take several skips).
+ * Normally 1 byte is okay, such process only happens when
+ * nvs is started, and it is acceptable to take some time during
+ * starting.
+ */
+
+#define NVS_CORRUPT_DATA_SKIP_STEP      1
+
+#define NVS_LOOKUP_CACHE_NO_ADDR        0xffffffff
+
+/* gc done or close ate has the id of 0xffffffff.
+ * We can tell if the ate is special by looking at its id.
+ */
+
+#define NVS_SPECIAL_ATE_ID              0xffffffff
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Non-volatile Storage File system structure */
+
+struct nvs_fs
+{
+  FAR struct mtd_dev_s  *mtd;          /* mtd device */
+  uint32_t              ate_wra;       /* next alloc table entry
+                                        * write address
+                                        */
+  uint32_t              data_wra;      /* next data write address */
+  struct mtd_geometry_s geo;
+  uint32_t              step_addr;     /* for traverse */
+  mutex_t               nvs_lock;
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  uint32_t lookup_cache[CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE];
+#endif
+};
+
+/* Allocation Table Entry */
+
+begin_packed_struct struct nvs_ate
+{
+  uint32_t id;           /* data id */
+  uint16_t offset;       /* data offset within sector */
+  uint16_t len;          /* data len within sector */
+  uint16_t key_len;      /* key string len */
+  uint8_t  part;         /* part of a multipart data - future extension */
+  uint8_t  crc8;         /* crc8 check of the ate entry */
+  uint8_t  expired;      /* 0xFF-newest entry, others-old entry */
+  uint8_t  reserved[3];  /* for future extension */
+} end_packed_struct;
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* MTD NVS opeation api */
+
+static int     mtdconfig_open(FAR struct file *filep);
+static int     mtdconfig_close(FAR struct file *filep);
+static ssize_t mtdconfig_read(FAR struct file *filep, FAR char *buffer,
+                              size_t buflen);
+static int     mtdconfig_ioctl(FAR struct file *filep, int cmd,
+                               unsigned long arg);
+static int     mtdconfig_poll(FAR struct file *filep, FAR struct pollfd *fds,
+                              bool setup);
+
+/* Basic flash operation api */
+
+static ssize_t flash_read(FAR struct mtd_dev_s *mtd, off_t offset,
+                          FAR void *data, size_t len);
+static ssize_t flash_write(FAR struct mtd_dev_s *mtd, off_t offset,
+                           FAR const uint8_t *data, size_t len);
+static int flash_erase(FAR struct mtd_dev_s *mtd, off_t offset, size_t len);
+
+/* NVS operations needed by lookup cache */
+
+static int nvs_prev_ate(FAR struct nvs_fs *fs, FAR uint32_t *addr,
+                        FAR struct nvs_ate *ate);
+static int nvs_ate_valid(FAR struct nvs_fs *fs,
+                         FAR const struct nvs_ate *entry);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_mtdnvs_fops =
+{
+  mtdconfig_open,  /* open */
+  mtdconfig_close, /* close */
+  mtdconfig_read,  /* read */
+  NULL,            /* write */
+  NULL,            /* seek */
+  mtdconfig_ioctl, /* ioctl */
+  mtdconfig_poll   /* poll */
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+  , NULL           /* unlink */
+#endif
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nvs_fnv_hash
+ ****************************************************************************/
+
+static uint32_t nvs_fnv_hash(FAR const uint8_t *input, uint32_t len)
+{
+  uint32_t i = 0;
+  uint32_t hval = 2166136261;
+
+  /* FNV-1 hash each octet in the buffer */
+
+  while (i++ < len)
+    {
+      /* multiply by the 32 bit FNV magic prime mod 2^32 */
+
+      hval *= 0x01000193;
+
+      /* xor the bottom with the current octet */
+
+      hval ^= *input++;
+    }
+
+  return hval;
+}
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_pos
+ ****************************************************************************/
+
+static inline size_t nvs_lookup_cache_pos(uint32_t id)
+{
+  return id % CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_rebuild
+ ****************************************************************************/
+
+static int nvs_lookup_cache_rebuild(FAR struct nvs_fs *fs)
+{
+  int rc;
+  uint32_t addr;
+  uint32_t ate_addr;
+  FAR uint32_t *cache_entry;
+  struct nvs_ate ate;
+
+  memset(fs->lookup_cache, CONFIG_MTD_CONFIG_ERASEDVALUE,
+         sizeof(fs->lookup_cache));
+  addr = fs->ate_wra;
+
+  while (true)
+    {
+      /* Make a copy of 'addr' as it will be advanced by nvs_prev_ate() */
+
+      ate_addr = addr;
+      rc = nvs_prev_ate(fs, &addr, &ate);
+
+      if (rc)
+        {
+          return rc;
+        }
+
+      cache_entry = &fs->lookup_cache[nvs_lookup_cache_pos(ate.id)];
+
+      if (ate.id != NVS_SPECIAL_ATE_ID
+          && *cache_entry == NVS_LOOKUP_CACHE_NO_ADDR
+          && nvs_ate_valid(fs, &ate))
+        {
+          *cache_entry = ate_addr;
+        }
+
+      if (addr == fs->ate_wra)
+        {
+          break;
+        }
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_invalidate
+ ****************************************************************************/
+
+static void nvs_lookup_cache_invalidate(FAR struct nvs_fs *fs,
+                                        uint32_t sector)
+{
+  FAR uint32_t *cache_entry = fs->lookup_cache;
+  FAR uint32_t *const cache_end =
+    &fs->lookup_cache[CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE];
+
+  for (; cache_entry < cache_end; ++cache_entry)
+    {
+      if ((*cache_entry >> ADDR_SECT_SHIFT) == sector)
+        {
+          *cache_entry = NVS_LOOKUP_CACHE_NO_ADDR;
+        }
+    }
+}
+
+#endif /* CONFIG_MTD_NVS_LOOKUP_CACHE */
+
+/****************************************************************************
+ * Name: flash_read
+ ****************************************************************************/
+
+static ssize_t flash_read(FAR struct mtd_dev_s *mtd, off_t offset,
+                          FAR void *data, size_t len)
+{
+  return MTD_READ(mtd, offset, len, data);
+}
+
+/****************************************************************************
+ * Name: flash_write
+ ****************************************************************************/
+
+static ssize_t flash_write(FAR struct mtd_dev_s *mtd, off_t offset,
+                           FAR const uint8_t *data, size_t len)
+{
+  return MTD_WRITE(mtd, offset, len, data);
+}
+
+/****************************************************************************
+ * Name: flash_erase
+ ****************************************************************************/
+
+static int flash_erase(FAR struct mtd_dev_s *mtd, off_t startblock,
+                       size_t nblocks)
+{
+  return MTD_ERASE(mtd, startblock, nblocks);
+}
+
+/****************************************************************************
+ * Name: nvs_flash_wrt
+ *
+ * Description:
+ *   flash routines, process offset then write.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_wrt(FAR struct nvs_fs *fs, uint32_t addr,
+                            FAR const void *data, size_t len)
+{
+  off_t offset;
+  int ret;
+
+  offset = fs->geo.erasesize * (addr >> ADDR_SECT_SHIFT);
+  offset += addr & ADDR_OFFS_MASK;
+
+  ret = flash_write(fs->mtd, offset, data, len);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_rd
+ *
+ * Description:
+ *   basic flash read from nvs address.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_rd(FAR struct nvs_fs *fs, uint32_t addr,
+                        FAR void *data, size_t len)
+{
+  off_t offset;
+  int ret;
+
+  offset = fs->geo.erasesize * (addr >> ADDR_SECT_SHIFT);
+  offset += addr & ADDR_OFFS_MASK;
+
+  ret = flash_read(fs->mtd, offset, data, len);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_ate_wrt
+ *
+ * Description:
+ *   allocation entry write.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_ate_wrt(FAR struct nvs_fs *fs,
+                             FAR const struct nvs_ate *entry)
+{
+  int rc;
+
+  rc = nvs_flash_wrt(fs, fs->ate_wra, entry, sizeof(struct nvs_ate));
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  /* 0xffffffff is a special-purpose identifier. Exclude it from the cache */
+
+  if (entry->id != NVS_SPECIAL_ATE_ID)
+    {
+      fs->lookup_cache[nvs_lookup_cache_pos(entry->id)] = fs->ate_wra;
+    }
+#endif
+
+  fs->ate_wra -= sizeof(struct nvs_ate);
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_data_wrt
+ ****************************************************************************/
+
+static int nvs_flash_data_wrt(FAR struct nvs_fs *fs,
+                              FAR const void *data, size_t len)
+{
+  int rc;
+
+  rc = nvs_flash_wrt(fs, fs->data_wra, data, len);
+  fs->data_wra += len;
+  finfo("write data done, data_wra=0x%lx\n",
+        fs->data_wra);
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_ate_rd
+ ****************************************************************************/
+
+static int nvs_flash_ate_rd(FAR struct nvs_fs *fs, uint32_t addr,
+                            FAR struct nvs_ate *entry)
+{
+  return nvs_flash_rd(fs, addr, entry, sizeof(struct nvs_ate));
+}
+
+/****************************************************************************
+ * Name: nvs_flash_block_cmp
+ *
+ * Description:
+ *   nvs_flash_block_cmp compares the data in flash at addr to data
+ *   in blocks of size NVS_BLOCK_SIZE aligned to fs->write_block_size
+ *   returns 0 if equal, 1 if not equal, errcode if error
+ *
+ ****************************************************************************/
+
+static int nvs_flash_block_cmp(FAR struct nvs_fs *fs, uint32_t addr,
+                               FAR const void *data, size_t len)
+{
+  FAR const uint8_t *data8 = (FAR const uint8_t *)data;
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t buf[NVS_BUFFER_SIZE];
+
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr, buf, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = memcmp(data8, buf, bytes_to_cmp);
+      if (rc)
+        {
+          return 1;
+        }
+
+      len -= bytes_to_cmp;
+      addr += bytes_to_cmp;
+      data8 += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_direct_cmp
+ *
+ * Description:
+ *   nvs_flash_direct_cmp compares the data in flash at addr1 and addr2
+ *   of len in blocks of size NVS_BLOCK_SIZE aligned to fs->write_block_size
+ *   returns 0 if equal, 1 if not equal, errcode if error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_direct_cmp(FAR struct nvs_fs *fs, uint32_t addr1,
+                                uint32_t addr2, size_t len)
+{
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t buf1[NVS_BUFFER_SIZE];
+  uint8_t buf2[NVS_BUFFER_SIZE];
+
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr1, buf1, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = nvs_flash_rd(fs, addr2, buf2, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = memcmp(buf1, buf2, bytes_to_cmp);
+      if (rc)
+        {
+          return 1;
+        }
+
+      len -= bytes_to_cmp;
+      addr1 += bytes_to_cmp;
+      addr2 += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_cmp_const
+ *
+ * Description:
+ *   nvs_flash_cmp_const compares the data in flash at addr to a constant
+ *   value. returns 0 if all data in flash is equal to value, 1 if not equal,
+ *   errcode if error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_cmp_const(FAR struct nvs_fs *fs, uint32_t addr,
+                               uint8_t value, size_t len)
+{
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t cmp[NVS_BUFFER_SIZE];
+
+  memset(cmp, value, NVS_BUFFER_SIZE);
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_block_cmp(fs, addr, cmp, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      len -= bytes_to_cmp;
+      addr += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_block_move
+ *
+ * Description:
+ *   flash block move: move a block at addr to the current data write
+ *   location and updates the data write location.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_block_move(FAR struct nvs_fs *fs, uint32_t addr,
+                                size_t len)
+{
+  int rc;
+  size_t bytes_to_copy;
+  uint8_t buf[NVS_BUFFER_SIZE];
+
+  while (len)
+    {
+      bytes_to_copy = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr, buf, bytes_to_copy);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = nvs_flash_data_wrt(fs, buf, bytes_to_copy);
+      if (rc)
+        {
+          return rc;
+        }
+
+      len -= bytes_to_copy;
+      addr += bytes_to_copy;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_erase_sector
+ *
+ * Description:
+ *   erase a sector by first checking it is used and then erasing if required
+ *   return 0 if OK, errorcode on error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_erase_sector(FAR struct nvs_fs *fs, uint32_t addr)
+{
+  int rc;
+
+  ferr("Erasing addr %lx\n", addr);
+  addr &= ADDR_SECT_MASK;
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  nvs_lookup_cache_invalidate(fs, addr >> ADDR_SECT_SHIFT);
+#endif
+  rc = flash_erase(fs->mtd, addr >> ADDR_SECT_SHIFT, 1);
+
+  if (rc < 0)
+    {
+      ferr("Erasing failed %d\n", rc);
+      return rc;
+    }
+  else
+    {
+      rc = OK;
+    }
+
+  if (nvs_flash_cmp_const(fs, addr, CONFIG_MTD_CONFIG_ERASEDVALUE,
+                          fs->geo.erasesize))
+    {
+      ferr("Erasing not complete\n");
+      rc = -ENXIO;
+    }
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_crc8_update
+ *
+ * Description:
+ *   crc update on allocation entry.
+ *
+ ****************************************************************************/
+
+static void nvs_ate_crc8_update(FAR struct nvs_ate *entry)
+{
+  uint8_t ate_crc;
+
+  ate_crc = crc8part((FAR const uint8_t *)entry,
+                     offsetof(struct nvs_ate, crc8), 0xff);
+  entry->crc8 = ate_crc;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_crc8_check
+ *
+ * Description:
+ *   crc check on allocation entry
+ *   returns 0 if OK, 1 on crc fail
+ *
+ ****************************************************************************/
+
+static int nvs_ate_crc8_check(FAR const struct nvs_ate *entry)
+{
+  uint8_t ate_crc;
+
+  ate_crc = crc8part((FAR const uint8_t *)entry,
+                     offsetof(struct nvs_ate, crc8), 0xff);
+  if (ate_crc == entry->crc8)
+    {
+      return 0;
+    }
+
+  return 1;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_cmp_const
+ *
+ * Description:
+ *   nvs_ate_cmp_const compares an ATE to a constant value. returns 0 if
+ *   the whole ATE is equal to value, 1 if not equal.
+ *
+ ****************************************************************************/
+
+static int nvs_ate_cmp_const(FAR const struct nvs_ate *entry,
+                             uint8_t value)
+{
+  FAR const uint8_t *data8 = (FAR const uint8_t *)entry;
+  int i;
+
+  for (i = 0; i < sizeof(struct nvs_ate); i++)
+    {
+      if (data8[i] != value)
+        {
+          return 1;
+        }
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_valid
+ *
+ * Description:
+ *   return 1 if crc8 and offset valid, 0 otherwise
+ *
+ ****************************************************************************/
+
+static int nvs_ate_valid(FAR struct nvs_fs *fs,
+                         FAR const struct nvs_ate *entry)
+{
+  if ((nvs_ate_crc8_check(entry)) ||
+      (entry->offset >= (fs->geo.erasesize - sizeof(struct nvs_ate))))
+    {
+      return 0;
+    }
+
+  return 1;
+}
+
+/****************************************************************************
+ * Name: nvs_close_ate_valid
+ *
+ * Description:
+ *   nvs_close_ate_valid validates an sector close ate:
+ *   a valid sector close ate:
+ *   - valid ate
+ *   - len = 0 and id = 0xFFFFFFFF
+ *   - offset points to location at ate multiple from sector size
+ *   return 1 if valid, 0 otherwise
+ *
+ ****************************************************************************/
+
+static int nvs_close_ate_valid(FAR struct nvs_fs *fs,
+                               FAR const struct nvs_ate *entry)
+{
+  if ((!nvs_ate_valid(fs, entry)) || (entry->len != 0U) ||

Review Comment:
   ```suggestion
     if ((!nvs_ate_valid(fs, entry)) || (entry->len != 0) ||
   ```



##########
drivers/mtd/mtd_config_fs.c:
##########
@@ -0,0 +1,2484 @@
+/****************************************************************************
+ * drivers/mtd/mtd_config_fs.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.
+ *
+ * NVS: non volatile storage in flash
+ *
+ * Copyright (c) 2018 Laczen
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <sys/types.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <debug.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include <nuttx/crc8.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mtd/mtd.h>
+#include <nuttx/mtd/configdata.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define MIN(a, b)                       ((a) > (b) ? (b) : (a))
+
+/* MASKS AND SHIFT FOR ADDRESSES
+ * an address in nvs is an uint32_t where:
+ *   high 2 bytes represent the sector number
+ *   low 2 bytes represent the offset in a sector
+ */
+
+#define ADDR_SECT_MASK                  0xFFFF0000
+#define ADDR_SECT_SHIFT                 16
+#define ADDR_OFFS_MASK                  0x0000FFFF
+
+/* we don't want to store all the read content in stack or heap,
+ * so we make a buffer to do compare or move.
+ */
+
+#define NVS_BUFFER_SIZE                 32
+
+/* if data is written after last ate, and power loss happens,
+ * we need to find a clean offset by skipping dirty data.
+ * This macro defines how many bytes to skip when dirty data
+ * is spotted(may take several skips).
+ * Normally 1 byte is okay, such process only happens when
+ * nvs is started, and it is acceptable to take some time during
+ * starting.
+ */
+
+#define NVS_CORRUPT_DATA_SKIP_STEP      1
+
+#define NVS_LOOKUP_CACHE_NO_ADDR        0xffffffff
+
+/* gc done or close ate has the id of 0xffffffff.
+ * We can tell if the ate is special by looking at its id.
+ */
+
+#define NVS_SPECIAL_ATE_ID              0xffffffff
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Non-volatile Storage File system structure */
+
+struct nvs_fs
+{
+  FAR struct mtd_dev_s  *mtd;          /* mtd device */
+  uint32_t              ate_wra;       /* next alloc table entry
+                                        * write address
+                                        */
+  uint32_t              data_wra;      /* next data write address */
+  struct mtd_geometry_s geo;
+  uint32_t              step_addr;     /* for traverse */
+  mutex_t               nvs_lock;
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  uint32_t lookup_cache[CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE];
+#endif
+};
+
+/* Allocation Table Entry */
+
+begin_packed_struct struct nvs_ate
+{
+  uint32_t id;           /* data id */
+  uint16_t offset;       /* data offset within sector */
+  uint16_t len;          /* data len within sector */
+  uint16_t key_len;      /* key string len */
+  uint8_t  part;         /* part of a multipart data - future extension */
+  uint8_t  crc8;         /* crc8 check of the ate entry */
+  uint8_t  expired;      /* 0xFF-newest entry, others-old entry */
+  uint8_t  reserved[3];  /* for future extension */
+} end_packed_struct;
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* MTD NVS opeation api */
+
+static int     mtdconfig_open(FAR struct file *filep);
+static int     mtdconfig_close(FAR struct file *filep);
+static ssize_t mtdconfig_read(FAR struct file *filep, FAR char *buffer,
+                              size_t buflen);
+static int     mtdconfig_ioctl(FAR struct file *filep, int cmd,
+                               unsigned long arg);
+static int     mtdconfig_poll(FAR struct file *filep, FAR struct pollfd *fds,
+                              bool setup);
+
+/* Basic flash operation api */
+
+static ssize_t flash_read(FAR struct mtd_dev_s *mtd, off_t offset,
+                          FAR void *data, size_t len);
+static ssize_t flash_write(FAR struct mtd_dev_s *mtd, off_t offset,
+                           FAR const uint8_t *data, size_t len);
+static int flash_erase(FAR struct mtd_dev_s *mtd, off_t offset, size_t len);
+
+/* NVS operations needed by lookup cache */
+
+static int nvs_prev_ate(FAR struct nvs_fs *fs, FAR uint32_t *addr,
+                        FAR struct nvs_ate *ate);
+static int nvs_ate_valid(FAR struct nvs_fs *fs,
+                         FAR const struct nvs_ate *entry);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_mtdnvs_fops =
+{
+  mtdconfig_open,  /* open */
+  mtdconfig_close, /* close */
+  mtdconfig_read,  /* read */
+  NULL,            /* write */
+  NULL,            /* seek */
+  mtdconfig_ioctl, /* ioctl */
+  mtdconfig_poll   /* poll */
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+  , NULL           /* unlink */
+#endif
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nvs_fnv_hash
+ ****************************************************************************/
+
+static uint32_t nvs_fnv_hash(FAR const uint8_t *input, uint32_t len)
+{
+  uint32_t i = 0;
+  uint32_t hval = 2166136261;
+
+  /* FNV-1 hash each octet in the buffer */
+
+  while (i++ < len)
+    {
+      /* multiply by the 32 bit FNV magic prime mod 2^32 */
+
+      hval *= 0x01000193;
+
+      /* xor the bottom with the current octet */
+
+      hval ^= *input++;
+    }
+
+  return hval;
+}
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_pos
+ ****************************************************************************/
+
+static inline size_t nvs_lookup_cache_pos(uint32_t id)
+{
+  return id % CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_rebuild
+ ****************************************************************************/
+
+static int nvs_lookup_cache_rebuild(FAR struct nvs_fs *fs)
+{
+  int rc;
+  uint32_t addr;
+  uint32_t ate_addr;
+  FAR uint32_t *cache_entry;
+  struct nvs_ate ate;
+
+  memset(fs->lookup_cache, CONFIG_MTD_CONFIG_ERASEDVALUE,
+         sizeof(fs->lookup_cache));
+  addr = fs->ate_wra;
+
+  while (true)
+    {
+      /* Make a copy of 'addr' as it will be advanced by nvs_prev_ate() */
+
+      ate_addr = addr;
+      rc = nvs_prev_ate(fs, &addr, &ate);
+
+      if (rc)
+        {
+          return rc;
+        }
+
+      cache_entry = &fs->lookup_cache[nvs_lookup_cache_pos(ate.id)];
+
+      if (ate.id != NVS_SPECIAL_ATE_ID
+          && *cache_entry == NVS_LOOKUP_CACHE_NO_ADDR
+          && nvs_ate_valid(fs, &ate))
+        {
+          *cache_entry = ate_addr;
+        }
+
+      if (addr == fs->ate_wra)
+        {
+          break;
+        }
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_lookup_cache_invalidate
+ ****************************************************************************/
+
+static void nvs_lookup_cache_invalidate(FAR struct nvs_fs *fs,
+                                        uint32_t sector)
+{
+  FAR uint32_t *cache_entry = fs->lookup_cache;
+  FAR uint32_t *const cache_end =
+    &fs->lookup_cache[CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE];
+
+  for (; cache_entry < cache_end; ++cache_entry)
+    {
+      if ((*cache_entry >> ADDR_SECT_SHIFT) == sector)
+        {
+          *cache_entry = NVS_LOOKUP_CACHE_NO_ADDR;
+        }
+    }
+}
+
+#endif /* CONFIG_MTD_NVS_LOOKUP_CACHE */
+
+/****************************************************************************
+ * Name: flash_read
+ ****************************************************************************/
+
+static ssize_t flash_read(FAR struct mtd_dev_s *mtd, off_t offset,
+                          FAR void *data, size_t len)
+{
+  return MTD_READ(mtd, offset, len, data);
+}
+
+/****************************************************************************
+ * Name: flash_write
+ ****************************************************************************/
+
+static ssize_t flash_write(FAR struct mtd_dev_s *mtd, off_t offset,
+                           FAR const uint8_t *data, size_t len)
+{
+  return MTD_WRITE(mtd, offset, len, data);
+}
+
+/****************************************************************************
+ * Name: flash_erase
+ ****************************************************************************/
+
+static int flash_erase(FAR struct mtd_dev_s *mtd, off_t startblock,
+                       size_t nblocks)
+{
+  return MTD_ERASE(mtd, startblock, nblocks);
+}
+
+/****************************************************************************
+ * Name: nvs_flash_wrt
+ *
+ * Description:
+ *   flash routines, process offset then write.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_wrt(FAR struct nvs_fs *fs, uint32_t addr,
+                            FAR const void *data, size_t len)
+{
+  off_t offset;
+  int ret;
+
+  offset = fs->geo.erasesize * (addr >> ADDR_SECT_SHIFT);
+  offset += addr & ADDR_OFFS_MASK;
+
+  ret = flash_write(fs->mtd, offset, data, len);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_rd
+ *
+ * Description:
+ *   basic flash read from nvs address.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_rd(FAR struct nvs_fs *fs, uint32_t addr,
+                        FAR void *data, size_t len)
+{
+  off_t offset;
+  int ret;
+
+  offset = fs->geo.erasesize * (addr >> ADDR_SECT_SHIFT);
+  offset += addr & ADDR_OFFS_MASK;
+
+  ret = flash_read(fs->mtd, offset, data, len);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_ate_wrt
+ *
+ * Description:
+ *   allocation entry write.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_ate_wrt(FAR struct nvs_fs *fs,
+                             FAR const struct nvs_ate *entry)
+{
+  int rc;
+
+  rc = nvs_flash_wrt(fs, fs->ate_wra, entry, sizeof(struct nvs_ate));
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  /* 0xffffffff is a special-purpose identifier. Exclude it from the cache */
+
+  if (entry->id != NVS_SPECIAL_ATE_ID)
+    {
+      fs->lookup_cache[nvs_lookup_cache_pos(entry->id)] = fs->ate_wra;
+    }
+#endif
+
+  fs->ate_wra -= sizeof(struct nvs_ate);
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_data_wrt
+ ****************************************************************************/
+
+static int nvs_flash_data_wrt(FAR struct nvs_fs *fs,
+                              FAR const void *data, size_t len)
+{
+  int rc;
+
+  rc = nvs_flash_wrt(fs, fs->data_wra, data, len);
+  fs->data_wra += len;
+  finfo("write data done, data_wra=0x%lx\n",
+        fs->data_wra);
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_ate_rd
+ ****************************************************************************/
+
+static int nvs_flash_ate_rd(FAR struct nvs_fs *fs, uint32_t addr,
+                            FAR struct nvs_ate *entry)
+{
+  return nvs_flash_rd(fs, addr, entry, sizeof(struct nvs_ate));
+}
+
+/****************************************************************************
+ * Name: nvs_flash_block_cmp
+ *
+ * Description:
+ *   nvs_flash_block_cmp compares the data in flash at addr to data
+ *   in blocks of size NVS_BLOCK_SIZE aligned to fs->write_block_size
+ *   returns 0 if equal, 1 if not equal, errcode if error
+ *
+ ****************************************************************************/
+
+static int nvs_flash_block_cmp(FAR struct nvs_fs *fs, uint32_t addr,
+                               FAR const void *data, size_t len)
+{
+  FAR const uint8_t *data8 = (FAR const uint8_t *)data;
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t buf[NVS_BUFFER_SIZE];
+
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr, buf, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = memcmp(data8, buf, bytes_to_cmp);
+      if (rc)
+        {
+          return 1;
+        }
+
+      len -= bytes_to_cmp;
+      addr += bytes_to_cmp;
+      data8 += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_direct_cmp
+ *
+ * Description:
+ *   nvs_flash_direct_cmp compares the data in flash at addr1 and addr2
+ *   of len in blocks of size NVS_BLOCK_SIZE aligned to fs->write_block_size
+ *   returns 0 if equal, 1 if not equal, errcode if error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_direct_cmp(FAR struct nvs_fs *fs, uint32_t addr1,
+                                uint32_t addr2, size_t len)
+{
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t buf1[NVS_BUFFER_SIZE];
+  uint8_t buf2[NVS_BUFFER_SIZE];
+
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr1, buf1, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = nvs_flash_rd(fs, addr2, buf2, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = memcmp(buf1, buf2, bytes_to_cmp);
+      if (rc)
+        {
+          return 1;
+        }
+
+      len -= bytes_to_cmp;
+      addr1 += bytes_to_cmp;
+      addr2 += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_cmp_const
+ *
+ * Description:
+ *   nvs_flash_cmp_const compares the data in flash at addr to a constant
+ *   value. returns 0 if all data in flash is equal to value, 1 if not equal,
+ *   errcode if error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_cmp_const(FAR struct nvs_fs *fs, uint32_t addr,
+                               uint8_t value, size_t len)
+{
+  int rc;
+  size_t bytes_to_cmp;
+  uint8_t cmp[NVS_BUFFER_SIZE];
+
+  memset(cmp, value, NVS_BUFFER_SIZE);
+  while (len > 0)
+    {
+      bytes_to_cmp = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_block_cmp(fs, addr, cmp, bytes_to_cmp);
+      if (rc)
+        {
+          return rc;
+        }
+
+      len -= bytes_to_cmp;
+      addr += bytes_to_cmp;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_block_move
+ *
+ * Description:
+ *   flash block move: move a block at addr to the current data write
+ *   location and updates the data write location.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_block_move(FAR struct nvs_fs *fs, uint32_t addr,
+                                size_t len)
+{
+  int rc;
+  size_t bytes_to_copy;
+  uint8_t buf[NVS_BUFFER_SIZE];
+
+  while (len)
+    {
+      bytes_to_copy = MIN(NVS_BUFFER_SIZE, len);
+      rc = nvs_flash_rd(fs, addr, buf, bytes_to_copy);
+      if (rc)
+        {
+          return rc;
+        }
+
+      rc = nvs_flash_data_wrt(fs, buf, bytes_to_copy);
+      if (rc)
+        {
+          return rc;
+        }
+
+      len -= bytes_to_copy;
+      addr += bytes_to_copy;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_erase_sector
+ *
+ * Description:
+ *   erase a sector by first checking it is used and then erasing if required
+ *   return 0 if OK, errorcode on error.
+ *
+ ****************************************************************************/
+
+static int nvs_flash_erase_sector(FAR struct nvs_fs *fs, uint32_t addr)
+{
+  int rc;
+
+  ferr("Erasing addr %lx\n", addr);
+  addr &= ADDR_SECT_MASK;
+
+#if CONFIG_MTD_CONFIG_FAIL_SAFE_LOOKUP_CACHE_SIZE > 0
+  nvs_lookup_cache_invalidate(fs, addr >> ADDR_SECT_SHIFT);
+#endif
+  rc = flash_erase(fs->mtd, addr >> ADDR_SECT_SHIFT, 1);
+
+  if (rc < 0)
+    {
+      ferr("Erasing failed %d\n", rc);
+      return rc;
+    }
+  else
+    {
+      rc = OK;
+    }
+
+  if (nvs_flash_cmp_const(fs, addr, CONFIG_MTD_CONFIG_ERASEDVALUE,
+                          fs->geo.erasesize))
+    {
+      ferr("Erasing not complete\n");
+      rc = -ENXIO;
+    }
+
+  return rc;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_crc8_update
+ *
+ * Description:
+ *   crc update on allocation entry.
+ *
+ ****************************************************************************/
+
+static void nvs_ate_crc8_update(FAR struct nvs_ate *entry)
+{
+  uint8_t ate_crc;
+
+  ate_crc = crc8part((FAR const uint8_t *)entry,
+                     offsetof(struct nvs_ate, crc8), 0xff);
+  entry->crc8 = ate_crc;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_crc8_check
+ *
+ * Description:
+ *   crc check on allocation entry
+ *   returns 0 if OK, 1 on crc fail
+ *
+ ****************************************************************************/
+
+static int nvs_ate_crc8_check(FAR const struct nvs_ate *entry)
+{
+  uint8_t ate_crc;
+
+  ate_crc = crc8part((FAR const uint8_t *)entry,
+                     offsetof(struct nvs_ate, crc8), 0xff);
+  if (ate_crc == entry->crc8)
+    {
+      return 0;
+    }
+
+  return 1;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_cmp_const
+ *
+ * Description:
+ *   nvs_ate_cmp_const compares an ATE to a constant value. returns 0 if
+ *   the whole ATE is equal to value, 1 if not equal.
+ *
+ ****************************************************************************/
+
+static int nvs_ate_cmp_const(FAR const struct nvs_ate *entry,
+                             uint8_t value)
+{
+  FAR const uint8_t *data8 = (FAR const uint8_t *)entry;
+  int i;
+
+  for (i = 0; i < sizeof(struct nvs_ate); i++)
+    {
+      if (data8[i] != value)
+        {
+          return 1;
+        }
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_ate_valid
+ *
+ * Description:
+ *   return 1 if crc8 and offset valid, 0 otherwise
+ *
+ ****************************************************************************/
+
+static int nvs_ate_valid(FAR struct nvs_fs *fs,
+                         FAR const struct nvs_ate *entry)
+{
+  if ((nvs_ate_crc8_check(entry)) ||
+      (entry->offset >= (fs->geo.erasesize - sizeof(struct nvs_ate))))
+    {
+      return 0;
+    }
+
+  return 1;
+}
+
+/****************************************************************************
+ * Name: nvs_close_ate_valid
+ *
+ * Description:
+ *   nvs_close_ate_valid validates an sector close ate:
+ *   a valid sector close ate:
+ *   - valid ate
+ *   - len = 0 and id = 0xFFFFFFFF
+ *   - offset points to location at ate multiple from sector size
+ *   return 1 if valid, 0 otherwise
+ *
+ ****************************************************************************/
+
+static int nvs_close_ate_valid(FAR struct nvs_fs *fs,
+                               FAR const struct nvs_ate *entry)
+{
+  if ((!nvs_ate_valid(fs, entry)) || (entry->len != 0U) ||
+      (entry->id != NVS_SPECIAL_ATE_ID))
+    {
+      return 0;
+    }
+
+  if ((fs->geo.erasesize - entry->offset) % sizeof(struct nvs_ate))
+    {
+      return 0;
+    }
+
+  return 1;
+}
+
+/****************************************************************************
+ * Name: nvs_flash_wrt_entry
+ *
+ * Description:
+ *   store an entry in flash
+ *
+ ****************************************************************************/
+
+static int nvs_flash_wrt_entry(FAR struct nvs_fs *fs, uint32_t id,
+            FAR const uint8_t *key, size_t key_size, FAR const void *data,
+            size_t len)
+{
+  int rc;
+  struct nvs_ate entry;
+
+  entry.id = id;
+  entry.offset = (uint16_t)(fs->data_wra & ADDR_OFFS_MASK);
+  entry.len = (uint16_t)len;
+  entry.key_len = (uint16_t)key_size;
+  entry.part = 0xff;
+
+  nvs_ate_crc8_update(&entry);
+
+  entry.expired = 0xff;
+  entry.reserved[0] = 0xff;
+  entry.reserved[1] = 0xff;
+  entry.reserved[2] = 0xff;
+
+  /* let's sew key and data into one, key comes first, then data */
+
+  rc = nvs_flash_data_wrt(fs, key, key_size);
+  if (rc)
+    {
+      ferr("Write key failed, rc=%d\n", rc);
+      return rc;
+    }
+
+  rc = nvs_flash_data_wrt(fs, data, len);
+  if (rc)
+    {
+      ferr("Write value failed, rc=%d\n", rc);
+      return rc;
+    }
+
+  rc = nvs_flash_ate_wrt(fs, &entry);
+  if (rc)
+    {
+      ferr("Write ate failed, rc=%d\n", rc);
+      return rc;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_recover_last_ate
+ *
+ * Description:
+ *   If the closing ate is invalid, its offset cannot be trusted and
+ *   the last valid ate of the sector should instead try to be recovered
+ *   by going through all ate's.
+ *
+ *   addr should point to the faulty closing ate and will be updated to
+ *   the last valid ate. If no valid ate is found it will be left untouched.
+ *
+ ****************************************************************************/
+
+static int nvs_recover_last_ate(FAR struct nvs_fs *fs,
+                                FAR uint32_t *addr)
+{
+  uint32_t data_end_addr;
+  uint32_t ate_end_addr;
+  struct nvs_ate end_ate;
+  int rc;
+
+  finfo("Recovering last ate from sector %" PRIu32 "\n",
+        (*addr >> ADDR_SECT_SHIFT));
+
+  *addr -= sizeof(struct nvs_ate);
+  ate_end_addr = *addr;
+  data_end_addr = *addr & ADDR_SECT_MASK;
+  while (ate_end_addr > data_end_addr)
+    {
+      rc = nvs_flash_ate_rd(fs, ate_end_addr, &end_ate);
+      if (rc)
+        {
+          return rc;
+        }
+
+      if (nvs_ate_valid(fs, &end_ate))
+        {
+          /* Found a valid ate, update data_end_addr and *addr */
+
+          data_end_addr &= ADDR_SECT_MASK;
+          data_end_addr += end_ate.offset + end_ate.len + end_ate.key_len;
+          *addr = ate_end_addr;
+        }
+
+      ate_end_addr -= sizeof(struct nvs_ate);
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_prev_ate
+ *
+ * Description:
+ *   walking through allocation entry list, from newest to oldest entries
+ *   read ate from addr, modify addr to the previous ate.
+ *
+ ****************************************************************************/
+
+static int nvs_prev_ate(FAR struct nvs_fs *fs, FAR uint32_t *addr,
+                        FAR struct nvs_ate *ate)
+{
+  int rc;
+  struct nvs_ate close_ate;
+
+  rc = nvs_flash_ate_rd(fs, *addr, ate);
+  if (rc)
+    {
+      return rc;
+    }
+
+  *addr += sizeof(struct nvs_ate);
+  if (((*addr) & ADDR_OFFS_MASK) !=
+      (fs->geo.erasesize - sizeof(struct nvs_ate)))
+    {
+      return 0;
+    }
+
+  /* last ate in sector, do jump to previous sector */
+
+  if (((*addr) >> ADDR_SECT_SHIFT) == 0U)
+    {
+      *addr += ((fs->geo.neraseblocks - 1) << ADDR_SECT_SHIFT);
+    }
+  else
+    {
+      *addr -= (1 << ADDR_SECT_SHIFT);
+    }
+
+  rc = nvs_flash_ate_rd(fs, *addr, &close_ate);
+  if (rc)
+    {
+      return rc;
+    }
+
+  rc = nvs_ate_cmp_const(&close_ate, CONFIG_MTD_CONFIG_ERASEDVALUE);
+
+  /* at the end of filesystem */
+
+  if (!rc)
+    {
+      *addr = fs->ate_wra;
+      return 0;
+    }
+
+  /* Update the address if the close ate is valid. */
+
+  if (nvs_close_ate_valid(fs, &close_ate))
+    {
+      (*addr) &= ADDR_SECT_MASK;
+      (*addr) += close_ate.offset;
+      return 0;
+    }
+
+  /* The close_ate was invalid, `lets find out the last valid ate
+   * and point the address to this found ate.
+   *
+   * remark: if there was absolutely no valid data in the sector *addr
+   * is kept at sector_end - 2*ate_size, the next read will contain
+   * invalid data and continue with a sector jump
+   */
+
+  return nvs_recover_last_ate(fs, addr);
+}
+
+/****************************************************************************
+ * Name: nvs_sector_advance
+ ****************************************************************************/
+
+static void nvs_sector_advance(FAR struct nvs_fs *fs, FAR uint32_t *addr)
+{
+  *addr += (1 << ADDR_SECT_SHIFT);
+  if ((*addr >> ADDR_SECT_SHIFT) == fs->geo.neraseblocks)
+    {
+      *addr -= (fs->geo.neraseblocks << ADDR_SECT_SHIFT);
+    }
+}
+
+/****************************************************************************
+ * Name: nvs_sector_close
+ *
+ * Description:
+ *   allocation entry close (this closes the current sector) by writing
+ *   offset of last ate to the sector end.
+ *
+ ****************************************************************************/
+
+static int nvs_sector_close(FAR struct nvs_fs *fs)
+{
+  int rc;
+  struct nvs_ate close_ate;
+
+  close_ate.id = NVS_SPECIAL_ATE_ID;
+  close_ate.len = 0U;
+  close_ate.key_len = 0U;
+  close_ate.offset =
+    (uint16_t)((fs->ate_wra + sizeof(struct nvs_ate)) & ADDR_OFFS_MASK);
+
+  fs->ate_wra &= ADDR_SECT_MASK;
+  fs->ate_wra += (fs->geo.erasesize - sizeof(struct nvs_ate));
+
+  nvs_ate_crc8_update(&close_ate);
+
+  rc = nvs_flash_ate_wrt(fs, &close_ate);
+  if (rc < 0)
+    {
+      ferr("Write ate failed, rc=%d\n", rc);
+    }
+
+  nvs_sector_advance(fs, &fs->ate_wra);
+
+  fs->data_wra = fs->ate_wra & ADDR_SECT_MASK;
+  finfo("sector close, data_wra=0x%lx\n", fs->data_wra);
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nvs_add_gc_done_ate
+ ****************************************************************************/
+
+static int nvs_add_gc_done_ate(FAR struct nvs_fs *fs)
+{
+  struct nvs_ate gc_done_ate;
+
+  finfo("Adding gc done ate at %lx\n", fs->ate_wra & ADDR_OFFS_MASK);
+  gc_done_ate.id = NVS_SPECIAL_ATE_ID;
+  gc_done_ate.len = 0U;
+  gc_done_ate.key_len = 0U;
+  gc_done_ate.offset = (uint16_t)(fs->data_wra & ADDR_OFFS_MASK);
+  nvs_ate_crc8_update(&gc_done_ate);
+
+  return nvs_flash_ate_wrt(fs, &gc_done_ate);
+}
+
+/****************************************************************************
+ * Name: nvs_expire_ate
+ ****************************************************************************/
+
+static int nvs_expire_ate(FAR struct nvs_fs *fs, uint32_t addr)
+{
+  uint8_t expired = 0x7f;
+  off_t offset;
+
+  offset = fs->geo.erasesize * (addr >> ADDR_SECT_SHIFT);
+  offset += addr & ADDR_OFFS_MASK;
+
+  return flash_write(fs->mtd, offset + offsetof(struct nvs_ate, expired),
+                     &expired, sizeof(expired));
+}
+
+/****************************************************************************
+ * Name: nvs_gc
+ *
+ * Description:
+ *   garbage collection: the address ate_wra has been updated to the new
+ *   sector that has just been started. The data to gc is in the sector
+ *   after this new sector.
+ *
+ ****************************************************************************/
+
+static int nvs_gc(FAR struct nvs_fs *fs)
+{
+  int rc;
+  struct nvs_ate close_ate;
+  struct nvs_ate gc_ate;
+  uint32_t sec_addr;
+  uint32_t gc_addr;
+  uint32_t gc_prev_addr;
+  uint32_t data_addr;
+  uint32_t stop_addr;
+
+  finfo("gc: before gc, ate_wra %lx, ate_size %d\n", fs->ate_wra,
+    sizeof(struct nvs_ate));
+
+  sec_addr = (fs->ate_wra & ADDR_SECT_MASK);
+  nvs_sector_advance(fs, &sec_addr);
+  gc_addr = sec_addr + fs->geo.erasesize - sizeof(struct nvs_ate);
+
+  finfo("gc: set, sec_addr %lx, gc_addr %lx\n", sec_addr, gc_addr);
+
+  /* if the sector is not closed don't do gc */
+
+  rc = nvs_flash_ate_rd(fs, gc_addr, &close_ate);
+  if (rc < 0)
+    {
+      /* flash error */
+
+      return rc;
+    }
+
+  rc = nvs_ate_cmp_const(&close_ate, CONFIG_MTD_CONFIG_ERASEDVALUE);
+  if (!rc)
+    {
+      goto gc_done;
+    }
+
+  stop_addr = gc_addr - sizeof(struct nvs_ate);
+
+  if (nvs_close_ate_valid(fs, &close_ate))
+    {
+      gc_addr &= ADDR_SECT_MASK;
+      gc_addr += close_ate.offset;
+    }
+  else
+    {
+      rc = nvs_recover_last_ate(fs, &gc_addr);
+      if (rc)
+        {
+          return rc;
+        }
+    }
+
+  do
+    {
+      gc_prev_addr = gc_addr;
+      rc = nvs_prev_ate(fs, &gc_addr, &gc_ate);
+      if (rc)
+        {
+          return rc;
+        }
+
+      if (gc_ate.expired != 0xff)

Review Comment:
   not always 0xff



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org