800*320工程文件+初始demo提交
This commit is contained in:
50
SW/components/modules/FlashDB/flashdb/inc/fdb_cfg.h
Normal file
50
SW/components/modules/FlashDB/flashdb/inc/fdb_cfg.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief configuration file
|
||||
*/
|
||||
|
||||
#ifndef _FDB_CFG_H_
|
||||
#define _FDB_CFG_H_
|
||||
|
||||
/* using KVDB feature */
|
||||
#define FDB_USING_KVDB
|
||||
|
||||
#ifdef FDB_USING_KVDB
|
||||
/* Auto update KV to latest default when current KVDB version number is changed. @see fdb_kvdb.ver_num */
|
||||
/* #define FDB_KV_AUTO_UPDATE */
|
||||
#endif
|
||||
|
||||
/* 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 8/* @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
|
||||
|
||||
/* log print macro. default EF_PRINT macro is printf() */
|
||||
#define FDB_PRINT(...) //printf(__VA_ARGS__)
|
||||
|
||||
/* print debug information */
|
||||
#define FDB_DEBUG_ENABLE 0
|
||||
|
||||
#endif /* _FDB_CFG_H_ */
|
342
SW/components/modules/FlashDB/flashdb/inc/fdb_def.h
Normal file
342
SW/components/modules/FlashDB/flashdb/inc/fdb_def.h
Normal file
@ -0,0 +1,342 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief Public definition.
|
||||
*/
|
||||
|
||||
#ifndef _FDB_DEF_H_
|
||||
#define _FDB_DEF_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* software version number */
|
||||
#define FDB_SW_VERSION "1.1.2"
|
||||
#define FDB_SW_VERSION_NUM 0x10102
|
||||
|
||||
/* the KV max name length must less then it */
|
||||
#ifndef FDB_KV_NAME_MAX
|
||||
#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 64
|
||||
#endif
|
||||
|
||||
/* the sector cache table size, it will improve KV save speed when using cache */
|
||||
#ifndef FDB_SECTOR_CACHE_TABLE_SIZE
|
||||
#define FDB_SECTOR_CACHE_TABLE_SIZE 4
|
||||
#endif
|
||||
|
||||
#if (FDB_KV_CACHE_TABLE_SIZE > 0) && (FDB_SECTOR_CACHE_TABLE_SIZE > 0)
|
||||
#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_PREFIX2() FDB_PRINT(" ")
|
||||
#define FDB_LOG_PREFIX() FDB_LOG_PREFIX1();FDB_LOG_PREFIX2()
|
||||
#ifdef FDB_DEBUG_ENABLE
|
||||
#define FDB_DEBUG(...) FDB_LOG_PREFIX();FDB_PRINT("(%s:%d) ", __FILE__, __LINE__);FDB_PRINT(__VA_ARGS__)
|
||||
#else
|
||||
#define FDB_DEBUG(...)
|
||||
#endif
|
||||
/* routine print function. Must be implement by user. */
|
||||
#define FDB_INFO(...) FDB_LOG_PREFIX();FDB_PRINT(__VA_ARGS__)
|
||||
/* assert for developer. */
|
||||
#define FDB_ASSERT(EXPR) \
|
||||
if (!(EXPR)) \
|
||||
{ \
|
||||
FDB_DEBUG("(%s) has assert failed at %s.\n", #EXPR, __FUNCTION__); \
|
||||
while (1); \
|
||||
}
|
||||
|
||||
#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 */
|
||||
|
||||
#ifdef FDB_USING_TIMESTAMP_64BIT
|
||||
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 {
|
||||
uint32_t key;
|
||||
void *value;
|
||||
size_t value_len;
|
||||
};
|
||||
|
||||
struct fdb_default_kv {
|
||||
struct fdb_default_kv_node *kvs;
|
||||
size_t num;
|
||||
};
|
||||
|
||||
/* error code */
|
||||
typedef enum {
|
||||
FDB_NO_ERR,
|
||||
FDB_ERASE_ERR,
|
||||
FDB_READ_ERR,
|
||||
FDB_WRITE_ERR,
|
||||
FDB_PART_NOT_FOUND,
|
||||
FDB_KV_NAME_ERR,
|
||||
FDB_KV_NAME_EXIST,
|
||||
FDB_SAVED_FULL,
|
||||
FDB_INIT_FAILED,
|
||||
} fdb_err_t;
|
||||
|
||||
enum fdb_kv_status {
|
||||
FDB_KV_UNUSED,
|
||||
FDB_KV_PRE_WRITE,
|
||||
FDB_KV_WRITE,
|
||||
FDB_KV_PRE_DELETE,
|
||||
FDB_KV_DELETED,
|
||||
FDB_KV_ERR_HDR,
|
||||
#define FDB_KV_STATUS_NUM 6
|
||||
};
|
||||
typedef enum fdb_kv_status fdb_kv_status_t;
|
||||
|
||||
enum fdb_tsl_status {
|
||||
FDB_TSL_UNUSED,
|
||||
FDB_TSL_PRE_WRITE,
|
||||
FDB_TSL_WRITE,
|
||||
FDB_TSL_USER_STATUS1,
|
||||
FDB_TSL_DELETED,
|
||||
FDB_TSL_USER_STATUS2,
|
||||
#define FDB_TSL_STATUS_NUM 6
|
||||
};
|
||||
typedef enum fdb_tsl_status fdb_tsl_status_t;
|
||||
|
||||
/* key-value node object */
|
||||
struct fdb_kv {
|
||||
fdb_kv_status_t status; /**< node status, @see fdb_kv_status_t */
|
||||
bool crc_is_ok; /**< node CRC32 check is OK */
|
||||
uint32_t key; /**< name length */
|
||||
uint32_t magic; /**< magic word(`K`, `V`, `4`, `0`) */
|
||||
uint32_t len; /**< node total length (header + name + value), must align by FDB_WRITE_GRAN */
|
||||
uint32_t value_len; /**< value length */
|
||||
struct {
|
||||
uint32_t start; /**< node start address */
|
||||
uint32_t value; /**< value start address */
|
||||
} addr;
|
||||
};
|
||||
typedef struct fdb_kv *fdb_kv_t;
|
||||
|
||||
struct fdb_kv_iterator {
|
||||
struct fdb_kv curr_kv; /**< Current KV we get from the iterator */
|
||||
uint32_t iterated_cnt; /**< How many KVs have we iterated already */
|
||||
size_t iterated_obj_bytes; /**< Total storage size of KVs we have iterated. */
|
||||
size_t iterated_value_bytes; /**< Total value size of KVs we have iterated. */
|
||||
uint32_t sector_addr; /**< Current sector address we're iterating. DO NOT touch it. */
|
||||
};
|
||||
typedef struct fdb_kv_iterator *fdb_kv_iterator_t;
|
||||
|
||||
/* time series log node object */
|
||||
struct fdb_tsl {
|
||||
fdb_tsl_status_t status; /**< node status, @see fdb_log_status_t */
|
||||
fdb_time_t time; /**< node timestamp */
|
||||
uint32_t log_len; /**< log length, must align by FDB_WRITE_GRAN */
|
||||
struct {
|
||||
uint32_t index; /**< node index address */
|
||||
uint32_t log; /**< log data address */
|
||||
} addr;
|
||||
};
|
||||
typedef struct fdb_tsl *fdb_tsl_t;
|
||||
typedef bool (*fdb_tsl_cb)(fdb_tsl_t tsl, void *arg);
|
||||
|
||||
typedef enum {
|
||||
FDB_DB_TYPE_KV,
|
||||
FDB_DB_TYPE_TS,
|
||||
} fdb_db_type;
|
||||
|
||||
/* the flash sector store status */
|
||||
enum fdb_sector_store_status {
|
||||
FDB_SECTOR_STORE_UNUSED,
|
||||
FDB_SECTOR_STORE_EMPTY,
|
||||
FDB_SECTOR_STORE_USING,
|
||||
FDB_SECTOR_STORE_FULL,
|
||||
#define FDB_SECTOR_STORE_STATUS_NUM 4
|
||||
};
|
||||
typedef enum fdb_sector_store_status fdb_sector_store_status_t;
|
||||
|
||||
/* the flash sector dirty status */
|
||||
enum fdb_sector_dirty_status {
|
||||
FDB_SECTOR_DIRTY_UNUSED,
|
||||
FDB_SECTOR_DIRTY_FALSE,
|
||||
FDB_SECTOR_DIRTY_TRUE,
|
||||
FDB_SECTOR_DIRTY_GC,
|
||||
#define FDB_SECTOR_DIRTY_STATUS_NUM 4
|
||||
};
|
||||
typedef enum fdb_sector_dirty_status fdb_sector_dirty_status_t;
|
||||
|
||||
/* KVDB section information */
|
||||
struct kvdb_sec_info {
|
||||
bool check_ok; /**< sector header check is OK */
|
||||
struct {
|
||||
fdb_sector_store_status_t store; /**< sector store status @see fdb_sector_store_status_t */
|
||||
fdb_sector_dirty_status_t dirty; /**< sector dirty status @see sector_dirty_status_t */
|
||||
} status;
|
||||
uint32_t addr; /**< sector start address */
|
||||
uint32_t magic; /**< magic word(`E`, `F`, `4`, `0`) */
|
||||
uint32_t combined; /**< the combined next sector number, 0xFFFFFFFF: not combined */
|
||||
size_t remain; /**< remain size */
|
||||
uint32_t empty_kv; /**< the next empty KV node start address */
|
||||
};
|
||||
typedef struct kvdb_sec_info *kv_sec_info_t;
|
||||
|
||||
/* TSDB section information */
|
||||
struct tsdb_sec_info {
|
||||
bool check_ok; /**< sector header check is OK */
|
||||
fdb_sector_store_status_t status; /**< sector store status @see fdb_sector_store_status_t */
|
||||
uint32_t addr; /**< sector start address */
|
||||
uint32_t magic; /**< magic word(`T`, `S`, `L`, `0`) */
|
||||
fdb_time_t start_time; /**< the first start node's timestamp, 0xFFFFFFFF: unused */
|
||||
fdb_time_t end_time; /**< the last end node's timestamp, 0xFFFFFFFF: unused */
|
||||
uint32_t end_idx; /**< the last end node's index, 0xFFFFFFFF: unused */
|
||||
fdb_tsl_status_t end_info_stat[2]; /**< the last end node's info status */
|
||||
size_t remain; /**< remain size */
|
||||
uint32_t empty_idx; /**< the next empty node index address */
|
||||
uint32_t empty_data; /**< the next empty node's data end address */
|
||||
};
|
||||
typedef struct tsdb_sec_info *tsdb_sec_info_t;
|
||||
|
||||
struct kv_cache_node {
|
||||
uint16_t name_crc; /**< KV name's CRC32 low 16bit value */
|
||||
uint16_t active; /**< KV node access active degree */
|
||||
uint32_t addr; /**< KV node address */
|
||||
};
|
||||
typedef struct kv_cache_node *kv_cache_node_t;
|
||||
|
||||
struct sector_cache_node {
|
||||
uint32_t addr; /**< sector start address */
|
||||
uint32_t empty_addr; /**< sector empty address */
|
||||
};
|
||||
typedef struct sector_cache_node *sector_cache_node_t;
|
||||
|
||||
/* database structure */
|
||||
typedef struct fdb_db *fdb_db_t;
|
||||
struct fdb_db {
|
||||
const char *name; /**< database name */
|
||||
fdb_db_type type; /**< database type */
|
||||
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
|
||||
#if defined(__ICCARM__)
|
||||
uint32_t dummy;
|
||||
#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 */
|
||||
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
/* KVDB structure */
|
||||
struct fdb_kvdb {
|
||||
struct fdb_db parent; /**< inherit from fdb_db */
|
||||
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 */
|
||||
struct kv_cache_node kv_cache_table[FDB_KV_CACHE_TABLE_SIZE];
|
||||
/* sector cache table, it caching the sector info which status is current using */
|
||||
struct sector_cache_node sector_cache_table[FDB_SECTOR_CACHE_TABLE_SIZE];
|
||||
#endif /* FDB_KV_USING_CACHE */
|
||||
|
||||
#ifdef FDB_KV_AUTO_UPDATE
|
||||
uint32_t ver_num; /**< setting version number for update */
|
||||
#endif
|
||||
|
||||
void *user_data;
|
||||
};
|
||||
typedef struct fdb_kvdb *fdb_kvdb_t;
|
||||
|
||||
/* TSDB structure */
|
||||
struct fdb_tsdb {
|
||||
struct fdb_db parent; /**< inherit from fdb_db */
|
||||
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 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 */
|
||||
|
||||
void *user_data;
|
||||
};
|
||||
typedef struct fdb_tsdb *fdb_tsdb_t;
|
||||
|
||||
/* blob structure */
|
||||
struct fdb_blob {
|
||||
void *buf; /**< blob data buffer */
|
||||
size_t size; /**< blob data buffer size */
|
||||
struct {
|
||||
uint32_t meta_addr; /**< saved KV or TSL index address */
|
||||
uint32_t addr; /**< blob data saved address */
|
||||
size_t len; /**< blob data saved length */
|
||||
} saved;
|
||||
};
|
||||
typedef struct fdb_blob *fdb_blob_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _FDB_DEF_H_ */
|
||||
|
57
SW/components/modules/FlashDB/flashdb/inc/fdb_low_lvl.h
Normal file
57
SW/components/modules/FlashDB/flashdb/inc/fdb_low_lvl.h
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief low level API and definition
|
||||
*/
|
||||
|
||||
#ifndef _FDB_LOW_LVL_H_
|
||||
#define _FDB_LOW_LVL_H_
|
||||
|
||||
#include <fdb_cfg.h>
|
||||
#include <fdb_def.h>
|
||||
|
||||
#if (FDB_WRITE_GRAN == 1)
|
||||
#define FDB_STATUS_TABLE_SIZE(status_number) ((status_number * FDB_WRITE_GRAN + 7)/8)
|
||||
#else
|
||||
#define FDB_STATUS_TABLE_SIZE(status_number) (((status_number - 1) * FDB_WRITE_GRAN + 7)/8)
|
||||
#endif
|
||||
|
||||
/* Return the most contiguous size aligned at specified width. RT_ALIGN(13, 4)
|
||||
* would return 16.
|
||||
*/
|
||||
#define FDB_ALIGN(size, align) (((size) + (align) - 1) & ~((align) - 1))
|
||||
/* align by write granularity */
|
||||
#define FDB_WG_ALIGN(size) (FDB_ALIGN(size, (FDB_WRITE_GRAN + 7)/8))
|
||||
/**
|
||||
* Return the down number of aligned at specified width. RT_ALIGN_DOWN(13, 4)
|
||||
* would return 12.
|
||||
*/
|
||||
#define FDB_ALIGN_DOWN(size, align) ((size) & ~((align) - 1))
|
||||
/* align down by write granularity */
|
||||
#define FDB_WG_ALIGN_DOWN(size) (FDB_ALIGN_DOWN(size, (FDB_WRITE_GRAN + 7)/8))
|
||||
|
||||
#define FDB_STORE_STATUS_TABLE_SIZE FDB_STATUS_TABLE_SIZE(FDB_SECTOR_STORE_STATUS_NUM)
|
||||
#define FDB_DIRTY_STATUS_TABLE_SIZE FDB_STATUS_TABLE_SIZE(FDB_SECTOR_DIRTY_STATUS_NUM)
|
||||
|
||||
/* the data is unused */
|
||||
#define FDB_DATA_UNUSED 0xFFFFFFFF
|
||||
|
||||
fdb_err_t _fdb_kv_load(fdb_kvdb_t db);
|
||||
size_t _fdb_set_status(uint8_t status_table[], size_t status_num, size_t status_index);
|
||||
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);
|
||||
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, bool sync);
|
||||
|
||||
#endif /* _FDB_LOW_LVL_H_ */
|
76
SW/components/modules/FlashDB/flashdb/inc/flashdb.h
Normal file
76
SW/components/modules/FlashDB/flashdb/inc/flashdb.h
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief Public APIs.
|
||||
*/
|
||||
|
||||
#ifndef _FLASHDB_H_
|
||||
#define _FLASHDB_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <time.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 *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_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);
|
||||
size_t fdb_blob_read (fdb_db_t db, fdb_blob_t blob);
|
||||
|
||||
/* Key-Value API like a KV DB */
|
||||
fdb_err_t fdb_kv_set (fdb_kvdb_t db, uint32_t key, const char *value);
|
||||
char *fdb_kv_get (fdb_kvdb_t db, uint32_t key);
|
||||
fdb_err_t fdb_kv_set_blob (fdb_kvdb_t db, uint32_t key, fdb_blob_t blob);
|
||||
size_t fdb_kv_get_blob (fdb_kvdb_t db, uint32_t key, fdb_blob_t blob);
|
||||
fdb_err_t fdb_kv_del (fdb_kvdb_t db, uint32_t key);
|
||||
fdb_kv_t fdb_kv_get_obj (fdb_kvdb_t db, uint32_t key, fdb_kv_t kv);
|
||||
fdb_blob_t fdb_kv_to_blob (fdb_kv_t kv, fdb_blob_t blob);
|
||||
fdb_err_t fdb_kv_set_default (fdb_kvdb_t db);
|
||||
void fdb_kv_print (fdb_kvdb_t db);
|
||||
fdb_kv_iterator_t fdb_kv_iterator_init(fdb_kv_iterator_t itr);
|
||||
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_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);
|
||||
void fdb_tsl_clean (fdb_tsdb_t db);
|
||||
fdb_blob_t fdb_tsl_to_blob (fdb_tsl_t tsl, fdb_blob_t blob);
|
||||
|
||||
/* fdb_utils.c */
|
||||
uint32_t fdb_calc_crc32(uint32_t crc, const void *buf, size_t size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _FLASHDB_H_ */
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief basic KV samples.
|
||||
*
|
||||
* basic Key-Value Database KV feature samples
|
||||
* get and show currnet boot counts
|
||||
*/
|
||||
|
||||
#include <flashdb.h>
|
||||
|
||||
#ifdef FDB_USING_KVDB
|
||||
|
||||
#define FDB_LOG_TAG "[sample][kvdb][basic]"
|
||||
|
||||
void kvdb_basic_sample(fdb_kvdb_t kvdb)
|
||||
{
|
||||
struct fdb_blob blob;
|
||||
int boot_count = 0;
|
||||
FDB_INFO("==================== kvdb_basic_sample ====================\n");
|
||||
{ /* GET the KV value */
|
||||
/* get the "boot_count" KV value */
|
||||
fdb_kv_get_blob(kvdb, "boot_count", fdb_blob_make(&blob, &boot_count, sizeof(boot_count)));
|
||||
/* the blob.saved.len is more than 0 when get the value successful */
|
||||
if (blob.saved.len > 0) {
|
||||
FDB_INFO("get the 'boot_count' value is %d\n", boot_count);
|
||||
} else {
|
||||
FDB_INFO("get the 'boot_count' failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
{ /* CHANGE the KV value */
|
||||
/* increase the boot count */
|
||||
boot_count ++;
|
||||
/* change the "boot_count" KV's value */
|
||||
fdb_kv_set_blob(kvdb, "boot_count", fdb_blob_make(&blob, &boot_count, sizeof(boot_count)));
|
||||
FDB_INFO("set the 'boot_count' value to %d\n", boot_count);
|
||||
}
|
||||
|
||||
FDB_INFO("===========================================================\n");
|
||||
}
|
||||
|
||||
#endif /* FDB_USING_KVDB */
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief blob KV samples.
|
||||
*
|
||||
* Key-Value Database blob type KV feature samples
|
||||
*/
|
||||
|
||||
#include <flashdb.h>
|
||||
|
||||
#ifdef FDB_USING_KVDB
|
||||
|
||||
#define FDB_LOG_TAG "[sample][kvdb][blob]"
|
||||
|
||||
void kvdb_type_blob_sample(fdb_kvdb_t kvdb)
|
||||
{
|
||||
struct fdb_blob blob;
|
||||
|
||||
FDB_INFO("==================== kvdb_type_blob_sample ====================\n");
|
||||
|
||||
{ /* CREATE new Key-Value */
|
||||
int temp_data = 36;
|
||||
|
||||
/* It will create new KV node when "temp" KV not in database.
|
||||
* fdb_blob_make: It's a blob make function, and it will return the blob when make finish.
|
||||
*/
|
||||
fdb_kv_set_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data)));
|
||||
FDB_INFO("create the 'temp' blob KV, value is: %d\n", temp_data);
|
||||
}
|
||||
|
||||
{ /* GET the KV value */
|
||||
int temp_data = 0;
|
||||
|
||||
/* get the "temp" KV value */
|
||||
fdb_kv_get_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data)));
|
||||
/* the blob.saved.len is more than 0 when get the value successful */
|
||||
if (blob.saved.len > 0) {
|
||||
FDB_INFO("get the 'temp' value is: %d\n", temp_data);
|
||||
}
|
||||
}
|
||||
|
||||
{ /* CHANGE the KV value */
|
||||
int temp_data = 38;
|
||||
|
||||
/* change the "temp" KV's value to 38 */
|
||||
fdb_kv_set_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data)));
|
||||
FDB_INFO("set 'temp' value to %d\n", temp_data);
|
||||
}
|
||||
|
||||
{ /* DELETE the KV by name */
|
||||
fdb_kv_del(kvdb, "temp");
|
||||
FDB_INFO("delete the 'temp' finish\n");
|
||||
}
|
||||
|
||||
FDB_INFO("===========================================================\n");
|
||||
}
|
||||
|
||||
#endif /* FDB_USING_KVDB */
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief string KV samples.
|
||||
*
|
||||
* Key-Value Database string type KV feature samples source file.
|
||||
*/
|
||||
|
||||
#include <flashdb.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef FDB_USING_KVDB
|
||||
|
||||
#define FDB_LOG_TAG "[sample][kvdb][string]"
|
||||
|
||||
void kvdb_type_string_sample(fdb_kvdb_t kvdb)
|
||||
{
|
||||
FDB_INFO("==================== kvdb_type_string_sample ====================\n");
|
||||
|
||||
{ /* CREATE new Key-Value */
|
||||
char temp_data[10] = "36C";
|
||||
|
||||
/* It will create new KV node when "temp" KV not in database. */
|
||||
fdb_kv_set(kvdb, "temp", temp_data);
|
||||
FDB_INFO("create the 'temp' string KV, value is: %s\n", temp_data);
|
||||
}
|
||||
|
||||
{ /* GET the KV value */
|
||||
char *return_value, temp_data[10] = { 0 };
|
||||
|
||||
/* Get the "temp" KV value.
|
||||
* NOTE: The return value saved in fdb_kv_get's buffer. Please copy away as soon as possible.
|
||||
*/
|
||||
return_value = fdb_kv_get(kvdb, "temp");
|
||||
/* the return value is NULL when get the value failed */
|
||||
if (return_value != NULL) {
|
||||
strncpy(temp_data, return_value, sizeof(temp_data));
|
||||
FDB_INFO("get the 'temp' value is: %s\n", temp_data);
|
||||
}
|
||||
}
|
||||
|
||||
{ /* CHANGE the KV value */
|
||||
char temp_data[10] = "38C";
|
||||
|
||||
/* change the "temp" KV's value to "38.1" */
|
||||
fdb_kv_set(kvdb, "temp", temp_data);
|
||||
FDB_INFO("set 'temp' value to %s\n", temp_data);
|
||||
}
|
||||
|
||||
// { /* DELETE the KV by name */
|
||||
// fdb_kv_del(kvdb, "temp");
|
||||
// FDB_INFO("delete the 'temp' finish\n");
|
||||
// }
|
||||
|
||||
FDB_INFO("===========================================================\n");
|
||||
}
|
||||
|
||||
#endif /* FDB_USING_KVDB */
|
128
SW/components/modules/FlashDB/flashdb/samples/tsdb_sample.c
Normal file
128
SW/components/modules/FlashDB/flashdb/samples/tsdb_sample.c
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief TSDB samples.
|
||||
*
|
||||
* Time series log (like TSDB) feature samples source file.
|
||||
*
|
||||
* TSL is time series log, the TSDB saved many TSLs.
|
||||
*/
|
||||
|
||||
#include <flashdb.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef FDB_USING_TSDB
|
||||
|
||||
#define FDB_LOG_TAG "[sample][tsdb]"
|
||||
|
||||
struct env_status {
|
||||
int temp;
|
||||
int humi;
|
||||
};
|
||||
|
||||
static bool query_cb(fdb_tsl_t tsl, void *arg);
|
||||
static bool query_by_time_cb(fdb_tsl_t tsl, void *arg);
|
||||
static bool set_status_cb(fdb_tsl_t tsl, void *arg);
|
||||
|
||||
void tsdb_sample(fdb_tsdb_t tsdb)
|
||||
{
|
||||
struct fdb_blob blob;
|
||||
|
||||
FDB_INFO("==================== tsdb_sample ====================\n");
|
||||
|
||||
{ /* APPEND new TSL (time series log) */
|
||||
struct env_status status;
|
||||
int test_data[15]={'h','e','l','l','o',' ','w','o','r','l','d'};
|
||||
|
||||
test_data[12] =0;
|
||||
|
||||
while(1)
|
||||
{
|
||||
test_data[12]++;
|
||||
fdb_tsl_append(tsdb, fdb_blob_make(&blob, test_data, sizeof(test_data)));
|
||||
}
|
||||
/* append new log to TSDB */
|
||||
// status.temp = 36;
|
||||
// status.humi = 85;
|
||||
// fdb_tsl_append(tsdb, fdb_blob_make(&blob, &status, sizeof(status)));
|
||||
// FDB_INFO("append the new status.temp (%d) and status.humi (%d)\n", status.temp, status.humi);
|
||||
//
|
||||
// status.temp = 38;
|
||||
// status.humi = 90;
|
||||
// fdb_tsl_append(tsdb, fdb_blob_make(&blob, &status, sizeof(status)));
|
||||
// FDB_INFO("append the new status.temp (%d) and status.humi (%d)\n", status.temp, status.humi);
|
||||
}
|
||||
|
||||
// { /* QUERY the TSDB */
|
||||
// /* query all TSL in TSDB by iterator */
|
||||
// fdb_tsl_iter(tsdb, query_cb, tsdb);
|
||||
// }
|
||||
|
||||
// { /* QUERY the TSDB by time */
|
||||
// /* prepare query time (from 1970-01-01 00:00:00 to 2020-05-05 00:00:00) */
|
||||
// struct tm tm_from = { .tm_year = 1970 - 1900, .tm_mon = 0, .tm_mday = 1, .tm_hour = 0, .tm_min = 0, .tm_sec = 0 };
|
||||
// struct tm tm_to = { .tm_year = 2020 - 1900, .tm_mon = 4, .tm_mday = 5, .tm_hour = 0, .tm_min = 0, .tm_sec = 0 };
|
||||
// time_t from_time = mktime(&tm_from), to_time = mktime(&tm_to);
|
||||
// size_t count;
|
||||
// /* query all TSL in TSDB by time */
|
||||
// fdb_tsl_iter_by_time(tsdb, from_time, to_time, query_by_time_cb, tsdb);
|
||||
// /* query all FDB_TSL_WRITE status TSL's count in TSDB by time */
|
||||
// count = fdb_tsl_query_count(tsdb, from_time, to_time, FDB_TSL_WRITE);
|
||||
// FDB_INFO("query count is: %zu\n", count);
|
||||
// }
|
||||
|
||||
// { /* SET the TSL status */
|
||||
// /* Change the TSL status by iterator or time iterator
|
||||
// * set_status_cb: the change operation will in this callback
|
||||
// *
|
||||
// * NOTE: The actions to modify the state must be in order.
|
||||
// * like: FDB_TSL_WRITE -> FDB_TSL_USER_STATUS1 -> FDB_TSL_DELETED -> FDB_TSL_USER_STATUS2
|
||||
// * The intermediate states can also be ignored.
|
||||
// * such as: FDB_TSL_WRITE -> FDB_TSL_DELETED
|
||||
// */
|
||||
// fdb_tsl_iter(tsdb, set_status_cb, tsdb);
|
||||
// }
|
||||
|
||||
FDB_INFO("===========================================================\n");
|
||||
}
|
||||
|
||||
static bool query_cb(fdb_tsl_t tsl, void *arg)
|
||||
{
|
||||
struct fdb_blob blob;
|
||||
struct env_status status;
|
||||
fdb_tsdb_t db = arg;
|
||||
char my_log[12];
|
||||
fdb_blob_read((fdb_db_t) db, fdb_tsl_to_blob(tsl, fdb_blob_make(&blob, &status, sizeof(status))));
|
||||
FDB_INFO("[query_cb] queried a TSL: time: %ld, temp: %d, humi: %d\n", tsl->time, status.temp, status.humi);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool query_by_time_cb(fdb_tsl_t tsl, void *arg)
|
||||
{
|
||||
struct fdb_blob blob;
|
||||
struct env_status status;
|
||||
fdb_tsdb_t db = arg;
|
||||
|
||||
fdb_blob_read((fdb_db_t) db, fdb_tsl_to_blob(tsl, fdb_blob_make(&blob, &status, sizeof(status))));
|
||||
FDB_INFO("[query_by_time_cb] queried a TSL: time: %ld, temp: %d, humi: %d\n", tsl->time, status.temp, status.humi);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool set_status_cb(fdb_tsl_t tsl, void *arg)
|
||||
{
|
||||
fdb_tsdb_t db = arg;
|
||||
|
||||
FDB_INFO("set the TSL (time %ld) status from %d to %d\n", tsl->time, tsl->status, FDB_TSL_USER_STATUS1);
|
||||
fdb_tsl_set_status(db, tsl, FDB_TSL_USER_STATUS1);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* FDB_USING_TSDB */
|
122
SW/components/modules/FlashDB/flashdb/src/fdb.c
Normal file
122
SW/components/modules/FlashDB/flashdb/src/fdb.c
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief Initialize interface.
|
||||
*
|
||||
* Some initialize interface for this library.
|
||||
*/
|
||||
|
||||
#include <flashdb.h>
|
||||
#include <fdb_low_lvl.h>
|
||||
#include <string.h>
|
||||
|
||||
#define FDB_LOG_TAG ""
|
||||
|
||||
#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(path);
|
||||
|
||||
if (db->init_ok) {
|
||||
return FDB_NO_ERR;
|
||||
}
|
||||
|
||||
db->name = name;
|
||||
db->type = type;
|
||||
db->user_data = user_data;
|
||||
|
||||
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 {
|
||||
#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;
|
||||
}
|
||||
|
||||
void _fdb_init_finish(fdb_db_t db, fdb_err_t result)
|
||||
{
|
||||
static bool log_is_show = false;
|
||||
if (result == FDB_NO_ERR) {
|
||||
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");
|
||||
log_is_show = true;
|
||||
}
|
||||
} 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
SW/components/modules/FlashDB/flashdb/src/fdb_file.c
Normal file
221
SW/components/modules/FlashDB/flashdb/src/fdb_file.c
Normal file
@ -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 */
|
||||
|
1717
SW/components/modules/FlashDB/flashdb/src/fdb_kvdb.c
Normal file
1717
SW/components/modules/FlashDB/flashdb/src/fdb_kvdb.c
Normal file
File diff suppressed because it is too large
Load Diff
836
SW/components/modules/FlashDB/flashdb/src/fdb_tsdb.c
Normal file
836
SW/components/modules/FlashDB/flashdb/src/fdb_tsdb.c
Normal file
@ -0,0 +1,836 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief TSDB feature.
|
||||
*
|
||||
* Time series log (like TSDB) feature implement source file.
|
||||
*
|
||||
* TSL is time series log, the TSDB saved many TSLs.
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <flashdb.h>
|
||||
#include <fdb_low_lvl.h>
|
||||
|
||||
#define FDB_LOG_TAG "[tsl]"
|
||||
/* rewrite log prefix */
|
||||
#undef FDB_LOG_PREFIX2
|
||||
#define FDB_LOG_PREFIX2() FDB_PRINT("[%s] ", db_name(db))
|
||||
|
||||
#if defined(FDB_USING_TSDB)
|
||||
|
||||
/* magic word(`T`, `S`, `L`, `0`) */
|
||||
#define SECTOR_MAGIC_WORD 0x304C5354
|
||||
|
||||
#define TSL_STATUS_TABLE_SIZE FDB_STATUS_TABLE_SIZE(FDB_TSL_STATUS_NUM)
|
||||
|
||||
#define SECTOR_HDR_DATA_SIZE (FDB_WG_ALIGN(sizeof(struct sector_hdr_data)))
|
||||
#define LOG_IDX_DATA_SIZE (FDB_WG_ALIGN(sizeof(struct log_idx_data)))
|
||||
#define LOG_IDX_TS_OFFSET ((unsigned long)(&((struct log_idx_data *)0)->time))
|
||||
#define SECTOR_MAGIC_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->magic))
|
||||
#define SECTOR_START_TIME_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->start_time))
|
||||
#define SECTOR_END0_TIME_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->end_info[0].time))
|
||||
#define SECTOR_END0_IDX_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->end_info[0].index))
|
||||
#define SECTOR_END0_STATUS_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->end_info[0].status))
|
||||
#define SECTOR_END1_TIME_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->end_info[1].time))
|
||||
#define SECTOR_END1_IDX_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->end_info[1].index))
|
||||
#define SECTOR_END1_STATUS_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->end_info[1].status))
|
||||
|
||||
/* the next address is get failed */
|
||||
#define FAILED_ADDR 0xFFFFFFFF
|
||||
|
||||
#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_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); \
|
||||
} while(0);
|
||||
|
||||
#define db_unlock(db) \
|
||||
do { \
|
||||
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, sync) \
|
||||
do { \
|
||||
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, sync) \
|
||||
do { \
|
||||
result = _fdb_flash_write((fdb_db_t)db, addr, buf, size, sync); \
|
||||
if (result != FDB_NO_ERR) return result; \
|
||||
} while(0);
|
||||
|
||||
struct sector_hdr_data {
|
||||
uint8_t status[FDB_STORE_STATUS_TABLE_SIZE]; /**< sector store status @see fdb_sector_store_status_t */
|
||||
uint32_t magic; /**< magic word(`T`, `S`, `L`, `0`) */
|
||||
fdb_time_t start_time; /**< the first start node's timestamp */
|
||||
struct {
|
||||
fdb_time_t time; /**< the last end node's timestamp */
|
||||
uint32_t index; /**< the last end node's index */
|
||||
uint8_t status[TSL_STATUS_TABLE_SIZE]; /**< end node status, @see fdb_tsl_status_t */
|
||||
} end_info[2];
|
||||
uint32_t reserved;
|
||||
};
|
||||
typedef struct sector_hdr_data *sector_hdr_data_t;
|
||||
|
||||
/* time series log node index data */
|
||||
struct log_idx_data {
|
||||
uint8_t status_table[TSL_STATUS_TABLE_SIZE]; /**< node status, @see fdb_tsl_status_t */
|
||||
fdb_time_t time; /**< node timestamp */
|
||||
uint32_t log_len; /**< node total length (header + name + value), must align by FDB_WRITE_GRAN */
|
||||
uint32_t log_addr; /**< node address */
|
||||
};
|
||||
typedef struct log_idx_data *log_idx_data_t;
|
||||
|
||||
struct query_count_args {
|
||||
fdb_tsl_status_t status;
|
||||
size_t count;
|
||||
};
|
||||
|
||||
struct check_sec_hdr_cb_args {
|
||||
fdb_tsdb_t db;
|
||||
bool check_failed;
|
||||
size_t empty_num;
|
||||
uint32_t empty_addr;
|
||||
};
|
||||
|
||||
static fdb_err_t read_tsl(fdb_tsdb_t db, fdb_tsl_t tsl)
|
||||
{
|
||||
struct log_idx_data idx;
|
||||
/* 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) || (tsl->status == FDB_TSL_UNUSED)) {
|
||||
tsl->log_len = db->max_len;
|
||||
tsl->addr.log = FDB_DATA_UNUSED;
|
||||
tsl->time = 0;
|
||||
} else {
|
||||
tsl->log_len = idx.log_len;
|
||||
tsl->addr.log = idx.log_addr;
|
||||
tsl->time = idx.time;
|
||||
}
|
||||
|
||||
return FDB_NO_ERR;
|
||||
}
|
||||
|
||||
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_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 database */
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
/* finished */
|
||||
return FAILED_ADDR;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t get_next_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 + LOG_IDX_DATA_SIZE <= sector->end_idx) {
|
||||
addr = pre_tsl->addr.index + LOG_IDX_DATA_SIZE;
|
||||
} else {
|
||||
/* no TSL */
|
||||
return FAILED_ADDR;
|
||||
}
|
||||
|
||||
return 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;
|
||||
struct sector_hdr_data sec_hdr;
|
||||
|
||||
FDB_ASSERT(sector);
|
||||
|
||||
/* read sector header raw data */
|
||||
_fdb_flash_read((fdb_db_t)db, addr, (uint32_t *)&sec_hdr, sizeof(struct sector_hdr_data));
|
||||
|
||||
sector->addr = addr;
|
||||
sector->magic = sec_hdr.magic;
|
||||
|
||||
/* check magic word */
|
||||
if (sector->magic != SECTOR_MAGIC_WORD) {
|
||||
sector->check_ok = false;
|
||||
return FDB_INIT_FAILED;
|
||||
}
|
||||
sector->check_ok = true;
|
||||
sector->status = (fdb_sector_store_status_t) _fdb_get_status(sec_hdr.status, FDB_SECTOR_STORE_STATUS_NUM);
|
||||
sector->start_time = sec_hdr.start_time;
|
||||
sector->end_info_stat[0] = (fdb_tsl_status_t) _fdb_get_status(sec_hdr.end_info[0].status, FDB_TSL_STATUS_NUM);
|
||||
sector->end_info_stat[1] = (fdb_tsl_status_t) _fdb_get_status(sec_hdr.end_info[1].status, FDB_TSL_STATUS_NUM);
|
||||
if (sector->end_info_stat[0] == FDB_TSL_WRITE) {
|
||||
sector->end_time = sec_hdr.end_info[0].time;
|
||||
sector->end_idx = sec_hdr.end_info[0].index;
|
||||
} else if (sector->end_info_stat[1] == FDB_TSL_WRITE) {
|
||||
sector->end_time = sec_hdr.end_info[1].time;
|
||||
sector->end_idx = sec_hdr.end_info[1].index;
|
||||
} else if (sector->end_info_stat[0] == FDB_TSL_PRE_WRITE && sector->end_info_stat[1] == FDB_TSL_PRE_WRITE) {
|
||||
//TODO There is no valid end node info on this sector, need impl fast query this sector by fdb_tsl_iter_by_time
|
||||
FDB_ASSERT(0);
|
||||
}
|
||||
/* traversal all TSL and calculate the remain space size */
|
||||
sector->empty_idx = sector->addr + SECTOR_HDR_DATA_SIZE;
|
||||
sector->empty_data = sector->addr + db_sec_size(db);
|
||||
/* the TSL's data is saved from sector bottom, and the TSL's index saved from the sector top */
|
||||
sector->remain = sector->empty_data - sector->empty_idx;
|
||||
if (sector->status == FDB_SECTOR_STORE_USING && traversal) {
|
||||
struct fdb_tsl tsl;
|
||||
|
||||
tsl.addr.index = sector->empty_idx;
|
||||
while (read_tsl(db, &tsl) == FDB_NO_ERR) {
|
||||
if (tsl.status == FDB_TSL_UNUSED) {
|
||||
break;
|
||||
}
|
||||
sector->end_time = tsl.time;
|
||||
sector->end_idx = tsl.addr.index;
|
||||
sector->empty_idx += LOG_IDX_DATA_SIZE;
|
||||
sector->empty_data -= FDB_WG_ALIGN(tsl.log_len);
|
||||
tsl.addr.index += LOG_IDX_DATA_SIZE;
|
||||
if (sector->remain > LOG_IDX_DATA_SIZE + FDB_WG_ALIGN(tsl.log_len)) {
|
||||
sector->remain -= (LOG_IDX_DATA_SIZE + FDB_WG_ALIGN(tsl.log_len));
|
||||
} else {
|
||||
FDB_INFO("Error: this TSL (0x%08" PRIX32 ") size (%" PRIu32 ") is out of bound.\n", tsl.addr.index, tsl.log_len);
|
||||
sector->remain = 0;
|
||||
result = FDB_READ_ERR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static fdb_err_t format_sector(fdb_tsdb_t db, uint32_t addr)
|
||||
{
|
||||
fdb_err_t result = FDB_NO_ERR;
|
||||
struct sector_hdr_data sec_hdr;
|
||||
|
||||
FDB_ASSERT(addr % db_sec_size(db) == 0);
|
||||
|
||||
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, true);
|
||||
/* set the magic */
|
||||
sec_hdr.magic = SECTOR_MAGIC_WORD;
|
||||
FLASH_WRITE(db, addr + SECTOR_MAGIC_OFFSET, &sec_hdr.magic, sizeof(sec_hdr.magic), true);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void sector_iterator(fdb_tsdb_t db, tsdb_sec_info_t sector, fdb_sector_store_status_t status, void *arg1,
|
||||
void *arg2, bool (*callback)(tsdb_sec_info_t sector, void *arg1, void *arg2), bool traversal)
|
||||
{
|
||||
uint32_t sec_addr = sector->addr, traversed_len = 0;
|
||||
|
||||
/* search all sectors */
|
||||
do {
|
||||
read_sector_info(db, sec_addr, sector, false);
|
||||
if (status == FDB_SECTOR_STORE_UNUSED || status == sector->status) {
|
||||
if (traversal) {
|
||||
read_sector_info(db, sec_addr, sector, true);
|
||||
}
|
||||
/* iterator is interrupted when callback return true */
|
||||
if (callback && callback(sector, arg1, arg2)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
traversed_len += db_sec_size(db);
|
||||
} while ((sec_addr = get_next_sector_addr(db, sector, traversed_len)) != FAILED_ADDR);
|
||||
}
|
||||
|
||||
static fdb_err_t write_tsl(fdb_tsdb_t db, fdb_blob_t blob, fdb_time_t time)
|
||||
{
|
||||
fdb_err_t result = FDB_NO_ERR;
|
||||
struct log_idx_data idx;
|
||||
uint32_t idx_addr = db->cur_sec.empty_idx;
|
||||
|
||||
idx.log_len = blob->size;
|
||||
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, 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, false);
|
||||
/* write blob data */
|
||||
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, true);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static fdb_err_t update_sec_status(fdb_tsdb_t db, tsdb_sec_info_t sector, fdb_blob_t blob, fdb_time_t cur_time)
|
||||
{
|
||||
fdb_err_t result = FDB_NO_ERR;
|
||||
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)) {
|
||||
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, true);
|
||||
/* save the start timestamp */
|
||||
FLASH_WRITE(db, sector->addr + SECTOR_START_TIME_OFFSET, (uint32_t *)&cur_time, sizeof(fdb_time_t), true);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static fdb_err_t tsl_append(fdb_tsdb_t db, fdb_blob_t blob)
|
||||
{
|
||||
fdb_err_t result = FDB_NO_ERR;
|
||||
fdb_time_t cur_time = db->get_time();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* write the TSL node */
|
||||
result = write_tsl(db, blob, cur_time);
|
||||
if (result != FDB_NO_ERR) {
|
||||
return result;
|
||||
}
|
||||
|
||||
/* recalculate the current using sector info */
|
||||
db->cur_sec.end_idx = db->cur_sec.empty_idx;
|
||||
db->cur_sec.end_time = cur_time;
|
||||
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);
|
||||
db->last_time = cur_time;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a new log to TSDB.
|
||||
*
|
||||
* @param db database object
|
||||
* @param blob log blob data
|
||||
*
|
||||
* @return result
|
||||
*/
|
||||
fdb_err_t fdb_tsl_append(fdb_tsdb_t db, fdb_blob_t blob)
|
||||
{
|
||||
fdb_err_t result = FDB_NO_ERR;
|
||||
|
||||
if (!db_init_ok(db)) {
|
||||
FDB_INFO("Error: TSL (%s) isn't initialize OK.\n", db_name(db));
|
||||
return FDB_INIT_FAILED;
|
||||
}
|
||||
|
||||
db_lock(db);
|
||||
result = tsl_append(db, blob);
|
||||
db_unlock(db);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* The TSDB iterator for each TSL.
|
||||
*
|
||||
* @param db database object
|
||||
* @param cb callback
|
||||
* @param arg callback argument
|
||||
*/
|
||||
void fdb_tsl_iter(fdb_tsdb_t db, fdb_tsl_cb cb, void *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->oldest_addr;
|
||||
/* search all sectors */
|
||||
do {
|
||||
traversed_len += db_sec_size(db);
|
||||
if (read_sector_info(db, sec_addr, §or, 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.addr + SECTOR_HDR_DATA_SIZE;
|
||||
/* search all TSL */
|
||||
do {
|
||||
read_tsl(db, &tsl);
|
||||
/* iterator is interrupted when callback return true */
|
||||
if (cb(&tsl, arg)) {
|
||||
return;
|
||||
}
|
||||
} while ((tsl.addr.index = get_next_tsl_addr(§or, &tsl)) != FAILED_ADDR);
|
||||
}
|
||||
} while ((sec_addr = get_next_sector_addr(db, §or, traversed_len)) != FAILED_ADDR);
|
||||
}
|
||||
|
||||
/**
|
||||
* The TSDB iterator for each TSL by timestamp.
|
||||
*
|
||||
* @param db database object
|
||||
* @param from starting timestap
|
||||
* @param to ending timestap
|
||||
* @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;
|
||||
struct fdb_tsl tsl;
|
||||
bool found_start_tsl = false;
|
||||
|
||||
if (!db_init_ok(db)) {
|
||||
FDB_INFO("Error: TSL (%s) isn't initialize OK.\n", db_name(db));
|
||||
}
|
||||
|
||||
// FDB_INFO("from %s", ctime((const time_t * )&from));
|
||||
// FDB_INFO("to %s", ctime((const time_t * )&to));
|
||||
|
||||
if (cb == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
sec_addr = oldest_addr;
|
||||
/* search all sectors */
|
||||
do {
|
||||
traversed_len += db_sec_size(db);
|
||||
if (read_sector_info(db, sec_addr, §or, 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;
|
||||
}
|
||||
if ((found_start_tsl) || (!found_start_tsl && ((from >= sector.start_time && from <= sector.end_time)
|
||||
|| (sec_addr == oldest_addr && 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 all TSL */
|
||||
do {
|
||||
read_tsl(db, &tsl);
|
||||
if (tsl.status != FDB_TSL_UNUSED) {
|
||||
if (tsl.time >= from && tsl.time <= to) {
|
||||
/* iterator is interrupted when callback return true */
|
||||
if (cb(&tsl, cb_arg)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} while ((tsl.addr.index = get_next_tsl_addr(§or, &tsl)) != FAILED_ADDR);
|
||||
}
|
||||
} else if (sector.status == FDB_SECTOR_STORE_EMPTY) {
|
||||
return;
|
||||
}
|
||||
} while ((sec_addr = get_next_sector_addr(db, §or, traversed_len)) != FAILED_ADDR);
|
||||
}
|
||||
|
||||
static bool query_count_cb(fdb_tsl_t tsl, void *arg)
|
||||
{
|
||||
struct query_count_args *args = arg;
|
||||
|
||||
if (tsl->status == args->status) {
|
||||
args->count++;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query some TSL's count by timestamp and status.
|
||||
*
|
||||
* @param db database object
|
||||
* @param from starting timestap
|
||||
* @param to ending timestap
|
||||
* @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)
|
||||
{
|
||||
struct query_count_args arg = { FDB_TSL_UNUSED, 0 };
|
||||
|
||||
arg.status = status;
|
||||
|
||||
if (!db_init_ok(db)) {
|
||||
FDB_INFO("Error: TSL (%s) isn't initialize OK.\n", db_name(db));
|
||||
return FDB_INIT_FAILED;
|
||||
}
|
||||
|
||||
fdb_tsl_iter_by_time(db, from, to, query_count_cb, &arg);
|
||||
|
||||
return arg.count;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the TSL status.
|
||||
*
|
||||
* @param db database object
|
||||
* @param tsl TSL object
|
||||
* @param status status
|
||||
*
|
||||
* @return result
|
||||
*/
|
||||
fdb_err_t fdb_tsl_set_status(fdb_tsdb_t db, fdb_tsl_t tsl, fdb_tsl_status_t status)
|
||||
{
|
||||
fdb_err_t result = FDB_NO_ERR;
|
||||
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, true);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the TSL object to blob object
|
||||
*
|
||||
* @param tsl TSL object
|
||||
* @param blob blob object
|
||||
*
|
||||
* @return new blob object
|
||||
*/
|
||||
fdb_blob_t fdb_tsl_to_blob(fdb_tsl_t tsl, fdb_blob_t blob)
|
||||
{
|
||||
blob->saved.addr = tsl->addr.log;
|
||||
blob->saved.meta_addr = tsl->addr.index;
|
||||
blob->saved.len = tsl->log_len;
|
||||
|
||||
return blob;
|
||||
}
|
||||
|
||||
static bool check_sec_hdr_cb(tsdb_sec_info_t sector, void *arg1, void *arg2)
|
||||
{
|
||||
struct check_sec_hdr_cb_args *arg = arg1;
|
||||
fdb_tsdb_t db = arg->db;
|
||||
|
||||
if (!sector->check_ok) {
|
||||
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) {
|
||||
if (db->cur_sec.addr == FDB_DATA_UNUSED) {
|
||||
memcpy(&db->cur_sec, sector, sizeof(struct tsdb_sec_info));
|
||||
} else {
|
||||
FDB_INFO("Warning: Sector status is wrong, there are multiple sectors in use.\n");
|
||||
(arg->check_failed) = true;
|
||||
return true;
|
||||
}
|
||||
} else if (sector->status == FDB_SECTOR_STORE_EMPTY) {
|
||||
(arg->empty_num) += 1;
|
||||
arg->empty_addr = sector->addr;
|
||||
if ((arg->empty_num) == 1 && db->cur_sec.addr == FDB_DATA_UNUSED) {
|
||||
memcpy(&db->cur_sec, sector, sizeof(struct tsdb_sec_info));
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
static bool format_all_cb(tsdb_sec_info_t sector, void *arg1, void *arg2)
|
||||
{
|
||||
fdb_tsdb_t db = arg1;
|
||||
|
||||
format_sector(db, sector->addr);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void tsl_format_all(fdb_tsdb_t db)
|
||||
{
|
||||
struct tsdb_sec_info sector;
|
||||
|
||||
sector.addr = 0;
|
||||
sector_iterator(db, §or, FDB_SECTOR_STORE_UNUSED, db, NULL, format_all_cb, false);
|
||||
db->oldest_addr = 0;
|
||||
db->cur_sec.addr = 0;
|
||||
db->last_time = 0;
|
||||
/* read the current using sector info */
|
||||
read_sector_info(db, db->cur_sec.addr, &db->cur_sec, false);
|
||||
|
||||
FDB_INFO("All sector format finished.\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean all the data in the TSDB.
|
||||
*
|
||||
* @note It's DANGEROUS. This operation is not reversible.
|
||||
*
|
||||
* @param db database object
|
||||
*/
|
||||
void fdb_tsl_clean(fdb_tsdb_t db)
|
||||
{
|
||||
db_lock(db);
|
||||
tsl_format_all(db);
|
||||
db_unlock(db);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will get or set some options of the database
|
||||
*
|
||||
* @param db database object
|
||||
* @param cmd the control command
|
||||
* @param arg the argument
|
||||
*/
|
||||
void fdb_tsdb_control(fdb_tsdb_t db, int cmd, void *arg)
|
||||
{
|
||||
FDB_ASSERT(db);
|
||||
|
||||
switch (cmd) {
|
||||
case FDB_TSDB_CTRL_SET_SEC_SIZE:
|
||||
/* this change MUST before database initialization */
|
||||
FDB_ASSERT(db->parent.init_ok == false);
|
||||
db->parent.sec_size = *(uint32_t *)arg;
|
||||
break;
|
||||
case FDB_TSDB_CTRL_GET_SEC_SIZE:
|
||||
*(uint32_t *)arg = db->parent.sec_size;
|
||||
break;
|
||||
case FDB_TSDB_CTRL_SET_LOCK:
|
||||
db->parent.lock = (void (*)(fdb_db_t db))arg;
|
||||
break;
|
||||
case FDB_TSDB_CTRL_SET_UNLOCK:
|
||||
db->parent.unlock = (void (*)(fdb_db_t db))arg;
|
||||
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:
|
||||
*(bool *)arg = db->rollover;
|
||||
break;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The time series database initialization.
|
||||
*
|
||||
* @param db database object
|
||||
* @param name database 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 *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;
|
||||
struct check_sec_hdr_cb_args check_sec_arg = { db, false, 0, 0};
|
||||
|
||||
FDB_ASSERT(get_time);
|
||||
|
||||
result = _fdb_init_ex((fdb_db_t)db, name, path, FDB_DB_TYPE_TS, user_data);
|
||||
if (result != FDB_NO_ERR) {
|
||||
goto __exit;
|
||||
}
|
||||
|
||||
db->get_time = get_time;
|
||||
db->max_len = max_len;
|
||||
/* default rollover flag is true */
|
||||
db->rollover = true;
|
||||
db->oldest_addr = FDB_DATA_UNUSED;
|
||||
db->cur_sec.addr = FDB_DATA_UNUSED;
|
||||
/* must less than sector size */
|
||||
FDB_ASSERT(max_len < db_sec_size(db));
|
||||
|
||||
/* check all sector header */
|
||||
sector.addr = 0;
|
||||
sector_iterator(db, §or, 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) {
|
||||
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 {
|
||||
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_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);
|
||||
}
|
||||
}
|
||||
FDB_DEBUG("TSDB (%s) oldest sectors is 0x%08" PRIX32 ", current using sector is 0x%08" PRIX32 ".\n", db_name(db), db->oldest_addr,
|
||||
db->cur_sec.addr);
|
||||
/* read the current using sector info */
|
||||
read_sector_info(db, db->cur_sec.addr, &db->cur_sec, true);
|
||||
/* get last save time */
|
||||
if (db->cur_sec.status == FDB_SECTOR_STORE_USING) {
|
||||
db->last_time = db->cur_sec.end_time;
|
||||
} else if (db->cur_sec.status == FDB_SECTOR_STORE_EMPTY && db->oldest_addr != db->cur_sec.addr) {
|
||||
struct tsdb_sec_info sec;
|
||||
uint32_t addr = db->cur_sec.addr;
|
||||
|
||||
if (addr == 0) {
|
||||
addr = db_max_size(db) - db_sec_size(db);
|
||||
} else {
|
||||
addr -= db_sec_size(db);
|
||||
}
|
||||
read_sector_info(db, addr, &sec, false);
|
||||
db->last_time = sec.end_time;
|
||||
}
|
||||
|
||||
__exit:
|
||||
|
||||
_fdb_init_finish((fdb_db_t)db, result);
|
||||
|
||||
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) */
|
309
SW/components/modules/FlashDB/flashdb/src/fdb_utils.c
Normal file
309
SW/components/modules/FlashDB/flashdb/src/fdb_utils.c
Normal file
@ -0,0 +1,309 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief utils
|
||||
*
|
||||
* Some utils for this library.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <flashdb.h>
|
||||
#include <fdb_low_lvl.h>
|
||||
|
||||
#define FDB_LOG_TAG "[utils]"
|
||||
|
||||
static const uint32_t crc32_table[] =
|
||||
{
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
|
||||
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
||||
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
|
||||
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
||||
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
|
||||
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
|
||||
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
|
||||
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
||||
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
|
||||
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
||||
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
|
||||
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
||||
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
|
||||
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
|
||||
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
|
||||
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
||||
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
|
||||
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
|
||||
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
|
||||
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
||||
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
|
||||
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
|
||||
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
|
||||
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
||||
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
|
||||
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
|
||||
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
|
||||
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
||||
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
|
||||
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
||||
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
|
||||
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
||||
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
|
||||
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
|
||||
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
|
||||
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
||||
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
|
||||
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
|
||||
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
|
||||
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
||||
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
|
||||
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate the CRC32 value of a memory buffer.
|
||||
*
|
||||
* @param crc accumulated CRC32 value, must be 0 on first call
|
||||
* @param buf buffer to calculate CRC32 value for
|
||||
* @param size bytes in buffer
|
||||
*
|
||||
* @return calculated CRC32 value
|
||||
*/
|
||||
uint32_t fdb_calc_crc32(uint32_t crc, const void *buf, size_t size)
|
||||
{
|
||||
const uint8_t *p;
|
||||
|
||||
p = (const uint8_t *)buf;
|
||||
crc = crc ^ ~0U;
|
||||
|
||||
while (size--) {
|
||||
crc = crc32_table[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
|
||||
}
|
||||
|
||||
return crc ^ ~0U;
|
||||
}
|
||||
|
||||
size_t _fdb_set_status(uint8_t status_table[], size_t status_num, size_t status_index)
|
||||
{
|
||||
size_t byte_index = ~0UL;
|
||||
/*
|
||||
* | write garn | status0 | status1 | status2 |
|
||||
* ---------------------------------------------------------------------------------
|
||||
* | 1bit | 0xFF | 0x7F | 0x3F |
|
||||
* | 8bit | 0xFFFF | 0x00FF | 0x0000 |
|
||||
* | 32bit | 0xFFFFFFFF FFFFFFFF | 0x00FFFFFF FFFFFFFF | 0x00FFFFFF 00FFFFFF |
|
||||
*/
|
||||
memset(status_table, 0xFF, FDB_STATUS_TABLE_SIZE(status_num));
|
||||
if (status_index > 0) {
|
||||
#if (FDB_WRITE_GRAN == 1)
|
||||
byte_index = (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;
|
||||
#endif /* FDB_WRITE_GRAN == 1 */
|
||||
}
|
||||
|
||||
return byte_index;
|
||||
}
|
||||
|
||||
size_t _fdb_get_status(uint8_t status_table[], size_t status_num)
|
||||
{
|
||||
size_t i = 0, status_num_bak = --status_num;
|
||||
|
||||
while (status_num --) {
|
||||
/* get the first 0 position from end address to start address */
|
||||
#if (FDB_WRITE_GRAN == 1)
|
||||
if ((status_table[status_num / 8] & (0x80 >> (status_num % 8))) == 0x00) {
|
||||
break;
|
||||
}
|
||||
#else /* (FDB_WRITE_GRAN == 8) || (FDB_WRITE_GRAN == 32) || (FDB_WRITE_GRAN == 64) */
|
||||
if (status_table[status_num * FDB_WRITE_GRAN / 8] == 0x00) {
|
||||
break;
|
||||
}
|
||||
#endif /* FDB_WRITE_GRAN == 1 */
|
||||
i++;
|
||||
}
|
||||
|
||||
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, bool sync)
|
||||
{
|
||||
fdb_err_t result = FDB_NO_ERR;
|
||||
size_t byte_index;
|
||||
|
||||
FDB_ASSERT(status_index < status_num);
|
||||
FDB_ASSERT(status_table);
|
||||
|
||||
/* set the status first */
|
||||
byte_index = _fdb_set_status(status_table, status_num, status_index);
|
||||
|
||||
/* the first status table value is all 1, so no need to write flash */
|
||||
if (byte_index == ~0UL) {
|
||||
return FDB_NO_ERR;
|
||||
}
|
||||
#if (FDB_WRITE_GRAN == 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, sync);
|
||||
#endif /* FDB_WRITE_GRAN == 1 */
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t _fdb_read_status(fdb_db_t db, uint32_t addr, uint8_t status_table[], size_t total_num)
|
||||
{
|
||||
FDB_ASSERT(status_table);
|
||||
|
||||
_fdb_flash_read(db, addr, (uint32_t *) status_table, FDB_STATUS_TABLE_SIZE(total_num));
|
||||
|
||||
return _fdb_get_status(status_table, total_num);
|
||||
}
|
||||
|
||||
/*
|
||||
* find the continue 0xFF flash address to end address
|
||||
*/
|
||||
uint32_t _fdb_continue_ff_addr(fdb_db_t db, uint32_t start, uint32_t end)
|
||||
{
|
||||
uint8_t buf[32], last_data = 0x00;
|
||||
size_t i, addr = start, read_size;
|
||||
|
||||
for (; start < end; start += sizeof(buf)) {
|
||||
if (start + sizeof(buf) < end) {
|
||||
read_size = sizeof(buf);
|
||||
} else {
|
||||
read_size = end - start;
|
||||
}
|
||||
_fdb_flash_read(db, start, (uint32_t *) buf, read_size);
|
||||
for (i = 0; i < read_size; i++) {
|
||||
if (last_data != 0xFF && buf[i] == 0xFF) {
|
||||
addr = start + i;
|
||||
}
|
||||
last_data = buf[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (last_data == 0xFF) {
|
||||
return FDB_WG_ALIGN(addr);
|
||||
} else {
|
||||
return end;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a blob object.
|
||||
*
|
||||
* @param blob blob object
|
||||
* @param value_buf value buffer
|
||||
* @param buf_len buffer length
|
||||
*
|
||||
* @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;
|
||||
blob->size = buf_len;
|
||||
|
||||
return blob;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the blob object in database.
|
||||
*
|
||||
* @param db database object
|
||||
* @param blob blob object
|
||||
*
|
||||
* @return read length
|
||||
*/
|
||||
size_t fdb_blob_read(fdb_db_t db, fdb_blob_t blob)
|
||||
{
|
||||
size_t read_len = blob->size;
|
||||
|
||||
if (read_len > blob->saved.len) {
|
||||
read_len = blob->saved.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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
fdb_err_t _fdb_flash_erase(fdb_db_t db, uint32_t addr, size_t size)
|
||||
{
|
||||
fdb_err_t result = FDB_NO_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, bool sync)
|
||||
{
|
||||
fdb_err_t result = FDB_NO_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;
|
||||
|
||||
}
|
151
SW/components/modules/FlashDB/port/fal/inc/fal.h
Normal file
151
SW/components/modules/FlashDB/port/fal/inc/fal.h
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2018, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2018-05-17 armink the first version
|
||||
*/
|
||||
|
||||
#ifndef _FAL_H_
|
||||
#define _FAL_H_
|
||||
|
||||
#include <fal_cfg.h>
|
||||
#include "fal_def.h"
|
||||
|
||||
/**
|
||||
* FAL (Flash Abstraction Layer) initialization.
|
||||
* It will initialize all flash device and all flash partition.
|
||||
*
|
||||
* @return >= 0: partitions total number
|
||||
*/
|
||||
int fal_init(void);
|
||||
|
||||
/* =============== flash device operator API =============== */
|
||||
/**
|
||||
* find flash device by name
|
||||
*
|
||||
* @param name flash device name
|
||||
*
|
||||
* @return != NULL: flash device
|
||||
* NULL: not found
|
||||
*/
|
||||
const struct fal_flash_dev *fal_flash_device_find(const char *name);
|
||||
|
||||
/* =============== partition operator API =============== */
|
||||
/**
|
||||
* find the partition by name
|
||||
*
|
||||
* @param name partition name
|
||||
*
|
||||
* @return != NULL: partition
|
||||
* NULL: not found
|
||||
*/
|
||||
const struct fal_partition *fal_partition_find(const char *name);
|
||||
|
||||
/**
|
||||
* get the partition table
|
||||
*
|
||||
* @param len return the partition table length
|
||||
*
|
||||
* @return partition table
|
||||
*/
|
||||
const struct fal_partition *fal_get_partition_table(size_t *len);
|
||||
|
||||
/**
|
||||
* set partition table temporarily
|
||||
* This setting will modify the partition table temporarily, the setting will be lost after restart.
|
||||
*
|
||||
* @param table partition table
|
||||
* @param len partition table length
|
||||
*/
|
||||
void fal_set_partition_table_temp(struct fal_partition *table, size_t len);
|
||||
|
||||
/**
|
||||
* read data from partition
|
||||
*
|
||||
* @param part partition
|
||||
* @param addr relative address for partition
|
||||
* @param buf read buffer
|
||||
* @param size read size
|
||||
*
|
||||
* @return >= 0: successful read data size
|
||||
* -1: error
|
||||
*/
|
||||
int fal_partition_read(const struct fal_partition *part, uint32_t addr, uint8_t *buf, size_t size);
|
||||
|
||||
/**
|
||||
* write data to partition
|
||||
*
|
||||
* @param part partition
|
||||
* @param addr relative address for partition
|
||||
* @param buf write buffer
|
||||
* @param size write size
|
||||
*
|
||||
* @return >= 0: successful write data size
|
||||
* -1: error
|
||||
*/
|
||||
int fal_partition_write(const struct fal_partition *part, uint32_t addr,uint8_t *buf, size_t size);
|
||||
|
||||
/**
|
||||
* erase partition data
|
||||
*
|
||||
* @param part partition
|
||||
* @param addr relative address for partition
|
||||
* @param size erase size
|
||||
*
|
||||
* @return >= 0: successful erased data size
|
||||
* -1: error
|
||||
*/
|
||||
int fal_partition_erase(const struct fal_partition *part, uint32_t addr, size_t size);
|
||||
|
||||
/**
|
||||
* erase partition all data
|
||||
*
|
||||
* @param part partition
|
||||
*
|
||||
* @return >= 0: successful erased data size
|
||||
* -1: error
|
||||
*/
|
||||
int fal_partition_erase_all(const struct fal_partition *part);
|
||||
|
||||
/**
|
||||
* print the partition table
|
||||
*/
|
||||
void fal_show_part_table(void);
|
||||
|
||||
/* =============== API provided to RT-Thread =============== */
|
||||
/**
|
||||
* create RT-Thread block device by specified partition
|
||||
*
|
||||
* @param parition_name partition name
|
||||
*
|
||||
* @return != NULL: created block device
|
||||
* NULL: created failed
|
||||
*/
|
||||
struct rt_device *fal_blk_device_create(const char *parition_name);
|
||||
|
||||
#if defined(RT_USING_MTD_NOR)
|
||||
/**
|
||||
* create RT-Thread MTD NOR device by specified partition
|
||||
*
|
||||
* @param parition_name partition name
|
||||
*
|
||||
* @return != NULL: created MTD NOR device
|
||||
* NULL: created failed
|
||||
*/
|
||||
struct rt_device *fal_mtd_nor_device_create(const char *parition_name);
|
||||
#endif /* defined(RT_USING_MTD_NOR) */
|
||||
|
||||
/**
|
||||
* create RT-Thread char device by specified partition
|
||||
*
|
||||
* @param parition_name partition name
|
||||
*
|
||||
* @return != NULL: created char device
|
||||
* NULL: created failed
|
||||
*/
|
||||
struct rt_device *fal_char_device_create(const char *parition_name);
|
||||
|
||||
#endif /* _FAL_H_ */
|
145
SW/components/modules/FlashDB/port/fal/inc/fal_def.h
Normal file
145
SW/components/modules/FlashDB/port/fal/inc/fal_def.h
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2018, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2018-05-17 armink the first version
|
||||
*/
|
||||
|
||||
#ifndef _FAL_DEF_H_
|
||||
#define _FAL_DEF_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#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
|
||||
#endif
|
||||
|
||||
#ifndef FAL_CALLOC
|
||||
#define FAL_CALLOC calloc
|
||||
#endif
|
||||
|
||||
#ifndef FAL_REALLOC
|
||||
#define FAL_REALLOC realloc
|
||||
#endif
|
||||
|
||||
#ifndef FAL_FREE
|
||||
#define FAL_FREE free
|
||||
#endif
|
||||
|
||||
#ifndef FAL_PRINTF
|
||||
#define FAL_PRINTF printf
|
||||
#endif
|
||||
|
||||
#ifndef FAL_DEBUG
|
||||
#define FAL_DEBUG 0
|
||||
#endif
|
||||
|
||||
#if FAL_DEBUG
|
||||
#ifdef assert
|
||||
#undef assert
|
||||
#endif
|
||||
#define assert(EXPR) \
|
||||
if (!(EXPR)) \
|
||||
{ \
|
||||
FAL_PRINTF("(%s) has assert failed at %s.\n", #EXPR, __FUNCTION__); \
|
||||
while (1); \
|
||||
}
|
||||
|
||||
/* debug level log */
|
||||
#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")
|
||||
|
||||
#else
|
||||
|
||||
#ifdef assert
|
||||
#undef assert
|
||||
#endif
|
||||
#define assert(EXPR) ((void)0);
|
||||
|
||||
/* debug level log */
|
||||
#ifdef log_d
|
||||
#undef log_d
|
||||
#endif
|
||||
#define log_d(...)
|
||||
#endif /* FAL_DEBUG */
|
||||
|
||||
/* error level log */
|
||||
#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")
|
||||
|
||||
/* info level log */
|
||||
#ifdef log_i
|
||||
#undef log_i
|
||||
#endif
|
||||
#define log_i(...) FAL_PRINTF("\033[32;22m[I/FAL] "); FAL_PRINTF(__VA_ARGS__);FAL_PRINTF("\033[0m\n")
|
||||
|
||||
/* FAL flash and partition device name max length */
|
||||
#ifndef FAL_DEV_NAME_MAX
|
||||
#define FAL_DEV_NAME_MAX 24
|
||||
#endif
|
||||
|
||||
struct fal_flash_dev
|
||||
{
|
||||
char name[FAL_DEV_NAME_MAX];
|
||||
|
||||
/* flash device start address and len */
|
||||
uint32_t addr;
|
||||
size_t len;
|
||||
/* the block size in the flash for erase minimum granularity */
|
||||
size_t blk_size;
|
||||
|
||||
struct
|
||||
{
|
||||
int (*init)(void);
|
||||
int (*read)(long offset, uint8_t *buf, size_t size);
|
||||
int (*write)(long offset, uint8_t *buf, size_t size);
|
||||
int (*erase)(long offset, size_t size);
|
||||
} ops;
|
||||
|
||||
/* write minimum granularity, unit: bit.
|
||||
1(nor flash)/ 8(stm32f2/f4)/ 32(stm32f1)/ 64(stm32l4)
|
||||
0 will not take effect. */
|
||||
size_t write_gran;
|
||||
};
|
||||
typedef struct fal_flash_dev *fal_flash_dev_t;
|
||||
|
||||
/**
|
||||
* FAL partition
|
||||
*/
|
||||
struct fal_partition
|
||||
{
|
||||
uint32_t magic_word;
|
||||
|
||||
/* partition name */
|
||||
char name[FAL_DEV_NAME_MAX];
|
||||
/* flash device name for partition */
|
||||
char flash_name[FAL_DEV_NAME_MAX];
|
||||
|
||||
/* partition offset address on flash device */
|
||||
long offset;
|
||||
size_t len;
|
||||
|
||||
uint32_t reserved;
|
||||
};
|
||||
typedef struct fal_partition *fal_partition_t;
|
||||
|
||||
#endif /* _FAL_DEF_H_ */
|
62
SW/components/modules/FlashDB/port/fal/src/fal.c
Normal file
62
SW/components/modules/FlashDB/port/fal/src/fal.c
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2018, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2018-05-17 armink the first version
|
||||
*/
|
||||
|
||||
#include <fal.h>
|
||||
|
||||
static uint8_t init_ok = 0;
|
||||
|
||||
/**
|
||||
* FAL (Flash Abstraction Layer) initialization.
|
||||
* It will initialize all flash device and all flash partition.
|
||||
*
|
||||
* @return >= 0: partitions total number
|
||||
*/
|
||||
int fal_init(void)
|
||||
{
|
||||
extern int fal_flash_init(void);
|
||||
extern int fal_partition_init(void);
|
||||
|
||||
int result;
|
||||
|
||||
/* initialize all flash device on FAL flash table */
|
||||
result = fal_flash_init();
|
||||
|
||||
if (result < 0) {
|
||||
goto __exit;
|
||||
}
|
||||
|
||||
/* initialize all flash partition on FAL partition table */
|
||||
result = fal_partition_init();
|
||||
|
||||
__exit:
|
||||
|
||||
if ((result > 0) && (!init_ok))
|
||||
{
|
||||
init_ok = 1;
|
||||
log_i("Flash Abstraction Layer (V%s) initialize success.", FAL_SW_VERSION);
|
||||
}
|
||||
else if(result <= 0)
|
||||
{
|
||||
init_ok = 0;
|
||||
log_e("Flash Abstraction Layer (V%s) initialize failed.", FAL_SW_VERSION);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the FAL is initialized successfully
|
||||
*
|
||||
* @return 0: not init or init failed; 1: init success
|
||||
*/
|
||||
int fal_init_check(void)
|
||||
{
|
||||
return init_ok;
|
||||
}
|
80
SW/components/modules/FlashDB/port/fal/src/fal_flash.c
Normal file
80
SW/components/modules/FlashDB/port/fal/src/fal_flash.c
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2018, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2018-05-17 armink the first version
|
||||
*/
|
||||
|
||||
#include <fal.h>
|
||||
#include "fal_cfg.h"
|
||||
#include <string.h>
|
||||
|
||||
/* flash device table, must defined by user */
|
||||
#if !defined(FAL_FLASH_DEV_TABLE)
|
||||
#error "You must defined flash device table (FAL_FLASH_DEV_TABLE) on 'fal_cfg.h'"
|
||||
#endif
|
||||
|
||||
static const struct fal_flash_dev * const device_table[] = FAL_FLASH_DEV_TABLE;
|
||||
static const size_t device_table_len = sizeof(device_table) / sizeof(device_table[0]);
|
||||
static uint8_t init_ok = 0;
|
||||
|
||||
/**
|
||||
* Initialize all flash device on FAL flash table
|
||||
*
|
||||
* @return result
|
||||
*/
|
||||
int fal_flash_init(void)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (init_ok)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < device_table_len; i++)
|
||||
{
|
||||
assert(device_table[i]->ops.read);
|
||||
assert(device_table[i]->ops.write);
|
||||
assert(device_table[i]->ops.erase);
|
||||
/* init flash device on flash table */
|
||||
if (device_table[i]->ops.init)
|
||||
{
|
||||
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, device_table[i]->name, device_table[i]->addr, device_table[i]->len,
|
||||
// device_table[i]->blk_size);
|
||||
}
|
||||
|
||||
init_ok = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* find flash device by name
|
||||
*
|
||||
* @param name flash device name
|
||||
*
|
||||
* @return != NULL: flash device
|
||||
* NULL: not found
|
||||
*/
|
||||
const struct fal_flash_dev *fal_flash_device_find(const char *name)
|
||||
{
|
||||
assert(init_ok);
|
||||
assert(name);
|
||||
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < device_table_len; i++)
|
||||
{
|
||||
if (!strncmp(name, device_table[i]->name, FAL_DEV_NAME_MAX)) {
|
||||
return device_table[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
531
SW/components/modules/FlashDB/port/fal/src/fal_partition.c
Normal file
531
SW/components/modules/FlashDB/port/fal/src/fal_partition.c
Normal file
@ -0,0 +1,531 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2018, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2018-05-17 armink the first version
|
||||
*/
|
||||
|
||||
#include <fal.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* partition magic word */
|
||||
#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'.
|
||||
* When this option is disable, it will auto find the partition table on a specified location in flash partition.
|
||||
*/
|
||||
#ifdef FAL_PART_HAS_TABLE_CFG
|
||||
|
||||
/* check partition table definition */
|
||||
#if !defined(FAL_PART_TABLE)
|
||||
#error "You must defined FAL_PART_TABLE on 'fal_cfg.h'"
|
||||
#endif
|
||||
|
||||
#ifdef __CC_ARM /* ARM Compiler */
|
||||
#define SECTION(x) __attribute__((section(x)))
|
||||
#define USED __attribute__((used))
|
||||
#elif defined (__IAR_SYSTEMS_ICC__) /* for IAR Compiler */
|
||||
#define SECTION(x) @ x
|
||||
#define USED __root
|
||||
#elif defined (__GNUC__) /* GNU GCC Compiler */
|
||||
#define SECTION(x) __attribute__((section(x)))
|
||||
#define USED __attribute__((used))
|
||||
#else
|
||||
#error not supported tool chain
|
||||
#endif /* __CC_ARM */
|
||||
//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 */
|
||||
|
||||
#if !defined(FAL_PART_TABLE_FLASH_DEV_NAME)
|
||||
#error "You must defined FAL_PART_TABLE_FLASH_DEV_NAME on 'fal_cfg.h'"
|
||||
#endif
|
||||
|
||||
/* check partition table end offset address definition */
|
||||
#if !defined(FAL_PART_TABLE_END_OFFSET)
|
||||
#error "You must defined FAL_PART_TABLE_END_OFFSET on 'fal_cfg.h'"
|
||||
#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;
|
||||
static size_t partition_table_len = 0;
|
||||
|
||||
/**
|
||||
* print the partition table
|
||||
*/
|
||||
void fal_show_part_table(void)
|
||||
{
|
||||
char *item1 = "name", *item2 = "flash_dev";
|
||||
size_t i, part_name_max = strlen(item1), flash_dev_name_max = strlen(item2);
|
||||
const struct fal_partition *part;
|
||||
|
||||
if (partition_table_len)
|
||||
{
|
||||
for (i = 0; i < partition_table_len; i++)
|
||||
{
|
||||
part = &partition_table[i];
|
||||
if (strlen(part->name) > part_name_max)
|
||||
{
|
||||
part_name_max = strlen(part->name);
|
||||
}
|
||||
if (strlen(part->flash_name) > flash_dev_name_max)
|
||||
{
|
||||
flash_dev_name_max = strlen(part->flash_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
log_i("==================== FAL partition table ====================");
|
||||
log_i("| %-*.*s | %-*.*s | offset | length |", part_name_max, FAL_DEV_NAME_MAX, item1, flash_dev_name_max,
|
||||
FAL_DEV_NAME_MAX, item2);
|
||||
log_i("-------------------------------------------------------------");
|
||||
for (i = 0; i < partition_table_len; i++)
|
||||
{
|
||||
|
||||
#ifdef FAL_PART_HAS_TABLE_CFG
|
||||
part = &partition_table[i];
|
||||
#else
|
||||
part = &partition_table[partition_table_len - i - 1];
|
||||
#endif
|
||||
|
||||
log_i("| %-*.*s | %-*.*s | 0x%08lx | 0x%08x |", part_name_max, FAL_DEV_NAME_MAX, part->name, flash_dev_name_max,
|
||||
FAL_DEV_NAME_MAX, part->flash_name, part->offset, part->len);
|
||||
}
|
||||
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
|
||||
*
|
||||
* @return partitions total number
|
||||
*/
|
||||
int fal_partition_init(void)
|
||||
{
|
||||
|
||||
if (init_ok)
|
||||
{
|
||||
return partition_table_len;
|
||||
}
|
||||
|
||||
#ifdef FAL_PART_HAS_TABLE_CFG
|
||||
partition_table = &partition_table_def[0];
|
||||
partition_table_len = sizeof(partition_table_def) / sizeof(partition_table_def[0]);
|
||||
#else
|
||||
/* load partition table from the end address FAL_PART_TABLE_END_OFFSET, error return 0 */
|
||||
long part_table_offset = FAL_PART_TABLE_END_OFFSET;
|
||||
size_t table_num = 0, table_item_size = 0;
|
||||
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)
|
||||
{
|
||||
log_e("Initialize failed! Flash device (%s) NOT found.", FAL_PART_TABLE_FLASH_DEV_NAME);
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
/* check partition table offset address */
|
||||
if (part_table_offset < 0 || part_table_offset >= (long) flash_dev->len)
|
||||
{
|
||||
log_e("Setting partition table end offset address(%ld) out of flash bound(<%d).", part_table_offset, flash_dev->len);
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
table_item_size = sizeof(struct fal_partition);
|
||||
new_part = (fal_partition_t)FAL_MALLOC(table_item_size);
|
||||
if (new_part == NULL)
|
||||
{
|
||||
log_e("Initialize failed! No memory for table buffer.");
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
/* find partition table location */
|
||||
{
|
||||
uint8_t read_buf[64];
|
||||
|
||||
part_table_offset -= sizeof(read_buf);
|
||||
while (part_table_offset >= 0)
|
||||
{
|
||||
if (flash_dev->ops.read(part_table_offset, read_buf, sizeof(read_buf)) > 0)
|
||||
{
|
||||
/* find magic word in read buf */
|
||||
for (i = 0; i < sizeof(read_buf) - sizeof(read_magic_word) + 1; i++)
|
||||
{
|
||||
read_magic_word = read_buf[0 + i] + (read_buf[1 + i] << 8) + (read_buf[2 + i] << 16) + (read_buf[3 + i] << 24);
|
||||
if (read_magic_word == ((FAL_PART_MAGIC_WORD_H << 16) + FAL_PART_MAGIC_WORD_L))
|
||||
{
|
||||
part_table_find_ok = 1;
|
||||
part_table_offset += i;
|
||||
log_d("Find the partition table on '%s' offset @0x%08lx.", FAL_PART_TABLE_FLASH_DEV_NAME,
|
||||
part_table_offset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* read failed */
|
||||
break;
|
||||
}
|
||||
|
||||
if (part_table_find_ok)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* calculate next read buf position */
|
||||
if (part_table_offset >= (long)sizeof(read_buf))
|
||||
{
|
||||
part_table_offset -= sizeof(read_buf);
|
||||
part_table_offset += (sizeof(read_magic_word) - 1);
|
||||
}
|
||||
else if (part_table_offset != 0)
|
||||
{
|
||||
part_table_offset = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* find failed */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* load partition table */
|
||||
while (part_table_find_ok)
|
||||
{
|
||||
memset(new_part, 0x00, table_num);
|
||||
if (flash_dev->ops.read(part_table_offset - table_item_size * (table_num), (uint8_t *) new_part,
|
||||
table_item_size) < 0)
|
||||
{
|
||||
log_e("Initialize failed! Flash device (%s) read error!", flash_dev->name);
|
||||
table_num = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (new_part->magic_word != ((FAL_PART_MAGIC_WORD_H << 16) + FAL_PART_MAGIC_WORD_L))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
partition_table = (fal_partition_t) FAL_REALLOC(partition_table, table_item_size * (table_num + 1));
|
||||
if (partition_table == NULL)
|
||||
{
|
||||
log_e("Initialize failed! No memory for partition table");
|
||||
table_num = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(partition_table + table_num, new_part, table_item_size);
|
||||
|
||||
table_num++;
|
||||
};
|
||||
|
||||
if (table_num == 0)
|
||||
{
|
||||
log_e("Partition table NOT found on flash: %s (len: %d) from offset: 0x%08x.", FAL_PART_TABLE_FLASH_DEV_NAME,
|
||||
FAL_DEV_NAME_MAX, FAL_PART_TABLE_END_OFFSET);
|
||||
goto _exit;
|
||||
}
|
||||
else
|
||||
{
|
||||
partition_table_len = table_num;
|
||||
}
|
||||
#endif /* FAL_PART_HAS_TABLE_CFG */
|
||||
|
||||
/* check the partition table device exists */
|
||||
if (check_and_update_part_cache(partition_table, partition_table_len) != 0)
|
||||
{
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
init_ok = 1;
|
||||
|
||||
_exit:
|
||||
|
||||
#if FAL_DEBUG
|
||||
fal_show_part_table();
|
||||
#endif
|
||||
|
||||
#ifndef FAL_PART_HAS_TABLE_CFG
|
||||
if (new_part)
|
||||
{
|
||||
FAL_FREE(new_part);
|
||||
}
|
||||
#endif /* !FAL_PART_HAS_TABLE_CFG */
|
||||
|
||||
return partition_table_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* find the partition by name
|
||||
*
|
||||
* @param name partition name
|
||||
*
|
||||
* @return != NULL: partition
|
||||
* NULL: not found
|
||||
*/
|
||||
const struct fal_partition *fal_partition_find(const char *name)
|
||||
{
|
||||
assert(init_ok);
|
||||
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < partition_table_len; i++)
|
||||
{
|
||||
if (!strcmp(name, partition_table[i].name))
|
||||
{
|
||||
return &partition_table[i];
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
*
|
||||
* @param len return the partition table length
|
||||
*
|
||||
* @return partition table
|
||||
*/
|
||||
const struct fal_partition *fal_get_partition_table(size_t *len)
|
||||
{
|
||||
assert(init_ok);
|
||||
assert(len);
|
||||
|
||||
*len = partition_table_len;
|
||||
|
||||
return partition_table;
|
||||
}
|
||||
|
||||
/**
|
||||
* set partition table temporarily
|
||||
* This setting will modify the partition table temporarily, the setting will be lost after restart.
|
||||
*
|
||||
* @param table partition table
|
||||
* @param len partition table length
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* read data from partition
|
||||
*
|
||||
* @param part partition
|
||||
* @param addr relative address for partition
|
||||
* @param buf read buffer
|
||||
* @param size read size
|
||||
*
|
||||
* @return >= 0: successful read data size
|
||||
* -1: error
|
||||
*/
|
||||
int fal_partition_read(const struct fal_partition *part, uint32_t addr, uint8_t *buf, size_t size)
|
||||
{
|
||||
int ret = 0;
|
||||
const struct fal_flash_dev *flash_dev = NULL;
|
||||
|
||||
assert(part);
|
||||
assert(buf);
|
||||
|
||||
if (addr + size > part->len)
|
||||
{
|
||||
log_e("Partition read error! Partition address out of bound.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
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);
|
||||
return -1;
|
||||
}
|
||||
//printf("flash read %x,%x:\r\n",part->offset + addr,size);
|
||||
ret = flash_dev->ops.read(part->offset + addr, buf, size);
|
||||
// for(uint8_t i = 0; i < size; i++)
|
||||
// {
|
||||
// printf(" %x",buf[i]);
|
||||
// }
|
||||
// printf("\r\n");
|
||||
if (ret < 0)
|
||||
{
|
||||
log_e("Partition read error! Flash device(%s) read error!", part->flash_name);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* write data to partition
|
||||
*
|
||||
* @param part partition
|
||||
* @param addr relative address for partition
|
||||
* @param buf write buffer
|
||||
* @param size write size
|
||||
*
|
||||
* @return >= 0: successful write data size
|
||||
* -1: error
|
||||
*/
|
||||
int fal_partition_write(const struct fal_partition *part, uint32_t addr, uint8_t *buf, size_t size)
|
||||
{
|
||||
int ret = 0;
|
||||
const struct fal_flash_dev *flash_dev = NULL;
|
||||
|
||||
assert(part);
|
||||
assert(buf);
|
||||
|
||||
if (addr + size > part->len)
|
||||
{
|
||||
log_e("Partition write error! Partition address out of bound.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
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);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = flash_dev->ops.write(part->offset + addr, buf, size);
|
||||
if (ret < 0)
|
||||
{
|
||||
log_e("Partition write error! Flash device(%s) write error!", part->flash_name);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* erase partition data
|
||||
*
|
||||
* @param part partition
|
||||
* @param addr relative address for partition
|
||||
* @param size erase size
|
||||
*
|
||||
* @return >= 0: successful erased data size
|
||||
* -1: error
|
||||
*/
|
||||
int fal_partition_erase(const struct fal_partition *part, uint32_t addr, size_t size)
|
||||
{
|
||||
int ret = 0;
|
||||
const struct fal_flash_dev *flash_dev = NULL;
|
||||
|
||||
assert(part);
|
||||
|
||||
if (addr + size > part->len)
|
||||
{
|
||||
log_e("Partition erase error! Partition address out of bound.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
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);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = flash_dev->ops.erase(part->offset + addr, size);
|
||||
if (ret < 0)
|
||||
{
|
||||
log_e("Partition erase error! Flash device(%s) erase error!", part->flash_name);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* erase partition all data
|
||||
*
|
||||
* @param part partition
|
||||
*
|
||||
* @return >= 0: successful erased data size
|
||||
* -1: error
|
||||
*/
|
||||
int fal_partition_erase_all(const struct fal_partition *part)
|
||||
{
|
||||
return fal_partition_erase(part, 0, part->len);
|
||||
}
|
934
SW/components/modules/FlashDB/port/fal/src/fal_rtt.c
Normal file
934
SW/components/modules/FlashDB/port/fal/src/fal_rtt.c
Normal file
@ -0,0 +1,934 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2018, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2018-06-23 armink the first version
|
||||
* 2019-08-22 MurphyZhao adapt to none rt-thread case
|
||||
*/
|
||||
|
||||
#include <fal.h>
|
||||
|
||||
#ifdef RT_VER_NUM
|
||||
#include <rtthread.h>
|
||||
#include <rtdevice.h>
|
||||
#include <string.h>
|
||||
|
||||
/* ========================== block device ======================== */
|
||||
struct fal_blk_device
|
||||
{
|
||||
struct rt_device parent;
|
||||
struct rt_device_blk_geometry geometry;
|
||||
const struct fal_partition *fal_part;
|
||||
};
|
||||
|
||||
/* RT-Thread device interface */
|
||||
#if RTTHREAD_VERSION >= 30000
|
||||
static rt_err_t blk_dev_control(rt_device_t dev, int cmd, void *args)
|
||||
#else
|
||||
static rt_err_t blk_dev_control(rt_device_t dev, rt_uint8_t cmd, void *args)
|
||||
#endif
|
||||
{
|
||||
struct fal_blk_device *part = (struct fal_blk_device*) dev;
|
||||
|
||||
assert(part != RT_NULL);
|
||||
|
||||
if (cmd == RT_DEVICE_CTRL_BLK_GETGEOME)
|
||||
{
|
||||
struct rt_device_blk_geometry *geometry;
|
||||
|
||||
geometry = (struct rt_device_blk_geometry *) args;
|
||||
if (geometry == RT_NULL)
|
||||
{
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
memcpy(geometry, &part->geometry, sizeof(struct rt_device_blk_geometry));
|
||||
}
|
||||
else if (cmd == RT_DEVICE_CTRL_BLK_ERASE)
|
||||
{
|
||||
rt_uint32_t *addrs = (rt_uint32_t *) args, start_addr = addrs[0], end_addr = addrs[1], phy_start_addr;
|
||||
rt_size_t phy_size;
|
||||
|
||||
if (addrs == RT_NULL || start_addr > end_addr)
|
||||
{
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
if (end_addr == start_addr)
|
||||
{
|
||||
end_addr++;
|
||||
}
|
||||
|
||||
phy_start_addr = start_addr * part->geometry.bytes_per_sector;
|
||||
phy_size = (end_addr - start_addr) * part->geometry.bytes_per_sector;
|
||||
|
||||
if (fal_partition_erase(part->fal_part, phy_start_addr, phy_size) < 0)
|
||||
{
|
||||
return -RT_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static rt_size_t blk_dev_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size)
|
||||
{
|
||||
int ret = 0;
|
||||
struct fal_blk_device *part = (struct fal_blk_device*) dev;
|
||||
|
||||
assert(part != RT_NULL);
|
||||
|
||||
ret = fal_partition_read(part->fal_part, pos * part->geometry.block_size, buffer, size * part->geometry.block_size);
|
||||
|
||||
if (ret != (int)(size * part->geometry.block_size))
|
||||
{
|
||||
ret = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = size;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static rt_size_t blk_dev_write(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size)
|
||||
{
|
||||
int ret = 0;
|
||||
struct fal_blk_device *part;
|
||||
rt_off_t phy_pos;
|
||||
rt_size_t phy_size;
|
||||
|
||||
part = (struct fal_blk_device*) dev;
|
||||
assert(part != RT_NULL);
|
||||
|
||||
/* change the block device's logic address to physical address */
|
||||
phy_pos = pos * part->geometry.bytes_per_sector;
|
||||
phy_size = size * part->geometry.bytes_per_sector;
|
||||
|
||||
ret = fal_partition_erase(part->fal_part, phy_pos, phy_size);
|
||||
|
||||
if (ret == (int) phy_size)
|
||||
{
|
||||
ret = fal_partition_write(part->fal_part, phy_pos, buffer, phy_size);
|
||||
}
|
||||
|
||||
if (ret != (int) phy_size)
|
||||
{
|
||||
ret = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = size;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef RT_USING_DEVICE_OPS
|
||||
const static struct rt_device_ops blk_dev_ops =
|
||||
{
|
||||
RT_NULL,
|
||||
RT_NULL,
|
||||
RT_NULL,
|
||||
blk_dev_read,
|
||||
blk_dev_write,
|
||||
blk_dev_control
|
||||
};
|
||||
#endif
|
||||
|
||||
/**
|
||||
* create RT-Thread block device by specified partition
|
||||
*
|
||||
* @param parition_name partition name
|
||||
*
|
||||
* @return != NULL: created block device
|
||||
* NULL: created failed
|
||||
*/
|
||||
struct rt_device *fal_blk_device_create(const char *parition_name)
|
||||
{
|
||||
struct fal_blk_device *blk_dev;
|
||||
const struct fal_partition *fal_part = fal_partition_find(parition_name);
|
||||
const struct fal_flash_dev *fal_flash = NULL;
|
||||
|
||||
if (!fal_part)
|
||||
{
|
||||
log_e("Error: the partition name (%s) is not found.", parition_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((fal_flash = fal_flash_device_find(fal_part->flash_name)) == NULL)
|
||||
{
|
||||
log_e("Error: the flash device name (%s) is not found.", fal_part->flash_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
blk_dev = (struct fal_blk_device*) rt_malloc(sizeof(struct fal_blk_device));
|
||||
if (blk_dev)
|
||||
{
|
||||
blk_dev->fal_part = fal_part;
|
||||
blk_dev->geometry.bytes_per_sector = fal_flash->blk_size;
|
||||
blk_dev->geometry.block_size = fal_flash->blk_size;
|
||||
blk_dev->geometry.sector_count = fal_part->len / fal_flash->blk_size;
|
||||
|
||||
/* register device */
|
||||
blk_dev->parent.type = RT_Device_Class_Block;
|
||||
|
||||
#ifdef RT_USING_DEVICE_OPS
|
||||
blk_dev->parent.ops = &blk_dev_ops;
|
||||
#else
|
||||
blk_dev->parent.init = NULL;
|
||||
blk_dev->parent.open = NULL;
|
||||
blk_dev->parent.close = NULL;
|
||||
blk_dev->parent.read = blk_dev_read;
|
||||
blk_dev->parent.write = blk_dev_write;
|
||||
blk_dev->parent.control = blk_dev_control;
|
||||
#endif
|
||||
|
||||
/* no private */
|
||||
blk_dev->parent.user_data = RT_NULL;
|
||||
|
||||
log_i("The FAL block device (%s) created successfully", fal_part->name);
|
||||
rt_device_register(RT_DEVICE(blk_dev), fal_part->name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STANDALONE);
|
||||
}
|
||||
else
|
||||
{
|
||||
log_e("Error: no memory for create FAL block device");
|
||||
}
|
||||
|
||||
return RT_DEVICE(blk_dev);
|
||||
}
|
||||
|
||||
/* ========================== MTD nor device ======================== */
|
||||
#if defined(RT_USING_MTD_NOR)
|
||||
|
||||
struct fal_mtd_nor_device
|
||||
{
|
||||
struct rt_mtd_nor_device parent;
|
||||
const struct fal_partition *fal_part;
|
||||
};
|
||||
|
||||
static rt_size_t mtd_nor_dev_read(struct rt_mtd_nor_device* device, rt_off_t offset, rt_uint8_t* data, rt_uint32_t length)
|
||||
{
|
||||
int ret = 0;
|
||||
struct fal_mtd_nor_device *part = (struct fal_mtd_nor_device*) device;
|
||||
|
||||
assert(part != RT_NULL);
|
||||
|
||||
ret = fal_partition_read(part->fal_part, offset, data, length);
|
||||
|
||||
if (ret != (int)length)
|
||||
{
|
||||
ret = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = length;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static rt_size_t mtd_nor_dev_write(struct rt_mtd_nor_device* device, rt_off_t offset, const rt_uint8_t* data, rt_uint32_t length)
|
||||
{
|
||||
int ret = 0;
|
||||
struct fal_mtd_nor_device *part;
|
||||
|
||||
part = (struct fal_mtd_nor_device*) device;
|
||||
assert(part != RT_NULL);
|
||||
|
||||
ret = fal_partition_write(part->fal_part, offset, data, length);
|
||||
|
||||
if (ret != (int) length)
|
||||
{
|
||||
ret = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = length;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static rt_err_t mtd_nor_dev_erase(struct rt_mtd_nor_device* device, rt_off_t offset, rt_uint32_t length)
|
||||
{
|
||||
int ret = 0;
|
||||
struct fal_mtd_nor_device *part;
|
||||
|
||||
part = (struct fal_mtd_nor_device*) device;
|
||||
assert(part != RT_NULL);
|
||||
|
||||
ret = fal_partition_erase(part->fal_part, offset, length);
|
||||
|
||||
if (ret != length)
|
||||
{
|
||||
return -RT_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
return RT_EOK;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct rt_mtd_nor_driver_ops _ops =
|
||||
{
|
||||
RT_NULL,
|
||||
mtd_nor_dev_read,
|
||||
mtd_nor_dev_write,
|
||||
mtd_nor_dev_erase,
|
||||
};
|
||||
|
||||
/**
|
||||
* create RT-Thread MTD NOR device by specified partition
|
||||
*
|
||||
* @param parition_name partition name
|
||||
*
|
||||
* @return != NULL: created MTD NOR device
|
||||
* NULL: created failed
|
||||
*/
|
||||
struct rt_device *fal_mtd_nor_device_create(const char *parition_name)
|
||||
{
|
||||
struct fal_mtd_nor_device *mtd_nor_dev;
|
||||
const struct fal_partition *fal_part = fal_partition_find(parition_name);
|
||||
const struct fal_flash_dev *fal_flash = NULL;
|
||||
|
||||
if (!fal_part)
|
||||
{
|
||||
log_e("Error: the partition name (%s) is not found.", parition_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((fal_flash = fal_flash_device_find(fal_part->flash_name)) == NULL)
|
||||
{
|
||||
log_e("Error: the flash device name (%s) is not found.", fal_part->flash_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mtd_nor_dev = (struct fal_mtd_nor_device*) rt_malloc(sizeof(struct fal_mtd_nor_device));
|
||||
if (mtd_nor_dev)
|
||||
{
|
||||
mtd_nor_dev->fal_part = fal_part;
|
||||
|
||||
mtd_nor_dev->parent.block_start = 0;
|
||||
mtd_nor_dev->parent.block_end = fal_part->len / fal_flash->blk_size;
|
||||
mtd_nor_dev->parent.block_size = fal_flash->blk_size;
|
||||
|
||||
/* set ops */
|
||||
mtd_nor_dev->parent.ops = &_ops;
|
||||
|
||||
log_i("The FAL MTD NOR device (%s) created successfully", fal_part->name);
|
||||
rt_mtd_nor_register_device(fal_part->name, &mtd_nor_dev->parent);
|
||||
}
|
||||
else
|
||||
{
|
||||
log_e("Error: no memory for create FAL MTD NOR device");
|
||||
}
|
||||
|
||||
return RT_DEVICE(&mtd_nor_dev->parent);
|
||||
}
|
||||
|
||||
#endif /* defined(RT_USING_MTD_NOR) */
|
||||
|
||||
|
||||
/* ========================== char device ======================== */
|
||||
struct fal_char_device
|
||||
{
|
||||
struct rt_device parent;
|
||||
const struct fal_partition *fal_part;
|
||||
};
|
||||
|
||||
/* RT-Thread device interface */
|
||||
static rt_size_t char_dev_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
|
||||
{
|
||||
int ret = 0;
|
||||
struct fal_char_device *part = (struct fal_char_device *) dev;
|
||||
|
||||
assert(part != RT_NULL);
|
||||
|
||||
if (pos + size > part->fal_part->len)
|
||||
size = part->fal_part->len - pos;
|
||||
|
||||
ret = fal_partition_read(part->fal_part, pos, buffer, size);
|
||||
|
||||
if (ret != (int)(size))
|
||||
ret = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static rt_size_t char_dev_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
|
||||
{
|
||||
int ret = 0;
|
||||
struct fal_char_device *part;
|
||||
|
||||
part = (struct fal_char_device *) dev;
|
||||
assert(part != RT_NULL);
|
||||
|
||||
if (pos == 0)
|
||||
{
|
||||
fal_partition_erase_all(part->fal_part);
|
||||
}
|
||||
else if (pos + size > part->fal_part->len)
|
||||
{
|
||||
size = part->fal_part->len - pos;
|
||||
}
|
||||
|
||||
ret = fal_partition_write(part->fal_part, pos, buffer, size);
|
||||
|
||||
if (ret != (int) size)
|
||||
ret = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef RT_USING_DEVICE_OPS
|
||||
const static struct rt_device_ops char_dev_ops =
|
||||
{
|
||||
RT_NULL,
|
||||
RT_NULL,
|
||||
RT_NULL,
|
||||
char_dev_read,
|
||||
char_dev_write,
|
||||
RT_NULL
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef RT_USING_POSIX
|
||||
#include <dfs_posix.h>
|
||||
|
||||
/* RT-Thread device filesystem interface */
|
||||
static int char_dev_fopen(struct dfs_fd *fd)
|
||||
{
|
||||
struct fal_char_device *part = (struct fal_char_device *) fd->data;
|
||||
|
||||
assert(part != RT_NULL);
|
||||
|
||||
switch (fd->flags & O_ACCMODE)
|
||||
{
|
||||
case O_RDONLY:
|
||||
break;
|
||||
case O_WRONLY:
|
||||
case O_RDWR:
|
||||
/* erase partition when device file open */
|
||||
fal_partition_erase_all(part->fal_part);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
fd->pos = 0;
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static int char_dev_fread(struct dfs_fd *fd, void *buf, size_t count)
|
||||
{
|
||||
int ret = 0;
|
||||
struct fal_char_device *part = (struct fal_char_device *) fd->data;
|
||||
|
||||
assert(part != RT_NULL);
|
||||
|
||||
if (fd->pos + count > part->fal_part->len)
|
||||
count = part->fal_part->len - fd->pos;
|
||||
|
||||
ret = fal_partition_read(part->fal_part, fd->pos, buf, count);
|
||||
|
||||
if (ret != (int)(count))
|
||||
return 0;
|
||||
|
||||
fd->pos += ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int char_dev_fwrite(struct dfs_fd *fd, const void *buf, size_t count)
|
||||
{
|
||||
int ret = 0;
|
||||
struct fal_char_device *part = (struct fal_char_device *) fd->data;
|
||||
|
||||
assert(part != RT_NULL);
|
||||
|
||||
if (fd->pos + count > part->fal_part->len)
|
||||
count = part->fal_part->len - fd->pos;
|
||||
|
||||
ret = fal_partition_write(part->fal_part, fd->pos, buf, count);
|
||||
|
||||
if (ret != (int) count)
|
||||
return 0;
|
||||
|
||||
fd->pos += ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct dfs_file_ops char_dev_fops =
|
||||
{
|
||||
char_dev_fopen,
|
||||
RT_NULL,
|
||||
RT_NULL,
|
||||
char_dev_fread,
|
||||
char_dev_fwrite,
|
||||
RT_NULL, /* flush */
|
||||
RT_NULL, /* lseek */
|
||||
RT_NULL, /* getdents */
|
||||
RT_NULL,
|
||||
};
|
||||
#endif /* defined(RT_USING_POSIX) */
|
||||
|
||||
/**
|
||||
* create RT-Thread char device by specified partition
|
||||
*
|
||||
* @param parition_name partition name
|
||||
*
|
||||
* @return != NULL: created char device
|
||||
* NULL: created failed
|
||||
*/
|
||||
struct rt_device *fal_char_device_create(const char *parition_name)
|
||||
{
|
||||
struct fal_char_device *char_dev;
|
||||
const struct fal_partition *fal_part = fal_partition_find(parition_name);
|
||||
|
||||
if (!fal_part)
|
||||
{
|
||||
log_e("Error: the partition name (%s) is not found.", parition_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((fal_flash_device_find(fal_part->flash_name)) == NULL)
|
||||
{
|
||||
log_e("Error: the flash device name (%s) is not found.", fal_part->flash_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char_dev = (struct fal_char_device *) rt_malloc(sizeof(struct fal_char_device));
|
||||
if (char_dev)
|
||||
{
|
||||
char_dev->fal_part = fal_part;
|
||||
|
||||
/* register device */
|
||||
char_dev->parent.type = RT_Device_Class_Char;
|
||||
|
||||
#ifdef RT_USING_DEVICE_OPS
|
||||
char_dev->parent.ops = &char_dev_ops;
|
||||
#else
|
||||
char_dev->parent.init = NULL;
|
||||
char_dev->parent.open = NULL;
|
||||
char_dev->parent.close = NULL;
|
||||
char_dev->parent.read = char_dev_read;
|
||||
char_dev->parent.write = char_dev_write;
|
||||
char_dev->parent.control = NULL;
|
||||
/* no private */
|
||||
char_dev->parent.user_data = NULL;
|
||||
#endif
|
||||
|
||||
rt_device_register(RT_DEVICE(char_dev), fal_part->name, RT_DEVICE_FLAG_RDWR);
|
||||
log_i("The FAL char device (%s) created successfully", fal_part->name);
|
||||
|
||||
#ifdef RT_USING_POSIX
|
||||
/* set fops */
|
||||
char_dev->parent.fops = &char_dev_fops;
|
||||
#endif
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
log_e("Error: no memory for create FAL char device");
|
||||
}
|
||||
|
||||
return RT_DEVICE(char_dev);
|
||||
}
|
||||
|
||||
#if defined(RT_USING_FINSH) && defined(FINSH_USING_MSH)
|
||||
|
||||
#include <finsh.h>
|
||||
extern int fal_init_check(void);
|
||||
|
||||
static void fal(uint8_t argc, char **argv) {
|
||||
|
||||
#define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ')
|
||||
#define HEXDUMP_WIDTH 16
|
||||
#define CMD_PROBE_INDEX 0
|
||||
#define CMD_READ_INDEX 1
|
||||
#define CMD_WRITE_INDEX 2
|
||||
#define CMD_ERASE_INDEX 3
|
||||
#define CMD_BENCH_INDEX 4
|
||||
|
||||
int result;
|
||||
static const struct fal_flash_dev *flash_dev = NULL;
|
||||
static const struct fal_partition *part_dev = NULL;
|
||||
size_t i = 0, j = 0;
|
||||
|
||||
const char* help_info[] =
|
||||
{
|
||||
[CMD_PROBE_INDEX] = "fal probe [dev_name|part_name] - probe flash device or partition by given name",
|
||||
[CMD_READ_INDEX] = "fal read addr size - read 'size' bytes starting at 'addr'",
|
||||
[CMD_WRITE_INDEX] = "fal write addr data1 ... dataN - write some bytes 'data' starting at 'addr'",
|
||||
[CMD_ERASE_INDEX] = "fal erase addr size - erase 'size' bytes starting at 'addr'",
|
||||
[CMD_BENCH_INDEX] = "fal bench <blk_size> - benchmark test with per block size",
|
||||
};
|
||||
|
||||
if (fal_init_check() != 1)
|
||||
{
|
||||
rt_kprintf("\n[Warning] FAL is not initialized or failed to initialize!\n\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (argc < 2)
|
||||
{
|
||||
rt_kprintf("Usage:\n");
|
||||
for (i = 0; i < sizeof(help_info) / sizeof(char*); i++)
|
||||
{
|
||||
rt_kprintf("%s\n", help_info[i]);
|
||||
}
|
||||
rt_kprintf("\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *operator = argv[1];
|
||||
uint32_t addr, size;
|
||||
|
||||
if (!strcmp(operator, "probe"))
|
||||
{
|
||||
if (argc >= 3)
|
||||
{
|
||||
char *dev_name = argv[2];
|
||||
if ((flash_dev = fal_flash_device_find(dev_name)) != NULL)
|
||||
{
|
||||
part_dev = NULL;
|
||||
}
|
||||
else if ((part_dev = fal_partition_find(dev_name)) != NULL)
|
||||
{
|
||||
flash_dev = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("Device %s NOT found. Probe failed.\n", dev_name);
|
||||
flash_dev = NULL;
|
||||
part_dev = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (flash_dev)
|
||||
{
|
||||
rt_kprintf("Probed a flash device | %s | addr: %ld | len: %d |.\n", flash_dev->name,
|
||||
flash_dev->addr, flash_dev->len);
|
||||
}
|
||||
else if (part_dev)
|
||||
{
|
||||
rt_kprintf("Probed a flash partition | %s | flash_dev: %s | offset: %ld | len: %d |.\n",
|
||||
part_dev->name, part_dev->flash_name, part_dev->offset, part_dev->len);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("No flash device or partition was probed.\n");
|
||||
rt_kprintf("Usage: %s.\n", help_info[CMD_PROBE_INDEX]);
|
||||
fal_show_part_table();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!flash_dev && !part_dev)
|
||||
{
|
||||
rt_kprintf("No flash device or partition was probed. Please run 'fal probe'.\n");
|
||||
return;
|
||||
}
|
||||
if (!rt_strcmp(operator, "read"))
|
||||
{
|
||||
if (argc < 4)
|
||||
{
|
||||
rt_kprintf("Usage: %s.\n", help_info[CMD_READ_INDEX]);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
addr = strtol(argv[2], NULL, 0);
|
||||
size = strtol(argv[3], NULL, 0);
|
||||
uint8_t *data = rt_malloc(size);
|
||||
if (data)
|
||||
{
|
||||
if (flash_dev)
|
||||
{
|
||||
result = flash_dev->ops.read(addr, data, size);
|
||||
}
|
||||
else if (part_dev)
|
||||
{
|
||||
result = fal_partition_read(part_dev, addr, data, size);
|
||||
}
|
||||
if (result >= 0)
|
||||
{
|
||||
rt_kprintf("Read data success. Start from 0x%08X, size is %ld. The data is:\n", addr,
|
||||
size);
|
||||
rt_kprintf("Offset (h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n");
|
||||
for (i = 0; i < size; i += HEXDUMP_WIDTH)
|
||||
{
|
||||
rt_kprintf("[%08X] ", addr + i);
|
||||
/* dump hex */
|
||||
for (j = 0; j < HEXDUMP_WIDTH; j++)
|
||||
{
|
||||
if (i + j < size)
|
||||
{
|
||||
rt_kprintf("%02X ", data[i + j]);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf(" ");
|
||||
}
|
||||
}
|
||||
/* dump char for hex */
|
||||
for (j = 0; j < HEXDUMP_WIDTH; j++)
|
||||
{
|
||||
if (i + j < size)
|
||||
{
|
||||
rt_kprintf("%c", __is_print(data[i + j]) ? data[i + j] : '.');
|
||||
}
|
||||
}
|
||||
rt_kprintf("\n");
|
||||
}
|
||||
rt_kprintf("\n");
|
||||
}
|
||||
rt_free(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("Low memory!\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!strcmp(operator, "write"))
|
||||
{
|
||||
if (argc < 4)
|
||||
{
|
||||
rt_kprintf("Usage: %s.\n", help_info[CMD_WRITE_INDEX]);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
addr = strtol(argv[2], NULL, 0);
|
||||
size = argc - 3;
|
||||
uint8_t *data = rt_malloc(size);
|
||||
if (data)
|
||||
{
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
data[i] = strtol(argv[3 + i], NULL, 0);
|
||||
}
|
||||
if (flash_dev)
|
||||
{
|
||||
result = flash_dev->ops.write(addr, data, size);
|
||||
}
|
||||
else if (part_dev)
|
||||
{
|
||||
result = fal_partition_write(part_dev, addr, data, size);
|
||||
}
|
||||
if (result >= 0)
|
||||
{
|
||||
rt_kprintf("Write data success. Start from 0x%08X, size is %ld.\n", addr, size);
|
||||
rt_kprintf("Write data: ");
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
rt_kprintf("%d ", data[i]);
|
||||
}
|
||||
rt_kprintf(".\n");
|
||||
}
|
||||
rt_free(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("Low memory!\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!rt_strcmp(operator, "erase"))
|
||||
{
|
||||
if (argc < 4)
|
||||
{
|
||||
rt_kprintf("Usage: %s.\n", help_info[CMD_ERASE_INDEX]);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
addr = strtol(argv[2], NULL, 0);
|
||||
size = strtol(argv[3], NULL, 0);
|
||||
if (flash_dev)
|
||||
{
|
||||
result = flash_dev->ops.erase(addr, size);
|
||||
}
|
||||
else if (part_dev)
|
||||
{
|
||||
result = fal_partition_erase(part_dev, addr, size);
|
||||
}
|
||||
if (result >= 0)
|
||||
{
|
||||
rt_kprintf("Erase data success. Start from 0x%08X, size is %ld.\n", addr, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!strcmp(operator, "bench"))
|
||||
{
|
||||
if (argc < 3)
|
||||
{
|
||||
rt_kprintf("Usage: %s.\n", help_info[CMD_BENCH_INDEX]);
|
||||
return;
|
||||
}
|
||||
else if ((argc > 3 && strcmp(argv[3], "yes")) || argc < 4)
|
||||
{
|
||||
rt_kprintf("DANGER: It will erase full chip or partition! Please run 'fal bench %d yes'.\n", strtol(argv[2], NULL, 0));
|
||||
return;
|
||||
}
|
||||
/* 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_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)
|
||||
{
|
||||
for (i = 0; i < write_size; i ++) {
|
||||
write_data[i] = i & 0xFF;
|
||||
}
|
||||
if (flash_dev)
|
||||
{
|
||||
size = flash_dev->len;
|
||||
}
|
||||
else if (part_dev)
|
||||
{
|
||||
size = part_dev->len;
|
||||
}
|
||||
/* benchmark testing */
|
||||
rt_kprintf("Erasing %ld bytes data, waiting...\n", size);
|
||||
start_time = rt_tick_get();
|
||||
if (flash_dev)
|
||||
{
|
||||
result = flash_dev->ops.erase(0, size);
|
||||
}
|
||||
else if (part_dev)
|
||||
{
|
||||
result = fal_partition_erase(part_dev, 0, size);
|
||||
}
|
||||
if (result >= 0)
|
||||
{
|
||||
time_cast = rt_tick_get() - start_time;
|
||||
rt_kprintf("Erase benchmark success, total time: %d.%03dS.\n", time_cast / RT_TICK_PER_SECOND,
|
||||
time_cast % RT_TICK_PER_SECOND / ((RT_TICK_PER_SECOND * 1 + 999) / 1000));
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("Erase benchmark has an error. Error code: %d.\n", result);
|
||||
}
|
||||
/* write test */
|
||||
rt_kprintf("Writing %ld bytes data, waiting...\n", size);
|
||||
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, cur_op_size);
|
||||
}
|
||||
else if (part_dev)
|
||||
{
|
||||
result = fal_partition_write(part_dev, i, write_data, cur_op_size);
|
||||
}
|
||||
if (result < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result >= 0)
|
||||
{
|
||||
time_cast = rt_tick_get() - start_time;
|
||||
rt_kprintf("Write benchmark success, total time: %d.%03dS.\n", time_cast / RT_TICK_PER_SECOND,
|
||||
time_cast % RT_TICK_PER_SECOND / ((RT_TICK_PER_SECOND * 1 + 999) / 1000));
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("Write benchmark has an error. Error code: %d.\n", result);
|
||||
}
|
||||
/* read test */
|
||||
rt_kprintf("Reading %ld bytes data, waiting...\n", size);
|
||||
start_time = rt_tick_get();
|
||||
for (i = 0; i < size; i += read_size)
|
||||
{
|
||||
if (i + read_size <= size)
|
||||
{
|
||||
cur_op_size = read_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
cur_op_size = size - i;
|
||||
}
|
||||
if (flash_dev)
|
||||
{
|
||||
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_op_size);
|
||||
}
|
||||
/* data check */
|
||||
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");
|
||||
}
|
||||
/* has an error */
|
||||
if (result < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result >= 0)
|
||||
{
|
||||
time_cast = rt_tick_get() - start_time;
|
||||
rt_kprintf("Read benchmark success, total time: %d.%03dS.\n", time_cast / RT_TICK_PER_SECOND,
|
||||
time_cast % RT_TICK_PER_SECOND / ((RT_TICK_PER_SECOND * 1 + 999) / 1000));
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("Read benchmark has an error. Error code: %d.\n", result);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("Low memory!\n");
|
||||
}
|
||||
rt_free(write_data);
|
||||
rt_free(read_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("Usage:\n");
|
||||
for (i = 0; i < sizeof(help_info) / sizeof(char*); i++)
|
||||
{
|
||||
rt_kprintf("%s\n", help_info[i]);
|
||||
}
|
||||
rt_kprintf("\n");
|
||||
return;
|
||||
}
|
||||
if (result < 0) {
|
||||
rt_kprintf("This operate has an error. Error code: %d.\n", result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MSH_CMD_EXPORT(fal, FAL (Flash Abstraction Layer) operate.);
|
||||
|
||||
#endif /* defined(RT_USING_FINSH) && defined(FINSH_USING_MSH) */
|
||||
#endif /* RT_VER_NUM */
|
Reference in New Issue
Block a user