Browse Source

Signed-off-by: zhuhongbin <kptzhu@163.com>

zhuhongbin 2 years ago
parent
commit
948b4b89c9

+ 3 - 1
.gitignore

@@ -2,4 +2,6 @@
 /MDK-ARM/test/
 *.o
 *.d
-*.crf
+*.crf
+*.zhuhongbin
+JLinkLog.txt

+ 3 - 17
fal/inc/fal.h

@@ -1,21 +1,7 @@
 /*
- * File      : fal.h
- * This file is part of FAL (Flash Abstraction Layer) package
- * COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
  *
  * Change Logs:
  * Date           Author       Notes

+ 21 - 31
fal/inc/fal_def.h

@@ -1,21 +1,7 @@
 /*
- * File      : fal_def.h
- * This file is part of FAL (Flash Abstraction Layer) package
- * COPYRIGHT (C) 2006 - 2019, RT-Thread Development Team
+ * Copyright (c) 2006-2018, RT-Thread Development Team
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * SPDX-License-Identifier: Apache-2.0
  *
  * Change Logs:
  * Date           Author       Notes
@@ -28,7 +14,16 @@
 #include <stdint.h>
 #include <stdio.h>
 
-#define FAL_SW_VERSION                 "0.5.0"
+#define FAL_SW_VERSION                 "0.5.99"
+
+#ifdef __RTTHREAD__ /* for RT-Thread platform */
+#include <rtthread.h>
+#define FAL_PRINTF      rt_kprintf
+#define FAL_MALLOC      rt_malloc
+#define FAL_CALLOC      rt_calloc
+#define FAL_REALLOC     rt_realloc
+#define FAL_FREE        rt_free
+#endif
 
 #ifndef FAL_MALLOC
 #define FAL_MALLOC                     malloc
@@ -46,20 +41,14 @@
 #define FAL_FREE                       free
 #endif
 
+#ifndef FAL_PRINTF
+#define FAL_PRINTF                     printf
+#endif
+
 #ifndef FAL_DEBUG
 #define FAL_DEBUG                      0
 #endif
 
-#ifndef FAL_PRINTF
-#ifdef RT_VER_NUM
-/* for RT-Thread platform */
-extern void rt_kprintf(const char *fmt, ...);
-#define FAL_PRINTF rt_kprintf
-#else
-#define FAL_PRINTF printf
-#endif /* RT_VER_NUM */
-#endif /* FAL_PRINTF */
-
 #if FAL_DEBUG
 #ifdef assert
 #undef assert
@@ -67,7 +56,7 @@ extern void rt_kprintf(const char *fmt, ...);
 #define assert(EXPR)                                                           \
 if (!(EXPR))                                                                   \
 {                                                                              \
-    FAL_PRINTF("(%s) has assert failed at %s.\n", #EXPR, __FUNCTION__);        \
+    FAL_PRINTF("(%s) has assert failed at %s.\n", #EXPR, __func__ );        \
     while (1);                                                                 \
 }
 
@@ -75,7 +64,8 @@ if (!(EXPR))                                                                   \
 #ifdef  log_d
 #undef  log_d
 #endif
-#define log_d(...)                     FAL_PRINTF("[D/FAL] (%s:%d) ", __FUNCTION__, __LINE__);           FAL_PRINTF(__VA_ARGS__);FAL_PRINTF("\n")
+#include <inttypes.h>
+#define log_d(...)                     FAL_PRINTF("[D/FAL] (%s:%" PRIdLEAST16 ") ", __func__, __LINE__);           FAL_PRINTF(__VA_ARGS__);FAL_PRINTF("\n")
 
 #else
 
@@ -95,7 +85,7 @@ if (!(EXPR))                                                                   \
 #ifdef  log_e
 #undef  log_e
 #endif
-#define log_e(...)                     FAL_PRINTF("\033[31;22m[E/FAL] (%s:%d) ", __FUNCTION__, __LINE__);FAL_PRINTF(__VA_ARGS__);FAL_PRINTF("\033[0m\n")
+#define log_e(...)                     FAL_PRINTF("\033[31;22m[E/FAL] (%s:%d) ", __func__, __LINE__);FAL_PRINTF(__VA_ARGS__);FAL_PRINTF("\033[0m\n")
 
 /* info level log */
 #ifdef  log_i
@@ -127,7 +117,7 @@ struct fal_flash_dev
     } ops;
 
     /* write minimum granularity, unit: bit. 
-       1(nor flash)/ 8(stm32f4)/ 32(stm32f1)/ 64(stm32l4)
+       1(nor flash)/ 8(stm32f2/f4)/ 32(stm32f1)/ 64(stm32l4)
        0 will not take effect. */
     size_t write_gran;
 };

+ 2 - 16
fal/src/fal.c

@@ -1,21 +1,7 @@
 /*
- * File      : fal.c
- * This file is part of FAL (Flash Abstraction Layer) package
- * COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
+ * Copyright (c) 2006-2018, RT-Thread Development Team
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * SPDX-License-Identifier: Apache-2.0
  *
  * Change Logs:
  * Date           Author       Notes

+ 4 - 20
fal/src/fal_flash.c

@@ -1,21 +1,7 @@
 /*
- * File      : fal_flash.c
- * This file is part of FAL (Flash Abstraction Layer) package
- * COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
+ * Copyright (c) 2006-2018, RT-Thread Development Team
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * SPDX-License-Identifier: Apache-2.0
  *
  * Change Logs:
  * Date           Author       Notes
@@ -42,7 +28,6 @@ static uint8_t init_ok = 0;
 int fal_flash_init(void)
 {
     size_t i;
-    const struct fal_flash_dev *dev;
 
     if (init_ok)
     {
@@ -51,7 +36,6 @@ int fal_flash_init(void)
 
     for (i = 0; i < device_table_len; i++)
     {
-        dev = device_table[i];
         assert(device_table[i]->ops.read);
         assert(device_table[i]->ops.write);
         assert(device_table[i]->ops.erase);
@@ -61,8 +45,8 @@ int fal_flash_init(void)
             device_table[i]->ops.init();
         }
         log_d("Flash device | %*.*s | addr: 0x%08lx | len: 0x%08x | blk_size: 0x%08x |initialized finish.",
-                FAL_DEV_NAME_MAX, FAL_DEV_NAME_MAX, dev->name, dev->addr, dev->len,
-                dev->blk_size);
+                FAL_DEV_NAME_MAX, FAL_DEV_NAME_MAX, device_table[i]->name, device_table[i]->addr, device_table[i]->len,
+                device_table[i]->blk_size);
     }
 
     init_ok = 1;

+ 71 - 39
fal/src/fal_partition.c

@@ -1,21 +1,7 @@
 /*
- * File      : fal_partition.c
- * This file is part of FAL (Flash Abstraction Layer) package
- * COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
+ * Copyright (c) 2006-2018, RT-Thread Development Team
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * SPDX-License-Identifier: Apache-2.0
  *
  * Change Logs:
  * Date           Author       Notes
@@ -30,7 +16,11 @@
 #define FAL_PART_MAGIC_WORD         0x45503130
 #define FAL_PART_MAGIC_WORD_H       0x4550L
 #define FAL_PART_MAGIC_WORD_L       0x3130L
-#define FAL_PART_MAGIC_WROD         0x45503130
+
+struct part_flash_info
+{
+    const struct fal_flash_dev *flash_dev;
+};
 
 /**
  * FAL partition table config has defined on 'fal_cfg.h'.
@@ -55,8 +45,11 @@
 #else
     #error not supported tool chain
 #endif /* __CC_ARM */
-USED static const struct fal_partition partition_table_def[] SECTION("FalPartTable") = FAL_PART_TABLE;
+//USED static const struct fal_partition partition_table_def[] SECTION("FalPartTable") = FAL_PART_TABLE;
+static const struct fal_partition partition_table_def[] = FAL_PART_TABLE;
 static const struct fal_partition *partition_table = NULL;
+/* partition and flash object information cache table */
+static struct part_flash_info part_flash_cache[sizeof(partition_table_def) / sizeof(partition_table_def[0])] = { 0 };
 
 #else /* FAL_PART_HAS_TABLE_CFG */
 
@@ -70,6 +63,7 @@ static const struct fal_partition *partition_table = NULL;
 #endif
 
 static struct fal_partition *partition_table = NULL;
+static struct part_flash_info *part_flash_cache = NULL;
 #endif /* FAL_PART_HAS_TABLE_CFG */
 
 static uint8_t init_ok = 0;
@@ -118,6 +112,48 @@ void fal_show_part_table(void)
     log_i("=============================================================");
 }
 
+static int check_and_update_part_cache(const struct fal_partition *table, size_t len)
+{
+    const struct fal_flash_dev *flash_dev = NULL;
+    size_t i;
+
+#ifndef FAL_PART_HAS_TABLE_CFG
+    if (part_flash_cache)
+    {
+        FAL_FREE(part_flash_cache);
+    }
+    part_flash_cache = FAL_MALLOC(len * sizeof(struct part_flash_info));
+    if (part_flash_cache == NULL)
+    {
+        log_e("Initialize failed! No memory for partition table cache");
+        return -2;
+    }
+#endif
+
+    for (i = 0; i < len; i++)
+    {
+        flash_dev = fal_flash_device_find(table[i].flash_name);
+        if (flash_dev == NULL)
+        {
+            log_d("Warning: Do NOT found the flash device(%s).", table[i].flash_name);
+            continue;
+        }
+
+        if (table[i].offset >= (long)flash_dev->len)
+        {
+            log_e("Initialize failed! Partition(%s) offset address(%ld) out of flash bound(<%d).",
+                    table[i].name, table[i].offset, flash_dev->len);
+            partition_table_len = 0;
+
+            return -1;
+        }
+
+        part_flash_cache[i].flash_dev = flash_dev;
+    }
+
+    return 0;
+}
+
 /**
  * Initialize all flash partition on FAL partition table
  *
@@ -125,8 +161,6 @@ void fal_show_part_table(void)
  */
 int fal_partition_init(void)
 {
-    size_t i;
-    const struct fal_flash_dev *flash_dev = NULL;
 
     if (init_ok)
     {
@@ -143,6 +177,8 @@ int fal_partition_init(void)
     uint8_t part_table_find_ok = 0;
     uint32_t read_magic_word;
     fal_partition_t new_part = NULL;
+    size_t i;
+    const struct fal_flash_dev *flash_dev = NULL;
 
     flash_dev = fal_flash_device_find(FAL_PART_TABLE_FLASH_DEV_NAME);
     if (flash_dev == NULL)
@@ -263,23 +299,9 @@ int fal_partition_init(void)
 #endif /* FAL_PART_HAS_TABLE_CFG */
 
     /* check the partition table device exists */
-
-    for (i = 0; i < partition_table_len; i++)
+    if (check_and_update_part_cache(partition_table, partition_table_len) != 0)
     {
-        flash_dev = fal_flash_device_find(partition_table[i].flash_name);
-        if (flash_dev == NULL)
-        {
-            log_d("Warning: Do NOT found the flash device(%s).", partition_table[i].flash_name);
-            continue;
-        }
-
-        if (partition_table[i].offset >= (long)flash_dev->len)
-        {
-            log_e("Initialize failed! Partition(%s) offset address(%ld) out of flash bound(<%d).",
-                    partition_table[i].name, partition_table[i].offset, flash_dev->len);
-            partition_table_len = 0;
-            goto _exit;
-        }
+        goto _exit;
     }
 
     init_ok = 1;
@@ -325,6 +347,14 @@ const struct fal_partition *fal_partition_find(const char *name)
     return NULL;
 }
 
+static const struct fal_flash_dev *flash_device_find_by_part(const struct fal_partition *part)
+{
+    assert(part >= partition_table);
+    assert(part <= &partition_table[partition_table_len - 1]);
+
+    return part_flash_cache[part - partition_table].flash_dev;
+}
+
 /**
  * get the partition table
  *
@@ -354,6 +384,8 @@ void fal_set_partition_table_temp(struct fal_partition *table, size_t len)
     assert(init_ok);
     assert(table);
 
+    check_and_update_part_cache(table, len);
+
     partition_table_len = len;
     partition_table = table;
 }
@@ -383,7 +415,7 @@ int fal_partition_read(const struct fal_partition *part, uint32_t addr, uint8_t
         return -1;
     }
 
-    flash_dev = fal_flash_device_find(part->flash_name);
+    flash_dev = flash_device_find_by_part(part);
     if (flash_dev == NULL)
     {
         log_e("Partition read error! Don't found flash device(%s) of the partition(%s).", part->flash_name, part->name);
@@ -424,7 +456,7 @@ int fal_partition_write(const struct fal_partition *part, uint32_t addr, const u
         return -1;
     }
 
-    flash_dev = fal_flash_device_find(part->flash_name);
+    flash_dev = flash_device_find_by_part(part);
     if (flash_dev == NULL)
     {
         log_e("Partition write error!  Don't found flash device(%s) of the partition(%s).", part->flash_name, part->name);
@@ -463,7 +495,7 @@ int fal_partition_erase(const struct fal_partition *part, uint32_t addr, size_t
         return -1;
     }
 
-    flash_dev = fal_flash_device_find(part->flash_name);
+    flash_dev = flash_device_find_by_part(part);
     if (flash_dev == NULL)
     {
         log_e("Partition erase error! Don't found flash device(%s) of the partition(%s).", part->flash_name, part->name);

+ 29 - 25
fal/src/fal_rtt.c

@@ -1,21 +1,7 @@
 /*
- * File      : fal_rtt.c
- * This file is part of FAL (Flash Abstraction Layer) package
- * COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
+ * Copyright (c) 2006-2018, RT-Thread Development Team
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * SPDX-License-Identifier: Apache-2.0
  *
  * Change Logs:
  * Date           Author       Notes
@@ -794,12 +780,14 @@ static void fal(uint8_t argc, char **argv) {
                 }
                 /* full chip benchmark test */
                 uint32_t start_time, time_cast;
-                size_t write_size = strtol(argv[2], NULL, 0), read_size = strtol(argv[2], NULL, 0), cur_read_size;
+                size_t write_size = strtol(argv[2], NULL, 0), read_size = strtol(argv[2], NULL, 0), cur_op_size;
                 uint8_t *write_data = (uint8_t *)rt_malloc(write_size), *read_data = (uint8_t *)rt_malloc(read_size);
 
                 if (write_data && read_data)
                 {
-                    memset(write_data, 0x55, write_size);
+                    for (i = 0; i < write_size; i ++) {
+                        write_data[i] = i & 0xFF;
+                    }
                     if (flash_dev)
                     {
                         size = flash_dev->len;
@@ -834,13 +822,21 @@ static void fal(uint8_t argc, char **argv) {
                     start_time = rt_tick_get();
                     for (i = 0; i < size; i += write_size)
                     {
+                        if (i + write_size <= size)
+                        {
+                            cur_op_size = write_size;
+                        }
+                        else
+                        {
+                            cur_op_size = size - i;
+                        }
                         if (flash_dev)
                         {
-                            result = flash_dev->ops.write(i, write_data, write_size);
+                            result = flash_dev->ops.write(i, write_data, cur_op_size);
                         }
                         else if (part_dev)
                         {
-                            result = fal_partition_write(part_dev, i, write_data, write_size);
+                            result = fal_partition_write(part_dev, i, write_data, cur_op_size);
                         }
                         if (result < 0)
                         {
@@ -864,22 +860,30 @@ static void fal(uint8_t argc, char **argv) {
                     {
                         if (i + read_size <= size)
                         {
-                            cur_read_size = read_size;
+                            cur_op_size = read_size;
                         }
                         else
                         {
-                            cur_read_size = size - i;
+                            cur_op_size = size - i;
                         }
                         if (flash_dev)
                         {
-                            result = flash_dev->ops.read(i, read_data, cur_read_size);
+                            result = flash_dev->ops.read(i, read_data, cur_op_size);
                         }
                         else if (part_dev)
                         {
-                            result = fal_partition_read(part_dev, i, read_data, cur_read_size);
+                            result = fal_partition_read(part_dev, i, read_data, cur_op_size);
                         }
                         /* data check */
-                        if (memcmp(write_data, read_data, cur_read_size))
+                        for (int index = 0; index < cur_op_size; index ++)
+                        {
+                            if (write_data[index] != read_data[index])
+                            {
+                                rt_kprintf("%d %d %02x %02x.\n", i, index, write_data[index], read_data[index]);
+                            }
+                        }
+
+                        if (memcmp(write_data, read_data, cur_op_size))
                         {
                             result = -RT_ERROR;
                             rt_kprintf("Data check ERROR! Please check you flash by other command.\n");

+ 13 - 2
flashdb/inc/fdb_cfg.h

@@ -23,17 +23,28 @@
 /* using TSDB (Time series database) feature */
 #define FDB_USING_TSDB
 
+/* Using FAL storage mode */
+#define FDB_USING_FAL_MODE
+
+#ifdef FDB_USING_FAL_MODE
 /* the flash write granularity, unit: bit
  * only support 1(nor flash)/ 8(stm32f2/f4)/ 32(stm32f1) */
 #define FDB_WRITE_GRAN         32       /* @note you must define it for a value */
+#endif
+
+/* Using file storage mode by LIBC file API, like fopen/fread/fwrte/fclose */
+/* #define FDB_USING_FILE_LIBC_MODE */
+
+/* Using file storage mode by POSIX file API, like open/read/write/close */
+/* #define FDB_USING_FILE_POSIX_MODE */
 
 /* MCU Endian Configuration, default is Little Endian Order. */
-/* #define FDB_BIG_ENDIAN  */ 
+/* #define FDB_BIG_ENDIAN */ 
 
 /* log print macro. default EF_PRINT macro is printf() */
 /* #define FDB_PRINT(...)              my_printf(__VA_ARGS__) */
 
 /* print debug information */
-//#define FDB_DEBUG_ENABLE
+#define FDB_DEBUG_ENABLE
 
 #endif /* _FDB_CFG_H_ */

+ 65 - 27
flashdb/inc/fdb_def.h

@@ -17,17 +17,17 @@ extern "C" {
 #endif
 
 /* software version number */
-#define FDB_SW_VERSION                 "1.0.0"
-#define FDB_SW_VERSION_NUM             0x10000
+#define FDB_SW_VERSION                 "1.2.0"
+#define FDB_SW_VERSION_NUM             0x10200
 
 /* the KV max name length must less then it */
 #ifndef FDB_KV_NAME_MAX
-#define FDB_KV_NAME_MAX                32
+#define FDB_KV_NAME_MAX                64
 #endif
 
 /* the KV cache table size, it will improve KV search speed when using cache */
 #ifndef FDB_KV_CACHE_TABLE_SIZE
-#define FDB_KV_CACHE_TABLE_SIZE        16
+#define FDB_KV_CACHE_TABLE_SIZE        64
 #endif
 
 /* the sector cache table size, it will improve KV save speed when using cache */
@@ -39,11 +39,19 @@ extern "C" {
 #define FDB_KV_USING_CACHE
 #endif
 
+#if defined(FDB_USING_FILE_LIBC_MODE) || defined(FDB_USING_FILE_POSIX_MODE)
+#define FDB_USING_FILE_MODE
+#endif
+
+#ifndef FDB_WRITE_GRAN
+#define FDB_WRITE_GRAN 1
+#endif
+
 /* log function. default FDB_PRINT macro is printf() */
 #ifndef FDB_PRINT
 #define FDB_PRINT(...)                 printf(__VA_ARGS__)
 #endif
-#define FDB_LOG_PREFIX1()              FDB_PRINT("[FlashDB]"FDB_LOG_TAG)
+#define FDB_LOG_PREFIX1()              FDB_PRINT("[FlashDB]" FDB_LOG_TAG)
 #define FDB_LOG_PREFIX2()              FDB_PRINT(" ")
 #define FDB_LOG_PREFIX()               FDB_LOG_PREFIX1();FDB_LOG_PREFIX2()
 #ifdef FDB_DEBUG_ENABLE
@@ -57,27 +65,35 @@ extern "C" {
 #define FDB_ASSERT(EXPR)                                                      \
 if (!(EXPR))                                                                  \
 {                                                                             \
-    FDB_DEBUG("(%s) has assert failed at %s.\n", #EXPR, __FUNCTION__);        \
+    FDB_DEBUG("(%s) has assert failed at %s.\n", #EXPR, __func__);            \
     while (1);                                                                \
 }
 
-#define FDB_KVDB_CTRL_SET_SEC_SIZE     0x0             /**< set sector size control command */
-#define FDB_KVDB_CTRL_GET_SEC_SIZE     0x1             /**< get sector size control command */
-#define FDB_KVDB_CTRL_SET_LOCK         0x2             /**< set lock function control command */
-#define FDB_KVDB_CTRL_SET_UNLOCK       0x3             /**< set unlock function control command */
+#define FDB_KVDB_CTRL_SET_SEC_SIZE     0x00             /**< set sector size control command, this change MUST before database initialization */
+#define FDB_KVDB_CTRL_GET_SEC_SIZE     0x01             /**< get sector size control command */
+#define FDB_KVDB_CTRL_SET_LOCK         0x02             /**< set lock function control command */
+#define FDB_KVDB_CTRL_SET_UNLOCK       0x03             /**< set unlock function control command */
+#define FDB_KVDB_CTRL_SET_FILE_MODE    0x09             /**< set file mode control command, this change MUST before database initialization */
+#define FDB_KVDB_CTRL_SET_MAX_SIZE     0x0A             /**< set database max size in file mode control command, this change MUST before database initialization */
+#define FDB_KVDB_CTRL_SET_NOT_FORMAT   0x0B             /**< set database NOT format mode control command, this change MUST before database initialization */
+
+#define FDB_TSDB_CTRL_SET_SEC_SIZE     0x00             /**< set sector size control command, this change MUST before database initialization */
+#define FDB_TSDB_CTRL_GET_SEC_SIZE     0x01             /**< get sector size control command */
+#define FDB_TSDB_CTRL_SET_LOCK         0x02             /**< set lock function control command */
+#define FDB_TSDB_CTRL_SET_UNLOCK       0x03             /**< set unlock function control command */
+#define FDB_TSDB_CTRL_SET_ROLLOVER     0x04             /**< set rollover control command, this change MUST after database initialization */
+#define FDB_TSDB_CTRL_GET_ROLLOVER     0x05             /**< get rollover control command */
+#define FDB_TSDB_CTRL_GET_LAST_TIME    0x06             /**< get last save time control command */
+#define FDB_TSDB_CTRL_SET_FILE_MODE    0x09             /**< set file mode control command, this change MUST before database initialization */
+#define FDB_TSDB_CTRL_SET_MAX_SIZE     0x0A             /**< set database max size in file mode control command, this change MUST before database initialization */
+#define FDB_TSDB_CTRL_SET_NOT_FORMAT   0x0B             /**< set database NOT formatable mode control command, this change MUST before database initialization */
 
-#define FDB_TSDB_CTRL_SET_SEC_SIZE     0x0             /**< set sector size control command */
-#define FDB_TSDB_CTRL_GET_SEC_SIZE     0x1             /**< get sector size control command */
-#define FDB_TSDB_CTRL_SET_LOCK         0x2             /**< set lock function control command */
-#define FDB_TSDB_CTRL_SET_UNLOCK       0x3             /**< set unlock function control command */
-#define FDB_TSDB_CTRL_SET_ROLLOVER     0x4             /**< set rollover control command */
-#define FDB_TSDB_CTRL_GET_ROLLOVER     0x5             /**< get rollover control command */
-#define FDB_TSDB_CTRL_GET_LAST_TIME    0x6             /**< get last save time control command */
-
-typedef time_t fdb_time_t;
 #ifdef FDB_USING_TIMESTAMP_64BIT
-typedef int64_t fdb_time_t;
-#endif
+    typedef int64_t fdb_time_t;
+#else
+    typedef int32_t fdb_time_t;
+#endif /* FDB_USING_TIMESTAMP_64BIT */
+
 typedef fdb_time_t (*fdb_get_time)(void);
 
 struct fdb_default_kv_node {
@@ -111,7 +127,7 @@ enum fdb_kv_status {
     FDB_KV_PRE_DELETE,
     FDB_KV_DELETED,
     FDB_KV_ERR_HDR,
-    FDB_KV_STATUS_NUM,
+#define FDB_KV_STATUS_NUM                        6
 };
 typedef enum fdb_kv_status fdb_kv_status_t;
 
@@ -122,7 +138,7 @@ enum fdb_tsl_status {
     FDB_TSL_USER_STATUS1,
     FDB_TSL_DELETED,
     FDB_TSL_USER_STATUS2,
-    FDB_TSL_STATUS_NUM,
+#define FDB_TSL_STATUS_NUM                       6
 };
 typedef enum fdb_tsl_status fdb_tsl_status_t;
 
@@ -175,7 +191,7 @@ enum fdb_sector_store_status {
     FDB_SECTOR_STORE_EMPTY,
     FDB_SECTOR_STORE_USING,
     FDB_SECTOR_STORE_FULL,
-    FDB_SECTOR_STORE_STATUS_NUM,
+#define FDB_SECTOR_STORE_STATUS_NUM              4
 };
 typedef enum fdb_sector_store_status fdb_sector_store_status_t;
 
@@ -185,7 +201,7 @@ enum fdb_sector_dirty_status {
     FDB_SECTOR_DIRTY_FALSE,
     FDB_SECTOR_DIRTY_TRUE,
     FDB_SECTOR_DIRTY_GC,
-    FDB_SECTOR_DIRTY_STATUS_NUM,
+#define FDB_SECTOR_DIRTY_STATUS_NUM              4
 };
 typedef enum fdb_sector_dirty_status fdb_sector_dirty_status_t;
 
@@ -238,9 +254,27 @@ typedef struct fdb_db *fdb_db_t;
 struct fdb_db {
     const char *name;                            /**< database name */
     fdb_db_type type;                            /**< database type */
-    const struct fal_partition *part;            /**< flash partition */
+    union {
+#ifdef FDB_USING_FAL_MODE
+        const struct fal_partition *part;        /**< flash partition for saving database */
+#endif
+#ifdef FDB_USING_FILE_MODE
+        const char *dir;                         /**< directory path for saving database */
+#endif
+    } storage;
     uint32_t sec_size;                           /**< flash section size. It's a multiple of block size */
+    uint32_t max_size;                           /**< database max size. It's a multiple of section size */
     bool init_ok;                                /**< initialized successfully */
+    bool file_mode;                              /**< is file mode, default is false */
+    bool not_formatable;                         /**< is can NOT be formated mode, default is false */
+#ifdef FDB_USING_FILE_MODE
+#if defined(FDB_USING_FILE_POSIX_MODE)
+    int cur_file;                                /**< current file object */
+#elif defined(FDB_USING_FILE_LIBC_MODE)
+    FILE *cur_file;                              /**< current file object */
+#endif
+    uint32_t cur_sec;                            /**< current operate sector address  */
+#endif
     void (*lock)(fdb_db_t db);                   /**< lock the database operate */
     void (*unlock)(fdb_db_t db);                 /**< unlock the database operate */
 
@@ -253,6 +287,9 @@ struct fdb_kvdb {
     struct fdb_default_kv default_kvs;           /**< default KV */
     bool gc_request;                             /**< request a GC check */
     bool in_recovery_check;                      /**< is in recovery check status when first reboot */
+    struct fdb_kv cur_kv;
+    struct kvdb_sec_info cur_sector;
+    bool last_is_complete_del;
 
 #ifdef FDB_KV_USING_CACHE
     /* KV cache table */
@@ -275,7 +312,7 @@ struct fdb_tsdb {
     struct tsdb_sec_info cur_sec;                /**< current using sector */
     fdb_time_t last_time;                        /**< last TSL timestamp */
     fdb_get_time get_time;                       /**< the current timestamp get function */
-    size_t max_len;                              /**< the max log length */
+    size_t max_len;                              /**< the maximum length of each log */
     uint32_t oldest_addr;                        /**< the oldest sector start address */
     bool rollover;                               /**< the oldest data will rollover by newest data, default is true */
 
@@ -300,3 +337,4 @@ typedef struct fdb_blob *fdb_blob_t;
 #endif
 
 #endif /* _FDB_DEF_H_ */
+

+ 3 - 2
flashdb/inc/fdb_low_lvl.h

@@ -47,10 +47,11 @@ size_t _fdb_get_status(uint8_t status_table[], size_t status_num);
 uint32_t _fdb_continue_ff_addr(fdb_db_t db, uint32_t start, uint32_t end);
 fdb_err_t _fdb_init_ex(fdb_db_t db, const char *name, const char *part_name, fdb_db_type type, void *user_data);
 void _fdb_init_finish(fdb_db_t db, fdb_err_t result);
-fdb_err_t _fdb_write_status(fdb_db_t db, uint32_t addr, uint8_t status_table[], size_t status_num, size_t status_index);
+void _fdb_deinit(fdb_db_t db);
+fdb_err_t _fdb_write_status(fdb_db_t db, uint32_t addr, uint8_t status_table[], size_t status_num, size_t status_index, bool sync);
 size_t _fdb_read_status(fdb_db_t db, uint32_t addr, uint8_t status_table[], size_t total_num);
 fdb_err_t _fdb_flash_read(fdb_db_t db, uint32_t addr, void *buf, size_t size);
 fdb_err_t _fdb_flash_erase(fdb_db_t db, uint32_t addr, size_t size);
-fdb_err_t _fdb_flash_write(fdb_db_t db, uint32_t addr, const void *buf, size_t size);
+fdb_err_t _fdb_flash_write(fdb_db_t db, uint32_t addr, const void *buf, size_t size, bool sync);
 
 #endif /* _FDB_LOW_LVL_H_ */

+ 12 - 3
flashdb/inc/flashdb.h

@@ -15,22 +15,30 @@
 #include <stdint.h>
 #include <stddef.h>
 #include <stdbool.h>
+#include <stdio.h>
 #include <time.h>
-#include <fal.h>
 #include <fdb_cfg.h>
+
+#ifdef FDB_USING_FAL_MODE
+#include <fal.h>
+#endif
+
 #include <fdb_def.h>
 
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 /* FlashDB database API */
-fdb_err_t fdb_kvdb_init   (fdb_kvdb_t db, const char *name, const char *part_name, struct fdb_default_kv *default_kv,
+fdb_err_t fdb_kvdb_init   (fdb_kvdb_t db, const char *name, const char *path, struct fdb_default_kv *default_kv,
         void *user_data);
 void      fdb_kvdb_control(fdb_kvdb_t db, int cmd, void *arg);
-fdb_err_t fdb_tsdb_init   (fdb_tsdb_t db, const char *name, const char *part_name, fdb_get_time get_time, size_t max_len,
+fdb_err_t fdb_kvdb_deinit(fdb_kvdb_t db);
+fdb_err_t fdb_tsdb_init   (fdb_tsdb_t db, const char *name, const char *path, fdb_get_time get_time, size_t max_len,
         void *user_data);
 void      fdb_tsdb_control(fdb_tsdb_t db, int cmd, void *arg);
+fdb_err_t fdb_tsdb_deinit(fdb_tsdb_t db);
 
 /* blob API */
 fdb_blob_t fdb_blob_make     (fdb_blob_t blob, const void *value_buf, size_t buf_len);
@@ -52,6 +60,7 @@ bool              fdb_kv_iterate      (fdb_kvdb_t db, fdb_kv_iterator_t itr);
 /* Time series log API like a TSDB */
 fdb_err_t  fdb_tsl_append      (fdb_tsdb_t db, fdb_blob_t blob);
 void       fdb_tsl_iter        (fdb_tsdb_t db, fdb_tsl_cb cb, void *cb_arg);
+void       fdb_tsl_iter_reverse(fdb_tsdb_t db, fdb_tsl_cb cb, void *cb_arg);
 void       fdb_tsl_iter_by_time(fdb_tsdb_t db, fdb_time_t from, fdb_time_t to, fdb_tsl_cb cb, void *cb_arg);
 size_t     fdb_tsl_query_count (fdb_tsdb_t db, fdb_time_t from, fdb_time_t to, fdb_tsl_status_t status);
 fdb_err_t  fdb_tsl_set_status  (fdb_tsdb_t db, fdb_tsl_t tsl, fdb_tsl_status_t status);

+ 72 - 18
flashdb/src/fdb.c

@@ -13,16 +13,19 @@
 
 #include <flashdb.h>
 #include <fdb_low_lvl.h>
+#include <string.h>
 
 #define FDB_LOG_TAG ""
 
-fdb_err_t _fdb_init_ex(fdb_db_t db, const char *name, const char *part_name, fdb_db_type type, void *user_data)
-{
-    size_t block_size;
+#if !defined(FDB_USING_FAL_MODE) && !defined(FDB_USING_FILE_MODE)
+#error "Please defined the FDB_USING_FAL_MODE or FDB_USING_FILE_MODE macro"
+#endif
 
+fdb_err_t _fdb_init_ex(fdb_db_t db, const char *name, const char *path, fdb_db_type type, void *user_data)
+{
     FDB_ASSERT(db);
     FDB_ASSERT(name);
-    FDB_ASSERT(part_name);
+    FDB_ASSERT(path);
 
     if (db->init_ok) {
         return FDB_NO_ERR;
@@ -31,22 +34,49 @@ fdb_err_t _fdb_init_ex(fdb_db_t db, const char *name, const char *part_name, fdb
     db->name = name;
     db->type = type;
     db->user_data = user_data;
-    /* FAL (Flash Abstraction Layer) initialization */
-    fal_init();
-    /* check the flash partition */
-    if ((db->part = fal_partition_find(part_name)) == NULL) {
-        FDB_INFO("Error: Partition (%s) not found.\n", part_name);
-        return FDB_PART_NOT_FOUND;
-    }
 
-    block_size = fal_flash_device_find(db->part->flash_name)->blk_size;
-    if (db->sec_size == 0) {
-        db->sec_size = block_size;
+    if (db->file_mode) {
+#ifdef FDB_USING_FILE_MODE
+        /* must set when using file mode */
+        FDB_ASSERT(db->sec_size != 0);
+        FDB_ASSERT(db->max_size != 0);
+#ifdef FDB_USING_FILE_POSIX_MODE
+        db->cur_file = -1;
+#else
+        db->cur_file = 0;
+#endif
+        db->storage.dir = path;
+        FDB_ASSERT(strlen(path) != 0)
+#endif
     } else {
-        /* must be aligned with block size */
-        FDB_ASSERT(db->sec_size % block_size == 0);
+#ifdef FDB_USING_FAL_MODE
+        size_t block_size;
+
+        /* FAL (Flash Abstraction Layer) initialization */
+        fal_init();
+        /* check the flash partition */
+        if ((db->storage.part = fal_partition_find(path)) == NULL) {
+            FDB_INFO("Error: Partition (%s) not found.\n", path);
+            return FDB_PART_NOT_FOUND;
+        }
+
+        block_size = fal_flash_device_find(db->storage.part->flash_name)->blk_size;
+        if (db->sec_size == 0) {
+            db->sec_size = block_size;
+        } else {
+            /* must be aligned with block size */
+            FDB_ASSERT(db->sec_size % block_size == 0);
+        }
+
+        db->max_size = db->storage.part->len;
+#endif /* FDB_USING_FAL_MODE */
     }
 
+    /* must align with sector size */
+    FDB_ASSERT(db->max_size % db->sec_size == 0);
+    /* must have more than or equal 2 sector */
+    FDB_ASSERT(db->max_size / db->sec_size >= 2);
+
     return FDB_NO_ERR;
 }
 
@@ -57,11 +87,35 @@ void _fdb_init_finish(fdb_db_t db, fdb_err_t result)
         db->init_ok = true;
         if (!log_is_show) {
             FDB_INFO("FlashDB V%s is initialize success.\n", FDB_SW_VERSION);
-           // FDB_INFO("You can get the latest version on https://github.com/armink/FlashDB .\n");
+            FDB_INFO("You can get the latest version on https://github.com/armink/FlashDB .\n");
             log_is_show = true;
         }
-    } else {
+    } else if (!db->not_formatable) {
         FDB_INFO("Error: %s (%s) is initialize fail (%d).\n", db->type == FDB_DB_TYPE_KV ? "KVDB" : "TSDB",
                 db->name, (int)result);
     }
 }
+
+void _fdb_deinit(fdb_db_t db)
+{
+    FDB_ASSERT(db);
+
+    if (db->init_ok) {
+#ifdef FDB_USING_FILE_MODE
+#ifdef FDB_USING_FILE_POSIX_MODE
+        if (db->cur_file > 0) {
+#if !defined(_MSC_VER)
+#include <unistd.h>
+#endif
+            close(db->cur_file);
+        }
+#else
+        if (db->cur_file != 0) {
+            fclose(db->cur_file);
+        }
+#endif /* FDB_USING_FILE_POSIX_MODE */
+#endif /* FDB_USING_FILE_MODE */
+    }
+
+    db->init_ok = false;
+}

+ 221 - 0
flashdb/src/fdb_file.c

@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
+ * Copyright (c) 2020, enkiller, <462747508@qq.com>
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <flashdb.h>
+#include <fdb_low_lvl.h>
+
+#define FDB_LOG_TAG "[file]"
+
+#ifdef FDB_USING_FILE_MODE
+
+#define DB_PATH_MAX            256
+
+static void get_db_file_path(fdb_db_t db, uint32_t addr, char *path, size_t size)
+{
+#define DB_NAME_MAX            8
+
+    /* from db_name.fdb.0 to db_name.fdb.n */
+    char file_name[DB_NAME_MAX + 4 + 10];
+    uint32_t sec_addr = FDB_ALIGN_DOWN(addr, db->sec_size);
+    int index = sec_addr / db->sec_size;
+
+    snprintf(file_name, sizeof(file_name), "%.*s.fdb.%d", DB_NAME_MAX, db->name, index);
+    if (strlen(db->storage.dir) + 1 + strlen(file_name) >= size) {
+        /* path is too long */
+        FDB_ASSERT(0)
+    }
+    snprintf(path, size, "%s/%s", db->storage.dir, file_name);
+}
+
+#if defined(FDB_USING_FILE_POSIX_MODE)
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#if !defined(_MSC_VER)
+#include <unistd.h>
+#endif
+
+static int open_db_file(fdb_db_t db, uint32_t addr, bool clean)
+{
+    uint32_t sec_addr = FDB_ALIGN_DOWN(addr, db->sec_size);
+    int fd = db->cur_file;
+    char path[DB_PATH_MAX];
+
+    if (sec_addr != db->cur_sec || fd <= 0 || clean) {
+        get_db_file_path(db, addr, path, DB_PATH_MAX);
+
+        if (fd > 0) {
+            close(fd);
+            fd = -1;
+        }
+        if (clean) {
+            /* clean the old file */
+            fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0777);
+            if (fd <= 0) {
+                FDB_INFO("Error: open (%s) file failed.\n", path);
+            }
+            else {
+                close(fd);
+                fd = -1;
+            }
+        }
+        /* open the database file */
+        fd = open(path, O_RDWR, 0777);
+        db->cur_sec = sec_addr;
+    }
+    db->cur_file = fd;
+
+    return db->cur_file;
+}
+
+fdb_err_t _fdb_file_read(fdb_db_t db, uint32_t addr, void *buf, size_t size)
+{
+    fdb_err_t result = FDB_NO_ERR;
+    int fd = open_db_file(db, addr, false);
+    if (fd > 0) {
+        addr = addr % db->sec_size;
+        lseek(fd, addr, SEEK_SET);
+        read(fd, buf, size);
+    } else {
+        result = FDB_READ_ERR;
+    }
+    return result;
+}
+
+fdb_err_t _fdb_file_write(fdb_db_t db, uint32_t addr, const void *buf, size_t size, bool sync)
+{
+    fdb_err_t result = FDB_NO_ERR;
+    int fd = open_db_file(db, addr, false);
+    if (fd > 0) {
+        addr = addr % db->sec_size;
+        lseek(fd, addr, SEEK_SET);
+        write(fd, buf, size);
+        if(sync) {
+            fsync(fd);
+        }
+    } else {
+        result = FDB_WRITE_ERR;
+    }
+
+    return result;
+}
+
+fdb_err_t _fdb_file_erase(fdb_db_t db, uint32_t addr, size_t size)
+{
+    fdb_err_t result = FDB_NO_ERR;
+    int fd = open_db_file(db, addr, true);
+    if (fd > 0) {
+#define BUF_SIZE 32
+        uint8_t buf[BUF_SIZE];
+        size_t i;
+        lseek(fd, 0, SEEK_SET);
+        for (i = 0; i * BUF_SIZE < size; i++)
+        {
+            memset(buf, 0xFF, BUF_SIZE);
+            write(fd, buf, BUF_SIZE);
+        }
+        memset(buf, 0xFF, BUF_SIZE);
+        write(fd, buf, size - i * BUF_SIZE);
+        fsync(fd);
+    } else {
+        result = FDB_ERASE_ERR;
+    }
+    return result;
+}
+#elif defined(FDB_USING_FILE_LIBC_MODE)
+static FILE *open_db_file(fdb_db_t db, uint32_t addr, bool clean)
+{
+    uint32_t sec_addr = FDB_ALIGN_DOWN(addr, db->sec_size);
+
+    if (sec_addr != db->cur_sec || db->cur_file == NULL || clean) {
+        char path[DB_PATH_MAX];
+
+        get_db_file_path(db, addr, path, DB_PATH_MAX);
+
+        if (db->cur_file) {
+            fclose(db->cur_file);
+        }
+
+        if (clean) {
+            /* clean the old file */
+            db->cur_file = fopen(path, "wb+");
+            if (db->cur_file == NULL) {
+                FDB_INFO("Error: open (%s) file failed.\n", path);
+            } else {
+                fclose(db->cur_file);
+            }
+        }
+
+        /* open the database file */
+        db->cur_file = fopen(path, "rb+");
+        db->cur_sec = sec_addr;
+    }
+
+    return db->cur_file;
+}
+
+fdb_err_t _fdb_file_read(fdb_db_t db, uint32_t addr, void *buf, size_t size)
+{
+    fdb_err_t result = FDB_NO_ERR;
+    FILE *fp = open_db_file(db, addr, false);
+    if (fp) {
+        addr = addr % db->sec_size;
+        fseek(fp, addr, SEEK_SET);
+        fread(buf, size, 1, fp);
+    } else {
+        result = FDB_READ_ERR;
+    }
+    return result;
+}
+
+fdb_err_t _fdb_file_write(fdb_db_t db, uint32_t addr, const void *buf, size_t size, bool sync)
+{
+    fdb_err_t result = FDB_NO_ERR;
+    FILE *fp = open_db_file(db, addr, false);
+    if (fp) {
+        addr = addr % db->sec_size;
+        fseek(fp, addr, SEEK_SET);
+        fwrite(buf, size, 1, fp);
+        if(sync) {
+            fflush(fp);
+        }
+    } else {
+        result = FDB_READ_ERR;
+    }
+
+    return result;
+}
+
+fdb_err_t _fdb_file_erase(fdb_db_t db, uint32_t addr, size_t size)
+{
+    fdb_err_t result = FDB_NO_ERR;
+
+    FILE *fp = open_db_file(db, addr, true);
+    if (fp != NULL) {
+#define BUF_SIZE 32
+        uint8_t buf[BUF_SIZE];
+        size_t i;
+        fseek(fp, 0, SEEK_SET);
+        for (i = 0; i * BUF_SIZE < size; i++)
+        {
+            memset(buf, 0xFF, BUF_SIZE);
+            fwrite(buf, BUF_SIZE, 1, fp);
+        }
+        memset(buf, 0xFF, BUF_SIZE);
+        fwrite(buf, size - i * BUF_SIZE, 1, fp);
+        fflush(fp);
+    } else {
+        result = FDB_ERASE_ERR;
+    }
+    return result;
+}
+#endif /* defined(FDB_USING_FILE_LIBC_MODE) */
+
+#endif /* FDB_USING_FILE_MODE */
+

+ 143 - 73
flashdb/src/fdb_kvdb.c

@@ -27,8 +27,8 @@
 #error "Please configure flash write granularity (in fdb_cfg.h)"
 #endif
 
-#if FDB_WRITE_GRAN != 1 && FDB_WRITE_GRAN != 8 && FDB_WRITE_GRAN != 32
-#error "the write gran can be only setting as 1, 8 and 32"
+#if FDB_WRITE_GRAN != 1 && FDB_WRITE_GRAN != 8 && FDB_WRITE_GRAN != 32 && FDB_WRITE_GRAN != 64
+#error "the write gran can be only setting as 1, 8, 32 and 64"
 #endif
 
 /* magic word(`F`, `D`, `B`, `1`) */
@@ -62,7 +62,7 @@
 
 #define KV_STATUS_TABLE_SIZE                     FDB_STATUS_TABLE_SIZE(FDB_KV_STATUS_NUM)
 
-#define SECTOR_NUM                               (db_part_size(db) / db_sec_size(db))
+#define SECTOR_NUM                               (db_max_size(db) / db_sec_size(db))
 
 #define SECTOR_HDR_DATA_SIZE                     (FDB_WG_ALIGN(sizeof(struct sector_hdr_data)))
 #define SECTOR_DIRTY_OFFSET                      ((unsigned long)(&((struct sector_hdr_data *)0)->status_table.dirty))
@@ -74,7 +74,8 @@
 #define db_name(db)                              (((fdb_db_t)db)->name)
 #define db_init_ok(db)                           (((fdb_db_t)db)->init_ok)
 #define db_sec_size(db)                          (((fdb_db_t)db)->sec_size)
-#define db_part_size(db)                         (((fdb_db_t)db)->part->len)
+#define db_max_size(db)                          (((fdb_db_t)db)->max_size)
+
 #define db_lock(db)                                                            \
     do {                                                                       \
         if (((fdb_db_t)db)->lock) ((fdb_db_t)db)->lock((fdb_db_t)db);          \
@@ -217,7 +218,7 @@ static bool get_kv_from_cache(fdb_kvdb_t db, const char *name, size_t name_len,
 
     for (i = 0; i < FDB_KV_CACHE_TABLE_SIZE; i++) {
         if ((db->kv_cache_table[i].addr != FDB_DATA_UNUSED) && (db->kv_cache_table[i].name_crc == name_crc)) {
-            char saved_name[FDB_KV_NAME_MAX];
+            char saved_name[FDB_KV_NAME_MAX] = { 0 };
             /* read the KV name in flash */
             _fdb_flash_read((fdb_db_t)db, db->kv_cache_table[i].addr + KV_HDR_DATA_SIZE, (uint32_t *) saved_name, FDB_KV_NAME_MAX);
             if (!strncmp(name, saved_name, name_len)) {
@@ -294,7 +295,7 @@ static uint32_t get_next_kv_addr(fdb_kvdb_t db, kv_sec_info_t sector, fdb_kv_t p
             addr = find_next_kv_addr(db, addr, sector->addr + db_sec_size(db) - SECTOR_HDR_DATA_SIZE);
 
             if (addr > sector->addr + db_sec_size(db) || pre_kv->len == 0) {
-                //TODO 扇区连续模式
+                //TODO Sector continuous mode
                 return FAILED_ADDR;
             }
         } else {
@@ -318,18 +319,18 @@ static fdb_err_t read_kv(fdb_kvdb_t db, fdb_kv_t kv)
     kv->status = (fdb_kv_status_t) _fdb_get_status(kv_hdr.status_table, FDB_KV_STATUS_NUM);
     kv->len = kv_hdr.len;
 
-    if (kv->len == ~0UL || kv->len > db_part_size(db) || kv->len < KV_NAME_LEN_OFFSET) {
+    if (kv->len == ~0UL || kv->len > db_max_size(db) || kv->len < KV_NAME_LEN_OFFSET) {
         /* the KV length was not write, so reserved the info for current KV */
         kv->len = KV_HDR_DATA_SIZE;
         if (kv->status != FDB_KV_ERR_HDR) {
             kv->status = FDB_KV_ERR_HDR;
             FDB_DEBUG("Error: The KV @0x%08" PRIX32 " length has an error.\n", kv->addr.start);
-            _fdb_write_status((fdb_db_t)db, kv->addr.start, kv_hdr.status_table, FDB_KV_STATUS_NUM, FDB_KV_ERR_HDR);
+            _fdb_write_status((fdb_db_t)db, kv->addr.start, kv_hdr.status_table, FDB_KV_STATUS_NUM, FDB_KV_ERR_HDR, true);
         }
         kv->crc_is_ok = false;
         return FDB_READ_ERR;
-    } else if (kv->len > db_sec_size(db) - SECTOR_HDR_DATA_SIZE && kv->len < db_part_size(db)) {
-        //TODO 扇区连续模式,或者写入长度没有写入完整
+    } else if (kv->len > db_sec_size(db) - SECTOR_HDR_DATA_SIZE && kv->len < db_max_size(db)) {
+        //TODO Sector continuous mode, or the write length is not written completely
         FDB_ASSERT(0);
     }
 
@@ -359,6 +360,10 @@ static fdb_err_t read_kv(fdb_kvdb_t db, fdb_kv_t kv)
         kv->addr.value = kv_name_addr + FDB_WG_ALIGN(kv_hdr.name_len);
         kv->value_len = kv_hdr.value_len;
         kv->name_len = kv_hdr.name_len;
+        if (kv_hdr.name_len >= sizeof(kv->name) / sizeof(kv->name[0])) {
+            kv_hdr.name_len = sizeof(kv->name) / sizeof(kv->name[0]) - 1;
+        }
+        kv->name[kv_hdr.name_len] = '\0';
     }
 
     return result;
@@ -367,7 +372,7 @@ static fdb_err_t read_kv(fdb_kvdb_t db, fdb_kv_t kv)
 static fdb_err_t read_sector_info(fdb_kvdb_t db, uint32_t addr, kv_sec_info_t sector, bool traversal)
 {
     fdb_err_t result = FDB_NO_ERR;
-    struct sector_hdr_data sec_hdr;
+    struct sector_hdr_data sec_hdr = { 0 };
 
     FDB_ASSERT(addr % db_sec_size(db) == 0);
     FDB_ASSERT(sector);
@@ -447,7 +452,7 @@ static uint32_t get_next_sector_addr(fdb_kvdb_t db, kv_sec_info_t pre_sec)
     uint32_t next_addr;
 
     if (pre_sec->addr == FAILED_ADDR) {
-        /* the next sector is on the top of the partition */
+        /* the next sector is on the top of the database */
         return 0;
     } else {
         /* check KV sector combined */
@@ -457,7 +462,7 @@ static uint32_t get_next_sector_addr(fdb_kvdb_t db, kv_sec_info_t pre_sec)
             next_addr = pre_sec->addr + pre_sec->combined * db_sec_size(db);
         }
         /* check range */
-        if (next_addr < db_part_size(db)) {
+        if (next_addr < db_max_size(db)) {
             return next_addr;
         } else {
             /* no sector */
@@ -681,8 +686,8 @@ char *fdb_kv_get(fdb_kvdb_t db, const char *key)
             value[get_size] = '\0';
             return value;
         } else if (blob.saved.len > FDB_STR_KV_VALUE_MAX_SIZE) {
-            FDB_INFO("Warning: The default string KV value buffer length (%d) is too less (%u).\n", FDB_STR_KV_VALUE_MAX_SIZE,
-                    blob.saved.len);
+            FDB_INFO("Warning: The default string KV value buffer length (%" PRIdLEAST16 ") is too less (%" PRIu32 ").\n", FDB_STR_KV_VALUE_MAX_SIZE,
+                    (uint32_t)blob.saved.len);
         } else {
             FDB_INFO("Warning: The KV value isn't string. Could not be returned\n");
             return NULL;
@@ -696,12 +701,12 @@ static fdb_err_t write_kv_hdr(fdb_kvdb_t db, uint32_t addr, kv_hdr_data_t kv_hdr
 {
     fdb_err_t result = FDB_NO_ERR;
     /* write the status will by write granularity */
-    result = _fdb_write_status((fdb_db_t)db, addr, kv_hdr->status_table, FDB_KV_STATUS_NUM, FDB_KV_PRE_WRITE);
+    result = _fdb_write_status((fdb_db_t)db, addr, kv_hdr->status_table, FDB_KV_STATUS_NUM, FDB_KV_PRE_WRITE, false);
     if (result != FDB_NO_ERR) {
         return result;
     }
     /* write other header data */
-    result = _fdb_flash_write((fdb_db_t)db, addr + KV_MAGIC_OFFSET, &kv_hdr->magic, sizeof(struct kv_hdr_data) - KV_MAGIC_OFFSET);
+    result = _fdb_flash_write((fdb_db_t)db, addr + KV_MAGIC_OFFSET, &kv_hdr->magic, KV_HDR_DATA_SIZE - KV_MAGIC_OFFSET, false);
 
     return result;
 }
@@ -709,7 +714,7 @@ static fdb_err_t write_kv_hdr(fdb_kvdb_t db, uint32_t addr, kv_hdr_data_t kv_hdr
 static fdb_err_t format_sector(fdb_kvdb_t db, uint32_t addr, uint32_t combined_value)
 {
     fdb_err_t result = FDB_NO_ERR;
-    struct sector_hdr_data sec_hdr;
+    struct sector_hdr_data sec_hdr = { 0 };
 
     FDB_ASSERT(addr % db_sec_size(db) == 0);
 
@@ -723,7 +728,7 @@ static fdb_err_t format_sector(fdb_kvdb_t db, uint32_t addr, uint32_t combined_v
         sec_hdr.combined = combined_value;
         sec_hdr.reserved = 0xFFFFFFFF;
         /* save the header */
-        result = _fdb_flash_write((fdb_db_t)db, addr, (uint32_t *)&sec_hdr, sizeof(struct sector_hdr_data));
+        result = _fdb_flash_write((fdb_db_t)db, addr, (uint32_t *)&sec_hdr, sizeof(struct sector_hdr_data), true);
 
 #ifdef FDB_KV_USING_CACHE
         /* delete the sector cache */
@@ -741,12 +746,12 @@ static fdb_err_t update_sec_status(fdb_kvdb_t db, kv_sec_info_t sector, size_t n
     /* change the current sector status */
     if (sector->status.store == FDB_SECTOR_STORE_EMPTY) {
         /* change the sector status to using */
-        result = _fdb_write_status((fdb_db_t)db, sector->addr, status_table, FDB_SECTOR_STORE_STATUS_NUM, FDB_SECTOR_STORE_USING);
+        result = _fdb_write_status((fdb_db_t)db, sector->addr, status_table, FDB_SECTOR_STORE_STATUS_NUM, FDB_SECTOR_STORE_USING, true);
     } else if (sector->status.store == FDB_SECTOR_STORE_USING) {
         /* check remain size */
         if (sector->remain < FDB_SEC_REMAIN_THRESHOLD || sector->remain - new_kv_len < FDB_SEC_REMAIN_THRESHOLD) {
             /* change the sector status to full */
-            result = _fdb_write_status((fdb_db_t)db, sector->addr, status_table, FDB_SECTOR_STORE_STATUS_NUM, FDB_SECTOR_STORE_FULL);
+            result = _fdb_write_status((fdb_db_t)db, sector->addr, status_table, FDB_SECTOR_STORE_STATUS_NUM, FDB_SECTOR_STORE_FULL, true);
 
 #ifdef FDB_KV_USING_CACHE
             /* delete the sector cache */
@@ -844,7 +849,6 @@ static fdb_err_t del_kv(fdb_kvdb_t db, const char *key, fdb_kv_t old_kv, bool co
 {
     fdb_err_t result = FDB_NO_ERR;
     uint32_t dirty_status_addr;
-    static bool last_is_complete_del = false;
 
 #if (KV_STATUS_TABLE_SIZE >= FDB_DIRTY_STATUS_TABLE_SIZE)
     uint8_t status_table[KV_STATUS_TABLE_SIZE];
@@ -865,12 +869,12 @@ static fdb_err_t del_kv(fdb_kvdb_t db, const char *key, fdb_kv_t old_kv, bool co
     }
     /* change and save the new status */
     if (!complete_del) {
-        result = _fdb_write_status((fdb_db_t)db, old_kv->addr.start, status_table, FDB_KV_STATUS_NUM, FDB_KV_PRE_DELETE);
-        last_is_complete_del = true;
+        result = _fdb_write_status((fdb_db_t)db, old_kv->addr.start, status_table, FDB_KV_STATUS_NUM, FDB_KV_PRE_DELETE, false);
+        db->last_is_complete_del = true;
     } else {
-        result = _fdb_write_status((fdb_db_t)db, old_kv->addr.start, status_table, FDB_KV_STATUS_NUM, FDB_KV_DELETED);
+        result = _fdb_write_status((fdb_db_t)db, old_kv->addr.start, status_table, FDB_KV_STATUS_NUM, FDB_KV_DELETED, true);
 
-        if (!last_is_complete_del && result == FDB_NO_ERR) {
+        if (!db->last_is_complete_del && result == FDB_NO_ERR) {
 #ifdef FDB_KV_USING_CACHE
             /* delete the KV in flash and cache */
             if (key != NULL) {
@@ -883,14 +887,14 @@ static fdb_err_t del_kv(fdb_kvdb_t db, const char *key, fdb_kv_t old_kv, bool co
 #endif /* FDB_KV_USING_CACHE */
         }
 
-        last_is_complete_del = false;
+        db->last_is_complete_del = false;
     }
 
     dirty_status_addr = FDB_ALIGN_DOWN(old_kv->addr.start, db_sec_size(db)) + SECTOR_DIRTY_OFFSET;
     /* read and change the sector dirty status */
     if (result == FDB_NO_ERR
             && _fdb_read_status((fdb_db_t)db, dirty_status_addr, status_table, FDB_SECTOR_DIRTY_STATUS_NUM) == FDB_SECTOR_DIRTY_FALSE) {
-        result = _fdb_write_status((fdb_db_t)db, dirty_status_addr, status_table, FDB_SECTOR_DIRTY_STATUS_NUM, FDB_SECTOR_DIRTY_TRUE);
+        result = _fdb_write_status((fdb_db_t)db, dirty_status_addr, status_table, FDB_SECTOR_DIRTY_STATUS_NUM, FDB_SECTOR_DIRTY_TRUE, true);
     }
 
     return result;
@@ -934,7 +938,7 @@ static fdb_err_t move_kv(fdb_kvdb_t db, fdb_kv_t kv)
         /* update the new KV sector status first */
         update_sec_status(db, &sector, kv->len, NULL);
 
-        _fdb_write_status((fdb_db_t)db, kv_addr, status_table, FDB_KV_STATUS_NUM, FDB_KV_PRE_WRITE);
+        _fdb_write_status((fdb_db_t)db, kv_addr, status_table, FDB_KV_STATUS_NUM, FDB_KV_PRE_WRITE, false);
         kv_len -= KV_MAGIC_OFFSET;
         for (len = 0, size = 0; len < kv_len; len += size) {
             if (len + sizeof(buf) < kv_len) {
@@ -943,9 +947,9 @@ static fdb_err_t move_kv(fdb_kvdb_t db, fdb_kv_t kv)
                 size = kv_len - len;
             }
             _fdb_flash_read((fdb_db_t)db, kv->addr.start + KV_MAGIC_OFFSET + len, (uint32_t *) buf, FDB_WG_ALIGN(size));
-            result = _fdb_flash_write((fdb_db_t)db, kv_addr + KV_MAGIC_OFFSET + len, (uint32_t *) buf, size);
+            result = _fdb_flash_write((fdb_db_t)db, kv_addr + KV_MAGIC_OFFSET + len, (uint32_t *) buf, size, true);
         }
-        _fdb_write_status((fdb_db_t)db, kv_addr, status_table, FDB_KV_STATUS_NUM, FDB_KV_WRITE);
+        _fdb_write_status((fdb_db_t)db, kv_addr, status_table, FDB_KV_STATUS_NUM, FDB_KV_WRITE, true);
 
 #ifdef FDB_KV_USING_CACHE
         update_sector_cache(db, FDB_ALIGN_DOWN(kv_addr, db_sec_size(db)),
@@ -969,11 +973,16 @@ static uint32_t new_kv(fdb_kvdb_t db, kv_sec_info_t sector, size_t kv_size)
 
 __retry:
 
-    if ((empty_kv = alloc_kv(db, sector, kv_size)) == FAILED_ADDR && db->gc_request && !already_gc) {
-        FDB_DEBUG("Warning: Alloc an KV (size %u) failed when new KV. Now will GC then retry.\n", kv_size);
-        gc_collect(db);
-        already_gc = true;
-        goto __retry;
+    if ((empty_kv = alloc_kv(db, sector, kv_size)) == FAILED_ADDR) {
+        if (db->gc_request && !already_gc) {
+            FDB_DEBUG("Warning: Alloc an KV (size %" PRIu32 ") failed when new KV. Now will GC then retry.\n", (uint32_t)kv_size);
+            gc_collect(db);
+            already_gc = true;
+            goto __retry;
+        } else if (already_gc) {
+            FDB_DEBUG("Error: Alloc an KV (size %" PRIuLEAST16 ") failed after GC. KV full.\n", kv_size);
+            db->gc_request = false;
+        }
     }
 
     return empty_kv;
@@ -1006,7 +1015,7 @@ static bool do_gc(kv_sec_info_t sector, void *arg1, void *arg2)
     if (sector->check_ok && (sector->status.dirty == FDB_SECTOR_DIRTY_TRUE || sector->status.dirty == FDB_SECTOR_DIRTY_GC)) {
         uint8_t status_table[FDB_DIRTY_STATUS_TABLE_SIZE];
         /* change the sector status to GC */
-        _fdb_write_status((fdb_db_t)db, sector->addr + SECTOR_DIRTY_OFFSET, status_table, FDB_SECTOR_DIRTY_STATUS_NUM, FDB_SECTOR_DIRTY_GC);
+        _fdb_write_status((fdb_db_t)db, sector->addr + SECTOR_DIRTY_OFFSET, status_table, FDB_SECTOR_DIRTY_STATUS_NUM, FDB_SECTOR_DIRTY_GC, true);
         /* search all KV */
         kv.addr.start = sector->addr + SECTOR_HDR_DATA_SIZE;
         do {
@@ -1039,7 +1048,7 @@ static void gc_collect(fdb_kvdb_t db)
     sector_iterator(db, &sector, FDB_SECTOR_STORE_EMPTY, &empty_sec, NULL, gc_check_cb, false);
 
     /* do GC collect */
-    FDB_DEBUG("The remain empty sector is %u, GC threshold is %d.\n", empty_sec, FDB_GC_EMPTY_SEC_THRESHOLD);
+    FDB_DEBUG("The remain empty sector is %" PRIu32 ", GC threshold is %" PRIdLEAST16 ".\n", (uint32_t)empty_sec, FDB_GC_EMPTY_SEC_THRESHOLD);
     if (empty_sec <= FDB_GC_EMPTY_SEC_THRESHOLD) {
         sector_iterator(db, &sector, FDB_SECTOR_STORE_UNUSED, db, NULL, do_gc, false);
     }
@@ -1062,12 +1071,12 @@ static fdb_err_t align_write(fdb_kvdb_t db, uint32_t addr, const uint32_t *buf,
 #endif
 
     memset(align_data, 0xFF, align_data_size);
-    result = _fdb_flash_write((fdb_db_t)db, addr, buf, FDB_WG_ALIGN_DOWN(size));
+    result = _fdb_flash_write((fdb_db_t)db, addr, buf, FDB_WG_ALIGN_DOWN(size), false);
 
     align_remain = size - FDB_WG_ALIGN_DOWN(size);
     if (result == FDB_NO_ERR && align_remain) {
         memcpy(align_data, (uint8_t *)buf + FDB_WG_ALIGN_DOWN(size), align_remain);
-        result = _fdb_flash_write((fdb_db_t)db, addr + FDB_WG_ALIGN_DOWN(size), (uint32_t *) align_data, align_data_size);
+        result = _fdb_flash_write((fdb_db_t)db, addr + FDB_WG_ALIGN_DOWN(size), (uint32_t *) align_data, align_data_size, false);
     }
 
     return result;
@@ -1139,7 +1148,7 @@ static fdb_err_t create_kv_blob(fdb_kvdb_t db, kv_sec_info_t sector, const char
         }
         /* change the KV status to KV_WRITE */
         if (result == FDB_NO_ERR) {
-            result = _fdb_write_status((fdb_db_t)db, kv_addr, kv_hdr.status_table, FDB_KV_STATUS_NUM, FDB_KV_WRITE);
+            result = _fdb_write_status((fdb_db_t)db, kv_addr, kv_hdr.status_table, FDB_KV_STATUS_NUM, FDB_KV_WRITE, true);
         }
         /* trigger GC collect when current sector is full */
         if (result == FDB_NO_ERR && is_full) {
@@ -1184,29 +1193,27 @@ fdb_err_t fdb_kv_del(fdb_kvdb_t db, const char *key)
 static fdb_err_t set_kv(fdb_kvdb_t db, const char *key, const void *value_buf, size_t buf_len)
 {
     fdb_err_t result = FDB_NO_ERR;
-    static struct fdb_kv kv;
-    static struct kvdb_sec_info sector;
     bool kv_is_found = false;
 
     if (value_buf == NULL) {
         result = del_kv(db, key, NULL, true);
     } else {
         /* make sure the flash has enough space */
-        if (new_kv_ex(db, &sector, strlen(key), buf_len) == FAILED_ADDR) {
+        if (new_kv_ex(db, &db->cur_sector, strlen(key), buf_len) == FAILED_ADDR) {
             return FDB_SAVED_FULL;
         }
-        kv_is_found = find_kv(db, key, &kv);
+        kv_is_found = find_kv(db, key, &db->cur_kv);
         /* prepare to delete the old KV */
         if (kv_is_found) {
-            result = del_kv(db, key, &kv, false);
+            result = del_kv(db, key, &db->cur_kv, false);
         }
         /* create the new KV */
         if (result == FDB_NO_ERR) {
-            result = create_kv_blob(db, &sector, key, value_buf, buf_len);
+            result = create_kv_blob(db, &db->cur_sector, key, value_buf, buf_len);
         }
         /* delete the old KV */
         if (kv_is_found && result == FDB_NO_ERR) {
-            result = del_kv(db, key, &kv, true);
+            result = del_kv(db, key, &db->cur_kv, true);
         }
         /* process the GC after set KV */
         if (db->gc_request) {
@@ -1279,7 +1286,7 @@ fdb_err_t fdb_kv_set_default(fdb_kvdb_t db)
     /* lock the KV cache */
     db_lock(db);
     /* format all sectors */
-    for (addr = 0; addr < db_part_size(db); addr += db_sec_size(db)) {
+    for (addr = 0; addr < db_max_size(db); addr += db_sec_size(db)) {
         result = format_sector(db, addr, SECTOR_NOT_COMBINED);
         if (result != FDB_NO_ERR) {
             goto __exit;
@@ -1334,7 +1341,7 @@ __reload:
                     }
                     _fdb_flash_read((fdb_db_t)db, kv->addr.value + len, (uint32_t *) buf, FDB_WG_ALIGN(size));
                     if (print_value) {
-                        FDB_PRINT("%.*s", size, buf);
+                        FDB_PRINT("%.*s", (int)size, buf);
                     } else if (!fdb_is_str(buf, size)) {
                         value_is_str = false;
                         break;
@@ -1378,8 +1385,8 @@ void fdb_kv_print(fdb_kvdb_t db)
     kv_iterator(db, &kv, &using_size, db, print_kv_cb);
 
     FDB_PRINT("\nmode: next generation\n");
-    FDB_PRINT("size: %u/%u bytes.\n", using_size + (size_t)((SECTOR_NUM - FDB_GC_EMPTY_SEC_THRESHOLD) * SECTOR_HDR_DATA_SIZE),
-            (size_t)(db_part_size(db) - db_sec_size(db) * FDB_GC_EMPTY_SEC_THRESHOLD));
+    FDB_PRINT("size: %" PRIu32 "/%" PRIu32 " bytes.\n", (uint32_t)using_size + ((SECTOR_NUM - FDB_GC_EMPTY_SEC_THRESHOLD) * SECTOR_HDR_DATA_SIZE),
+            db_max_size(db) - db_sec_size(db) * FDB_GC_EMPTY_SEC_THRESHOLD);
 
     /* unlock the KV cache */
     db_unlock(db);
@@ -1396,13 +1403,11 @@ static void kv_auto_update(fdb_kvdb_t db)
     if (get_kv(db, VER_NUM_KV_NAME, &saved_ver_num, sizeof(size_t), NULL) > 0) {
         /* check version number */
         if (saved_ver_num != setting_ver_num) {
-            struct fdb_kv kv;
             size_t i, value_len;
-            struct kvdb_sec_info sector;
-            FDB_DEBUG("Update the KV from version %u to %u.\n", saved_ver_num, setting_ver_num);
+            FDB_DEBUG("Update the KV from version %zu to %zu.\n", saved_ver_num, setting_ver_num);
             for (i = 0; i < db->default_kvs.num; i++) {
                 /* add a new KV when it's not found */
-                if (!find_kv(db, db->default_kvs.kvs[i].key, &kv)) {
+                if (!find_kv(db, db->default_kvs.kvs[i].key, &db->cur_kv)) {
                     /* It seems to be a string when value length is 0.
                      * This mechanism is for compatibility with older versions (less then V4.0). */
                     if (db->default_kvs.kvs[i].value_len == 0) {
@@ -1410,8 +1415,8 @@ static void kv_auto_update(fdb_kvdb_t db)
                     } else {
                         value_len = db->default_kvs.kvs[i].value_len;
                     }
-                    sector.empty_kv = FAILED_ADDR;
-                    create_kv_blob(db, &sector, db->default_kvs.kvs[i].key, db->default_kvs.kvs[i].value, value_len);
+                    db->cur_sector.empty_kv = FAILED_ADDR;
+                    create_kv_blob(db, &db->cur_sector, db->default_kvs.kvs[i].key, db->default_kvs.kvs[i].value, value_len);
                 }
             }
         } else {
@@ -1430,9 +1435,13 @@ static bool check_sec_hdr_cb(kv_sec_info_t sector, void *arg1, void *arg2)
         size_t *failed_count = arg1;
         fdb_kvdb_t db = arg2;
 
-        FDB_INFO("Sector header info is incorrect. Auto format this sector (0x%08" PRIX32 ").\n", sector->addr);
         (*failed_count) ++;
-        format_sector(db, sector->addr, SECTOR_NOT_COMBINED);
+        if (db->parent.not_formatable) {
+            return true;
+        } else {
+            FDB_DEBUG("Sector header info is incorrect. Auto format this sector (0x%08" PRIX32 ").\n", sector->addr);
+            format_sector(db, sector->addr, SECTOR_NOT_COMBINED);
+        }
     }
 
     return false;
@@ -1469,9 +1478,14 @@ static bool check_and_recovery_kv_cb(fdb_kv_t kv, void *arg1, void *arg2)
     } else if (kv->status == FDB_KV_PRE_WRITE) {
         uint8_t status_table[KV_STATUS_TABLE_SIZE];
         /* the KV has not write finish, change the status to error */
-        //TODO 绘制异常处理的状态装换图
-        _fdb_write_status((fdb_db_t)db, kv->addr.start, status_table, FDB_KV_STATUS_NUM, FDB_KV_ERR_HDR);
+        //TODO Draw the state replacement diagram of exception handling
+        _fdb_write_status((fdb_db_t)db, kv->addr.start, status_table, FDB_KV_STATUS_NUM, FDB_KV_ERR_HDR, true);
         return true;
+    } else if (kv->crc_is_ok && kv->status == FDB_KV_WRITE) {
+#ifdef FDB_KV_USING_CACHE
+        /* update the cache when first load. If caching is disabled, this step is not performed */
+        update_kv_cache(db, kv->name, kv->name_len, kv->addr.start);
+#endif
     }
 
     return false;
@@ -1492,6 +1506,10 @@ fdb_err_t _fdb_kv_load(fdb_kvdb_t db)
     db->in_recovery_check = true;
     /* check all sector header */
     sector_iterator(db, &sector, FDB_SECTOR_STORE_UNUSED, &check_failed_count, db, check_sec_hdr_cb, false);
+    if (db->parent.not_formatable && check_failed_count > 0) {
+        result = FDB_READ_ERR;
+        goto __exit;
+    }
     /* all sector header check failed */
     if (check_failed_count == SECTOR_NUM) {
         FDB_INFO("All sector header is incorrect. Set it to default.\n");
@@ -1513,6 +1531,7 @@ __retry:
 
     db->in_recovery_check = false;
 
+__exit:
     /* unlock the KV cache */
     db_unlock(db);
 
@@ -1532,7 +1551,7 @@ void fdb_kvdb_control(fdb_kvdb_t db, int cmd, void *arg)
 
     switch (cmd) {
     case FDB_KVDB_CTRL_SET_SEC_SIZE:
-        /* the sector size change MUST before database initialization */
+        /* this change MUST before database initialization */
         FDB_ASSERT(db->parent.init_ok == false);
         db->parent.sec_size = *(uint32_t *)arg;
         break;
@@ -1540,10 +1559,45 @@ void fdb_kvdb_control(fdb_kvdb_t db, int cmd, void *arg)
         *(uint32_t *)arg = db->parent.sec_size;
         break;
     case FDB_KVDB_CTRL_SET_LOCK:
+#if !defined(__ARMCC_VERSION) && defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+#endif
         db->parent.lock = (void (*)(fdb_db_t db))arg;
+#if !defined(__ARMCC_VERSION) && defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
         break;
     case FDB_KVDB_CTRL_SET_UNLOCK:
+#if !defined(__ARMCC_VERSION) && defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+#endif
         db->parent.unlock = (void (*)(fdb_db_t db))arg;
+#if !defined(__ARMCC_VERSION) && defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
+        break;
+    case FDB_KVDB_CTRL_SET_FILE_MODE:
+#ifdef FDB_USING_FILE_MODE
+        /* this change MUST before database initialization */
+        FDB_ASSERT(db->parent.init_ok == false);
+        db->parent.file_mode = *(bool *)arg;
+#else
+        FDB_INFO("Error: set file mode Failed. Please defined the FDB_USING_FILE_MODE macro.");
+#endif
+        break;
+    case FDB_KVDB_CTRL_SET_MAX_SIZE:
+#ifdef FDB_USING_FILE_MODE
+        /* this change MUST before database initialization */
+        FDB_ASSERT(db->parent.init_ok == false);
+        db->parent.max_size = *(uint32_t *)arg;
+#endif
+        break;
+    case FDB_KVDB_CTRL_SET_NOT_FORMAT:
+        /* this change MUST before database initialization */
+        FDB_ASSERT(db->parent.init_ok == false);
+        db->parent.not_formatable = *(bool *)arg;
         break;
     }
 }
@@ -1553,13 +1607,13 @@ void fdb_kvdb_control(fdb_kvdb_t db, int cmd, void *arg)
  *
  * @param db database object
  * @param name database name
- * @param part_name partition name
+ * @param path FAL mode: partition name, file mode: database saved directory path
  * @param default_kv the default KV set @see fdb_default_kv
  * @param user_data user data
  *
  * @return result
  */
-fdb_err_t fdb_kvdb_init(fdb_kvdb_t db, const char *name, const char *part_name, struct fdb_default_kv *default_kv,
+fdb_err_t fdb_kvdb_init(fdb_kvdb_t db, const char *name, const char *path, struct fdb_default_kv *default_kv,
         void *user_data)
 {
     fdb_err_t result = FDB_NO_ERR;
@@ -1568,24 +1622,25 @@ fdb_err_t fdb_kvdb_init(fdb_kvdb_t db, const char *name, const char *part_name,
     size_t i;
 #endif
 
-    FDB_ASSERT(default_kv);
     /* must be aligned with write granularity */
     FDB_ASSERT((FDB_STR_KV_VALUE_MAX_SIZE * 8) % FDB_WRITE_GRAN == 0);
 
-    result = _fdb_init_ex((fdb_db_t)db, name, part_name, FDB_DB_TYPE_KV, user_data);
+    result = _fdb_init_ex((fdb_db_t)db, name, path, FDB_DB_TYPE_KV, user_data);
     if (result != FDB_NO_ERR) {
         goto __exit;
     }
 
     db->gc_request = false;
     db->in_recovery_check = false;
-    db->default_kvs = *default_kv;
+    if (default_kv) {
+        db->default_kvs = *default_kv;
+    }
+    else {
+        db->default_kvs.num = 0;
+        db->default_kvs.kvs = NULL;
+    }
     /* there is at least one empty sector for GC. */
     FDB_ASSERT((FDB_GC_EMPTY_SEC_THRESHOLD > 0 && FDB_GC_EMPTY_SEC_THRESHOLD < SECTOR_NUM))
-    /* must be aligned with sector size */
-    FDB_ASSERT(db_part_size(db) % db_sec_size(db) == 0);
-    /* must be has more than 2 sector */
-    FDB_ASSERT(db_part_size(db) / db_sec_size(db) >= 2);
 
 #ifdef FDB_KV_USING_CACHE
     for (i = 0; i < FDB_SECTOR_CACHE_TABLE_SIZE; i++) {
@@ -1596,7 +1651,7 @@ fdb_err_t fdb_kvdb_init(fdb_kvdb_t db, const char *name, const char *part_name,
     }
 #endif /* FDB_KV_USING_CACHE */
 
-    FDB_DEBUG("KVDB in partition %s, size is %u bytes.\n", ((fdb_db_t)db)->part->name, db_part_size(db));
+    FDB_DEBUG("KVDB size is %" PRIu32 " bytes.\n", db_max_size(db));
 
     result = _fdb_kv_load(db);
 
@@ -1613,6 +1668,20 @@ __exit:
     return result;
 }
 
+/**
+ * The KV database initialization.
+ *
+ * @param db database object
+ *
+ * @return result
+ */
+fdb_err_t fdb_kvdb_deinit(fdb_kvdb_t db)
+{
+    _fdb_deinit((fdb_db_t) db);
+
+    return FDB_NO_ERR;
+}
+
 /**
  * The KV database initialization.
  *
@@ -1678,3 +1747,4 @@ bool fdb_kv_iterate(fdb_kvdb_t db, fdb_kv_iterator_t itr)
 }
 
 #endif /* defined(FDB_USING_KVDB) */
+

+ 297 - 102
flashdb/src/fdb_tsdb.c

@@ -48,7 +48,8 @@
 #define db_name(db)                              (((fdb_db_t)db)->name)
 #define db_init_ok(db)                           (((fdb_db_t)db)->init_ok)
 #define db_sec_size(db)                          (((fdb_db_t)db)->sec_size)
-#define db_part_size(db)                         (((fdb_db_t)db)->part->len)
+#define db_max_size(db)                          (((fdb_db_t)db)->max_size)
+
 #define db_lock(db)                                                            \
     do {                                                                       \
         if (((fdb_db_t)db)->lock) ((fdb_db_t)db)->lock((fdb_db_t)db);          \
@@ -59,15 +60,15 @@
         if (((fdb_db_t)db)->unlock) ((fdb_db_t)db)->unlock((fdb_db_t)db);      \
     } while(0);
 
-#define _FDB_WRITE_STATUS(db, addr, status_table, status_num, status_index)    \
+#define _FDB_WRITE_STATUS(db, addr, status_table, status_num, status_index, sync)    \
     do {                                                                       \
-        result = _fdb_write_status((fdb_db_t)db, addr, status_table, status_num, status_index);\
+        result = _fdb_write_status((fdb_db_t)db, addr, status_table, status_num, status_index, sync);\
         if (result != FDB_NO_ERR) return result;                               \
     } while(0);
 
-#define FLASH_WRITE(db, addr, buf, size)                                       \
+#define FLASH_WRITE(db, addr, buf, size, sync)                                 \
     do {                                                                       \
-        result = _fdb_flash_write((fdb_db_t)db, addr, buf, size);              \
+        result = _fdb_flash_write((fdb_db_t)db, addr, buf, size, sync);        \
         if (result != FDB_NO_ERR) return result;                               \
     } while(0);
 
@@ -111,7 +112,7 @@ static fdb_err_t read_tsl(fdb_tsdb_t db, fdb_tsl_t tsl)
     /* read TSL index raw data */
     _fdb_flash_read((fdb_db_t)db, tsl->addr.index, (uint32_t *) &idx, sizeof(struct log_idx_data));
     tsl->status = (fdb_tsl_status_t) _fdb_get_status(idx.status_table, FDB_TSL_STATUS_NUM);
-    if (tsl->status == FDB_TSL_PRE_WRITE) {
+    if ((tsl->status == FDB_TSL_PRE_WRITE) || (tsl->status == FDB_TSL_UNUSED)) {
         tsl->log_len = db->max_len;
         tsl->addr.log = FDB_DATA_UNUSED;
         tsl->time = 0;
@@ -126,11 +127,11 @@ static fdb_err_t read_tsl(fdb_tsdb_t db, fdb_tsl_t tsl)
 
 static uint32_t get_next_sector_addr(fdb_tsdb_t db, tsdb_sec_info_t pre_sec, uint32_t traversed_len)
 {
-    if (traversed_len + db_sec_size(db) <= db_part_size(db)) {
-        if (pre_sec->addr + db_sec_size(db) < db_part_size(db)) {
+    if (traversed_len + db_sec_size(db) <= db_max_size(db)) {
+        if (pre_sec->addr + db_sec_size(db) < db_max_size(db)) {
             return pre_sec->addr + db_sec_size(db);
         } else {
-            /* the next sector is on the top of the partition */
+            /* the next sector is on the top of the database */
             return 0;
         }
     } else {
@@ -157,6 +158,38 @@ static uint32_t get_next_tsl_addr(tsdb_sec_info_t sector, fdb_tsl_t pre_tsl)
     return addr;
 }
 
+static uint32_t get_last_tsl_addr(tsdb_sec_info_t sector, fdb_tsl_t pre_tsl)
+{
+    uint32_t addr = FAILED_ADDR;
+
+    if (sector->status == FDB_SECTOR_STORE_EMPTY) {
+        return FAILED_ADDR;
+    }
+
+    if (pre_tsl->addr.index >= (sector->addr + SECTOR_HDR_DATA_SIZE + LOG_IDX_DATA_SIZE)) {
+        addr = pre_tsl->addr.index - LOG_IDX_DATA_SIZE;
+    } else {
+        return FAILED_ADDR;
+    }
+
+    return addr;
+}
+
+static uint32_t get_last_sector_addr(fdb_tsdb_t db, tsdb_sec_info_t pre_sec, uint32_t traversed_len)
+{
+    if (traversed_len + db_sec_size(db) <= db_max_size(db)) {
+        if (pre_sec->addr >= db_sec_size(db)) {
+            /* the next sector is previous sector */
+            return pre_sec->addr - db_sec_size(db);
+        } else {
+            /* the next sector is the last sector */
+            return db_max_size(db) - db_sec_size(db);
+        }
+    } else {
+        return FAILED_ADDR;
+    }
+}
+
 static fdb_err_t read_sector_info(fdb_tsdb_t db, uint32_t addr, tsdb_sec_info_t sector, bool traversal)
 {
     fdb_err_t result = FDB_NO_ERR;
@@ -231,10 +264,10 @@ static fdb_err_t format_sector(fdb_tsdb_t db, uint32_t addr)
 
     result = _fdb_flash_erase((fdb_db_t)db, addr, db_sec_size(db));
     if (result == FDB_NO_ERR) {
-        _FDB_WRITE_STATUS(db, addr, sec_hdr.status, FDB_SECTOR_STORE_STATUS_NUM, FDB_SECTOR_STORE_EMPTY);
+        _FDB_WRITE_STATUS(db, addr, sec_hdr.status, FDB_SECTOR_STORE_STATUS_NUM, FDB_SECTOR_STORE_EMPTY, true);
         /* set the magic */
         sec_hdr.magic = SECTOR_MAGIC_WORD;
-        FLASH_WRITE(db, addr + SECTOR_MAGIC_OFFSET, &sec_hdr.magic, sizeof(sec_hdr.magic));
+        FLASH_WRITE(db, addr + SECTOR_MAGIC_OFFSET, &sec_hdr.magic, sizeof(sec_hdr.magic), true);
     }
 
     return result;
@@ -271,13 +304,13 @@ static fdb_err_t write_tsl(fdb_tsdb_t db, fdb_blob_t blob, fdb_time_t time)
     idx.time = time;
     idx.log_addr = db->cur_sec.empty_data - FDB_WG_ALIGN(idx.log_len);
     /* write the status will by write granularity */
-    _FDB_WRITE_STATUS(db, idx_addr, idx.status_table, FDB_TSL_STATUS_NUM, FDB_TSL_PRE_WRITE);
+    _FDB_WRITE_STATUS(db, idx_addr, idx.status_table, FDB_TSL_STATUS_NUM, FDB_TSL_PRE_WRITE, false);
     /* write other index info */
-    FLASH_WRITE(db, idx_addr + LOG_IDX_TS_OFFSET, &idx.time,  sizeof(struct log_idx_data) - LOG_IDX_TS_OFFSET);
+    FLASH_WRITE(db, idx_addr + LOG_IDX_TS_OFFSET, &idx.time,  sizeof(struct log_idx_data) - LOG_IDX_TS_OFFSET, false);
     /* write blob data */
-    FLASH_WRITE(db, idx.log_addr, blob->buf, blob->size);
+    FLASH_WRITE(db, idx.log_addr, blob->buf, blob->size, false);
     /* write the status will by write granularity */
-    _FDB_WRITE_STATUS(db, idx_addr, idx.status_table, FDB_TSL_STATUS_NUM, FDB_TSL_WRITE);
+    _FDB_WRITE_STATUS(db, idx_addr, idx.status_table, FDB_TSL_STATUS_NUM, FDB_TSL_WRITE, true);
 
     return result;
 }
@@ -288,53 +321,56 @@ static fdb_err_t update_sec_status(fdb_tsdb_t db, tsdb_sec_info_t sector, fdb_bl
     uint8_t status[FDB_STORE_STATUS_TABLE_SIZE];
 
     if (sector->status == FDB_SECTOR_STORE_USING && sector->remain < LOG_IDX_DATA_SIZE + FDB_WG_ALIGN(blob->size)) {
-        if (db->rollover) {
-            uint8_t end_status[TSL_STATUS_TABLE_SIZE];
-            uint32_t end_index = sector->empty_idx - LOG_IDX_DATA_SIZE, new_sec_addr, cur_sec_addr = sector->addr;
-            /* save the end node index and timestamp */
-            if (sector->end_info_stat[0] == FDB_TSL_UNUSED) {
-                _FDB_WRITE_STATUS(db, cur_sec_addr + SECTOR_END0_STATUS_OFFSET, end_status, FDB_TSL_STATUS_NUM, FDB_TSL_PRE_WRITE);
-                FLASH_WRITE(db, cur_sec_addr + SECTOR_END0_TIME_OFFSET, (uint32_t * )&db->last_time, sizeof(fdb_time_t));
-                FLASH_WRITE(db, cur_sec_addr + SECTOR_END0_IDX_OFFSET, &end_index, sizeof(end_index));
-                _FDB_WRITE_STATUS(db, cur_sec_addr + SECTOR_END0_STATUS_OFFSET, end_status, FDB_TSL_STATUS_NUM, FDB_TSL_WRITE);
-            } else if (sector->end_info_stat[1] == FDB_TSL_UNUSED) {
-                _FDB_WRITE_STATUS(db, cur_sec_addr + SECTOR_END1_STATUS_OFFSET, end_status, FDB_TSL_STATUS_NUM, FDB_TSL_PRE_WRITE);
-                FLASH_WRITE(db, cur_sec_addr + SECTOR_END1_TIME_OFFSET, (uint32_t * )&db->last_time, sizeof(fdb_time_t));
-                FLASH_WRITE(db, cur_sec_addr + SECTOR_END1_IDX_OFFSET, &end_index, sizeof(end_index));
-                _FDB_WRITE_STATUS(db, cur_sec_addr + SECTOR_END1_STATUS_OFFSET, end_status, FDB_TSL_STATUS_NUM, FDB_TSL_WRITE);
-            }
-            /* change current sector to full */
-            _FDB_WRITE_STATUS(db, cur_sec_addr, status, FDB_SECTOR_STORE_STATUS_NUM, FDB_SECTOR_STORE_FULL);
-            /* calculate next sector address */
-            if (sector->addr + db_sec_size(db) < db_part_size(db)) {
-                new_sec_addr = sector->addr + db_sec_size(db);
-            } else {
-                new_sec_addr = 0;
-            }
-            read_sector_info(db, new_sec_addr, &db->cur_sec, false);
-            if (sector->status != FDB_SECTOR_STORE_EMPTY) {
-                /* calculate the oldest sector address */
-                if (new_sec_addr + db_sec_size(db) < db_part_size(db)) {
-                    db->oldest_addr = new_sec_addr + db_sec_size(db);
-                } else {
-                    db->oldest_addr = 0;
-                }
-                format_sector(db, new_sec_addr);
-                read_sector_info(db, new_sec_addr, &db->cur_sec, false);
-            }
+        uint8_t end_status[TSL_STATUS_TABLE_SIZE];
+        uint32_t end_index = sector->empty_idx - LOG_IDX_DATA_SIZE, new_sec_addr, cur_sec_addr = sector->addr;
+        /* save the end node index and timestamp */
+        if (sector->end_info_stat[0] == FDB_TSL_UNUSED) {
+            _FDB_WRITE_STATUS(db, cur_sec_addr + SECTOR_END0_STATUS_OFFSET, end_status, FDB_TSL_STATUS_NUM, FDB_TSL_PRE_WRITE, false);
+            FLASH_WRITE(db, cur_sec_addr + SECTOR_END0_TIME_OFFSET, (uint32_t * )&db->last_time, sizeof(fdb_time_t), false);
+            FLASH_WRITE(db, cur_sec_addr + SECTOR_END0_IDX_OFFSET, &end_index, sizeof(end_index), false);
+            _FDB_WRITE_STATUS(db, cur_sec_addr + SECTOR_END0_STATUS_OFFSET, end_status, FDB_TSL_STATUS_NUM, FDB_TSL_WRITE, true);
+        } else if (sector->end_info_stat[1] == FDB_TSL_UNUSED) {
+            _FDB_WRITE_STATUS(db, cur_sec_addr + SECTOR_END1_STATUS_OFFSET, end_status, FDB_TSL_STATUS_NUM, FDB_TSL_PRE_WRITE, false);
+            FLASH_WRITE(db, cur_sec_addr + SECTOR_END1_TIME_OFFSET, (uint32_t * )&db->last_time, sizeof(fdb_time_t), false);
+            FLASH_WRITE(db, cur_sec_addr + SECTOR_END1_IDX_OFFSET, &end_index, sizeof(end_index), false);
+            _FDB_WRITE_STATUS(db, cur_sec_addr + SECTOR_END1_STATUS_OFFSET, end_status, FDB_TSL_STATUS_NUM, FDB_TSL_WRITE, true);
+        }
+        /* change current sector to full */
+        _FDB_WRITE_STATUS(db, cur_sec_addr, status, FDB_SECTOR_STORE_STATUS_NUM, FDB_SECTOR_STORE_FULL, true);
+        sector->status = FDB_SECTOR_STORE_FULL;
+        /* calculate next sector address */
+        if (sector->addr + db_sec_size(db) < db_max_size(db)) {
+            new_sec_addr = sector->addr + db_sec_size(db);
+        }
+        else if (db->rollover) {
+            new_sec_addr = 0;
         } else {
             /* not rollover */
             return FDB_SAVED_FULL;
         }
+        read_sector_info(db, new_sec_addr, &db->cur_sec, false);
+        if (sector->status != FDB_SECTOR_STORE_EMPTY) {
+            /* calculate the oldest sector address */
+            if (new_sec_addr + db_sec_size(db) < db_max_size(db)) {
+                db->oldest_addr = new_sec_addr + db_sec_size(db);
+            } else {
+                db->oldest_addr = 0;
+            }
+            format_sector(db, new_sec_addr);
+            read_sector_info(db, new_sec_addr, &db->cur_sec, false);
+        }
+    } else if (sector->status == FDB_SECTOR_STORE_FULL) {
+        /* database full */
+        return FDB_SAVED_FULL;
     }
 
     if (sector->status == FDB_SECTOR_STORE_EMPTY) {
         /* change the sector to using */
         sector->status = FDB_SECTOR_STORE_USING;
         sector->start_time = cur_time;
-        _FDB_WRITE_STATUS(db, sector->addr, status, FDB_SECTOR_STORE_STATUS_NUM, FDB_SECTOR_STORE_USING);
+        _FDB_WRITE_STATUS(db, sector->addr, status, FDB_SECTOR_STORE_STATUS_NUM, FDB_SECTOR_STORE_USING, true);
         /* save the start timestamp */
-        FLASH_WRITE(db, sector->addr + SECTOR_START_TIME_OFFSET, (uint32_t *)&cur_time, sizeof(fdb_time_t));
+        FLASH_WRITE(db, sector->addr + SECTOR_START_TIME_OFFSET, (uint32_t *)&cur_time, sizeof(fdb_time_t), true);
     }
 
     return result;
@@ -347,6 +383,13 @@ static fdb_err_t tsl_append(fdb_tsdb_t db, fdb_blob_t blob)
 
     FDB_ASSERT(blob->size <= db->max_len);
 
+    /* check the current timestamp, MUST more than the last save timestamp */
+    if (cur_time < db->last_time) {
+        FDB_INFO("Warning: current timestamp (%" PRIdMAX ") is less than the last save timestamp (%" PRIdMAX "). This tsl will be dropped.\n",
+                (intmax_t )cur_time, (intmax_t )(db->last_time));
+        return FDB_WRITE_ERR;
+    }
+
     result = update_sec_status(db, &db->cur_sec, blob, cur_time);
     if (result != FDB_NO_ERR) {
         return result;
@@ -364,12 +407,7 @@ static fdb_err_t tsl_append(fdb_tsdb_t db, fdb_blob_t blob)
     db->cur_sec.empty_idx += LOG_IDX_DATA_SIZE;
     db->cur_sec.empty_data -= FDB_WG_ALIGN(blob->size);
     db->cur_sec.remain -= LOG_IDX_DATA_SIZE + FDB_WG_ALIGN(blob->size);
-
-    if (cur_time >= db->last_time) {
-        db->last_time = cur_time;
-    } else {
-        FDB_INFO("Warning: current timestamp (%" PRIdMAX ") is less than the last save timestamp (%" PRIdMAX ")\n", (intmax_t)cur_time, (intmax_t)(db->last_time));
-    }
+    db->last_time = cur_time;
 
     return result;
 }
@@ -420,8 +458,10 @@ void fdb_tsl_iter(fdb_tsdb_t db, fdb_tsl_cb cb, void *arg)
     }
 
     sec_addr = db->oldest_addr;
+    db_lock(db);
     /* search all sectors */
     do {
+        traversed_len += db_sec_size(db);
         if (read_sector_info(db, sec_addr, &sector, false) != FDB_NO_ERR) {
             continue;
         }
@@ -437,34 +477,131 @@ void fdb_tsl_iter(fdb_tsdb_t db, fdb_tsl_cb cb, void *arg)
                 read_tsl(db, &tsl);
                 /* iterator is interrupted when callback return true */
                 if (cb(&tsl, arg)) {
+                    db_unlock(db);
                     return;
                 }
             } while ((tsl.addr.index = get_next_tsl_addr(&sector, &tsl)) != FAILED_ADDR);
         }
-        traversed_len += db_sec_size(db);
     } while ((sec_addr = get_next_sector_addr(db, &sector, traversed_len)) != FAILED_ADDR);
+    db_unlock(db);
+}
+
+/**
+ * The TSDB iterator for each TSL.
+ *
+ * @param db database object
+ * @param cb callback
+ * @param arg callback argument
+ */
+void fdb_tsl_iter_reverse(fdb_tsdb_t db, fdb_tsl_cb cb, void *cb_arg)
+{
+    struct tsdb_sec_info sector;
+    uint32_t sec_addr, traversed_len = 0;
+    struct fdb_tsl tsl;
+
+    if (!db_init_ok(db)) {
+        FDB_INFO("Error: TSL (%s) isn't initialize OK.\n", db_name(db));
+    }
+
+    if (cb == NULL) {
+        return;
+    }
+
+    sec_addr = db->cur_sec.addr;
+    db_lock(db);
+    /* search all sectors */
+    do {
+        traversed_len += db_sec_size(db);
+        if (read_sector_info(db, sec_addr, &sector, false) != FDB_NO_ERR) {
+            continue;
+        }
+        /* sector has TSL */
+        if (sector.status == FDB_SECTOR_STORE_USING || sector.status == FDB_SECTOR_STORE_FULL) {
+            if (sector.status == FDB_SECTOR_STORE_USING) {
+                /* copy the current using sector status  */
+                sector = db->cur_sec;
+            }
+            tsl.addr.index = sector.end_idx;
+            /* search all TSL */
+            do {
+                read_tsl(db, &tsl);
+                /* iterator is interrupted when callback return true */
+                if (cb(&tsl, cb_arg)) {
+                    goto __exit;
+                }
+            } while ((tsl.addr.index = get_last_tsl_addr(&sector, &tsl)) != FAILED_ADDR);
+        } else if (sector.status == FDB_SECTOR_STORE_EMPTY || sector.status == FDB_SECTOR_STORE_UNUSED)
+            goto __exit;
+    } while ((sec_addr = get_last_sector_addr(db, &sector, traversed_len)) != FAILED_ADDR);
+
+__exit:
+    db_unlock(db);
+}
+
+/*
+ * Found the matched TSL address.
+ */
+static int search_start_tsl_addr(fdb_tsdb_t db, int start, int end, fdb_time_t from, fdb_time_t to)
+{
+    struct fdb_tsl tsl;
+    while (true) {
+        tsl.addr.index = start + FDB_ALIGN((end - start) / 2, LOG_IDX_DATA_SIZE);
+        read_tsl(db, &tsl);
+        if (tsl.time < from) {
+            start = tsl.addr.index + LOG_IDX_DATA_SIZE;
+        } else if (tsl.time > from) {
+            end = tsl.addr.index - LOG_IDX_DATA_SIZE;
+        } else {
+            return tsl.addr.index;
+        }
+
+        if (start > end) {
+            if (from > to) {
+                tsl.addr.index = start;
+                read_tsl(db, &tsl);
+                if (tsl.time > from) {
+                    start -= LOG_IDX_DATA_SIZE;
+                }
+            }
+            break;
+        }
+    }
+    return start;
 }
 
 /**
  * The TSDB iterator for each TSL by timestamp.
  *
  * @param db database object
- * @param from starting timestap
- * @param to ending timestap
+ * @param from starting timestamp. It will be a reverse iterator when ending timestamp less than starting timestamp
+ * @param to ending timestamp
  * @param cb callback
  * @param arg callback argument
  */
 void fdb_tsl_iter_by_time(fdb_tsdb_t db, fdb_time_t from, fdb_time_t to, fdb_tsl_cb cb, void *cb_arg)
 {
     struct tsdb_sec_info sector;
-    uint32_t sec_addr, oldest_addr = db->oldest_addr, traversed_len = 0;
+    uint32_t sec_addr, start_addr, traversed_len = 0;
     struct fdb_tsl tsl;
     bool found_start_tsl = false;
 
+    uint32_t (*get_sector_addr)(fdb_tsdb_t , tsdb_sec_info_t , uint32_t);
+    uint32_t (*get_tsl_addr)(tsdb_sec_info_t , fdb_tsl_t);
+
     if (!db_init_ok(db)) {
         FDB_INFO("Error: TSL (%s) isn't initialize OK.\n", db_name(db));
     }
 
+    if(from <= to) {
+        start_addr = db->oldest_addr;
+        get_sector_addr = get_next_sector_addr;
+        get_tsl_addr = get_next_tsl_addr;
+    } else {
+        start_addr = db->cur_sec.addr;
+        get_sector_addr = get_last_sector_addr;
+        get_tsl_addr = get_last_tsl_addr;
+    }
+
 //    FDB_INFO("from %s", ctime((const time_t * )&from));
 //    FDB_INFO("to %s", ctime((const time_t * )&to));
 
@@ -472,9 +609,11 @@ void fdb_tsl_iter_by_time(fdb_tsdb_t db, fdb_time_t from, fdb_time_t to, fdb_tsl
         return;
     }
 
-    sec_addr = oldest_addr;
+    sec_addr = start_addr;
+    db_lock(db);
     /* search all sectors */
     do {
+        traversed_len += db_sec_size(db);
         if (read_sector_info(db, sec_addr, &sector, false) != FDB_NO_ERR) {
             continue;
         }
@@ -484,40 +623,39 @@ void fdb_tsl_iter_by_time(fdb_tsdb_t db, fdb_time_t from, fdb_time_t to, fdb_tsl
                 /* copy the current using sector status  */
                 sector = db->cur_sec;
             }
-            if ((found_start_tsl) || (!found_start_tsl && ((from >= sector.start_time && from <= sector.end_time)
-                                       || (sec_addr == oldest_addr && from <= sector.start_time)))) {
+            if ((found_start_tsl)
+                    || (!found_start_tsl &&
+                            ((from <= to && ((sec_addr == start_addr && from <= sector.start_time) || from <= sector.end_time)) ||
+                             (from > to  && ((sec_addr == start_addr && from >= sector.end_time) || from >= sector.start_time)))
+                             )) {
                 uint32_t start = sector.addr + SECTOR_HDR_DATA_SIZE, end = sector.end_idx;
 
                 found_start_tsl = true;
-                /* search start TSL address, using binary search algorithm */
-                while (start <= end) {
-                    tsl.addr.index = start + ((end - start) / 2 + 1) / LOG_IDX_DATA_SIZE * LOG_IDX_DATA_SIZE;
-                    read_tsl(db, &tsl);
-                    if (tsl.time < from) {
-                        start = tsl.addr.index + LOG_IDX_DATA_SIZE;
-                    } else {
-                        end = tsl.addr.index - LOG_IDX_DATA_SIZE;
-                    }
-                }
-                tsl.addr.index = start;
+                /* search the first start TSL address */
+                tsl.addr.index = search_start_tsl_addr(db, start, end, from, to);
                 /* search all TSL */
                 do {
                     read_tsl(db, &tsl);
-                    if (tsl.time >= from && tsl.time <= to) {
-                        /* iterator is interrupted when callback return true */
-                        if (cb(&tsl, cb_arg)) {
-                            return;
+                    if (tsl.status != FDB_TSL_UNUSED) {
+                        if ((from <= to && tsl.time >= from && tsl.time <= to)
+                                || (from > to && tsl.time <= from && tsl.time >= to)) {
+                            /* iterator is interrupted when callback return true */
+                            if (cb(&tsl, cb_arg)) {
+                                goto __exit;
+                            }
+                        } else {
+                            goto __exit;
                         }
-                    } else {
-                        return;
                     }
-                } while ((tsl.addr.index = get_next_tsl_addr(&sector, &tsl)) != FAILED_ADDR);
+                } while ((tsl.addr.index = get_tsl_addr(&sector, &tsl)) != FAILED_ADDR);
             }
         } else if (sector.status == FDB_SECTOR_STORE_EMPTY) {
-            return;
+            goto __exit;
         }
-        traversed_len += db_sec_size(db);
-    } while ((sec_addr = get_next_sector_addr(db, &sector, traversed_len)) != FAILED_ADDR);
+    } while ((sec_addr = get_sector_addr(db, &sector, traversed_len)) != FAILED_ADDR);
+
+__exit:
+    db_unlock(db);
 }
 
 static bool query_count_cb(fdb_tsl_t tsl, void *arg)
@@ -535,8 +673,8 @@ static bool query_count_cb(fdb_tsl_t tsl, void *arg)
  * Query some TSL's count by timestamp and status.
  *
  * @param db database object
- * @param from starting timestap
- * @param to ending timestap
+ * @param from starting timestamp
+ * @param to ending timestamp
  * @param status status
  */
 size_t fdb_tsl_query_count(fdb_tsdb_t db, fdb_time_t from, fdb_time_t to, fdb_tsl_status_t status)
@@ -571,7 +709,7 @@ fdb_err_t fdb_tsl_set_status(fdb_tsdb_t db, fdb_tsl_t tsl, fdb_tsl_status_t stat
     uint8_t status_table[TSL_STATUS_TABLE_SIZE];
 
     /* write the status will by write granularity */
-    _FDB_WRITE_STATUS(db, tsl->addr.index, status_table, FDB_TSL_STATUS_NUM, status);
+    _FDB_WRITE_STATUS(db, tsl->addr.index, status_table, FDB_TSL_STATUS_NUM, status, true);
 
     return result;
 }
@@ -599,7 +737,7 @@ static bool check_sec_hdr_cb(tsdb_sec_info_t sector, void *arg1, void *arg2)
     fdb_tsdb_t db = arg->db;
 
     if (!sector->check_ok) {
-        FDB_INFO("Sector (0x%08" PRIu32 ") header info is incorrect.\n", sector->addr);
+        FDB_INFO("Sector (0x%08" PRIX32 ") header info is incorrect.\n", sector->addr);
         (arg->check_failed) = true;
         return true;
     } else if (sector->status == FDB_SECTOR_STORE_USING) {
@@ -671,7 +809,7 @@ void fdb_tsdb_control(fdb_tsdb_t db, int cmd, void *arg)
 
     switch (cmd) {
     case FDB_TSDB_CTRL_SET_SEC_SIZE:
-        /* the sector size change MUST before database initialization */
+        /* this change MUST before database initialization */
         FDB_ASSERT(db->parent.init_ok == false);
         db->parent.sec_size = *(uint32_t *)arg;
         break;
@@ -679,12 +817,28 @@ void fdb_tsdb_control(fdb_tsdb_t db, int cmd, void *arg)
         *(uint32_t *)arg = db->parent.sec_size;
         break;
     case FDB_TSDB_CTRL_SET_LOCK:
+#if !defined(__ARMCC_VERSION) && defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+#endif
         db->parent.lock = (void (*)(fdb_db_t db))arg;
+#if !defined(__ARMCC_VERSION) && defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
         break;
     case FDB_TSDB_CTRL_SET_UNLOCK:
+#if !defined(__ARMCC_VERSION) && defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+#endif
         db->parent.unlock = (void (*)(fdb_db_t db))arg;
+#if !defined(__ARMCC_VERSION) && defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
         break;
     case FDB_TSDB_CTRL_SET_ROLLOVER:
+        /* this change MUST after database initialized */
+        FDB_ASSERT(db->parent.init_ok == true);
         db->rollover = *(bool *)arg;
         break;
     case FDB_TSDB_CTRL_GET_ROLLOVER:
@@ -693,6 +847,27 @@ void fdb_tsdb_control(fdb_tsdb_t db, int cmd, void *arg)
     case FDB_TSDB_CTRL_GET_LAST_TIME:
         *(fdb_time_t *)arg = db->last_time;
         break;
+    case FDB_TSDB_CTRL_SET_FILE_MODE:
+#ifdef FDB_USING_FILE_MODE
+        /* this change MUST before database initialization */
+        FDB_ASSERT(db->parent.init_ok == false);
+        db->parent.file_mode = *(bool *)arg;
+#else
+        FDB_INFO("Error: set file mode Failed. Please defined the FDB_USING_FILE_MODE macro.");
+#endif
+        break;
+    case FDB_TSDB_CTRL_SET_MAX_SIZE:
+#ifdef FDB_USING_FILE_MODE
+        /* this change MUST before database initialization */
+        FDB_ASSERT(db->parent.init_ok == false);
+        db->parent.max_size = *(uint32_t *)arg;
+#endif
+        break;
+    case FDB_TSDB_CTRL_SET_NOT_FORMAT:
+        /* this change MUST before database initialization */
+        FDB_ASSERT(db->parent.init_ok == false);
+        db->parent.not_formatable = *(bool *)arg;
+        break;
     }
 }
 
@@ -701,14 +876,14 @@ void fdb_tsdb_control(fdb_tsdb_t db, int cmd, void *arg)
  *
  * @param db database object
  * @param name database name
- * @param part_name partition name
+ * @param path FAL mode: partition name, file mode: database saved directory path
  * @param get_time get current time function
  * @param max_len maximum length of each log
  * @param user_data user data
  *
  * @return result
  */
-fdb_err_t fdb_tsdb_init(fdb_tsdb_t db, const char *name, const char *part_name, fdb_get_time get_time, size_t max_len, void *user_data)
+fdb_err_t fdb_tsdb_init(fdb_tsdb_t db, const char *name, const char *path, fdb_get_time get_time, size_t max_len, void *user_data)
 {
     fdb_err_t result = FDB_NO_ERR;
     struct tsdb_sec_info sector;
@@ -716,7 +891,7 @@ fdb_err_t fdb_tsdb_init(fdb_tsdb_t db, const char *name, const char *part_name,
 
     FDB_ASSERT(get_time);
 
-    result = _fdb_init_ex((fdb_db_t)db, name, part_name, FDB_DB_TYPE_TS, user_data);
+    result = _fdb_init_ex((fdb_db_t)db, name, path, FDB_DB_TYPE_TS, user_data);
     if (result != FDB_NO_ERR) {
         goto __exit;
     }
@@ -727,10 +902,6 @@ fdb_err_t fdb_tsdb_init(fdb_tsdb_t db, const char *name, const char *part_name,
     db->rollover = true;
     db->oldest_addr = FDB_DATA_UNUSED;
     db->cur_sec.addr = FDB_DATA_UNUSED;
-    /* must align with sector size */
-    FDB_ASSERT(db_part_size(db) % db_sec_size(db) == 0);
-    /* must have more than or equal 2 sector */
-    FDB_ASSERT(db_part_size(db) / db_sec_size(db) >= 2);
     /* must less than sector size */
     FDB_ASSERT(max_len < db_sec_size(db));
 
@@ -739,17 +910,27 @@ fdb_err_t fdb_tsdb_init(fdb_tsdb_t db, const char *name, const char *part_name,
     sector_iterator(db, &sector, FDB_SECTOR_STORE_UNUSED, &check_sec_arg, NULL, check_sec_hdr_cb, true);
     /* format all sector when check failed */
     if (check_sec_arg.check_failed) {
-        tsl_format_all(db);
+        if (db->parent.not_formatable) {
+            result = FDB_READ_ERR;
+            goto __exit;
+        } else {
+            tsl_format_all(db);
+        }
     } else {
         uint32_t latest_addr;
         if (check_sec_arg.empty_num > 0) {
             latest_addr = check_sec_arg.empty_addr;
         } else {
-            latest_addr = db->cur_sec.addr;
+            if (db->rollover) {
+                latest_addr = db->cur_sec.addr;
+            } else {
+                /* There is no empty sector. */
+                latest_addr = db->cur_sec.addr = db_max_size(db) - db_sec_size(db);
+            }
         }
         /* db->cur_sec is the latest sector, and the next is the oldest sector */
-        if (latest_addr + db_sec_size(db) >= db_part_size(db)) {
-            /* db->cur_sec is the the bottom of the partition */
+        if (latest_addr + db_sec_size(db) >= db_max_size(db)) {
+            /* db->cur_sec is the the bottom of the database */
             db->oldest_addr = 0;
         } else {
             db->oldest_addr = latest_addr + db_sec_size(db);
@@ -767,7 +948,7 @@ fdb_err_t fdb_tsdb_init(fdb_tsdb_t db, const char *name, const char *part_name,
         uint32_t addr = db->cur_sec.addr;
 
         if (addr == 0) {
-            addr = db_part_size(db) - db_sec_size(db);
+            addr = db_max_size(db) - db_sec_size(db);
         } else {
             addr -= db_sec_size(db);
         }
@@ -782,4 +963,18 @@ __exit:
     return result;
 }
 
+/**
+ * The time series database deinitialization.
+ *
+ * @param db database object
+ *
+ * @return result
+ */
+fdb_err_t fdb_tsdb_deinit(fdb_tsdb_t db)
+{
+    _fdb_deinit((fdb_db_t) db);
+
+    return FDB_NO_ERR;
+}
+
 #endif /* defined(FDB_USING_TSDB) */

+ 52 - 14
flashdb/src/fdb_utils.c

@@ -102,7 +102,7 @@ size_t _fdb_set_status(uint8_t status_table[], size_t status_num, size_t status_
     if (status_index > 0) {
 #if (FDB_WRITE_GRAN == 1)
         byte_index = (status_index - 1) / 8;
-        status_table[byte_index] &= ~(0x80 >> ((status_index - 1) % 8));
+        status_table[byte_index] &= (0x00ff >> (status_index % 8));
 #else
         byte_index = (status_index - 1) * (FDB_WRITE_GRAN / 8);
         status_table[byte_index] = 0x00;
@@ -133,7 +133,7 @@ size_t _fdb_get_status(uint8_t status_table[], size_t status_num)
     return status_num_bak - i;
 }
 
-fdb_err_t _fdb_write_status(fdb_db_t db, uint32_t addr, uint8_t status_table[], size_t status_num, size_t status_index)
+fdb_err_t _fdb_write_status(fdb_db_t db, uint32_t addr, uint8_t status_table[], size_t status_num, size_t status_index, bool sync)
 {
     fdb_err_t result = FDB_NO_ERR;
     size_t byte_index;
@@ -149,11 +149,11 @@ fdb_err_t _fdb_write_status(fdb_db_t db, uint32_t addr, uint8_t status_table[],
         return FDB_NO_ERR;
     }
 #if (FDB_WRITE_GRAN == 1)
-    result = _fdb_flash_write(db, addr + byte_index, (uint32_t *)&status_table[byte_index], 1);
+    result = _fdb_flash_write(db, addr + byte_index, (uint32_t *)&status_table[byte_index], 1, sync);
 #else /*  (FDB_WRITE_GRAN == 8) ||  (FDB_WRITE_GRAN == 32) ||  (FDB_WRITE_GRAN == 64) */
     /* write the status by write granularity
      * some flash (like stm32 onchip) NOT supported repeated write before erase */
-    result = _fdb_flash_write(db, addr + byte_index, (uint32_t *) &status_table[byte_index], FDB_WRITE_GRAN / 8);
+    result = _fdb_flash_write(db, addr + byte_index, (uint32_t *) &status_table[byte_index], FDB_WRITE_GRAN / 8, sync);
 #endif /* FDB_WRITE_GRAN == 1 */
 
     return result;
@@ -207,7 +207,6 @@ uint32_t _fdb_continue_ff_addr(fdb_db_t db, uint32_t start, uint32_t end)
  *
  * @return new blob object
  */
-
 fdb_blob_t fdb_blob_make(fdb_blob_t blob, const void *value_buf, size_t buf_len)
 {
     blob->buf = (void *)value_buf;
@@ -231,16 +230,36 @@ size_t fdb_blob_read(fdb_db_t db, fdb_blob_t blob)
     if (read_len > blob->saved.len) {
         read_len = blob->saved.len;
     }
-    _fdb_flash_read(db, blob->saved.addr, blob->buf, read_len);
+    if (_fdb_flash_read(db, blob->saved.addr, blob->buf, read_len) != FDB_NO_ERR) {
+        read_len = 0;
+    }
 
     return read_len;
 }
 
+#ifdef FDB_USING_FILE_MODE
+extern fdb_err_t _fdb_file_read(fdb_db_t db, uint32_t addr, void *buf, size_t size);
+extern fdb_err_t _fdb_file_write(fdb_db_t db, uint32_t addr, const void *buf, size_t size, bool sync);
+extern fdb_err_t _fdb_file_erase(fdb_db_t db, uint32_t addr, size_t size);
+#endif /* FDB_USING_FILE_LIBC */
+
 fdb_err_t _fdb_flash_read(fdb_db_t db, uint32_t addr, void *buf, size_t size)
 {
     fdb_err_t result = FDB_NO_ERR;
 
-    fal_partition_read(db->part, addr, (uint8_t *)buf, size);
+    if (db->file_mode) {
+#ifdef FDB_USING_FILE_MODE
+        return _fdb_file_read(db, addr, buf, size);
+#else
+        return FDB_READ_ERR;
+#endif
+    } else {
+#ifdef FDB_USING_FAL_MODE
+        if (fal_partition_read(db->storage.part, addr, (uint8_t *) buf, size) < 0) {
+            result = FDB_READ_ERR;
+        }
+#endif
+    }
 
     return result;
 }
@@ -249,21 +268,40 @@ fdb_err_t _fdb_flash_erase(fdb_db_t db, uint32_t addr, size_t size)
 {
     fdb_err_t result = FDB_NO_ERR;
 
-    if (fal_partition_erase(db->part, addr, size) < 0)
-    {
-        result = FDB_ERASE_ERR;
+    if (db->file_mode) {
+#ifdef FDB_USING_FILE_MODE
+        return _fdb_file_erase(db, addr, size);
+#else
+        return FDB_ERASE_ERR;
+#endif /* FDB_USING_FILE_MODE */
+    } else {
+#ifdef FDB_USING_FAL_MODE
+        if (fal_partition_erase(db->storage.part, addr, size) < 0) {
+            result = FDB_ERASE_ERR;
+        }
+#endif
     }
 
     return result;
 }
 
-fdb_err_t _fdb_flash_write(fdb_db_t db, uint32_t addr, const void *buf, size_t size)
+fdb_err_t _fdb_flash_write(fdb_db_t db, uint32_t addr, const void *buf, size_t size, bool sync)
 {
     fdb_err_t result = FDB_NO_ERR;
 
-    if (fal_partition_write(db->part, addr, (uint8_t *)buf, size) < 0)
-    {
-        result = FDB_WRITE_ERR;
+    if (db->file_mode) {
+#ifdef FDB_USING_FILE_MODE
+        return _fdb_file_write(db, addr, buf, size, sync);
+#else
+        return FDB_READ_ERR;
+#endif /* FDB_USING_FILE_MODE */
+    } else {
+#ifdef FDB_USING_FAL_MODE
+        if (fal_partition_write(db->storage.part, addr, (uint8_t *)buf, size) < 0)
+        {
+            result = FDB_WRITE_ERR;
+        }
+#endif
     }
 
     return result;