diff --git a/README.md b/README.md index 73499fb..9d56606 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ - 支持用户自定义输出方式(例如:终端、文件、数据库、串口、485、Flash...); - 日志内容可包含级别、时间戳、线程信息、进程信息等; - 日志输出被设计为线程安全的方式,并支持 **异步输出** 及 **缓冲输出** 模式; -- 支持多种操作系统([RT-Thread](http://www.rt-thread.org/)、UCOS、Linux、Windows...),也支持裸机平台; +- 支持多种操作系统([RT-Thread](http://www.rt-thread.org/)、UCOS、Linux、Windows、Nuttx...),也支持裸机平台; - 日志支持 **RAW格式** ,支持 **hexdump** ; - 支持按 **标签** 、 **级别** 、 **关键词** 进行动态过滤; - 各级别日志支持不同颜色显示; diff --git a/demo/os/nuttx-spiflash/README.md b/demo/os/nuttx-spiflash/README.md new file mode 100644 index 0000000..cb57dd5 --- /dev/null +++ b/demo/os/nuttx-spiflash/README.md @@ -0,0 +1,89 @@ +# Nuttx SPI Flash demo + +--- + +## 1 + +ͨ `apps/examples/easylogger/elog_main.c` `test_elog()` ־ĬϿ첽ģʽûԽն־á +`test_env()` ʾĶȡ޸ĹܣÿϵͳҳʼEasyFlashɹø÷ + +- ƽ̨NuttX-12.0.0NuttX-10.X.XҪ޸Makefileinclude÷ +- ӲSTM32F103NuttxappsӲƽ̨ +- FlashW25QXXNuttxֵ֧SPI Flash`nuttx/drivers/mtd`ļ + +## 2ʹ÷ + +ɺnsh̨`elog`سű۲ + +![ElogNuttxSpiFlashDemo](https://raw.githubusercontent.com/armink/EasyLogger/master/docs/zh/images/ElogNuttxSpiFlashDemo.png) + +## 3ļУ˵ + +|ԴļУ | | +|:------------------------------ |:----- | +|apps |nuttx-appsӦòĿ¼| +|apps/examples/ |nuttxӦòʾĿ¼| +|apps/examples/easylogger/elog_main.c |EasyloggerEasyflashExample Demo| +|apps/system/ |nuttxӦòϵͳĿ¼| +|apps/system/easylogger/inc/elog_cfg.h |Easyloggerļ| +|apps/system/easylogger/port/elog_port.c |Easyloggerֲοļ| +|apps/system/easyflash/inc/ef_cfg.h |Easyflashļ| +|apps/system/easyflash/port/ef_port.c |Easyflashֲοļ| + +## 4˵ + +### 4.1ֲ˵ + +1ѸĿ¼`EasyLogger/`Щļָλã +``` +cd EasyLogger/ +cp easylogger/inc/elog.h demo/os/nuttx-spiflash/apps/system/easylogger/inc +cp easylogger/plugins/flash/elog_flash.* demo/os/nuttx-spiflash/apps/system/easylogger/plugins/flash/ +cp -R easylogger/src/ demo/os/nuttx-spiflash/apps/system/easylogger/ +``` + +2appsĿ¼ǵnuttxappsĿ¼¡ + +3nuttxĿ¼appsĿ¼ṹ档 +``` +make apps_distclean +make menuconfig +``` + +4`make menuconfig`ѡãEasylogger Demo Example +``` +CONFIG_EXAMPLES_EASYLOGGER=y +``` +Զ +``` +CONFIG_MTD_W25=y +``` + +5mtd ioctlָIDرEasyflashܿʡԣ +- `nuttx/include/nuttx/mtd/mtd.h`ļӣ +```C +#define MTDIOC_GETMTDDEV _MTDIOC(0x000c) +``` + +6¶w25mtd豸ڵ㣺رEasyflashܿʡԣ +- `nuttx/drivers/mtd/w25.c`ļ +- ӱ +```C +static FAR struct mtd_dev_s *mtd_w25; +``` +- ҵ`w25_ioctl``switch (cmd)`ӣ +```C + case MTDIOC_GETMTDDEV:{ + FAR struct mtd_dev_s **mtd = + (FAR struct mtd_dev_s *)((uintptr_t)arg); + DEBUGASSERT(*mtd != NULL); + *mtd = mtd_w25; + ret = OK; + }break; +``` +- ҵ`w25_initialize``ret = w25_readid(priv)`IDȷصӣ +```C +mtd_w25=&priv->mtd; +``` + +7`make -j4`صӺNSH̨`elog`ɲ鿴 diff --git a/demo/os/nuttx-spiflash/apps/examples/easylogger/Kconfig b/demo/os/nuttx-spiflash/apps/examples/easylogger/Kconfig new file mode 100644 index 0000000..e8cf6c6 --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/examples/easylogger/Kconfig @@ -0,0 +1,35 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config EXAMPLES_EASYLOGGER + tristate "Easylogger and EasyFlash Demo" + default n + select SYSTEM_EASYLOGGER + select SYSTEM_EASYLOGGER_FLASH + select SYSTEM_EASYFLASH + select CONFIG_MTD + select CONFIG_MTD_BYTE_WRITE + select CONFIG_MTD_W25 + ---help--- + Enable the Easylogger Demo + +if EXAMPLES_EASYLOGGER + +config EXAMPLES_EASYLOGGER_PROGNAME + string "Program name" + default "elog" + ---help--- + This is the name of the program that will be used when the NSH ELF + program is installed. + +config EXAMPLES_EASYLOGGER_PRIORITY + int "Easylogger task priority" + default 100 + +config EXAMPLES_EASYLOGGER_STACKSIZE + int "Easylogger stack size" + default DEFAULT_TASK_STACKSIZE + +endif diff --git a/demo/os/nuttx-spiflash/apps/examples/easylogger/Make.defs b/demo/os/nuttx-spiflash/apps/examples/easylogger/Make.defs new file mode 100644 index 0000000..abb3ae6 --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/examples/easylogger/Make.defs @@ -0,0 +1,23 @@ +############################################################################ +# apps/examples/easylogger/Make.defs +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_EXAMPLES_EASYLOGGER),) +CONFIGURED_APPS += $(APPDIR)/examples/easylogger +endif diff --git a/demo/os/nuttx-spiflash/apps/examples/easylogger/Makefile b/demo/os/nuttx-spiflash/apps/examples/easylogger/Makefile new file mode 100644 index 0000000..bb09c31 --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/examples/easylogger/Makefile @@ -0,0 +1,38 @@ +############################################################################ +# apps/examples/easylogger/Make.defs +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +# Hello, easylogger! built-in application info + +PROGNAME = $(CONFIG_EXAMPLES_EASYLOGGER_PROGNAME) +PRIORITY = $(CONFIG_EXAMPLES_EASYLOGGER_PRIORITY) +STACKSIZE = $(CONFIG_EXAMPLES_EASYLOGGER_STACKSIZE) +MODULE = $(CONFIG_EXAMPLES_EASYLOGGER) + +# Hello, easylogger! Example + +MAINSRC = elog_main.c + +CFLAGS += ${shell $(INCDIR) "$(CC)" $(APPDIR)/system/easylogger/inc} +CFLAGS += ${shell $(INCDIR) "$(CC)" $(APPDIR)/system/easylogger/plugins/flash} +CFLAGS += ${shell $(INCDIR) "$(CC)" $(APPDIR)/system/easyflash/inc} + +include $(APPDIR)/Application.mk diff --git a/demo/os/nuttx-spiflash/apps/examples/easylogger/elog_main.c b/demo/os/nuttx-spiflash/apps/examples/easylogger/elog_main.c new file mode 100644 index 0000000..99d9ca2 --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/examples/easylogger/elog_main.c @@ -0,0 +1,144 @@ +/* + * This file is part of the EasyLogger Library. + * + * Copyright (c) 2015-2017, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: nuttx demo. + * Created on: 2023-02-15 + */ + +#define LOG_TAG "elogdemo" + +#include +#include +#include +#include + +#include +#include + + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/** + * Env demo. + */ +static void test_env(void) { + uint32_t i_boot_times = NULL; + char *c_old_boot_times, c_new_boot_times[11] = {0}; + + /* get the boot count number from Env */ + c_old_boot_times = ef_get_env("boot_times"); +// assert_param(c_old_boot_times); + i_boot_times = atol(c_old_boot_times); + /* boot count +1 */ + i_boot_times ++; + printf("The system now boot %d times\n\r", i_boot_times); + /* interger to string */ + sprintf(c_new_boot_times,"%ld", i_boot_times); + /* set and store the boot count number to Env */ + ef_set_env("boot_times", c_new_boot_times); + ef_save_env(); +} + +/** + * EasyLogger demo + */ +static void test_elog(void) { + uint8_t buf[256]= {0}; + int i = 0; + + for (i = 0; i < sizeof(buf); i++) + { + buf[i] = i; + } + while(true) { + /* test log output for all level */ + log_a("Hello EasyLogger!"); + log_e("Hello EasyLogger!"); + log_w("Hello EasyLogger!"); + log_i("Hello EasyLogger!"); + log_d("Hello EasyLogger!"); + log_v("Hello EasyLogger!"); +// elog_raw("Hello EasyLogger!"); + elog_hexdump("test", 16, buf, sizeof(buf)); + break; + } +} + +static void easylogger_demo(void) { + /* close printf buffer */ + setbuf(stdout, NULL); + + /* initialize EasyFlash and EasyLogger */ + if ((easyflash_init() == EF_NO_ERR)&&(elog_init() == ELOG_NO_ERR)) { + /* set EasyLogger log format */ + elog_set_fmt(ELOG_LVL_ASSERT, ELOG_FMT_ALL); + elog_set_fmt(ELOG_LVL_ERROR, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME); + elog_set_fmt(ELOG_LVL_WARN, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME); + elog_set_fmt(ELOG_LVL_INFO, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME); + elog_set_fmt(ELOG_LVL_DEBUG, ELOG_FMT_ALL & ~ELOG_FMT_FUNC); + elog_set_fmt(ELOG_LVL_VERBOSE, ELOG_FMT_ALL & ~ELOG_FMT_FUNC); + #ifdef ELOG_COLOR_ENABLE + elog_set_text_color_enabled(true); + #endif + /* initialize EasyLogger Flash plugin */ + elog_flash_init(); + /* start EasyLogger */ + elog_start(); + + /* dynamic set enable or disable for output logs (true or false) */ + // elog_set_output_enabled(false); + /* dynamic set output logs's level (from ELOG_LVL_ASSERT to ELOG_LVL_VERBOSE) */ + // elog_set_filter_lvl(ELOG_LVL_WARN); + /* dynamic set output logs's filter for tag */ + // elog_set_filter_tag("main"); + /* dynamic set output logs's filter for keyword */ + // elog_set_filter_kw("Hello"); + /* dynamic set output logs's tag filter */ + // elog_set_filter_tag_lvl("main", ELOG_LVL_WARN); + + /* test logger output */ + test_env(); + test_elog(); + } + else{ + printf("easyflash_init or elog_init init fail\n"); + } + + return EXIT_SUCCESS; +} + + +/**************************************************************************** + * elog_demo + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + printf("Hello, EasyLogger!!\n"); + easylogger_demo(); + return 0; +} + diff --git a/demo/os/nuttx-spiflash/apps/system/easyflash/Kconfig b/demo/os/nuttx-spiflash/apps/system/easyflash/Kconfig new file mode 100644 index 0000000..780bab1 --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/system/easyflash/Kconfig @@ -0,0 +1,15 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +menuconfig SYSTEM_EASYFLASH + tristate "Easyflash" + default n + ---help--- + Enable support for the Easyflash + +if SYSTEM_EASYFLASH + + +endif # SYSTEM_EASYFLASH diff --git a/demo/os/nuttx-spiflash/apps/system/easyflash/Make.defs b/demo/os/nuttx-spiflash/apps/system/easyflash/Make.defs new file mode 100644 index 0000000..45806e3 --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/system/easyflash/Make.defs @@ -0,0 +1,23 @@ +############################################################################ +# apps/system/easyflash/Make.defs +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_SYSTEM_EASYFLASH),) +CONFIGURED_APPS += $(APPDIR)/system/easyflash +endif diff --git a/demo/os/nuttx-spiflash/apps/system/easyflash/Makefile b/demo/os/nuttx-spiflash/apps/system/easyflash/Makefile new file mode 100644 index 0000000..d50be04 --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/system/easyflash/Makefile @@ -0,0 +1,33 @@ +############################################################################ +# apps/system/easyflash/Makefile +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +# easyflash Application + +CSRCS = easyflash.c ef_utils.c ef_port.c +CSRCS += ef_env.c # ef_env_wl.c +# CSRCS += ef_iap.c ef_log.c + +CFLAGS += ${shell $(INCDIR) "$(CC)" $(APPDIR)/system/easyflash/inc} + +VPATH += :src port + +include $(APPDIR)/Application.mk diff --git a/demo/os/nuttx-spiflash/apps/system/easyflash/inc/easyflash.h b/demo/os/nuttx-spiflash/apps/system/easyflash/inc/easyflash.h new file mode 100644 index 0000000..2aab0b1 --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/system/easyflash/inc/easyflash.h @@ -0,0 +1,107 @@ +/* + * This file is part of the EasyFlash Library. + * + * Copyright (c) 2014-2019, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: It is an head file for this library. You can see all be called functions. + * Created on: 2014-09-10 + */ + + +#ifndef EASYFLASH_H_ +#define EASYFLASH_H_ + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* easyflash.c */ +EfErrCode easyflash_init(void); + +#ifdef EF_USING_ENV +/* only supported on ef_env.c */ +size_t ef_get_env_blob(const char *key, void *value_buf, size_t buf_len, size_t *saved_value_len); +bool ef_get_env_obj(const char *key, env_node_obj_t env); +size_t ef_read_env_value(env_node_obj_t env, uint8_t *value_buf, size_t buf_len); +EfErrCode ef_set_env_blob(const char *key, const void *value_buf, size_t buf_len); + +/* ef_env.c, ef_env_legacy_wl.c and ef_env_legacy.c */ +EfErrCode ef_load_env(void); +void ef_print_env(void); +char *ef_get_env(const char *key); +EfErrCode ef_set_env(const char *key, const char *value); +EfErrCode ef_del_env(const char *key); +EfErrCode ef_save_env(void); +EfErrCode ef_env_set_default(void); +size_t ef_get_env_write_bytes(void); +EfErrCode ef_set_and_save_env(const char *key, const char *value); +EfErrCode ef_del_and_save_env(const char *key); +#endif + +#ifdef EF_USING_IAP +/* ef_iap.c */ +EfErrCode ef_erase_bak_app(size_t app_size); +EfErrCode ef_erase_user_app(uint32_t user_app_addr, size_t user_app_size); +EfErrCode ef_erase_spec_user_app(uint32_t user_app_addr, size_t app_size, + EfErrCode (*app_erase)(uint32_t addr, size_t size)); +EfErrCode ef_erase_bl(uint32_t bl_addr, size_t bl_size); +EfErrCode ef_write_data_to_bak(uint8_t *data, size_t size, size_t *cur_size, + size_t total_size); +EfErrCode ef_copy_app_from_bak(uint32_t user_app_addr, size_t app_size); +EfErrCode ef_copy_spec_app_from_bak(uint32_t user_app_addr, size_t app_size, + EfErrCode (*app_write)(uint32_t addr, const uint32_t *buf, size_t size)); +EfErrCode ef_copy_bl_from_bak(uint32_t bl_addr, size_t bl_size); +uint32_t ef_get_bak_app_start_addr(void); +#endif + +#ifdef EF_USING_LOG +/* ef_log.c */ +EfErrCode ef_log_read(size_t index, uint32_t *log, size_t size); +EfErrCode ef_log_write(const uint32_t *log, size_t size); +EfErrCode ef_log_clean(void); +size_t ef_log_get_used_size(void); +#endif + +/* ef_utils.c */ +uint32_t ef_calc_crc32(uint32_t crc, const void *buf, size_t size); + +/* ef_port.c */ +EfErrCode ef_port_read(uint32_t addr, uint32_t *buf, size_t size); +EfErrCode ef_port_erase(uint32_t addr, size_t size); +EfErrCode ef_port_write(uint32_t addr, const uint32_t *buf, size_t size); +void ef_port_env_lock(void); +void ef_port_env_unlock(void); +void ef_log_debug(const char *file, const long line, const char *format, ...); +void ef_log_info(const char *format, ...); +void ef_print(const char *format, ...); + +#ifdef __cplusplus +} +#endif + +#endif /* EASYFLASH_H_ */ diff --git a/demo/os/nuttx-spiflash/apps/system/easyflash/inc/ef_cfg.h b/demo/os/nuttx-spiflash/apps/system/easyflash/inc/ef_cfg.h new file mode 100644 index 0000000..c257a50 --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/system/easyflash/inc/ef_cfg.h @@ -0,0 +1,79 @@ +/* + * This file is part of the EasyFlash Library. + * + * Copyright (c) 2015-2019, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: It is the configure head file for this library. + * Created on: 2015-07-14 + */ + +#ifndef EF_CFG_H_ +#define EF_CFG_H_ + +/* using ENV function, default is NG (Next Generation) mode start from V4.0 */ +#define EF_USING_ENV + +/* using IAP function */ +// #define EF_USING_IAP + +/* using save log function */ +// #define EF_USING_LOG + +/* the minimum size of flash erasure */ +#define EF_ERASE_MIN_SIZE 4096 + +/* the flash write granularity, unit: bit + * only support 1(nor flash)/ 8(stm32f4)/ 32(stm32f1)/ 64(stm32l4) */ +#define EF_WRITE_GRAN 1 + +/* + * + * This all Backup Area Flash storage index. All used flash area configure is under here. + * |----------------------------| Storage Size + * | Environment variables area | ENV area size @see ENV_AREA_SIZE + * |----------------------------| + * | Saved log area | Log area size @see LOG_AREA_SIZE + * |----------------------------| + * |(IAP)Downloaded application | IAP already downloaded application, unfixed size + * |----------------------------| + * + * @note all area sizes must be aligned with EF_ERASE_MIN_SIZE + * + * The EasyFlash add the NG (Next Generation) mode start from V4.0. All old mode before V4.0, called LEGACY mode. + * + * - NG (Next Generation) mode is default mode from V4.0. It's easy to settings, only defined the ENV_AREA_SIZE. + * - The LEGACY mode has been DEPRECATED. It is NOT RECOMMENDED to continue using. + * Beacuse it will use ram to buffer the ENV and spend more flash erase times. + * If you want use it please using the V3.X version. + */ + +/* backup area start address */ +#define EF_START_ADDR (0) /* from the SPI Flash position: 0KB*/ +/* ENV area size. It's at least one empty sector for GC. So it's definination must more then or equal 2 flash sector size. */ +#define ENV_AREA_SIZE (2 * EF_ERASE_MIN_SIZE) /* 8K */ +/* saved log area size */ +#define LOG_AREA_SIZE (10 * EF_ERASE_MIN_SIZE) /* 40K */ + +/* print debug information of flash */ +#define PRINT_DEBUG + +#endif /* EF_CFG_H_ */ diff --git a/demo/os/nuttx-spiflash/apps/system/easyflash/inc/ef_def.h b/demo/os/nuttx-spiflash/apps/system/easyflash/inc/ef_def.h new file mode 100644 index 0000000..0339c01 --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/system/easyflash/inc/ef_def.h @@ -0,0 +1,124 @@ +/* + * This file is part of the EasyFlash Library. + * + * Copyright (c) 2019-2020, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: It is the definitions head file for this library. + * Created on: 2019-11-20 + */ + +#ifndef EF_DEF_H_ +#define EF_DEF_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* EasyFlash software version number */ +#define EF_SW_VERSION "4.1.99" +#define EF_SW_VERSION_NUM 0x40199 + +/* + * ENV version number defined by user. + * Please change it when your firmware add a new ENV to default_env_set. + */ +#ifndef EF_ENV_VER_NUM +#define EF_ENV_VER_NUM 0 +#endif + +/* the ENV max name length must less then it */ +#ifndef EF_ENV_NAME_MAX +#define EF_ENV_NAME_MAX 32 +#endif + +/* EasyFlash debug print function. Must be implement by user. */ +#ifdef PRINT_DEBUG +#define EF_DEBUG(...) ef_log_debug(__FILE__, __LINE__, __VA_ARGS__) +#else +#define EF_DEBUG(...) +#endif +/* EasyFlash routine print function. Must be implement by user. */ +#define EF_INFO(...) ef_log_info(__VA_ARGS__) +/* EasyFlash assert for developer. */ +#define EF_ASSERT(EXPR) \ +if (!(EXPR)) \ +{ \ + EF_DEBUG("(%s) has assert failed at %s.\n", #EXPR, __FUNCTION__); \ + while (1); \ +} + +typedef struct _ef_env { + char *key; + void *value; + size_t value_len; +} ef_env, *ef_env_t; + +/* EasyFlash error code */ +typedef enum { + EF_NO_ERR, + EF_ERASE_ERR, + EF_READ_ERR, + EF_WRITE_ERR, + EF_ENV_NAME_ERR, + EF_ENV_NAME_EXIST, + EF_ENV_FULL, + EF_ENV_INIT_FAILED, +} EfErrCode; + +/* the flash sector current status */ +typedef enum { + EF_SECTOR_EMPTY, + EF_SECTOR_USING, + EF_SECTOR_FULL, +} EfSecrorStatus; + +enum env_status { + ENV_UNUSED, + ENV_PRE_WRITE, + ENV_WRITE, + ENV_PRE_DELETE, + ENV_DELETED, + ENV_ERR_HDR, + ENV_STATUS_NUM, +}; +typedef enum env_status env_status_t; + +struct env_node_obj { + env_status_t status; /**< ENV node status, @see node_status_t */ + bool crc_is_ok; /**< ENV node CRC32 check is OK */ + uint8_t name_len; /**< name length */ + uint32_t magic; /**< magic word(`K`, `V`, `4`, `0`) */ + uint32_t len; /**< ENV node total length (header + name + value), must align by EF_WRITE_GRAN */ + uint32_t value_len; /**< value length */ + char name[EF_ENV_NAME_MAX]; /**< name */ + struct { + uint32_t start; /**< ENV node start address */ + uint32_t value; /**< value start address */ + } addr; +}; +typedef struct env_node_obj *env_node_obj_t; + +#ifdef __cplusplus +} +#endif + +#endif /* EF_DEF_H_ */ diff --git a/demo/os/nuttx-spiflash/apps/system/easyflash/plugins/types/README.md b/demo/os/nuttx-spiflash/apps/system/easyflash/plugins/types/README.md new file mode 100644 index 0000000..9f70741 --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/system/easyflash/plugins/types/README.md @@ -0,0 +1,152 @@ +# EasyFlash Types plugin + +--- + +## 1 + +Ŀǰ EasyFlash Ὣַʽ洢 Flash Уģʽ£ڷַ͵ĻʹʱͱӶַת롣 Types Ϊ˷ûʹ EasyFlash ʱԸӼ򵥵ķʽȥ͵Ļ + +Ҫֵ֧ͰC **** **** Լ **ṹ** ڽṹͣ Types ڲ [struct2json](https://github.com/armink/struct2json) תĿҪ [struct2json](https://github.com/armink/struct2json) ⡣ + +## 2ʹ + +### 2.1 Դ뵼 + +֮ǰҪȷԼĿѰ EasyFlash Դ룬 "\easyflash\inc""\easyflash\port" "\easyflash\src" ļ뷽Բοֲĵ[](https://github.com/armink/EasyFlash/blob/master/docs/zh/port.md)ٽ Types Դ뵼뵽ĿУͬ "plugins\types" ļһ𿽱Ŀе easyflash ļ¡ȻҪ `easyflash\plugins\types\struct2json\inc` `easyflash\plugins\types` ļ·Ŀͷļ·мɡ + +### 2.2 ʼ + +```C +void ef_types_init(S2jHook *hook) +``` + +Ϊ Types ijʼҪʼ struct2json ڴĬʹõ malloc free ΪڴʹĬڴʽʼʹ RT-Thread ϵͳԴڴԲοijʼ룺 + +```C +S2jHook s2jHook = { + .free_fn = rt_free, + .malloc_fn = (void *(*)(size_t))rt_malloc, +}; +ef_types_init(&s2jHook); +``` + +### 2.3 + +#### 2.3.1 + +ڻ͵Ļ EasyFlash ԭе API һ£ֻ޸μεͣпõ API £ + +```C +bool ef_get_bool(const char *key); +char ef_get_char(const char *key); +short ef_get_short(const char *key); +int ef_get_int(const char *key); +long ef_get_long(const char *key); +float ef_get_float(const char *key); +double ef_get_double(const char *key); +EfErrCode ef_set_bool(const char *key, bool value); +EfErrCode ef_set_char(const char *key, char value); +EfErrCode ef_set_short(const char *key, short value); +EfErrCode ef_set_int(const char *key, int value); +EfErrCode ef_set_long(const char *key, long value); +EfErrCode ef_set_float(const char *key, float value); +EfErrCode ef_set_double(const char *key, double value); +``` + +#### 2.3.2 + +͵IJһ£ͬڣȡĻָͨ͵νзءпõ API £ + +```C +void ef_get_bool_array(const char *key, bool *value); +void ef_get_char_array(const char *key, char *value); +void ef_get_short_array(const char *key, short *value); +void ef_get_int_array(const char *key, int *value); +void ef_get_long_array(const char *key, long *value); +void ef_get_float_array(const char *key, float *value); +void ef_get_double_array(const char *key, double *value); +void ef_get_string_array(const char *key, char **value); +EfErrCode ef_set_bool_array(const char *key, bool *value, size_t len); +EfErrCode ef_set_char_array(const char *key, char *value, size_t len); +EfErrCode ef_set_short_array(const char *key, short *value, size_t len); +EfErrCode ef_set_int_array(const char *key, int *value, size_t len); +EfErrCode ef_set_long_array(const char *key, long *value, size_t len); +EfErrCode ef_set_float_array(const char *key, float *value, size_t len); +EfErrCode ef_set_double_array(const char *key, double *value, size_t len); +EfErrCode ef_set_string_array(const char *key, char **value, size_t len); +``` +#### 2.3.3 ṹ + +ڽṹͣҪʹ struct2json дýṹӦ JSON תٽдõĻתΪνʹáṹͻ API £ + +```C +void *ef_get_struct(const char *key, ef_types_get_cb get_cb); +EfErrCode ef_set_struct(const char *key, void *value, ef_types_set_cb set_cb); +``` + +ʹ̼ṹ JSON ֮ĻתԲο Demo + +```C +/* ṹ */ +typedef struct { + char name[16]; +} Hometown; +typedef struct { + uint8_t id; + double weight; + uint8_t score[8]; + char name[16]; + Hometown hometown; +} Student; + +/* ṹת JSON ķ */ +static cJSON *stu_set_cb(void* struct_obj) { + Student *struct_student = (Student *)struct_obj; + /* Student JSON */ + s2j_create_json_obj(json_student); + /* лݵ Student JSON */ + s2j_json_set_basic_element(json_student, struct_student, int, id); + s2j_json_set_basic_element(json_student, struct_student, double, weight); + s2j_json_set_array_element(json_student, struct_student, int, score, 8); + s2j_json_set_basic_element(json_student, struct_student, string, name); + /* лݵ Student.Hometown JSON */ + s2j_json_set_struct_element(json_hometown, json_student, struct_hometown, struct_student, Hometown, hometown); + s2j_json_set_basic_element(json_hometown, struct_hometown, string, name); + return json_student; +} + +/* JSON תṹķ */ +static void *stu_get_cb(cJSON* json_obj) { + /* Student ṹʾ s2j_ ͷķ struct2json ṩģ */ + s2j_create_struct_obj(struct_student, Student); + /* лݵ Student ṹ */ + s2j_struct_get_basic_element(struct_student, json_obj, int, id); + s2j_struct_get_array_element(struct_student, json_obj, int, score); + s2j_struct_get_basic_element(struct_student, json_obj, string, name); + s2j_struct_get_basic_element(struct_student, json_obj, double, weight); + /* лݵ Student.Hometown ṹ */ + s2j_struct_get_struct_element(struct_hometown, struct_student, json_hometown, json_obj, Hometown, hometown); + s2j_struct_get_basic_element(struct_hometown, json_hometown, string, name); + return struct_student; +} + +/* ýṹͻ */ +Student orignal_student = { + .id = 24, + .weight = 71.2, + .score = {1, 2, 3, 4, 5, 6, 7, 8}, + .name = "", + .hometown.name = "", +}; +ef_set_struct("ѧ", &orignal_student, stu_set_cb); + +/* ȡṹͻ */ +Student *student; +ef_get_struct("ѧ", student, stu_get_cb); + +/* ӡȡĽṹ */ +printf("%s ᣺%s \n", student->name, student->hometown.name); + +/* ͷŻȡṹͻпٵĶ̬ڴ */ +s2jHook.free_fn(student); +``` \ No newline at end of file diff --git a/demo/os/nuttx-spiflash/apps/system/easyflash/plugins/types/ef_types.c b/demo/os/nuttx-spiflash/apps/system/easyflash/plugins/types/ef_types.c new file mode 100644 index 0000000..8c516b9 --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/system/easyflash/plugins/types/ef_types.c @@ -0,0 +1,395 @@ +/* + * This file is part of the EasyFlash Library. + * + * Copyright (c) 2015-2016, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: Types plugin source code for this library. + * Created on: 2015-12-16 + */ + +#include "ef_types.h" +#include +#include + +/** + * array support types + */ +typedef enum { + EF_ARRAY_TYPES_BOOL, + EF_ARRAY_TYPES_CHAR, + EF_ARRAY_TYPES_SHORT, + EF_ARRAY_TYPES_INT, + EF_ARRAY_TYPES_LONG, + EF_ARRAY_TYPES_FLOAT, + EF_ARRAY_TYPES_DOUBLE, + EF_ARRAY_TYPES_STRING, +} ef_array_types; + +/** + * EasyFlash types plugin initialize. + * + * @param hook Memory management hook function. + * If hook is null or not call this function, then use free and malloc of C library. + */ +void ef_types_init(S2jHook *hook) { + s2j_init(hook); +} + +bool ef_get_bool(const char *key) { + char *value = ef_get_env(key); + if(value) { + return atoi(value) == 0 ? false : true; + } else { + EF_INFO("Couldn't find this ENV(%s)!\n", key); + return false; + } +} + +char ef_get_char(const char *key) { + return ef_get_long(key); +} + +short ef_get_short(const char *key) { + return ef_get_long(key); +} + +int ef_get_int(const char *key) { + return ef_get_long(key); +} + +long ef_get_long(const char *key) { + char *value = ef_get_env(key); + if(value) { + return atol(value); + } else { + EF_INFO("Couldn't find this ENV(%s)!\n", key); + return NULL; + } +} + +float ef_get_float(const char *key) { + return ef_get_double(key); +} + +double ef_get_double(const char *key) { + char *value = ef_get_env(key); + if(value) { + return atof(value); + } else { + EF_INFO("Couldn't find this ENV(%s)!\n", key); + return NULL; + } +} + +/** + * get array ENV value + * + * @param key ENV name + * @param value returned ENV value + * @param types ENV array's type + */ +static void ef_get_array(const char *key, void *value, ef_array_types types) { + char *char_value = ef_get_env(key); + cJSON *array; + size_t size, i; + + EF_ASSERT(value); + + if (char_value) { + array = cJSON_Parse(char_value); + if (array) { + size = cJSON_GetArraySize(array); + for (i = 0; i < size; i++) { + switch (types) { + case EF_ARRAY_TYPES_BOOL: { + *((bool *) value + i) = cJSON_GetArrayItem(array, i)->valueint; + break; + } + case EF_ARRAY_TYPES_CHAR: { + *((char *) value + i) = cJSON_GetArrayItem(array, i)->valueint; + break; + } + case EF_ARRAY_TYPES_SHORT: { + *((short *) value + i) = cJSON_GetArrayItem(array, i)->valueint; + break; + } + case EF_ARRAY_TYPES_INT: { + *((int *) value + i) = cJSON_GetArrayItem(array, i)->valueint; + break; + } + case EF_ARRAY_TYPES_LONG: { + *((long *) value + i) = cJSON_GetArrayItem(array, i)->valueint; + break; + } + case EF_ARRAY_TYPES_FLOAT: { + *((float *) value + i) = cJSON_GetArrayItem(array, i)->valuedouble; + break; + } + case EF_ARRAY_TYPES_DOUBLE: { + *((double *) value + i) = cJSON_GetArrayItem(array, i)->valuedouble; + break; + } + case EF_ARRAY_TYPES_STRING: { + *((char **) value + i) = cJSON_GetArrayItem(array, i)->valuestring; + break; + } + } + } + } else { + EF_INFO("This ENV(%s) value type has error!\n", key); + } + cJSON_Delete(array); + } else { + EF_INFO("Couldn't find this ENV(%s)!\n", key); + } +} + +void ef_get_bool_array(const char *key, bool *value) { + ef_get_array(key, value, EF_ARRAY_TYPES_BOOL); +} + +void ef_get_char_array(const char *key, char *value) { + ef_get_array(key, value, EF_ARRAY_TYPES_CHAR); +} + +void ef_get_short_array(const char *key, short *value) { + ef_get_array(key, value, EF_ARRAY_TYPES_SHORT); +} + +void ef_get_int_array(const char *key, int *value) { + ef_get_array(key, value, EF_ARRAY_TYPES_INT); +} + +void ef_get_long_array(const char *key, long *value) { + ef_get_array(key, value, EF_ARRAY_TYPES_LONG); +} + +void ef_get_float_array(const char *key, float *value) { + ef_get_array(key, value, EF_ARRAY_TYPES_FLOAT); +} + +void ef_get_double_array(const char *key, double *value) { + ef_get_array(key, value, EF_ARRAY_TYPES_DOUBLE); +} + +void ef_get_string_array(const char *key, char **value) { + ef_get_array(key, value, EF_ARRAY_TYPES_STRING); +} + +/** + * get structure ENV value + * + * @param key ENV name + * @param get_cb get structure callback function. + * You can use json to structure function which in the struct2json lib(https://github.com/armink/struct2json). + * + * @return value returned structure ENV value pointer. @note The returned value will malloc new ram. + * You must free the value then used finish. + */ +void *ef_get_struct(const char *key, ef_types_get_cb get_cb) { + char *char_value = ef_get_env(key); + cJSON *json_value = cJSON_Parse(char_value); + void *value = NULL; + + if (json_value) { + value = get_cb(json_value); + cJSON_Delete(json_value); + } + return value; +} + +EfErrCode ef_set_bool(const char *key, bool value) { + char char_value[2] = { 0 }; + if (!value) { + strcpy(char_value, "0"); + } else { + strcpy(char_value, "1"); + } + return ef_set_env(key, char_value); +} + +EfErrCode ef_set_char(const char *key, char value) { + return ef_set_long(key, value); +} + +EfErrCode ef_set_short(const char *key, short value) { + return ef_set_long(key, value); +} + +EfErrCode ef_set_int(const char *key, int value) { + return ef_set_long(key, value); +} + +EfErrCode ef_set_long(const char *key, long value) { + char char_value[21] = { 0 }; + + snprintf(char_value, 20, "%ld", value); + + return ef_set_env(key, char_value); +} + +EfErrCode ef_set_float(const char *key, float value) { + return ef_set_double(key, value); +} + +EfErrCode ef_set_double(const char *key, double value) { + char char_value[21] = { 0 }; + + snprintf(char_value, 20, "%lf", value); + + return ef_set_env(key, char_value); +} + +/** + * set array ENV value + * + * @param key ENV name + * @param value ENV value + * @param len array length + * @param types ENV array's type + * + * @return ENV set result + */ +static EfErrCode ef_set_array(const char *key, void *value, size_t len, ef_array_types types) { + char *char_value = NULL; + cJSON *array = NULL, *array_item = NULL; + size_t i; + EfErrCode result = EF_NO_ERR; + + EF_ASSERT(value); + + array = cJSON_CreateArray(); + if (array) { + for (i = 0; i < len; i++) { + switch (types) { + case EF_ARRAY_TYPES_BOOL: { + array_item = cJSON_CreateBool(*((bool *) value + i)); + break; + } + case EF_ARRAY_TYPES_CHAR: { + array_item = cJSON_CreateNumber(*((char *) value + i)); + break; + } + case EF_ARRAY_TYPES_SHORT: { + array_item = cJSON_CreateNumber(*((short *) value + i)); + break; + } + case EF_ARRAY_TYPES_INT: { + array_item = cJSON_CreateNumber(*((int *) value + i)); + break; + } + case EF_ARRAY_TYPES_LONG: { + array_item = cJSON_CreateNumber(*((long *) value + i)); + break; + } + case EF_ARRAY_TYPES_FLOAT: { + array_item = cJSON_CreateNumber(*((float *) value + i)); + break; + } + case EF_ARRAY_TYPES_DOUBLE: { + array_item = cJSON_CreateNumber(*((double *) value + i)); + break; + } + case EF_ARRAY_TYPES_STRING: { + array_item = cJSON_CreateString(*((char **) value + i)); + break; + } + default: + /* the types parameter has error */ + EF_ASSERT(0); + } + if (array_item) { + cJSON_AddItemToArray(array, array_item); + } else { + result = EF_ENV_FULL; + EF_INFO("Memory full!\n", key); + break; + } + } + char_value = cJSON_PrintUnformatted(array); + if (char_value) { + result = ef_set_env(key, char_value); + s2jHook.free_fn(char_value); + } else { + result = EF_ENV_FULL; + EF_INFO("Memory full!\n", key); + } + cJSON_Delete(array); + } else { + result = EF_ENV_FULL; + EF_INFO("Memory full!\n", key); + } + return result; +} + +EfErrCode ef_set_bool_array(const char *key, bool *value, size_t len) { + return ef_set_array(key, value, len, EF_ARRAY_TYPES_BOOL); +} + +EfErrCode ef_set_char_array(const char *key, char *value, size_t len) { + return ef_set_array(key, value, len, EF_ARRAY_TYPES_CHAR); +} + +EfErrCode ef_set_short_array(const char *key, short *value, size_t len) { + return ef_set_array(key, value, len, EF_ARRAY_TYPES_SHORT); +} + +EfErrCode ef_set_int_array(const char *key, int *value, size_t len) { + return ef_set_array(key, value, len, EF_ARRAY_TYPES_INT); +} + +EfErrCode ef_set_long_array(const char *key, long *value, size_t len) { + return ef_set_array(key, value, len, EF_ARRAY_TYPES_LONG); +} + +EfErrCode ef_set_float_array(const char *key, float *value, size_t len) { + return ef_set_array(key, value, len, EF_ARRAY_TYPES_FLOAT); +} + +EfErrCode ef_set_double_array(const char *key, double *value, size_t len) { + return ef_set_array(key, value, len, EF_ARRAY_TYPES_DOUBLE); +} + +EfErrCode ef_set_string_array(const char *key, char **value, size_t len) { + return ef_set_array(key, value, len, EF_ARRAY_TYPES_STRING); +} + +/** + * set structure ENV value + * + * @param key ENV name + * @param value structure ENV value pointer + * @param get_cb set structure callback function. + * You can use structure to json function which in the struct2json lib(https://github.com/armink/struct2json). + */ +EfErrCode ef_set_struct(const char *key, void *value, ef_types_set_cb set_cb) { + EfErrCode result = EF_NO_ERR; + cJSON *json_value = set_cb(value); + char *char_value = cJSON_PrintUnformatted(json_value); + + result = ef_set_env(key, char_value); + + cJSON_Delete(json_value); + s2jHook.free_fn(char_value); + + return result; +} diff --git a/demo/os/nuttx-spiflash/apps/system/easyflash/plugins/types/ef_types.h b/demo/os/nuttx-spiflash/apps/system/easyflash/plugins/types/ef_types.h new file mode 100644 index 0000000..f861898 --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/system/easyflash/plugins/types/ef_types.h @@ -0,0 +1,76 @@ +/* + * This file is part of the EasyFlash Library. + * + * Copyright (c) 2015-2016, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: It is an head file for this plugin. You can see all be called functions. + * Created on: 2015-12-16 + */ + +#ifndef EF_TYPES_H_ +#define EF_TYPES_H_ + +#include +#include +#include "struct2json\inc\s2j.h" + +/* EasyFlash types plugin's software version number */ +#define EF_TYPES_SW_VERSION "0.11.03" + +typedef cJSON *(*ef_types_set_cb)(void* struct_obj); +typedef void *(*ef_types_get_cb)(cJSON* json_obj); + +void ef_types_init(S2jHook *hook); +bool ef_get_bool(const char *key); +char ef_get_char(const char *key); +short ef_get_short(const char *key); +int ef_get_int(const char *key); +long ef_get_long(const char *key); +float ef_get_float(const char *key); +double ef_get_double(const char *key); +void ef_get_bool_array(const char *key, bool *value); +void ef_get_char_array(const char *key, char *value); +void ef_get_short_array(const char *key, short *value); +void ef_get_int_array(const char *key, int *value); +void ef_get_long_array(const char *key, long *value); +void ef_get_float_array(const char *key, float *value); +void ef_get_double_array(const char *key, double *value); +void ef_get_string_array(const char *key, char **value); +void *ef_get_struct(const char *key, ef_types_get_cb get_cb); +EfErrCode ef_set_bool(const char *key, bool value); +EfErrCode ef_set_char(const char *key, char value); +EfErrCode ef_set_short(const char *key, short value); +EfErrCode ef_set_int(const char *key, int value); +EfErrCode ef_set_long(const char *key, long value); +EfErrCode ef_set_float(const char *key, float value); +EfErrCode ef_set_double(const char *key, double value); +EfErrCode ef_set_bool_array(const char *key, bool *value, size_t len); +EfErrCode ef_set_char_array(const char *key, char *value, size_t len); +EfErrCode ef_set_short_array(const char *key, short *value, size_t len); +EfErrCode ef_set_int_array(const char *key, int *value, size_t len); +EfErrCode ef_set_long_array(const char *key, long *value, size_t len); +EfErrCode ef_set_float_array(const char *key, float *value, size_t len); +EfErrCode ef_set_double_array(const char *key, double *value, size_t len); +EfErrCode ef_set_string_array(const char *key, char **value, size_t len); +EfErrCode ef_set_struct(const char *key, void *value, ef_types_set_cb set_cb); + +#endif /* EF_TYPES_H_ */ diff --git a/demo/os/nuttx-spiflash/apps/system/easyflash/plugins/types/struct2json/inc/cJSON.h b/demo/os/nuttx-spiflash/apps/system/easyflash/plugins/types/struct2json/inc/cJSON.h new file mode 100644 index 0000000..634fe22 --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/system/easyflash/plugins/types/struct2json/inc/cJSON.h @@ -0,0 +1,154 @@ +/* + Copyright (c) 2009 Dave Gamble + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef cJSON__h +#define cJSON__h + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* cJSON Types: */ +#define cJSON_False 0 +#define cJSON_True 1 +#define cJSON_NULL 2 +#define cJSON_Number 3 +#define cJSON_String 4 +#define cJSON_Array 5 +#define cJSON_Object 6 + +#define cJSON_IsReference 256 +#define cJSON_StringIsConst 512 + +/* The cJSON structure: */ +typedef struct cJSON { + struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + + int type; /* The type of the item, as above. */ + + char *valuestring; /* The item's string, if type==cJSON_String */ + int valueint; /* The item's number, if type==cJSON_Number */ + double valuedouble; /* The item's number, if type==cJSON_Number */ + + char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ +} cJSON; + +typedef struct cJSON_Hooks { + void *(*malloc_fn)(size_t sz); + void (*free_fn)(void *ptr); +} cJSON_Hooks; + +/* Supply malloc, realloc and free functions to cJSON */ +extern void cJSON_InitHooks(cJSON_Hooks* hooks); + + +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */ +extern cJSON *cJSON_Parse(const char *value); +/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */ +extern char *cJSON_Print(cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */ +extern char *cJSON_PrintUnformatted(cJSON *item); +/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ +extern char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt); +/* Delete a cJSON entity and all subentities. */ +extern void cJSON_Delete(cJSON *c); + +/* Returns the number of items in an array (or object). */ +extern int cJSON_GetArraySize(cJSON *array); +/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ +extern cJSON *cJSON_GetArrayItem(cJSON *array,int item); +/* Get item "string" from object. Case insensitive. */ +extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string); +extern int cJSON_HasObjectItem(cJSON *object,const char *string); +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +extern const char *cJSON_GetErrorPtr(void); + +/* These calls create a cJSON item of the appropriate type. */ +extern cJSON *cJSON_CreateNull(void); +extern cJSON *cJSON_CreateTrue(void); +extern cJSON *cJSON_CreateFalse(void); +extern cJSON *cJSON_CreateBool(int b); +extern cJSON *cJSON_CreateNumber(double num); +extern cJSON *cJSON_CreateString(const char *string); +extern cJSON *cJSON_CreateArray(void); +extern cJSON *cJSON_CreateObject(void); + +/* These utilities create an Array of count items. */ +extern cJSON *cJSON_CreateIntArray(const int *numbers,int count); +extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count); +extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count); +extern cJSON *cJSON_CreateStringArray(const char **strings,int count); + +/* Append item to the specified array/object. */ +extern void cJSON_AddItemToArray(cJSON *array, cJSON *item); +extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item); +extern void cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item); /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object */ +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item); + +/* Remove/Detatch items from Arrays/Objects. */ +extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which); +extern void cJSON_DeleteItemFromArray(cJSON *array,int which); +extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string); +extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string); + +/* Update array items. */ +extern void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem); /* Shifts pre-existing items to the right. */ +extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem); +extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); + +/* Duplicate a cJSON item */ +extern cJSON *cJSON_Duplicate(cJSON *item,int recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will +need to be released. With recurse!=0, it will duplicate any children connected to the item. +The item->next and ->prev pointers are always zero on return from Duplicate. */ + +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated); + +extern void cJSON_Minify(char *json); + +/* Macros for creating things quickly. */ +#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) +#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) +#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) +#define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b)) +#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) +#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val)) +#define cJSON_SetNumberValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val)) + +/* Macro for iterating over an array */ +#define cJSON_ArrayForEach(pos, head) for(pos = (head)->child; pos != NULL; pos = pos->next) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/demo/os/nuttx-spiflash/apps/system/easyflash/plugins/types/struct2json/inc/s2j.h b/demo/os/nuttx-spiflash/apps/system/easyflash/plugins/types/struct2json/inc/s2j.h new file mode 100644 index 0000000..73ec4df --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/system/easyflash/plugins/types/struct2json/inc/s2j.h @@ -0,0 +1,91 @@ +/* + * This file is part of the struct2json Library. + * + * Copyright (c) 2015, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: It is an head file for this library. You can see all be called functions. + * Created on: 2015-10-14 + */ + +#ifndef __S2J_H__ +#define __S2J_H__ + +#include +#include +#include "s2jdef.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* struct2json software version number */ +#define S2J_SW_VERSION "1.0.2" + +/* Create JSON object */ +#define s2j_create_json_obj(json_obj) \ + S2J_CREATE_JSON_OBJECT(json_obj) + +/* Delete JSON object */ +#define s2j_delete_json_obj(json_obj) \ + S2J_DELETE_JSON_OBJECT(json_obj) + +/* Set basic type element for JSON object */ +#define s2j_json_set_basic_element(to_json, from_struct, type, element) \ + S2J_JSON_SET_BASIC_ELEMENT(to_json, from_struct, type, element) + +/* Set array type element for JSON object */ +#define s2j_json_set_array_element(to_json, from_struct, type, element, size) \ + S2J_JSON_SET_ARRAY_ELEMENT(to_json, from_struct, type, element, size) + +/* Set child structure type element for JSON object */ +#define s2j_json_set_struct_element(child_json, to_json, child_struct, from_struct, type, element) \ + S2J_JSON_SET_STRUCT_ELEMENT(child_json, to_json, child_struct, from_struct, type, element) + +/* Create structure object */ +#define s2j_create_struct_obj(struct_obj, type) \ + S2J_CREATE_STRUCT_OBJECT(struct_obj, type) + +/* Delete structure object */ +#define s2j_delete_struct_obj(struct_obj) \ + S2J_DELETE_STRUCT_OBJECT(struct_obj) + +/* Get basic type element for structure object */ +#define s2j_struct_get_basic_element(to_struct, from_json, type, element) \ + S2J_STRUCT_GET_BASIC_ELEMENT(to_struct, from_json, type, element) + +/* Get array type element for structure object */ +#define s2j_struct_get_array_element(to_struct, from_json, type, element) \ + S2J_STRUCT_GET_ARRAY_ELEMENT(to_struct, from_json, type, element) + +/* Get child structure type element for structure object */ +#define s2j_struct_get_struct_element(child_struct, to_struct, child_json, from_json, type, element) \ + S2J_STRUCT_GET_STRUCT_ELEMENT(child_struct, to_struct, child_json, from_json, type, element) + +/* s2j.c */ +extern S2jHook s2jHook; +void s2j_init(S2jHook *hook); + +#ifdef __cplusplus +} +#endif + +#endif /* __S2J_H__ */ diff --git a/demo/os/nuttx-spiflash/apps/system/easyflash/plugins/types/struct2json/inc/s2jdef.h b/demo/os/nuttx-spiflash/apps/system/easyflash/plugins/types/struct2json/inc/s2jdef.h new file mode 100644 index 0000000..be445ac --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/system/easyflash/plugins/types/struct2json/inc/s2jdef.h @@ -0,0 +1,150 @@ +/* + * This file is part of the struct2json Library. + * + * Copyright (c) 2015, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: It is an head file for this library. + * Created on: 2015-10-14 + */ + +#ifndef __S2JDEF_H__ +#define __S2JDEF_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + void *(*malloc_fn)(size_t sz); + void (*free_fn)(void *ptr); +} S2jHook, *S2jHook_t; + +#define S2J_STRUCT_GET_int_ELEMENT(to_struct, from_json, _element) \ + json_temp = cJSON_GetObjectItem(from_json, #_element); \ + if (json_temp) (to_struct)->_element = json_temp->valueint; + +#define S2J_STRUCT_GET_string_ELEMENT(to_struct, from_json, _element) \ + json_temp = cJSON_GetObjectItem(from_json, #_element); \ + if (json_temp) strcpy((to_struct)->_element, json_temp->valuestring); + +#define S2J_STRUCT_GET_double_ELEMENT(to_struct, from_json, _element) \ + json_temp = cJSON_GetObjectItem(from_json, #_element); \ + if (json_temp) (to_struct)->_element = json_temp->valuedouble; + +#define S2J_STRUCT_ARRAY_GET_int_ELEMENT(to_struct, from_json, _element, index) \ + (to_struct)->_element[index] = from_json->valueint; + +#define S2J_STRUCT_ARRAY_GET_string_ELEMENT(to_struct, from_json, _element, index) \ + strcpy((to_struct)->_element[index], from_json->valuestring); + +#define S2J_STRUCT_ARRAY_GET_double_ELEMENT(to_struct, from_json, _element, index) \ + (to_struct)->_element[index] = from_json->valuedouble; + +#define S2J_STRUCT_ARRAY_GET_ELEMENT(to_struct, from_json, type, _element, index) \ + S2J_STRUCT_ARRAY_GET_##type##_ELEMENT(to_struct, from_json, _element, index) + +#define S2J_JSON_SET_int_ELEMENT(to_json, from_struct, _element) \ + cJSON_AddNumberToObject(to_json, #_element, (from_struct)->_element); + +#define S2J_JSON_SET_double_ELEMENT(to_json, from_struct, _element) \ + cJSON_AddNumberToObject(to_json, #_element, (from_struct)->_element); + +#define S2J_JSON_SET_string_ELEMENT(to_json, from_struct, _element) \ + cJSON_AddStringToObject(to_json, #_element, (from_struct)->_element); + +#define S2J_JSON_ARRAY_SET_int_ELEMENT(to_json, from_struct, _element, index) \ + cJSON_AddItemToArray(to_json, cJSON_CreateNumber((from_struct)->_element[index])); + +#define S2J_JSON_ARRAY_SET_double_ELEMENT(to_json, from_struct, _element, index) \ + cJSON_AddItemToArray(to_json, cJSON_CreateNumber((from_struct)->_element[index])); + +#define S2J_JSON_ARRAY_SET_string_ELEMENT(to_json, from_struct, _element, index) \ + cJSON_AddItemToArray(to_json, cJSON_CreateString((from_struct)->_element[index])); + +#define S2J_JSON_ARRAY_SET_ELEMENT(to_json, from_struct, type, _element, index) \ + S2J_JSON_ARRAY_SET_##type##_ELEMENT(to_json, from_struct, _element, index) + + +#define S2J_CREATE_JSON_OBJECT(json_obj) \ + cJSON *json_obj = cJSON_CreateObject(); + +#define S2J_DELETE_JSON_OBJECT(json_obj) \ + cJSON_Delete(json_obj); + +#define S2J_JSON_SET_BASIC_ELEMENT(to_json, from_struct, type, _element) \ + S2J_JSON_SET_##type##_ELEMENT(to_json, from_struct, _element) + +#define S2J_JSON_SET_ARRAY_ELEMENT(to_json, from_struct, type, _element, size) \ + { \ + cJSON *array; \ + size_t index = 0; \ + array = cJSON_CreateArray(); \ + if (array) { \ + while (index < size) { \ + S2J_JSON_ARRAY_SET_ELEMENT(array, from_struct, type, _element, index++); \ + } \ + cJSON_AddItemToObject(to_json, #_element, array); \ + } \ + } + +#define S2J_JSON_SET_STRUCT_ELEMENT(child_json, to_json, child_struct, from_struct, type, _element) \ + type *child_struct = &((from_struct)->_element); \ + cJSON *child_json = cJSON_CreateObject(); \ + if (child_json) cJSON_AddItemToObject(to_json, #_element, child_json); + +#define S2J_CREATE_STRUCT_OBJECT(struct_obj, type) \ + cJSON *json_temp; \ + type *struct_obj = s2jHook.malloc_fn(sizeof(type)); \ + if (struct_obj) memset(struct_obj, 0, sizeof(type)); + +#define S2J_DELETE_STRUCT_OBJECT(struct_obj) \ + s2jHook.free_fn(struct_obj); + +#define S2J_STRUCT_GET_BASIC_ELEMENT(to_struct, from_json, type, _element) \ + S2J_STRUCT_GET_##type##_ELEMENT(to_struct, from_json, _element) + +#define S2J_STRUCT_GET_ARRAY_ELEMENT(to_struct, from_json, type, _element) \ + { \ + cJSON *array, *array_element; \ + size_t index = 0, size = 0; \ + array = cJSON_GetObjectItem(from_json, #_element); \ + if (array) { \ + size = cJSON_GetArraySize(array); \ + while (index < size) { \ + array_element = cJSON_GetArrayItem(array, index); \ + if (array_element) S2J_STRUCT_ARRAY_GET_ELEMENT(to_struct, array_element, type, _element, index++); \ + } \ + } \ + } + +#define S2J_STRUCT_GET_STRUCT_ELEMENT(child_struct, to_struct, child_json, from_json, type, _element) \ + type *child_struct = &((to_struct)->_element); \ + cJSON *child_json = cJSON_GetObjectItem(from_json, #_element); + +#ifdef __cplusplus +} +#endif + +#endif /* __S2JDEF_H__ */ diff --git a/demo/os/nuttx-spiflash/apps/system/easyflash/plugins/types/struct2json/readme.md b/demo/os/nuttx-spiflash/apps/system/easyflash/plugins/types/struct2json/readme.md new file mode 100644 index 0000000..dea28be --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/system/easyflash/plugins/types/struct2json/readme.md @@ -0,0 +1,61 @@ +# C结构体与 JSON 快速互转库 + +--- + +## struct2json + +[struct2json](https://github.com/armink/struct2json) 是一个开源的C结构体与 JSON 快速互转库,它可以快速实现 **结构体对象** 与 **JSON 对象** 之间序列化及反序列化要求。快速、简洁的 API 设计,大大降低直接使用 JSON 解析库来实现此类功能的代码复杂度。 + +## 起源 + +把面向对象设计应用到C语言中,是当下很流行的设计思想。由于C语言中没有类,所以一般使用结构体 `struct` 充当类,那么结构体变量就是对象。有了对象之后,很多时候需要考虑对象的序列化及反序列化问题。C语言不像很多高级语言拥有反射等机制,使得对象序列化及反序列化被原生的支持。 + +对于C语言来说,序列化为 JSON 字符串是个不错的选择,所以就得使用 [cJSON](https://github.com/kbranigan/cJSON) 这类 JSON 解析库,但是使用后的代码冗余且逻辑性差,所以萌生对cJSON库进行二次封装,实现一个 struct 与 JSON 之间快速互转的库。 struct2json 就诞生于此。下面是 struct2json 主要使用场景: + +- **持久化** :结构体对象序列化为 JSON 对象后,可直接保存至文件、Flash,实现对结构体对象的掉电存储; +- **通信** :高级语言对JSON支持的很友好,例如: Javascript、Groovy 就对 JSON 具有原生的支持,所以 JSON 也可作为C语言与其他语言软件之间的通信协议格式及对象传递格式; +- **可视化** :序列化为 JSON 后的对象,可以更加直观的展示到控制台或者 UI 上,可用于产品调试、产品二次开发等场景; + +## 如何使用 + +### 声明结构体 + +如下声明了两个结构体,结构体 `Hometown` 是结构体 `Student` 的子结构体 + +```C +/* 籍贯 */ +typedef struct { + char name[16]; +} Hometown; + +/* 学生 */ +typedef struct { + uint8_t id; + uint8_t score[8]; + char name[10]; + double weight; + Hometown hometown; +} Student; +``` + +### 将结构体对象序列化为 JSON 对象 + +|使用前([源文件](https://github.com/armink/struct2json/blob/master/docs/zh/assets/not_use_struct2json.c))|使用后([源文件](https://github.com/armink/struct2json/blob/master/docs/zh/assets/used_struct2json.c))| +|:-----:|:-----:| +|![结构体转JSON-使用前](https://git.oschina.net/Armink/struct2json/raw/master/docs/zh/images/not_use_struct2json.png)| ![结构体转JSON-使用后](https://git.oschina.net/Armink/struct2json/raw/master/docs/zh/images/used_struct2json.png)| + +### 将 JSON 对象反序列化为结构体对象 + +|使用前([源文件](https://github.com/armink/struct2json/blob/master/docs/zh/assets/not_use_struct2json_for_json.c))|使用后([源文件](https://github.com/armink/struct2json/blob/master/docs/zh/assets/used_struct2json_for_json.c))| +|:-----:|:-----:| +|![JSON转结构体-使用前](https://git.oschina.net/Armink/struct2json/raw/master/docs/zh/images/not_use_struct2json_for_json.png)| ![JSON转结构体-使用后](https://git.oschina.net/Armink/struct2json/raw/master/docs/zh/images/used_struct2json_for_json.png)| + +欢迎大家 **fork and pull request**([Github](https://github.com/armink/struct2json)|[OSChina](http://git.oschina.net/armink/struct2json)|[Coding](https://coding.net/u/armink/p/struct2json/git)) 。如果觉得这个开源项目很赞,可以点击[项目主页](https://github.com/armink/struct2json) 右上角的**Star**,同时把它推荐给更多有需要的朋友。 + +## 文档 + +具体内容参考[`\docs\zh\`](https://github.com/armink/struct2json/tree/master/docs/zh)下的文件。务必保证在 **阅读文档** 后再使用。 + +## 许可 + +MIT Copyright (c) armink.ztl@gmail.com diff --git a/demo/os/nuttx-spiflash/apps/system/easyflash/plugins/types/struct2json/src/cJSON.c b/demo/os/nuttx-spiflash/apps/system/easyflash/plugins/types/struct2json/src/cJSON.c new file mode 100644 index 0000000..67afd8f --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/system/easyflash/plugins/types/struct2json/src/cJSON.c @@ -0,0 +1,762 @@ +/* + Copyright (c) 2009 Dave Gamble + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/* cJSON */ +/* JSON parser in C. */ + +#include +#include +#include +#include +#include +#include +#include +#include "cJSON.h" + +static const char *ep; + +const char *cJSON_GetErrorPtr(void) {return ep;} + +static int cJSON_strcasecmp(const char *s1,const char *s2) +{ + if (!s1) return (s1==s2)?0:1;if (!s2) return 1; + for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0; + return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); +} + +static void *(*cJSON_malloc)(size_t sz) = malloc; +static void (*cJSON_free)(void *ptr) = free; + +static char* cJSON_strdup(const char* str) +{ + size_t len; + char* copy; + + len = strlen(str) + 1; + if ((copy = (char*)cJSON_malloc(len)) == NULL) return 0; + memcpy(copy,str,len); + return copy; +} + +void cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (!hooks) { /* Reset hooks */ + cJSON_malloc = malloc; + cJSON_free = free; + return; + } + + cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc; + cJSON_free = (hooks->free_fn)?hooks->free_fn:free; +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item(void) +{ + cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON)); + if (node) memset(node,0,sizeof(cJSON)); + return node; +} + +/* Delete a cJSON structure. */ +void cJSON_Delete(cJSON *c) +{ + cJSON *next; + while (c) + { + next=c->next; + if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child); + if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring); + if (!(c->type&cJSON_StringIsConst) && c->string) cJSON_free(c->string); + cJSON_free(c); + c=next; + } +} + +/* Parse the input text to generate a number, and populate the result into item. */ +static const char *parse_number(cJSON *item,const char *num) +{ + double n=0,sign=1,scale=0;int subscale=0,signsubscale=1; + + if (*num=='-') sign=-1,num++; /* Has sign? */ + if (*num=='0') num++; /* is zero */ + if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */ + if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */ + if (*num=='e' || *num=='E') /* Exponent? */ + { num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */ + while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */ + } + + n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */ + + item->valuedouble=n; + item->valueint=(int)n; + item->type=cJSON_Number; + return num; +} + +static int pow2gt (int x) { --x; x|=x>>1; x|=x>>2; x|=x>>4; x|=x>>8; x|=x>>16; return x+1; } + +typedef struct {char *buffer; int length; int offset; } printbuffer; + +static char* ensure(printbuffer *p,int needed) +{ + char *newbuffer;int newsize; + if (!p || !p->buffer) return 0; + needed+=p->offset; + if (needed<=p->length) return p->buffer+p->offset; + + newsize=pow2gt(needed); + newbuffer=(char*)cJSON_malloc(newsize); + if (!newbuffer) {cJSON_free(p->buffer);p->length=0,p->buffer=0;return 0;} + if (newbuffer) memcpy(newbuffer,p->buffer,p->length); + cJSON_free(p->buffer); + p->length=newsize; + p->buffer=newbuffer; + return newbuffer+p->offset; +} + +static int update(printbuffer *p) +{ + char *str; + if (!p || !p->buffer) return 0; + str=p->buffer+p->offset; + return p->offset+strlen(str); +} + +/* Render the number nicely from the given item into a string. */ +static char *print_number(cJSON *item,printbuffer *p) +{ + char *str=0; + double d=item->valuedouble; + if (d==0) + { + if (p) str=ensure(p,2); + else str=(char*)cJSON_malloc(2); /* special case for 0. */ + if (str) strcpy(str,"0"); + } + else if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN) + { + if (p) str=ensure(p,21); + else str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */ + if (str) sprintf(str,"%d",item->valueint); + } + else + { + if (p) str=ensure(p,64); + else str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */ + if (str) + { + if (fpclassify(d) != FP_ZERO && !isnormal(d)) sprintf(str,"null"); + else if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60) sprintf(str,"%.0f",d); + else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d); + else sprintf(str,"%f",d); + } + } + return str; +} + +static unsigned parse_hex4(const char *str) +{ + unsigned h=0; + if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; + h=h<<4;str++; + if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; + h=h<<4;str++; + if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; + h=h<<4;str++; + if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; + return h; +} + +/* Parse the input text into an unescaped cstring, and populate item. */ +static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; +static const char *parse_string(cJSON *item,const char *str) +{ + const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2; + if (*str!='\"') {ep=str;return 0;} /* not a string! */ + + while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */ + + out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */ + if (!out) return 0; + + ptr=str+1;ptr2=out; + while (*ptr!='\"' && *ptr) + { + if (*ptr!='\\') *ptr2++=*ptr++; + else + { + ptr++; + switch (*ptr) + { + case 'b': *ptr2++='\b'; break; + case 'f': *ptr2++='\f'; break; + case 'n': *ptr2++='\n'; break; + case 'r': *ptr2++='\r'; break; + case 't': *ptr2++='\t'; break; + case 'u': /* transcode utf16 to utf8. */ + uc=parse_hex4(ptr+1);ptr+=4; /* get the unicode char. */ + + if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; /* check for invalid. */ + + if (uc>=0xD800 && uc<=0xDBFF) /* UTF16 surrogate pairs. */ + { + if (ptr[1]!='\\' || ptr[2]!='u') break; /* missing second-half of surrogate. */ + uc2=parse_hex4(ptr+3);ptr+=6; + if (uc2<0xDC00 || uc2>0xDFFF) break; /* invalid second-half of surrogate. */ + uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF)); + } + + len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len; + + switch (len) { + case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 1: *--ptr2 =(uc | firstByteMark[len]); + } + ptr2+=len; + break; + default: *ptr2++=*ptr; break; + } + ptr++; + } + } + *ptr2=0; + if (*ptr=='\"') ptr++; + item->valuestring=out; + item->type=cJSON_String; + return ptr; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static char *print_string_ptr(const char *str,printbuffer *p) +{ + const char *ptr;char *ptr2,*out;int len=0,flag=0;unsigned char token; + + for (ptr=str;*ptr;ptr++) flag|=((*ptr>0 && *ptr<32)||(*ptr=='\"')||(*ptr=='\\'))?1:0; + if (!flag) + { + len=ptr-str; + if (p) out=ensure(p,len+3); + else out=(char*)cJSON_malloc(len+3); + if (!out) return 0; + ptr2=out;*ptr2++='\"'; + strcpy(ptr2,str); + ptr2[len]='\"'; + ptr2[len+1]=0; + return out; + } + + if (!str) + { + if (p) out=ensure(p,3); + else out=(char*)cJSON_malloc(3); + if (!out) return 0; + strcpy(out,"\"\""); + return out; + } + ptr=str;while (('\0' != (token=*ptr)) && ++len) {if (NULL != strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;} + + if (p) out=ensure(p,len+3); + else out=(char*)cJSON_malloc(len+3); + if (!out) return 0; + + ptr2=out;ptr=str; + *ptr2++='\"'; + while (*ptr) + { + if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++; + else + { + *ptr2++='\\'; + switch (token=*ptr++) + { + case '\\': *ptr2++='\\'; break; + case '\"': *ptr2++='\"'; break; + case '\b': *ptr2++='b'; break; + case '\f': *ptr2++='f'; break; + case '\n': *ptr2++='n'; break; + case '\r': *ptr2++='r'; break; + case '\t': *ptr2++='t'; break; + default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */ + } + } + } + *ptr2++='\"';*ptr2++=0; + return out; +} +/* Invote print_string_ptr (which is useful) on an item. */ +static char *print_string(cJSON *item,printbuffer *p) {return print_string_ptr(item->valuestring,p);} + +/* Predeclare these prototypes. */ +static const char *parse_value(cJSON *item,const char *value); +static char *print_value(cJSON *item,int depth,int fmt,printbuffer *p); +static const char *parse_array(cJSON *item,const char *value); +static char *print_array(cJSON *item,int depth,int fmt,printbuffer *p); +static const char *parse_object(cJSON *item,const char *value); +static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p); + +/* Utility to jump whitespace and cr/lf */ +static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;} + +/* Parse an object - create a new root, and populate. */ +cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated) +{ + const char *end=0; + cJSON *c=cJSON_New_Item(); + ep=0; + if (!c) return 0; /* memory fail */ + + end=parse_value(c,skip(value)); + if (!end) {cJSON_Delete(c);return 0;} /* parse failure. ep is set. */ + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);ep=end;return 0;}} + if (return_parse_end) *return_parse_end=end; + return c; +} +/* Default options for cJSON_Parse */ +cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);} + +/* Render a cJSON item/entity/structure to text. */ +char *cJSON_Print(cJSON *item) {return print_value(item,0,1,0);} +char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0,0);} + +char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt) +{ + printbuffer p; + p.buffer=(char*)cJSON_malloc(prebuffer); + p.length=prebuffer; + p.offset=0; + return print_value(item,0,fmt,&p); + //return p.buffer; +} + + +/* Parser core - when encountering text, process appropriately. */ +static const char *parse_value(cJSON *item,const char *value) +{ + if (!value) return 0; /* Fail on null. */ + if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; } + if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; } + if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; } + if (*value=='\"') { return parse_string(item,value); } + if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); } + if (*value=='[') { return parse_array(item,value); } + if (*value=='{') { return parse_object(item,value); } + + ep=value;return 0; /* failure. */ +} + +/* Render a value to text. */ +static char *print_value(cJSON *item,int depth,int fmt,printbuffer *p) +{ + char *out=0; + if (!item) return 0; + if (p) + { + switch ((item->type)&255) + { + case cJSON_NULL: {out=ensure(p,5); if (out) strcpy(out,"null"); break;} + case cJSON_False: {out=ensure(p,6); if (out) strcpy(out,"false"); break;} + case cJSON_True: {out=ensure(p,5); if (out) strcpy(out,"true"); break;} + case cJSON_Number: out=print_number(item,p);break; + case cJSON_String: out=print_string(item,p);break; + case cJSON_Array: out=print_array(item,depth,fmt,p);break; + case cJSON_Object: out=print_object(item,depth,fmt,p);break; + } + } + else + { + switch ((item->type)&255) + { + case cJSON_NULL: out=cJSON_strdup("null"); break; + case cJSON_False: out=cJSON_strdup("false");break; + case cJSON_True: out=cJSON_strdup("true"); break; + case cJSON_Number: out=print_number(item,0);break; + case cJSON_String: out=print_string(item,0);break; + case cJSON_Array: out=print_array(item,depth,fmt,0);break; + case cJSON_Object: out=print_object(item,depth,fmt,0);break; + } + } + return out; +} + +/* Build an array from input text. */ +static const char *parse_array(cJSON *item,const char *value) +{ + cJSON *child; + if (*value!='[') {ep=value;return 0;} /* not an array! */ + + item->type=cJSON_Array; + value=skip(value+1); + if (*value==']') return value+1; /* empty array. */ + + item->child=child=cJSON_New_Item(); + if (!item->child) return 0; /* memory fail */ + value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */ + if (!value) return 0; + + while (*value==',') + { + cJSON *new_item; + if ((new_item=cJSON_New_Item()) == NULL) return 0; /* memory fail */ + child->next=new_item;new_item->prev=child;child=new_item; + value=skip(parse_value(child,skip(value+1))); + if (!value) return 0; /* memory fail */ + } + + if (*value==']') return value+1; /* end of array */ + ep=value;return 0; /* malformed. */ +} + +/* Render an array to text */ +static char *print_array(cJSON *item,int depth,int fmt,printbuffer *p) +{ + char **entries; + char *out=0,*ptr,*ret;int len=5; + cJSON *child=item->child; + int numentries=0,i=0,fail=0; + size_t tmplen=0; + + /* How many entries in the array? */ + while (child) numentries++,child=child->next; + /* Explicitly handle numentries==0 */ + if (!numentries) + { + if (p) out=ensure(p,3); + else out=(char*)cJSON_malloc(3); + if (out) strcpy(out,"[]"); + return out; + } + + if (p) + { + /* Compose the output array. */ + i=p->offset; + ptr=ensure(p,1);if (!ptr) return 0; *ptr='['; p->offset++; + child=item->child; + while (child && !fail) + { + print_value(child,depth+1,fmt,p); + p->offset=update(p); + if (child->next) {len=fmt?2:1;ptr=ensure(p,len+1);if (!ptr) return 0;*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;p->offset+=len;} + child=child->next; + } + ptr=ensure(p,2);if (!ptr) return 0; *ptr++=']';*ptr=0; + out=(p->buffer)+i; + } + else + { + /* Allocate an array to hold the values for each */ + entries=(char**)cJSON_malloc(numentries*sizeof(char*)); + if (!entries) return 0; + memset(entries,0,numentries*sizeof(char*)); + /* Retrieve all the results: */ + child=item->child; + while (child && !fail) + { + ret=print_value(child,depth+1,fmt,0); + entries[i++]=ret; + if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1; + child=child->next; + } + + /* If we didn't fail, try to malloc the output string */ + if (!fail) out=(char*)cJSON_malloc(len); + /* If that fails, we fail. */ + if (!out) fail=1; + + /* Handle failure. */ + if (fail) + { + for (i=0;itype=cJSON_Object; + value=skip(value+1); + if (*value=='}') return value+1; /* empty array. */ + + item->child=child=cJSON_New_Item(); + if (!item->child) return 0; + value=skip(parse_string(child,skip(value))); + if (!value) return 0; + child->string=child->valuestring;child->valuestring=0; + if (*value!=':') {ep=value;return 0;} /* fail! */ + value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ + if (!value) return 0; + + while (*value==',') + { + cJSON *new_item; + if ((new_item=cJSON_New_Item()) == NULL) return 0; /* memory fail */ + child->next=new_item;new_item->prev=child;child=new_item; + value=skip(parse_string(child,skip(value+1))); + if (!value) return 0; + child->string=child->valuestring;child->valuestring=0; + if (*value!=':') {ep=value;return 0;} /* fail! */ + value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ + if (!value) return 0; + } + + if (*value=='}') return value+1; /* end of array */ + ep=value;return 0; /* malformed. */ +} + +/* Render an object to text. */ +static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p) +{ + char **entries=0,**names=0; + char *out=0,*ptr,*ret,*str;int len=7,i=0,j; + cJSON *child=item->child; + int numentries=0,fail=0; + size_t tmplen=0; + /* Count the number of entries. */ + while (child) numentries++,child=child->next; + /* Explicitly handle empty object case */ + if (!numentries) + { + if (p) out=ensure(p,fmt?depth+4:3); + else out=(char*)cJSON_malloc(fmt?depth+4:3); + if (!out) return 0; + ptr=out;*ptr++='{'; + if (fmt) {*ptr++='\n';for (i=0;ioffset; + len=fmt?2:1; ptr=ensure(p,len+1); if (!ptr) return 0; + *ptr++='{'; if (fmt) *ptr++='\n'; *ptr=0; p->offset+=len; + child=item->child;depth++; + while (child) + { + if (fmt) + { + ptr=ensure(p,depth); if (!ptr) return 0; + for (j=0;joffset+=depth; + } + print_string_ptr(child->string,p); + p->offset=update(p); + + len=fmt?2:1; + ptr=ensure(p,len); if (!ptr) return 0; + *ptr++=':';if (fmt) *ptr++='\t'; + p->offset+=len; + + print_value(child,depth,fmt,p); + p->offset=update(p); + + len=(fmt?1:0)+(child->next?1:0); + ptr=ensure(p,len+1); if (!ptr) return 0; + if (child->next) *ptr++=','; + if (fmt) *ptr++='\n';*ptr=0; + p->offset+=len; + child=child->next; + } + ptr=ensure(p,fmt?(depth+1):2); if (!ptr) return 0; + if (fmt) for (i=0;ibuffer)+i; + } + else + { + /* Allocate space for the names and the objects */ + entries=(char**)cJSON_malloc(numentries*sizeof(char*)); + if (!entries) return 0; + names=(char**)cJSON_malloc(numentries*sizeof(char*)); + if (!names) {cJSON_free(entries);return 0;} + memset(entries,0,sizeof(char*)*numentries); + memset(names,0,sizeof(char*)*numentries); + + /* Collect all the results into our arrays: */ + child=item->child;depth++;if (fmt) len+=depth; + while (child && !fail) + { + names[i]=str=print_string_ptr(child->string,0); + entries[i++]=ret=print_value(child,depth,fmt,0); + if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1; + child=child->next; + } + + /* Try to allocate the output string */ + if (!fail) out=(char*)cJSON_malloc(len); + if (!out) fail=1; + + /* Handle failure */ + if (fail) + { + for (i=0;ichild;int i=0;while(c)i++,c=c->next;return i;} +cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;} +cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;} +int cJSON_HasObjectItem(cJSON *object,const char *string) { + cJSON *c=object->child; + while (c ) + { + if(cJSON_strcasecmp(c->string,string)==0){ + return 1; + } + c=c->next; + } + return 0; +} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;} +/* Utility for handling references. */ +static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;} + +/* Add item to array/object. */ +void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}} +void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);} +void cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (!(item->type&cJSON_StringIsConst) && item->string) cJSON_free(item->string);item->string=(char*)string;item->type|=cJSON_StringIsConst;cJSON_AddItemToArray(object,item);} +void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));} +void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));} + +cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0; + if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;} +void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));} +cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;} +void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));} + +/* Replace array/object items with new ones. */ +void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) {cJSON_AddItemToArray(array,newitem);return;} + newitem->next=c;newitem->prev=c->prev;c->prev=newitem;if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;} +void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return; + newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem; + if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);} +void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}} + +/* Create basic types: */ +cJSON *cJSON_CreateNull(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;} +cJSON *cJSON_CreateTrue(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;} +cJSON *cJSON_CreateFalse(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;} +cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;} +cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;} +cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;} +cJSON *cJSON_CreateArray(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;} +cJSON *cJSON_CreateObject(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;} + +/* Create Arrays: */ +cJSON *cJSON_CreateIntArray(const int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateFloatArray(const float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateDoubleArray(const double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} + +/* Duplication */ +cJSON *cJSON_Duplicate(cJSON *item,int recurse) +{ + cJSON *newitem,*cptr,*nptr=0,*newchild; + /* Bail on bad ptr */ + if (!item) return 0; + /* Create new item */ + newitem=cJSON_New_Item(); + if (!newitem) return 0; + /* Copy over all vars */ + newitem->type=item->type&(~cJSON_IsReference),newitem->valueint=item->valueint,newitem->valuedouble=item->valuedouble; + if (item->valuestring) {newitem->valuestring=cJSON_strdup(item->valuestring); if (!newitem->valuestring) {cJSON_Delete(newitem);return 0;}} + if (item->string) {newitem->string=cJSON_strdup(item->string); if (!newitem->string) {cJSON_Delete(newitem);return 0;}} + /* If non-recursive, then we're done! */ + if (!recurse) return newitem; + /* Walk the ->next chain for the child. */ + cptr=item->child; + while (cptr) + { + newchild=cJSON_Duplicate(cptr,1); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) {cJSON_Delete(newitem);return 0;} + if (nptr) {nptr->next=newchild,newchild->prev=nptr;nptr=newchild;} /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + else {newitem->child=newchild;nptr=newchild;} /* Set newitem->child and move to it */ + cptr=cptr->next; + } + return newitem; +} + +void cJSON_Minify(char *json) +{ + char *into=json; + while (*json) + { + if (*json==' ') json++; + else if (*json=='\t') json++; /* Whitespace characters. */ + else if (*json=='\r') json++; + else if (*json=='\n') json++; + else if (*json=='/' && json[1]=='/') while (*json && *json!='\n') json++; /* double-slash comments, to end of line. */ + else if (*json=='/' && json[1]=='*') {while (*json && !(*json=='*' && json[1]=='/')) json++;json+=2;} /* multiline comments. */ + else if (*json=='\"'){*into++=*json++;while (*json && *json!='\"'){if (*json=='\\') *into++=*json++;*into++=*json++;}*into++=*json++;} /* string literals, which are \" sensitive. */ + else *into++=*json++; /* All other characters. */ + } + *into=0; /* and null-terminate. */ +} diff --git a/demo/os/nuttx-spiflash/apps/system/easyflash/plugins/types/struct2json/src/s2j.c b/demo/os/nuttx-spiflash/apps/system/easyflash/plugins/types/struct2json/src/s2j.c new file mode 100644 index 0000000..32ff16e --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/system/easyflash/plugins/types/struct2json/src/s2j.c @@ -0,0 +1,52 @@ +/* + * This file is part of the struct2json Library. + * + * Copyright (c) 2015, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: Initialize interface for this library. + * Created on: 2015-10-14 + */ + +#include +#include + +S2jHook s2jHook = { + .malloc_fn = malloc, + .free_fn = free, +}; + +/** + * struct2json library initialize + * @note It will initialize cJSON library hooks. + */ +void s2j_init(S2jHook *hook) { + /* initialize cJSON library */ + cJSON_InitHooks((cJSON_Hooks *)hook); + /* initialize hooks */ + if (hook) { + s2jHook.malloc_fn = (hook->malloc_fn) ? hook->malloc_fn : malloc; + s2jHook.free_fn = (hook->free_fn) ? hook->free_fn : free; + } else { + hook->malloc_fn = malloc; + hook->free_fn = free; + } +} diff --git a/demo/os/nuttx-spiflash/apps/system/easyflash/port/ef_port.c b/demo/os/nuttx-spiflash/apps/system/easyflash/port/ef_port.c new file mode 100644 index 0000000..e7e354b --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/system/easyflash/port/ef_port.c @@ -0,0 +1,232 @@ +/* + * This file is part of the EasyFlash Library. + * + * Copyright (c) 2015, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: Portable interface for stm32f10x platform. + * Created on: 2015-01-16 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* default environment variables set for user */ +static const ef_env default_env_set[] = { + {"iap_need_copy_app","0"}, + {"iap_copy_app_size","0"}, + {"stop_in_bootloader","0"}, + {"device_id","1"}, + {"boot_times","0"}, +}; + +static char log_buf[128]; +static pthread_mutex_t env_cache_lock; +static FAR struct mtd_dev_s *mtd_dev; +static const char *devpath="/dev/mtd0"; + +/** + * Flash port for hardware initialize. + * + * @param default_env default ENV set for user + * @param default_env_size default ENV size + * + * @return result + */ +EfErrCode ef_port_init(ef_env const **default_env, size_t *default_env_size) { + EfErrCode result = EF_NO_ERR; + int fd; + + *default_env = default_env_set; + *default_env_size = sizeof(default_env_set) / sizeof(default_env_set[0]); + + pthread_mutex_init(&env_cache_lock, NULL); + + if (bchdev_register("/dev/mtdblock0", devpath, false) < 0){ + result = EF_ENV_INIT_FAILED; + fprintf(stderr, "ERROR: bchdev_register /dev/mtd0 failed: %d\n", result); + return result; + } + + fd = open(devpath, O_RDWR); + if(fd < 0){ + result = EF_ENV_INIT_FAILED; + fprintf(stderr, "ERROR: Failed to open %s: %d\n", devpath, result); + return result; + } + + if (ioctl(fd, MTDIOC_GETMTDDEV, (unsigned long) ((uintptr_t)&mtd_dev)) < 0){ + result = EF_ENV_INIT_FAILED; + fprintf(stderr, "ERROR: Failed to set pintype on %s: %d\n", + devpath, result); + } + close(fd); + return result; +} + +/** + * Read data from flash. + * @note This operation's units is word. + * + * @param addr flash address + * @param buf buffer to store read data + * @param size read bytes size + * + * @return result + */ +EfErrCode ef_port_read(uint32_t addr, uint32_t *buf, size_t size) { + EfErrCode result = EF_NO_ERR; + + if(!MTD_READ(mtd_dev, addr, size, (uint8_t *)buf)){ + result = EF_READ_ERR; + } + + return result; +} + +/** + * Erase data on flash. + * @note This operation is irreversible. + * @note This operation's units is different which on many chips. + * + * @param addr flash address + * @param size erase bytes size + * + * @return result + */ +EfErrCode ef_port_erase(uint32_t addr, size_t size) { + EfErrCode result = EF_NO_ERR; + + /* make sure the start address is a multiple of FLASH_ERASE_MIN_SIZE */ + EF_ASSERT(addr % EF_ERASE_MIN_SIZE == 0); + + if(!MTD_ERASE(mtd_dev, addr/EF_ERASE_MIN_SIZE, size/EF_ERASE_MIN_SIZE)){ + result = EF_ERASE_ERR; + } + + return result; +} + +/** + * Write data to flash. + * @note This operation's units is word. + * @note This operation must after erase. @see flash_erase. + * + * @param addr flash address + * @param buf the write data buffer + * @param size write bytes size + * + * @return result + */ +EfErrCode ef_port_write(uint32_t addr, const uint32_t *buf, size_t size) { + EfErrCode result = EF_NO_ERR; + + if(!MTD_WRITE(mtd_dev, addr, size, (const uint8_t *)buf)){ + result = EF_WRITE_ERR; + } + + return result; +} + +/** + * lock the ENV ram cache + */ +void ef_port_env_lock(void) { + pthread_mutex_lock(&env_cache_lock); +} + +/** + * unlock the ENV ram cache + */ +void ef_port_env_unlock(void) { + pthread_mutex_unlock(&env_cache_lock); +} + + +/** + * This function is print flash debug info. + * + * @param file the file which has call this function + * @param line the line number which has call this function + * @param format output format + * @param ... args + * + */ +void ef_log_debug(const char *file, const long line, const char *format, ...) { + +#ifdef PRINT_DEBUG + + va_list args; + + /* args point to the first variable parameter */ + va_start(args, format); + printf("[Flash](%s:%ld) ", file, line); + /* must use vprintf to print */ + vsprintf(log_buf, format, args); + printf("%s", log_buf); + va_end(args); + +#endif + +} + +/** + * This function is print flash routine info. + * + * @param format output format + * @param ... args + */ +void ef_log_info(const char *format, ...) { + va_list args; + + /* args point to the first variable parameter */ + va_start(args, format); + printf("[Flash]"); + /* must use vprintf to print */ + vsprintf(log_buf, format, args); + printf("%s", log_buf); + va_end(args); +} +/** + * This function is print flash non-package info. + * + * @param format output format + * @param ... args + */ +void ef_print(const char *format, ...) { + va_list args; + + /* args point to the first variable parameter */ + va_start(args, format); + /* must use vprintf to print */ + vsprintf(log_buf, format, args); + printf("%s", log_buf); + va_end(args); +} diff --git a/demo/os/nuttx-spiflash/apps/system/easyflash/src/easyflash.c b/demo/os/nuttx-spiflash/apps/system/easyflash/src/easyflash.c new file mode 100644 index 0000000..7455886 --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/system/easyflash/src/easyflash.c @@ -0,0 +1,109 @@ +/* + * This file is part of the EasyFlash Library. + * + * Copyright (c) 2014-2019, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: Initialize interface for this library. + * Created on: 2014-09-09 + */ + +/* + * + * This all Backup Area Flash storage index. All used flash area configure is under here. + * |----------------------------| Storage Size + * | Environment variables area | ENV area size @see ENV_AREA_SIZE + * |----------------------------| + * | Saved log area | Log area size @see LOG_AREA_SIZE + * |----------------------------| + * |(IAP)Downloaded application | IAP already downloaded application, unfixed size + * |----------------------------| + * + * @note all area sizes must be aligned with EF_ERASE_MIN_SIZE + * + * The EasyFlash add the NG (Next Generation) mode start from V4.0. All old mode before V4.0, called LEGACY mode. + * + * - NG (Next Generation) mode is default mode from V4.0. It's easy to settings, only defined the ENV_AREA_SIZE. + * - The LEGACY mode has been DEPRECATED. It is NOT RECOMMENDED to continue using. + * Beacuse it will use ram to buffer the ENV and spend more flash erase times. + * If you want use it please using the V3.X version. + */ + +#include + +#if !defined(EF_START_ADDR) +#error "Please configure backup area start address (in ef_cfg.h)" +#endif + +#if !defined(EF_ERASE_MIN_SIZE) +#error "Please configure minimum size of flash erasure (in ef_cfg.h)" +#endif + +/** + * EasyFlash system initialize. + * + * @return result + */ +EfErrCode easyflash_init(void) { + extern EfErrCode ef_port_init(ef_env const **default_env, size_t *default_env_size); + extern EfErrCode ef_env_init(ef_env const *default_env, size_t default_env_size); + extern EfErrCode ef_iap_init(void); + extern EfErrCode ef_log_init(void); + + size_t default_env_set_size = 0; + const ef_env *default_env_set; + EfErrCode result = EF_NO_ERR; + static bool init_ok = false; + + if (init_ok) { + return EF_NO_ERR; + } + + result = ef_port_init(&default_env_set, &default_env_set_size); + +#ifdef EF_USING_ENV + if (result == EF_NO_ERR) { + result = ef_env_init(default_env_set, default_env_set_size); + } +#endif + +#ifdef EF_USING_IAP + if (result == EF_NO_ERR) { + result = ef_iap_init(); + } +#endif + +#ifdef EF_USING_LOG + if (result == EF_NO_ERR) { + result = ef_log_init(); + } +#endif + + if (result == EF_NO_ERR) { + init_ok = true; + EF_INFO("EasyFlash V%s is initialize success.\n", EF_SW_VERSION); + } else { + EF_INFO("EasyFlash V%s is initialize fail.\n", EF_SW_VERSION); + } + EF_INFO("You can get the latest version on https://github.com/armink/EasyFlash .\n"); + + return result; +} diff --git a/demo/os/nuttx-spiflash/apps/system/easyflash/src/ef_env.c b/demo/os/nuttx-spiflash/apps/system/easyflash/src/ef_env.c new file mode 100644 index 0000000..3bde326 --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/system/easyflash/src/ef_env.c @@ -0,0 +1,1841 @@ +/* + * This file is part of the EasyFlash Library. + * + * Copyright (c) 2019, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: Environment variables operating interface. This is the Next Generation version. + * Created on: 2019-02-02 + */ + +#include +#include + +#if defined(EF_USING_ENV) && !defined(EF_ENV_USING_LEGACY_MODE) + +#ifndef EF_WRITE_GRAN +#error "Please configure flash write granularity (in ef_cfg.h)" +#endif + +#if EF_WRITE_GRAN != 1 && EF_WRITE_GRAN != 8 && EF_WRITE_GRAN != 32 && EF_WRITE_GRAN != 64 +#error "the write gran can be only setting as 1, 8, 32 and 64" +#endif + +/* magic word(`E`, `F`, `4`, `0`) */ +#define SECTOR_MAGIC_WORD 0x30344645 +/* magic word(`K`, `V`, `4`, `0`) */ +#define ENV_MAGIC_WORD 0x3034564B + +/* the using status sector table length */ +#ifndef USING_SECTOR_TABLE_LEN +#define USING_SECTOR_TABLE_LEN 4 +#endif + +/* the string ENV value buffer size for legacy ef_get_env() function */ +#ifndef EF_STR_ENV_VALUE_MAX_SIZE +#define EF_STR_ENV_VALUE_MAX_SIZE 128 +#endif + +/* the sector remain threshold before full status */ +#ifndef EF_SEC_REMAIN_THRESHOLD +#define EF_SEC_REMAIN_THRESHOLD (ENV_HDR_DATA_SIZE + EF_ENV_NAME_MAX) +#endif + +/* the total remain empty sector threshold before GC */ +#ifndef EF_GC_EMPTY_SEC_THRESHOLD +#define EF_GC_EMPTY_SEC_THRESHOLD 1 +#endif + +/* the ENV cache table size, it will improve ENV search speed when using cache */ +#ifndef EF_ENV_CACHE_TABLE_SIZE +#define EF_ENV_CACHE_TABLE_SIZE 16 +#endif + +/* the sector cache table size, it will improve ENV save speed when using cache */ +#ifndef EF_SECTOR_CACHE_TABLE_SIZE +#define EF_SECTOR_CACHE_TABLE_SIZE 4 +#endif + +#if EF_ENV_CACHE_TABLE_SIZE > 0xFFFF +#error "The ENV cache table size must less than 0xFFFF" +#endif + +#if (EF_ENV_CACHE_TABLE_SIZE > 0) && (EF_SECTOR_CACHE_TABLE_SIZE > 0) +#define EF_ENV_USING_CACHE +#endif + +/* the sector is not combined value */ +#define SECTOR_NOT_COMBINED 0xFFFFFFFF +/* the next address is get failed */ +#define FAILED_ADDR 0xFFFFFFFF + +/* Return the most contiguous size aligned at specified width. RT_ALIGN(13, 4) + * would return 16. + */ +#define EF_ALIGN(size, align) (((size) + (align) - 1) & ~((align) - 1)) +/* align by write granularity */ +#define EF_WG_ALIGN(size) (EF_ALIGN(size, (EF_WRITE_GRAN + 7)/8)) +/** + * Return the down number of aligned at specified width. RT_ALIGN_DOWN(13, 4) + * would return 12. + */ +#define EF_ALIGN_DOWN(size, align) ((size) & ~((align) - 1)) +/* align down by write granularity */ +#define EF_WG_ALIGN_DOWN(size) (EF_ALIGN_DOWN(size, (EF_WRITE_GRAN + 7)/8)) + +#if (EF_WRITE_GRAN == 1) +#define STATUS_TABLE_SIZE(status_number) ((status_number * EF_WRITE_GRAN + 7)/8) +#else +#define STATUS_TABLE_SIZE(status_number) (((status_number - 1) * EF_WRITE_GRAN + 7)/8) +#endif + +#define STORE_STATUS_TABLE_SIZE STATUS_TABLE_SIZE(SECTOR_STORE_STATUS_NUM) +#define DIRTY_STATUS_TABLE_SIZE STATUS_TABLE_SIZE(SECTOR_DIRTY_STATUS_NUM) +#define ENV_STATUS_TABLE_SIZE STATUS_TABLE_SIZE(ENV_STATUS_NUM) + +#define SECTOR_SIZE EF_ERASE_MIN_SIZE +#define SECTOR_NUM (ENV_AREA_SIZE / (EF_ERASE_MIN_SIZE)) + +#if (SECTOR_NUM < 2) +#error "The sector number must lager then or equal to 2" +#endif + +#if (EF_GC_EMPTY_SEC_THRESHOLD == 0 || EF_GC_EMPTY_SEC_THRESHOLD >= SECTOR_NUM) +#error "There is at least one empty sector for GC." +#endif + +#define SECTOR_HDR_DATA_SIZE (EF_WG_ALIGN(sizeof(struct sector_hdr_data))) +#define SECTOR_DIRTY_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->status_table.dirty)) +#define ENV_HDR_DATA_SIZE (EF_WG_ALIGN(sizeof(struct env_hdr_data))) +#define ENV_MAGIC_OFFSET ((unsigned long)(&((struct env_hdr_data *)0)->magic)) +#define ENV_LEN_OFFSET ((unsigned long)(&((struct env_hdr_data *)0)->len)) +#define ENV_NAME_LEN_OFFSET ((unsigned long)(&((struct env_hdr_data *)0)->name_len)) + +#define VER_NUM_ENV_NAME "__ver_num__" + +enum sector_store_status { + SECTOR_STORE_UNUSED, + SECTOR_STORE_EMPTY, + SECTOR_STORE_USING, + SECTOR_STORE_FULL, + SECTOR_STORE_STATUS_NUM, +}; +typedef enum sector_store_status sector_store_status_t; + +enum sector_dirty_status { + SECTOR_DIRTY_UNUSED, + SECTOR_DIRTY_FALSE, + SECTOR_DIRTY_TRUE, + SECTOR_DIRTY_GC, + SECTOR_DIRTY_STATUS_NUM, +}; +typedef enum sector_dirty_status sector_dirty_status_t; + +struct sector_hdr_data { + struct { + uint8_t store[STORE_STATUS_TABLE_SIZE]; /**< sector store status @see sector_store_status_t */ + uint8_t dirty[DIRTY_STATUS_TABLE_SIZE]; /**< sector dirty status @see sector_dirty_status_t */ + } status_table; + uint32_t magic; /**< magic word(`E`, `F`, `4`, `0`) */ + uint32_t combined; /**< the combined next sector number, 0xFFFFFFFF: not combined */ + uint32_t reserved; +}; +typedef struct sector_hdr_data *sector_hdr_data_t; + +struct sector_meta_data { + bool check_ok; /**< sector header check is OK */ + struct { + sector_store_status_t store; /**< sector store status @see sector_store_status_t */ + 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_env; /**< the next empty ENV node start address */ +}; +typedef struct sector_meta_data *sector_meta_data_t; + +struct env_hdr_data { + uint8_t status_table[ENV_STATUS_TABLE_SIZE]; /**< ENV node status, @see node_status_t */ + uint32_t magic; /**< magic word(`K`, `V`, `4`, `0`) */ + uint32_t len; /**< ENV node total length (header + name + value), must align by EF_WRITE_GRAN */ + uint32_t crc32; /**< ENV node crc32(name_len + data_len + name + value) */ + uint8_t name_len; /**< name length */ + uint32_t value_len; /**< value length */ +}; +typedef struct env_hdr_data *env_hdr_data_t; + +struct env_cache_node { + uint16_t name_crc; /**< ENV name's CRC32 low 16bit value */ + uint16_t active; /**< ENV node access active degree */ + uint32_t addr; /**< ENV node address */ +}; +typedef struct env_cache_node *env_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; + +static void gc_collect(void); + +/* ENV start address in flash */ +static uint32_t env_start_addr = 0; +/* default ENV set, must be initialized by user */ +static ef_env const *default_env_set; +/* default ENV set size, must be initialized by user */ +static size_t default_env_set_size = 0; +/* initialize OK flag */ +static bool init_ok = false; +/* request a GC check */ +static bool gc_request = false; +/* is in recovery check status when first reboot */ +static bool in_recovery_check = false; + +#ifdef EF_ENV_USING_CACHE +/* ENV cache table */ +struct env_cache_node env_cache_table[EF_ENV_CACHE_TABLE_SIZE] = { 0 }; +/* sector cache table, it caching the sector info which status is current using */ +struct sector_cache_node sector_cache_table[EF_SECTOR_CACHE_TABLE_SIZE] = { 0 }; +#endif /* EF_ENV_USING_CACHE */ + +static size_t 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, STATUS_TABLE_SIZE(status_num)); + if (status_index > 0) { +#if (EF_WRITE_GRAN == 1) + byte_index = (status_index - 1) / 8; + status_table[byte_index] &= ~(0x80 >> ((status_index - 1) % 8)); +#else + byte_index = (status_index - 1) * (EF_WRITE_GRAN / 8); + status_table[byte_index] = 0x00; +#endif /* EF_WRITE_GRAN == 1 */ + } + + return byte_index; +} + +static size_t 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 (EF_WRITE_GRAN == 1) + if ((status_table[status_num / 8] & (0x80 >> (status_num % 8))) == 0x00) { + break; + } +#else /* (EF_WRITE_GRAN == 8) || (EF_WRITE_GRAN == 32) || (EF_WRITE_GRAN == 64) */ + if (status_table[status_num * EF_WRITE_GRAN / 8] == 0x00) { + break; + } +#endif /* EF_WRITE_GRAN == 1 */ + i++; + } + + return status_num_bak - i; +} + +static EfErrCode write_status(uint32_t addr, uint8_t status_table[], size_t status_num, size_t status_index) +{ + EfErrCode result = EF_NO_ERR; + size_t byte_index; + + EF_ASSERT(status_index < status_num); + EF_ASSERT(status_table); + + /* set the status first */ + byte_index = 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 EF_NO_ERR; + } +#if (EF_WRITE_GRAN == 1) + result = ef_port_write(addr + byte_index, (uint32_t *)&status_table[byte_index], 1); +#else /* (EF_WRITE_GRAN == 8) || (EF_WRITE_GRAN == 32) || (EF_WRITE_GRAN == 64) */ + /* write the status by write granularity + * some flash (like stm32 onchip) NOT supported repeated write before erase */ + result = ef_port_write(addr + byte_index, (uint32_t *) &status_table[byte_index], EF_WRITE_GRAN / 8); +#endif /* EF_WRITE_GRAN == 1 */ + + return result; +} + +static size_t read_status(uint32_t addr, uint8_t status_table[], size_t total_num) +{ + EF_ASSERT(status_table); + + ef_port_read(addr, (uint32_t *) status_table, STATUS_TABLE_SIZE(total_num)); + + return get_status(status_table, total_num); +} + +#ifdef EF_ENV_USING_CACHE +/* + * It's only caching the current using status sector's empty_addr + */ +static void update_sector_cache(uint32_t sec_addr, uint32_t empty_addr) +{ + size_t i, empty_index = EF_SECTOR_CACHE_TABLE_SIZE; + + for (i = 0; i < EF_SECTOR_CACHE_TABLE_SIZE; i++) { + if ((empty_addr > sec_addr) && (empty_addr < sec_addr + SECTOR_SIZE)) { + /* update the sector empty_addr in cache */ + if (sector_cache_table[i].addr == sec_addr) { + sector_cache_table[i].addr = sec_addr; + sector_cache_table[i].empty_addr = empty_addr; + return; + } else if ((sector_cache_table[i].addr == FAILED_ADDR) && (empty_index == EF_SECTOR_CACHE_TABLE_SIZE)) { + empty_index = i; + } + } else if (sector_cache_table[i].addr == sec_addr) { + /* delete the sector which status is not current using */ + sector_cache_table[i].addr = FAILED_ADDR; + return; + } + } + /* add the sector empty_addr to cache */ + if (empty_index < EF_SECTOR_CACHE_TABLE_SIZE) { + sector_cache_table[empty_index].addr = sec_addr; + sector_cache_table[empty_index].empty_addr = empty_addr; + } +} + +/* + * Get sector info from cache. It's return true when cache is hit. + */ +static bool get_sector_from_cache(uint32_t sec_addr, uint32_t *empty_addr) +{ + size_t i; + + for (i = 0; i < EF_SECTOR_CACHE_TABLE_SIZE; i++) { + if (sector_cache_table[i].addr == sec_addr) { + if (empty_addr) { + *empty_addr = sector_cache_table[i].empty_addr; + } + return true; + } + } + + return false; +} + +static void update_env_cache(const char *name, size_t name_len, uint32_t addr) +{ + size_t i, empty_index = EF_ENV_CACHE_TABLE_SIZE, min_activity_index = EF_ENV_CACHE_TABLE_SIZE; + uint16_t name_crc = (uint16_t) (ef_calc_crc32(0, name, name_len) >> 16), min_activity = 0xFFFF; + + for (i = 0; i < EF_ENV_CACHE_TABLE_SIZE; i++) { + if (addr != FAILED_ADDR) { + /* update the ENV address in cache */ + if (env_cache_table[i].name_crc == name_crc) { + env_cache_table[i].addr = addr; + return; + } else if ((env_cache_table[i].addr == FAILED_ADDR) && (empty_index == EF_ENV_CACHE_TABLE_SIZE)) { + empty_index = i; + } else if (env_cache_table[i].addr != FAILED_ADDR) { + if (env_cache_table[i].active > 0) { + env_cache_table[i].active--; + } + if (env_cache_table[i].active < min_activity) { + min_activity_index = i; + min_activity = env_cache_table[i].active; + } + } + } else if (env_cache_table[i].name_crc == name_crc) { + /* delete the ENV */ + env_cache_table[i].addr = FAILED_ADDR; + env_cache_table[i].active = 0; + return; + } + } + /* add the ENV to cache, using LRU (Least Recently Used) like algorithm */ + if (empty_index < EF_ENV_CACHE_TABLE_SIZE) { + env_cache_table[empty_index].addr = addr; + env_cache_table[empty_index].name_crc = name_crc; + env_cache_table[empty_index].active = 0; + } else if (min_activity_index < EF_ENV_CACHE_TABLE_SIZE) { + env_cache_table[min_activity_index].addr = addr; + env_cache_table[min_activity_index].name_crc = name_crc; + env_cache_table[min_activity_index].active = 0; + } +} + +/* + * Get ENV info from cache. It's return true when cache is hit. + */ +static bool get_env_from_cache(const char *name, size_t name_len, uint32_t *addr) +{ + size_t i; + uint16_t name_crc = (uint16_t) (ef_calc_crc32(0, name, name_len) >> 16); + + for (i = 0; i < EF_ENV_CACHE_TABLE_SIZE; i++) { + if ((env_cache_table[i].addr != FAILED_ADDR) && (env_cache_table[i].name_crc == name_crc)) { + char saved_name[EF_ENV_NAME_MAX]; + /* read the ENV name in flash */ + ef_port_read(env_cache_table[i].addr + ENV_HDR_DATA_SIZE, (uint32_t *) saved_name, EF_ENV_NAME_MAX); + if (!strncmp(name, saved_name, name_len)) { + *addr = env_cache_table[i].addr; + if (env_cache_table[i].active >= 0xFFFF - EF_ENV_CACHE_TABLE_SIZE) { + env_cache_table[i].active = 0xFFFF; + } else { + env_cache_table[i].active += EF_ENV_CACHE_TABLE_SIZE; + } + return true; + } + } + } + + return false; +} +#endif /* EF_ENV_USING_CACHE */ + +/* + * find the continue 0xFF flash address to end address + */ +static uint32_t continue_ff_addr(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; + } + ef_port_read(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 EF_WG_ALIGN(addr); + } else { + return end; + } +} + +/* + * find the next ENV address by magic word on the flash + */ +static uint32_t find_next_env_addr(uint32_t start, uint32_t end) +{ + uint8_t buf[32]; + uint32_t start_bak = start, i; + uint32_t magic; + +#ifdef EF_ENV_USING_CACHE + uint32_t empty_env; + + if (get_sector_from_cache(EF_ALIGN_DOWN(start, SECTOR_SIZE), &empty_env) && start == empty_env) { + return FAILED_ADDR; + } +#endif /* EF_ENV_USING_CACHE */ + + for (; start < end; start += (sizeof(buf) - sizeof(uint32_t))) { + ef_port_read(start, (uint32_t *) buf, sizeof(buf)); + for (i = 0; i < sizeof(buf) - sizeof(uint32_t) && start + i < end; i++) { +#ifndef EF_BIG_ENDIAN /* Little Endian Order */ + magic = buf[i] + (buf[i + 1] << 8) + (buf[i + 2] << 16) + (buf[i + 3] << 24); +#else /* Big Endian Order */ + magic = buf[i + 3] + (buf[i + 2] << 8) + (buf[i + 1] << 16) + (buf[i] << 24); +#endif + if (magic == ENV_MAGIC_WORD && (start + i - ENV_MAGIC_OFFSET) >= start_bak) { + return start + i - ENV_MAGIC_OFFSET; + } + } + } + + return FAILED_ADDR; +} + +static uint32_t get_next_env_addr(sector_meta_data_t sector, env_node_obj_t pre_env) +{ + uint32_t addr = FAILED_ADDR; + + if (sector->status.store == SECTOR_STORE_EMPTY) { + return FAILED_ADDR; + } + + if (pre_env->addr.start == FAILED_ADDR) { + /* the first ENV address */ + addr = sector->addr + SECTOR_HDR_DATA_SIZE; + } else { + if (pre_env->addr.start <= sector->addr + SECTOR_SIZE) { + if (pre_env->crc_is_ok) { + addr = pre_env->addr.start + pre_env->len; + } else { + /* when pre_env CRC check failed, maybe the flash has error data + * find_next_env_addr after pre_env address */ + addr = pre_env->addr.start + EF_WG_ALIGN(1); + } + /* check and find next ENV address */ + addr = find_next_env_addr(addr, sector->addr + SECTOR_SIZE - SECTOR_HDR_DATA_SIZE); + + if (addr > sector->addr + SECTOR_SIZE || pre_env->len == 0) { + //TODO ��������ģʽ + return FAILED_ADDR; + } + } else { + /* no ENV */ + return FAILED_ADDR; + } + } + + return addr; +} + +static EfErrCode read_env(env_node_obj_t env) +{ + struct env_hdr_data env_hdr; + uint8_t buf[32]; + uint32_t calc_crc32 = 0, crc_data_len, env_name_addr; + EfErrCode result = EF_NO_ERR; + size_t len, size; + /* read ENV header raw data */ + ef_port_read(env->addr.start, (uint32_t *)&env_hdr, sizeof(struct env_hdr_data)); + env->status = (env_status_t) get_status(env_hdr.status_table, ENV_STATUS_NUM); + env->len = env_hdr.len; + + if (env->len == ~0UL || env->len > ENV_AREA_SIZE || env->len < ENV_NAME_LEN_OFFSET) { + /* the ENV length was not write, so reserved the meta data for current ENV */ + env->len = ENV_HDR_DATA_SIZE; + if (env->status != ENV_ERR_HDR) { + env->status = ENV_ERR_HDR; + EF_DEBUG("Error: The ENV @0x%08X length has an error.\n", env->addr.start); + write_status(env->addr.start, env_hdr.status_table, ENV_STATUS_NUM, ENV_ERR_HDR); + } + env->crc_is_ok = false; + return EF_READ_ERR; + } else if (env->len > SECTOR_SIZE - SECTOR_HDR_DATA_SIZE && env->len < ENV_AREA_SIZE) { + //TODO ��������ģʽ������д�볤��û��д������ + EF_ASSERT(0); + } + + /* CRC32 data len(header.name_len + header.value_len + name + value) */ + crc_data_len = env->len - ENV_NAME_LEN_OFFSET; + /* calculate the CRC32 value */ + for (len = 0, size = 0; len < crc_data_len; len += size) { + if (len + sizeof(buf) < crc_data_len) { + size = sizeof(buf); + } else { + size = crc_data_len - len; + } + + ef_port_read(env->addr.start + ENV_NAME_LEN_OFFSET + len, (uint32_t *) buf, EF_WG_ALIGN(size)); + calc_crc32 = ef_calc_crc32(calc_crc32, buf, size); + } + /* check CRC32 */ + if (calc_crc32 != env_hdr.crc32) { + env->crc_is_ok = false; + result = EF_READ_ERR; + } else { + env->crc_is_ok = true; + /* the name is behind aligned ENV header */ + env_name_addr = env->addr.start + ENV_HDR_DATA_SIZE; + ef_port_read(env_name_addr, (uint32_t *) env->name, EF_WG_ALIGN(env_hdr.name_len)); + /* the value is behind aligned name */ + env->addr.value = env_name_addr + EF_WG_ALIGN(env_hdr.name_len); + env->value_len = env_hdr.value_len; + env->name_len = env_hdr.name_len; + } + + return result; +} + +static EfErrCode read_sector_meta_data(uint32_t addr, sector_meta_data_t sector, bool traversal) +{ + EfErrCode result = EF_NO_ERR; + struct sector_hdr_data sec_hdr; + + EF_ASSERT(addr % SECTOR_SIZE == 0); + EF_ASSERT(sector); + + /* read sector header raw data */ + ef_port_read(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; + sector->combined = SECTOR_NOT_COMBINED; + return EF_ENV_INIT_FAILED; + } + sector->check_ok = true; + /* get other sector meta data */ + sector->combined = sec_hdr.combined; + sector->status.store = (sector_store_status_t) get_status(sec_hdr.status_table.store, SECTOR_STORE_STATUS_NUM); + sector->status.dirty = (sector_dirty_status_t) get_status(sec_hdr.status_table.dirty, SECTOR_DIRTY_STATUS_NUM); + /* traversal all ENV and calculate the remain space size */ + if (traversal) { + sector->remain = 0; + sector->empty_env = sector->addr + SECTOR_HDR_DATA_SIZE; + if (sector->status.store == SECTOR_STORE_EMPTY) { + sector->remain = SECTOR_SIZE - SECTOR_HDR_DATA_SIZE; + } else if (sector->status.store == SECTOR_STORE_USING) { + struct env_node_obj env_meta; + +#ifdef EF_ENV_USING_CACHE + if (get_sector_from_cache(addr, §or->empty_env)) { + sector->remain = SECTOR_SIZE - (sector->empty_env - sector->addr); + return result; + } +#endif /* EF_ENV_USING_CACHE */ + + sector->remain = SECTOR_SIZE - SECTOR_HDR_DATA_SIZE; + env_meta.addr.start = FAILED_ADDR; + while ((env_meta.addr.start = get_next_env_addr(sector, &env_meta)) != FAILED_ADDR) { + read_env(&env_meta); + if (!env_meta.crc_is_ok) { + if (env_meta.status != ENV_PRE_WRITE && env_meta.status!= ENV_ERR_HDR) { + EF_INFO("Error: The ENV (@0x%08X) CRC32 check failed!\n", env_meta.addr.start); + sector->remain = 0; + result = EF_READ_ERR; + break; + } + } + sector->empty_env += env_meta.len; + sector->remain -= env_meta.len; + } + /* check the empty ENV address by read continue 0xFF on flash */ + { + uint32_t ff_addr; + + ff_addr = continue_ff_addr(sector->empty_env, sector->addr + SECTOR_SIZE); + /* check the flash data is clean */ + if (sector->empty_env != ff_addr) { + /* update the sector information */ + sector->empty_env = ff_addr; + sector->remain = SECTOR_SIZE - (ff_addr - sector->addr); + } + } + +#ifdef EF_ENV_USING_CACHE + update_sector_cache(sector->addr, sector->empty_env); +#endif + } + } + + return result; +} + +static uint32_t get_next_sector_addr(sector_meta_data_t pre_sec) +{ + uint32_t next_addr; + + if (pre_sec->addr == FAILED_ADDR) { + return env_start_addr; + } else { + /* check ENV sector combined */ + if (pre_sec->combined == SECTOR_NOT_COMBINED) { + next_addr = pre_sec->addr + SECTOR_SIZE; + } else { + next_addr = pre_sec->addr + pre_sec->combined * SECTOR_SIZE; + } + /* check range */ + if (next_addr < env_start_addr + ENV_AREA_SIZE) { + return next_addr; + } else { + /* no sector */ + return FAILED_ADDR; + } + } +} + +static void env_iterator(env_node_obj_t env, void *arg1, void *arg2, + bool (*callback)(env_node_obj_t env, void *arg1, void *arg2)) +{ + struct sector_meta_data sector; + uint32_t sec_addr; + + sector.addr = FAILED_ADDR; + /* search all sectors */ + while ((sec_addr = get_next_sector_addr(§or)) != FAILED_ADDR) { + if (read_sector_meta_data(sec_addr, §or, false) != EF_NO_ERR) { + continue; + } + if (callback == NULL) { + continue; + } + /* sector has ENV */ + if (sector.status.store == SECTOR_STORE_USING || sector.status.store == SECTOR_STORE_FULL) { + env->addr.start = FAILED_ADDR; + /* search all ENV */ + while ((env->addr.start = get_next_env_addr(§or, env)) != FAILED_ADDR) { + read_env(env); + /* iterator is interrupted when callback return true */ + if (callback(env, arg1, arg2)) { + return; + } + } + } + } +} + +static bool find_env_cb(env_node_obj_t env, void *arg1, void *arg2) +{ + const char *key = arg1; + bool *find_ok = arg2; + size_t key_len = strlen(key); + + if (key_len != env->name_len) { + return false; + } + /* check ENV */ + if (env->crc_is_ok && env->status == ENV_WRITE && !strncmp(env->name, key, key_len)) { + *find_ok = true; + return true; + } + return false; +} + +static bool find_env_no_cache(const char *key, env_node_obj_t env) +{ + bool find_ok = false; + + env_iterator(env, (void *)key, &find_ok, find_env_cb); + + return find_ok; +} + +static bool find_env(const char *key, env_node_obj_t env) +{ + bool find_ok = false; + +#ifdef EF_ENV_USING_CACHE + size_t key_len = strlen(key); + + if (get_env_from_cache(key, key_len, &env->addr.start)) { + read_env(env); + return true; + } +#endif /* EF_ENV_USING_CACHE */ + + find_ok = find_env_no_cache(key, env); + +#ifdef EF_ENV_USING_CACHE + if (find_ok) { + update_env_cache(key, key_len, env->addr.start); + } +#endif /* EF_ENV_USING_CACHE */ + + return find_ok; +} + +static bool ef_is_str(uint8_t *value, size_t len) +{ +#define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ') + size_t i; + + for (i = 0; i < len; i++) { + if (!__is_print(value[i])) { + return false; + } + } + return true; +} + +static size_t get_env(const char *key, void *value_buf, size_t buf_len, size_t *value_len) +{ + struct env_node_obj env; + size_t read_len = 0; + + if (find_env(key, &env)) { + if (value_len) { + *value_len = env.value_len; + } + if (buf_len > env.value_len) { + read_len = env.value_len; + } else { + read_len = buf_len; + } + if (value_buf){ + ef_port_read(env.addr.value, (uint32_t *) value_buf, read_len); + } + } else if (value_len) { + *value_len = 0; + } + + return read_len; +} + +/** + * Get a ENV object by key name + * + * @param key ENV name + * @param env ENV object + * + * @return TRUE: find the ENV is OK, else return false + */ +bool ef_get_env_obj(const char *key, env_node_obj_t env) +{ + bool find_ok = false; + + if (!init_ok) { + EF_INFO("ENV isn't initialize OK.\n"); + return 0; + } + + /* lock the ENV cache */ + ef_port_env_lock(); + + find_ok = find_env(key, env); + + /* unlock the ENV cache */ + ef_port_env_unlock(); + + return find_ok; +} + +/** + * Get a blob ENV value by key name. + * + * @param key ENV name + * @param value_buf ENV blob buffer + * @param buf_len ENV blob buffer length + * @param saved_value_len return the length of the value saved on the flash, 0: NOT found + * + * @return the actually get size on successful + */ +size_t ef_get_env_blob(const char *key, void *value_buf, size_t buf_len, size_t *saved_value_len) +{ + size_t read_len = 0; + + if (!init_ok) { + EF_INFO("ENV isn't initialize OK.\n"); + return 0; + } + + /* lock the ENV cache */ + ef_port_env_lock(); + + read_len = get_env(key, value_buf, buf_len, saved_value_len); + + /* unlock the ENV cache */ + ef_port_env_unlock(); + + return read_len; +} + +/** + * Get an ENV value by key name. + * + * @note this function is NOT supported reentrant + * @note this function is DEPRECATED + * + * @param key ENV name + * + * @return value + */ +char *ef_get_env(const char *key) +{ + static char value[EF_STR_ENV_VALUE_MAX_SIZE + 1]; + size_t get_size; + + if ((get_size = ef_get_env_blob(key, value, EF_STR_ENV_VALUE_MAX_SIZE, NULL)) > 0) { + /* the return value must be string */ + if (ef_is_str((uint8_t *)value, get_size)) { + value[get_size] = '\0'; + return value; + } else { + EF_INFO("Warning: The ENV value isn't string. Could not be returned\n"); + return NULL; + } + } + + return NULL; +} + +/** + * read the ENV value by ENV object + * + * @param env ENV object + * @param value_buf the buffer for store ENV value + * @param buf_len buffer length + * + * @return the actually read size on successful + */ +size_t ef_read_env_value(env_node_obj_t env, uint8_t *value_buf, size_t buf_len) +{ + size_t read_len = 0; + + EF_ASSERT(env); + EF_ASSERT(value_buf); + + if (!init_ok) { + EF_INFO("ENV isn't initialize OK.\n"); + return 0; + } + + if (env->crc_is_ok) { + /* lock the ENV cache */ + ef_port_env_lock(); + + if (buf_len > env->value_len) { + read_len = env->value_len; + } else { + read_len = buf_len; + } + + ef_port_read(env->addr.value, (uint32_t *) value_buf, read_len); + /* unlock the ENV cache */ + ef_port_env_unlock(); + } + + return read_len; +} + +static EfErrCode write_env_hdr(uint32_t addr, env_hdr_data_t env_hdr) { + EfErrCode result = EF_NO_ERR; + /* write the status will by write granularity */ + result = write_status(addr, env_hdr->status_table, ENV_STATUS_NUM, ENV_PRE_WRITE); + if (result != EF_NO_ERR) { + return result; + } + /* write other header data */ + result = ef_port_write(addr + ENV_MAGIC_OFFSET, &env_hdr->magic, sizeof(struct env_hdr_data) - ENV_MAGIC_OFFSET); + + return result; +} + +static EfErrCode format_sector(uint32_t addr, uint32_t combined_value) +{ + EfErrCode result = EF_NO_ERR; + struct sector_hdr_data sec_hdr; + + EF_ASSERT(addr % SECTOR_SIZE == 0); + + result = ef_port_erase(addr, SECTOR_SIZE); + if (result == EF_NO_ERR) { + /* initialize the header data */ + memset(&sec_hdr, 0xFF, sizeof(struct sector_hdr_data)); + set_status(sec_hdr.status_table.store, SECTOR_STORE_STATUS_NUM, SECTOR_STORE_EMPTY); + set_status(sec_hdr.status_table.dirty, SECTOR_DIRTY_STATUS_NUM, SECTOR_DIRTY_FALSE); + sec_hdr.magic = SECTOR_MAGIC_WORD; + sec_hdr.combined = combined_value; + sec_hdr.reserved = 0xFFFFFFFF; + /* save the header */ + result = ef_port_write(addr, (uint32_t *)&sec_hdr, sizeof(struct sector_hdr_data)); + +#ifdef EF_ENV_USING_CACHE + /* delete the sector cache */ + update_sector_cache(addr, addr + SECTOR_SIZE); +#endif /* EF_ENV_USING_CACHE */ + } + + return result; +} + +static EfErrCode update_sec_status(sector_meta_data_t sector, size_t new_env_len, bool *is_full) +{ + uint8_t status_table[STORE_STATUS_TABLE_SIZE]; + EfErrCode result = EF_NO_ERR; + /* change the current sector status */ + if (sector->status.store == SECTOR_STORE_EMPTY) { + /* change the sector status to using */ + result = write_status(sector->addr, status_table, SECTOR_STORE_STATUS_NUM, SECTOR_STORE_USING); + } else if (sector->status.store == SECTOR_STORE_USING) { + /* check remain size */ + if (sector->remain < EF_SEC_REMAIN_THRESHOLD || sector->remain - new_env_len < EF_SEC_REMAIN_THRESHOLD) { + /* change the sector status to full */ + result = write_status(sector->addr, status_table, SECTOR_STORE_STATUS_NUM, SECTOR_STORE_FULL); + +#ifdef EF_ENV_USING_CACHE + /* delete the sector cache */ + update_sector_cache(sector->addr, sector->addr + SECTOR_SIZE); +#endif /* EF_ENV_USING_CACHE */ + + if (is_full) { + *is_full = true; + } + } else if (is_full) { + *is_full = false; + } + } + + return result; +} + +static void sector_iterator(sector_meta_data_t sector, sector_store_status_t status, void *arg1, void *arg2, + bool (*callback)(sector_meta_data_t sector, void *arg1, void *arg2), bool traversal_env) { + uint32_t sec_addr; + + /* search all sectors */ + sector->addr = FAILED_ADDR; + while ((sec_addr = get_next_sector_addr(sector)) != FAILED_ADDR) { + read_sector_meta_data(sec_addr, sector, false); + if (status == SECTOR_STORE_UNUSED || status == sector->status.store) { + if (traversal_env) { + read_sector_meta_data(sec_addr, sector, traversal_env); + } + /* iterator is interrupted when callback return true */ + if (callback && callback(sector, arg1, arg2)) { + return; + } + } + } +} + +static bool sector_statistics_cb(sector_meta_data_t sector, void *arg1, void *arg2) +{ + size_t *empty_sector = arg1, *using_sector = arg2; + + if (sector->check_ok && sector->status.store == SECTOR_STORE_EMPTY) { + (*empty_sector)++; + } else if (sector->check_ok && sector->status.store == SECTOR_STORE_USING) { + (*using_sector)++; + } + + return false; +} + +static bool alloc_env_cb(sector_meta_data_t sector, void *arg1, void *arg2) +{ + size_t *env_size = arg1; + uint32_t *empty_env = arg2; + + /* 1. sector has space + * 2. the NO dirty sector + * 3. the dirty sector only when the gc_request is false */ + if (sector->check_ok && sector->remain > *env_size + && ((sector->status.dirty == SECTOR_DIRTY_FALSE) + || (sector->status.dirty == SECTOR_DIRTY_TRUE && !gc_request))) { + *empty_env = sector->empty_env; + return true; + } + + return false; +} + +static uint32_t alloc_env(sector_meta_data_t sector, size_t env_size) +{ + uint32_t empty_env = FAILED_ADDR; + size_t empty_sector = 0, using_sector = 0; + + /* sector status statistics */ + sector_iterator(sector, SECTOR_STORE_UNUSED, &empty_sector, &using_sector, sector_statistics_cb, false); + if (using_sector > 0) { + /* alloc the ENV from the using status sector first */ + sector_iterator(sector, SECTOR_STORE_USING, &env_size, &empty_env, alloc_env_cb, true); + } + if (empty_sector > 0 && empty_env == FAILED_ADDR) { + if (empty_sector > EF_GC_EMPTY_SEC_THRESHOLD || gc_request) { + sector_iterator(sector, SECTOR_STORE_EMPTY, &env_size, &empty_env, alloc_env_cb, true); + } else { + /* no space for new ENV now will GC and retry */ + EF_DEBUG("Trigger a GC check after alloc ENV failed.\n"); + gc_request = true; + } + } + + return empty_env; +} + +static EfErrCode del_env(const char *key, env_node_obj_t old_env, bool complete_del) { + EfErrCode result = EF_NO_ERR; + uint32_t dirty_status_addr; + static bool last_is_complete_del = false; + +#if (ENV_STATUS_TABLE_SIZE >= DIRTY_STATUS_TABLE_SIZE) + uint8_t status_table[ENV_STATUS_TABLE_SIZE]; +#else + uint8_t status_table[DIRTY_STATUS_TABLE_SIZE]; +#endif + + /* need find ENV */ + if (!old_env) { + struct env_node_obj env; + /* find ENV */ + if (find_env(key, &env)) { + old_env = &env; + } else { + EF_DEBUG("Not found '%s' in ENV.\n", key); + return EF_ENV_NAME_ERR; + } + } + /* change and save the new status */ + if (!complete_del) { + result = write_status(old_env->addr.start, status_table, ENV_STATUS_NUM, ENV_PRE_DELETE); + last_is_complete_del = true; + } else { + result = write_status(old_env->addr.start, status_table, ENV_STATUS_NUM, ENV_DELETED); + + if (!last_is_complete_del && result == EF_NO_ERR) { +#ifdef EF_ENV_USING_CACHE + /* delete the ENV in flash and cache */ + if (key != NULL) { + /* when using del_env(key, NULL, true) or del_env(key, env, true) in ef_del_env() and set_env() */ + update_env_cache(key, strlen(key), FAILED_ADDR); + } else if (old_env != NULL) { + /* when using del_env(NULL, env, true) in move_env() */ + update_env_cache(old_env->name, old_env->name_len, FAILED_ADDR); + } +#endif /* EF_ENV_USING_CACHE */ + } + + last_is_complete_del = false; + } + + dirty_status_addr = EF_ALIGN_DOWN(old_env->addr.start, SECTOR_SIZE) + SECTOR_DIRTY_OFFSET; + /* read and change the sector dirty status */ + if (result == EF_NO_ERR + && read_status(dirty_status_addr, status_table, SECTOR_DIRTY_STATUS_NUM) == SECTOR_DIRTY_FALSE) { + result = write_status(dirty_status_addr, status_table, SECTOR_DIRTY_STATUS_NUM, SECTOR_DIRTY_TRUE); + } + + return result; +} + +/* + * move the ENV to new space + */ +static EfErrCode move_env(env_node_obj_t env) +{ + EfErrCode result = EF_NO_ERR; + uint8_t status_table[ENV_STATUS_TABLE_SIZE]; + uint32_t env_addr; + struct sector_meta_data sector; + + /* prepare to delete the current ENV */ + if (env->status == ENV_WRITE) { + del_env(NULL, env, false); + } + + if ((env_addr = alloc_env(§or, env->len)) != FAILED_ADDR) { + if (in_recovery_check) { + struct env_node_obj env_bak; + char name[EF_ENV_NAME_MAX + 1] = { 0 }; + strncpy(name, env->name, env->name_len); + /* check the ENV in flash is already create success */ + if (find_env_no_cache(name, &env_bak)) { + /* already create success, don't need to duplicate */ + result = EF_NO_ERR; + goto __exit; + } + } + } else { + return EF_ENV_FULL; + } + /* start move the ENV */ + { + uint8_t buf[32]; + size_t len, size, env_len = env->len; + + /* update the new ENV sector status first */ + update_sec_status(§or, env->len, NULL); + + write_status(env_addr, status_table, ENV_STATUS_NUM, ENV_PRE_WRITE); + env_len -= ENV_MAGIC_OFFSET; + for (len = 0, size = 0; len < env_len; len += size) { + if (len + sizeof(buf) < env_len) { + size = sizeof(buf); + } else { + size = env_len - len; + } + ef_port_read(env->addr.start + ENV_MAGIC_OFFSET + len, (uint32_t *) buf, EF_WG_ALIGN(size)); + result = ef_port_write(env_addr + ENV_MAGIC_OFFSET + len, (uint32_t *) buf, size); + } + write_status(env_addr, status_table, ENV_STATUS_NUM, ENV_WRITE); + +#ifdef EF_ENV_USING_CACHE + update_sector_cache(EF_ALIGN_DOWN(env_addr, SECTOR_SIZE), + env_addr + ENV_HDR_DATA_SIZE + EF_WG_ALIGN(env->name_len) + EF_WG_ALIGN(env->value_len)); + update_env_cache(env->name, env->name_len, env_addr); +#endif /* EF_ENV_USING_CACHE */ + } + + EF_DEBUG("Moved the ENV (%.*s) from 0x%08X to 0x%08X.\n", env->name_len, env->name, env->addr.start, env_addr); + +__exit: + del_env(NULL, env, true); + + return result; +} + +static uint32_t new_env(sector_meta_data_t sector, size_t env_size) +{ + bool already_gc = false; + uint32_t empty_env = FAILED_ADDR; + +__retry: + + if ((empty_env = alloc_env(sector, env_size)) == FAILED_ADDR && gc_request && !already_gc) { + EF_DEBUG("Warning: Alloc an ENV (size %d) failed when new ENV. Now will GC then retry.\n", env_size); + gc_collect(); + already_gc = true; + goto __retry; + } + + return empty_env; +} + +static uint32_t new_env_by_kv(sector_meta_data_t sector, size_t key_len, size_t buf_len) +{ + size_t env_len = ENV_HDR_DATA_SIZE + EF_WG_ALIGN(key_len) + EF_WG_ALIGN(buf_len); + + return new_env(sector, env_len); +} + +static bool gc_check_cb(sector_meta_data_t sector, void *arg1, void *arg2) +{ + size_t *empty_sec = arg1; + + if (sector->check_ok) { + *empty_sec = *empty_sec + 1; + } + + return false; + +} + +static bool do_gc(sector_meta_data_t sector, void *arg1, void *arg2) +{ + struct env_node_obj env; + + if (sector->check_ok && (sector->status.dirty == SECTOR_DIRTY_TRUE || sector->status.dirty == SECTOR_DIRTY_GC)) { + uint8_t status_table[DIRTY_STATUS_TABLE_SIZE]; + /* change the sector status to GC */ + write_status(sector->addr + SECTOR_DIRTY_OFFSET, status_table, SECTOR_DIRTY_STATUS_NUM, SECTOR_DIRTY_GC); + /* search all ENV */ + env.addr.start = FAILED_ADDR; + while ((env.addr.start = get_next_env_addr(sector, &env)) != FAILED_ADDR) { + read_env(&env); + if (env.crc_is_ok && (env.status == ENV_WRITE || env.status == ENV_PRE_DELETE)) { + /* move the ENV to new space */ + if (move_env(&env) != EF_NO_ERR) { + EF_DEBUG("Error: Moved the ENV (%.*s) for GC failed.\n", env.name_len, env.name); + } + } + } + format_sector(sector->addr, SECTOR_NOT_COMBINED); + EF_DEBUG("Collect a sector @0x%08X\n", sector->addr); + } + + return false; +} + +/* + * The GC will be triggered on the following scene: + * 1. alloc an ENV when the flash not has enough space + * 2. write an ENV then the flash not has enough space + */ +static void gc_collect(void) +{ + struct sector_meta_data sector; + size_t empty_sec = 0; + + /* GC check the empty sector number */ + sector_iterator(§or, SECTOR_STORE_EMPTY, &empty_sec, NULL, gc_check_cb, false); + + /* do GC collect */ + EF_DEBUG("The remain empty sector is %d, GC threshold is %d.\n", empty_sec, EF_GC_EMPTY_SEC_THRESHOLD); + if (empty_sec <= EF_GC_EMPTY_SEC_THRESHOLD) { + sector_iterator(§or, SECTOR_STORE_UNUSED, NULL, NULL, do_gc, false); + } + + gc_request = false; +} + +static EfErrCode align_write(uint32_t addr, const uint32_t *buf, size_t size) +{ + EfErrCode result = EF_NO_ERR; + size_t align_remain; + +#if (EF_WRITE_GRAN / 8 > 0) + uint8_t align_data[EF_WRITE_GRAN / 8]; + size_t align_data_size = sizeof(align_data); +#else + /* For compatibility with C89 */ + uint8_t align_data_u8, *align_data = &align_data_u8; + size_t align_data_size = 1; +#endif + + memset(align_data, 0xFF, align_data_size); + align_remain = EF_WG_ALIGN_DOWN(size);//use align_remain temporary to save aligned size. + + if(align_remain > 0){//it may be 0 in this function. + result = ef_port_write(addr, buf, align_remain); + } + + align_remain = size - align_remain; + if (result == EF_NO_ERR && align_remain) { + memcpy(align_data, (uint8_t *)buf + EF_WG_ALIGN_DOWN(size), align_remain); + result = ef_port_write(addr + EF_WG_ALIGN_DOWN(size), (uint32_t *) align_data, align_data_size); + } + + return result; +} + +static EfErrCode create_env_blob(sector_meta_data_t sector, const char *key, const void *value, size_t len) +{ + EfErrCode result = EF_NO_ERR; + struct env_hdr_data env_hdr; + bool is_full = false; + uint32_t env_addr = sector->empty_env; + + if (strlen(key) > EF_ENV_NAME_MAX) { + EF_INFO("Error: The ENV name length is more than %d\n", EF_ENV_NAME_MAX); + return EF_ENV_NAME_ERR; + } + + memset(&env_hdr, 0xFF, sizeof(struct env_hdr_data)); + env_hdr.magic = ENV_MAGIC_WORD; + env_hdr.name_len = strlen(key); + env_hdr.value_len = len; + env_hdr.len = ENV_HDR_DATA_SIZE + EF_WG_ALIGN(env_hdr.name_len) + EF_WG_ALIGN(env_hdr.value_len); + + if (env_hdr.len > SECTOR_SIZE - SECTOR_HDR_DATA_SIZE) { + EF_INFO("Error: The ENV size is too big\n"); + return EF_ENV_FULL; + } + + if (env_addr != FAILED_ADDR || (env_addr = new_env(sector, env_hdr.len)) != FAILED_ADDR) { + size_t align_remain; + /* update the sector status */ + if (result == EF_NO_ERR) { + result = update_sec_status(sector, env_hdr.len, &is_full); + } + if (result == EF_NO_ERR) { + uint8_t ff = 0xFF; + /* start calculate CRC32 */ + env_hdr.crc32 = ef_calc_crc32(0, &env_hdr.name_len, ENV_HDR_DATA_SIZE - ENV_NAME_LEN_OFFSET); + env_hdr.crc32 = ef_calc_crc32(env_hdr.crc32, key, env_hdr.name_len); + align_remain = EF_WG_ALIGN(env_hdr.name_len) - env_hdr.name_len; + while (align_remain--) { + env_hdr.crc32 = ef_calc_crc32(env_hdr.crc32, &ff, 1); + } + env_hdr.crc32 = ef_calc_crc32(env_hdr.crc32, value, env_hdr.value_len); + align_remain = EF_WG_ALIGN(env_hdr.value_len) - env_hdr.value_len; + while (align_remain--) { + env_hdr.crc32 = ef_calc_crc32(env_hdr.crc32, &ff, 1); + } + /* write ENV header data */ + result = write_env_hdr(env_addr, &env_hdr); + + } + /* write key name */ + if (result == EF_NO_ERR) { + result = align_write(env_addr + ENV_HDR_DATA_SIZE, (uint32_t *) key, env_hdr.name_len); + +#ifdef EF_ENV_USING_CACHE + if (!is_full) { + update_sector_cache(sector->addr, + env_addr + ENV_HDR_DATA_SIZE + EF_WG_ALIGN(env_hdr.name_len) + EF_WG_ALIGN(env_hdr.value_len)); + } + update_env_cache(key, env_hdr.name_len, env_addr); +#endif /* EF_ENV_USING_CACHE */ + } + /* write value */ + if (result == EF_NO_ERR) { + result = align_write(env_addr + ENV_HDR_DATA_SIZE + EF_WG_ALIGN(env_hdr.name_len), value, + env_hdr.value_len); + } + /* change the ENV status to ENV_WRITE */ + if (result == EF_NO_ERR) { + result = write_status(env_addr, env_hdr.status_table, ENV_STATUS_NUM, ENV_WRITE); + } + /* trigger GC collect when current sector is full */ + if (result == EF_NO_ERR && is_full) { + EF_DEBUG("Trigger a GC check after created ENV.\n"); + gc_request = true; + } + } else { + result = EF_ENV_FULL; + } + + return result; +} + +/** + * Delete an ENV. + * + * @param key ENV name + * + * @return result + */ +EfErrCode ef_del_env(const char *key) +{ + EfErrCode result = EF_NO_ERR; + + if (!init_ok) { + EF_INFO("Error: ENV isn't initialize OK.\n"); + return EF_ENV_INIT_FAILED; + } + + /* lock the ENV cache */ + ef_port_env_lock(); + + result = del_env(key, NULL, true); + + /* unlock the ENV cache */ + ef_port_env_unlock(); + + return result; +} + +/** + * The same to ef_del_env on this mode + * It's compatibility with older versions (less then V4.0). + * + * @note this function is DEPRECATED + * + * @param key ENV name + * + * @return result + */ +EfErrCode ef_del_and_save_env(const char *key) +{ + return ef_del_env(key); +} + +static EfErrCode set_env(const char *key, const void *value_buf, size_t buf_len) +{ + EfErrCode result = EF_NO_ERR; + static struct env_node_obj env; + static struct sector_meta_data sector; + bool env_is_found = false; + + if (value_buf == NULL) { + result = del_env(key, NULL, true); + } else { + /* make sure the flash has enough space */ + if (new_env_by_kv(§or, strlen(key), buf_len) == FAILED_ADDR) { + return EF_ENV_FULL; + } + env_is_found = find_env(key, &env); + /* prepare to delete the old ENV */ + if (env_is_found) { + result = del_env(key, &env, false); + } + /* create the new ENV */ + if (result == EF_NO_ERR) { + result = create_env_blob(§or, key, value_buf, buf_len); + } + /* delete the old ENV */ + if (env_is_found && result == EF_NO_ERR) { + result = del_env(key, &env, true); + } + /* process the GC after set ENV */ + if (gc_request) { + gc_collect(); + } + } + + return result; +} + +/** + * Set a blob ENV. If it value is NULL, delete it. + * If not find it in flash, then create it. + * + * @param key ENV name + * @param value ENV value + * @param len ENV value length + * + * @return result + */ +EfErrCode ef_set_env_blob(const char *key, const void *value_buf, size_t buf_len) +{ + EfErrCode result = EF_NO_ERR; + + + if (!init_ok) { + EF_INFO("ENV isn't initialize OK.\n"); + return EF_ENV_INIT_FAILED; + } + + /* lock the ENV cache */ + ef_port_env_lock(); + + result = set_env(key, value_buf, buf_len); + + /* unlock the ENV cache */ + ef_port_env_unlock(); + + return result; +} + +/** + * Set a string ENV. If it value is NULL, delete it. + * If not find it in flash, then create it. + * + * @param key ENV name + * @param value ENV value + * + * @return result + */ +EfErrCode ef_set_env(const char *key, const char *value) +{ + return ef_set_env_blob(key, value, strlen(value)); +} + +/** + * The same to ef_set_env on this mode. + * It's compatibility with older versions (less then V4.0). + * + * @note this function is DEPRECATED + * + * @param key ENV name + * @param value ENV value + * + * @return result + */ +EfErrCode ef_set_and_save_env(const char *key, const char *value) +{ + return ef_set_env_blob(key, value, strlen(value)); +} + +/** + * Save ENV to flash. + * + * @note this function is DEPRECATED + */ +EfErrCode ef_save_env(void) +{ + /* do nothing not cur mode */ + return EF_NO_ERR; +} + +/** + * ENV set default. + * + * @return result + */ +EfErrCode ef_env_set_default(void) +{ + EfErrCode result = EF_NO_ERR; + uint32_t addr, i, value_len; + struct sector_meta_data sector; + + EF_ASSERT(default_env_set); + EF_ASSERT(default_env_set_size); + + /* lock the ENV cache */ + ef_port_env_lock(); + /* format all sectors */ + for (addr = env_start_addr; addr < env_start_addr + ENV_AREA_SIZE; addr += SECTOR_SIZE) { + result = format_sector(addr, SECTOR_NOT_COMBINED); + if (result != EF_NO_ERR) { + goto __exit; + } + } + /* create default ENV */ + for (i = 0; i < default_env_set_size; i++) { + /* It seems to be a string when value length is 0. + * This mechanism is for compatibility with older versions (less then V4.0). */ + if (default_env_set[i].value_len == 0) { + value_len = strlen(default_env_set[i].value); + } else { + value_len = default_env_set[i].value_len; + } + sector.empty_env = FAILED_ADDR; + create_env_blob(§or, default_env_set[i].key, default_env_set[i].value, value_len); + if (result != EF_NO_ERR) { + goto __exit; + } + } + +__exit: + /* unlock the ENV cache */ + ef_port_env_unlock(); + + return result; +} + +static bool print_env_cb(env_node_obj_t env, void *arg1, void *arg2) +{ + bool value_is_str = true, print_value = false; + size_t *using_size = arg1; + + if (env->crc_is_ok) { + /* calculate the total using flash size */ + *using_size += env->len; + /* check ENV */ + if (env->status == ENV_WRITE) { + ef_print("%.*s=", env->name_len, env->name); + + if (env->value_len < EF_STR_ENV_VALUE_MAX_SIZE ) { + uint8_t buf[32]; + size_t len, size; +__reload: + /* check the value is string */ + for (len = 0, size = 0; len < env->value_len; len += size) { + if (len + sizeof(buf) < env->value_len) { + size = sizeof(buf); + } else { + size = env->value_len - len; + } + ef_port_read(env->addr.value + len, (uint32_t *) buf, EF_WG_ALIGN(size)); + if (print_value) { + ef_print("%.*s", size, buf); + } else if (!ef_is_str(buf, size)) { + value_is_str = false; + break; + } + } + } else { + value_is_str = false; + } + if (value_is_str && !print_value) { + print_value = true; + goto __reload; + } else if (!value_is_str) { + ef_print("blob @0x%08X %dbytes", env->addr.value, env->value_len); + } + ef_print("\n"); + } + } + + return false; +} + + +/** + * Print ENV. + */ +void ef_print_env(void) +{ + struct env_node_obj env; + size_t using_size = 0; + + if (!init_ok) { + EF_INFO("ENV isn't initialize OK.\n"); + return; + } + + /* lock the ENV cache */ + ef_port_env_lock(); + + env_iterator(&env, &using_size, NULL, print_env_cb); + + ef_print("\nmode: next generation\n"); + ef_print("size: %lu/%lu bytes.\n", using_size + (SECTOR_NUM - EF_GC_EMPTY_SEC_THRESHOLD) * SECTOR_HDR_DATA_SIZE, + ENV_AREA_SIZE - SECTOR_SIZE * EF_GC_EMPTY_SEC_THRESHOLD); + + /* unlock the ENV cache */ + ef_port_env_unlock(); +} + +#ifdef EF_ENV_AUTO_UPDATE +/* + * Auto update ENV to latest default when current EF_ENV_VER_NUM is changed. + */ +static void env_auto_update(void) +{ + size_t saved_ver_num, setting_ver_num = EF_ENV_VER_NUM; + + if (get_env(VER_NUM_ENV_NAME, &saved_ver_num, sizeof(size_t), NULL) > 0) { + /* check version number */ + if (saved_ver_num != setting_ver_num) { + struct env_node_obj env; + size_t i, value_len; + struct sector_meta_data sector; + EF_DEBUG("Update the ENV from version %d to %d.\n", saved_ver_num, setting_ver_num); + for (i = 0; i < default_env_set_size; i++) { + /* add a new ENV when it's not found */ + if (!find_env(default_env_set[i].key, &env)) { + /* It seems to be a string when value length is 0. + * This mechanism is for compatibility with older versions (less then V4.0). */ + if (default_env_set[i].value_len == 0) { + value_len = strlen(default_env_set[i].value); + } else { + value_len = default_env_set[i].value_len; + } + sector.empty_env = FAILED_ADDR; + create_env_blob(§or, default_env_set[i].key, default_env_set[i].value, value_len); + } + } + } else { + /* version number not changed now return */ + return; + } + } + + set_env(VER_NUM_ENV_NAME, &setting_ver_num, sizeof(size_t)); +} +#endif /* EF_ENV_AUTO_UPDATE */ + +static bool check_sec_hdr_cb(sector_meta_data_t sector, void *arg1, void *arg2) +{ + if (!sector->check_ok) { + size_t *failed_count = arg1; + + EF_INFO("Warning: Sector header check failed. Format this sector (0x%08x).\n", sector->addr); + (*failed_count) ++; + format_sector(sector->addr, SECTOR_NOT_COMBINED); + } + + return false; +} + +static bool check_and_recovery_gc_cb(sector_meta_data_t sector, void *arg1, void *arg2) +{ + if (sector->check_ok && sector->status.dirty == SECTOR_DIRTY_GC) { + /* make sure the GC request flag to true */ + gc_request = true; + /* resume the GC operate */ + gc_collect(); + } + + return false; +} + +static bool check_and_recovery_env_cb(env_node_obj_t env, void *arg1, void *arg2) +{ + /* recovery the prepare deleted ENV */ + if (env->crc_is_ok && env->status == ENV_PRE_DELETE) { + EF_INFO("Found an ENV (%.*s) which has changed value failed. Now will recovery it.\n", env->name_len, env->name); + /* recovery the old ENV */ + if (move_env(env) == EF_NO_ERR) { + EF_DEBUG("Recovery the ENV successful.\n"); + } else { + EF_DEBUG("Warning: Moved an ENV (size %d) failed when recovery. Now will GC then retry.\n", env->len); + return true; + } + } else if (env->status == ENV_PRE_WRITE) { + uint8_t status_table[ENV_STATUS_TABLE_SIZE]; + /* the ENV has not write finish, change the status to error */ + //TODO �����쳣������״̬װ��ͼ + write_status(env->addr.start, status_table, ENV_STATUS_NUM, ENV_ERR_HDR); + return true; + } + + return false; +} + +/** + * Check and load the flash ENV meta data. + * + * @return result + */ +EfErrCode ef_load_env(void) +{ + EfErrCode result = EF_NO_ERR; + struct env_node_obj env; + struct sector_meta_data sector; + size_t check_failed_count = 0; + + in_recovery_check = true; + /* check all sector header */ + sector_iterator(§or, SECTOR_STORE_UNUSED, &check_failed_count, NULL, check_sec_hdr_cb, false); + /* all sector header check failed */ + if (check_failed_count == SECTOR_NUM) { + EF_INFO("Warning: All sector header check failed. Set it to default.\n"); + ef_env_set_default(); + } + + /* lock the ENV cache */ + ef_port_env_lock(); + /* check all sector header for recovery GC */ + sector_iterator(§or, SECTOR_STORE_UNUSED, NULL, NULL, check_and_recovery_gc_cb, false); + +__retry: + /* check all ENV for recovery */ + env_iterator(&env, NULL, NULL, check_and_recovery_env_cb); + if (gc_request) { + gc_collect(); + goto __retry; + } + + in_recovery_check = false; + + /* unlock the ENV cache */ + ef_port_env_unlock(); + + return result; +} + +/** + * Flash ENV initialize. + * + * @param default_env default ENV set for user + * @param default_env_size default ENV set size + * + * @return result + */ +EfErrCode ef_env_init(ef_env const *default_env, size_t default_env_size) { + EfErrCode result = EF_NO_ERR; + +#ifdef EF_ENV_USING_CACHE + size_t i; +#endif + + EF_ASSERT(default_env); + EF_ASSERT(ENV_AREA_SIZE); + /* must be aligned with erase_min_size */ + EF_ASSERT(ENV_AREA_SIZE % EF_ERASE_MIN_SIZE == 0); + /* sector number must be greater than or equal to 2 */ + EF_ASSERT(SECTOR_NUM >= 2); + /* must be aligned with write granularity */ + EF_ASSERT((EF_STR_ENV_VALUE_MAX_SIZE * 8) % EF_WRITE_GRAN == 0); + + if (init_ok) { + return EF_NO_ERR; + } + +#ifdef EF_ENV_USING_CACHE + for (i = 0; i < EF_SECTOR_CACHE_TABLE_SIZE; i++) { + sector_cache_table[i].addr = FAILED_ADDR; + } + for (i = 0; i < EF_ENV_CACHE_TABLE_SIZE; i++) { + env_cache_table[i].addr = FAILED_ADDR; + } +#endif /* EF_ENV_USING_CACHE */ + + env_start_addr = EF_START_ADDR; + default_env_set = default_env; + default_env_set_size = default_env_size; + + EF_DEBUG("ENV start address is 0x%08X, size is %d bytes.\n", EF_START_ADDR, ENV_AREA_SIZE); + + result = ef_load_env(); + +#ifdef EF_ENV_AUTO_UPDATE + if (result == EF_NO_ERR) { + env_auto_update(); + } +#endif + + if (result == EF_NO_ERR) { + init_ok = true; + } + + return result; +} + +#endif /* defined(EF_USING_ENV) && !defined(EF_ENV_USING_LEGACY_MODE) */ diff --git a/demo/os/nuttx-spiflash/apps/system/easyflash/src/ef_env_legacy.c b/demo/os/nuttx-spiflash/apps/system/easyflash/src/ef_env_legacy.c new file mode 100644 index 0000000..c71a714 --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/system/easyflash/src/ef_env_legacy.c @@ -0,0 +1,922 @@ +/* + * This file is part of the EasyFlash Library. + * + * Copyright (c) 2014-2018, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: Environment variables operating interface. (normal mode) + * Created on: 2014-10-06 + */ + +#include +#include +#include + +#if defined(EF_USING_ENV) && defined(EF_ENV_USING_LEGACY_MODE) + +#ifndef EF_ENV_USING_WL_MODE + +#if defined(EF_USING_ENV) && (!defined(ENV_USER_SETTING_SIZE) || !defined(ENV_AREA_SIZE)) +#error "Please configure user setting ENV size or ENV area size (in ef_cfg.h)" +#endif + +/** + * ENV area has 2 sections + * 1. System section + * It storages ENV parameters. (Units: Word) + * 2. Data section + * It storages all ENV. Storage format is key=value\0. + * All ENV must be 4 bytes alignment. The remaining part must fill '\0'. + * + * @note Word = 4 Bytes in this file + * @note When using power fail safeguard mode, it has two ENV areas(Area0, Area1). + */ + +/* flash ENV parameters index and size in system section */ +enum { + /* data section ENV end address index in system section */ + ENV_PARAM_INDEX_END_ADDR = 0, + +#ifdef EF_ENV_USING_PFS_MODE + /* saved count for ENV area */ + ENV_PARAM_INDEX_SAVED_COUNT, +#endif + +#ifdef EF_ENV_AUTO_UPDATE + /* current version number for ENV */ + ENV_PARAM_INDEX_VER_NUM, +#endif + + /* data section CRC32 code index in system section */ + ENV_PARAM_INDEX_DATA_CRC, + /* flash ENV parameters word size */ + ENV_PARAM_WORD_SIZE, + /* flash ENV parameters byte size */ + ENV_PARAM_BYTE_SIZE = ENV_PARAM_WORD_SIZE * 4, +}; + +/* default ENV set, must be initialized by user */ +static ef_env const *default_env_set; +/* default ENV set size, must be initialized by user */ +static size_t default_env_set_size = 0; +/* ENV ram cache */ +static uint32_t env_cache[ENV_USER_SETTING_SIZE / 4] = { 0 }; +/* ENV start address in flash */ +static uint32_t env_start_addr = 0; +/* ENV ram cache has changed when ENV created, deleted and changed value. */ +static bool env_cache_changed = false; +/* initialize OK flag */ +static bool init_ok = false; + +#ifdef EF_ENV_USING_PFS_MODE +/* current load ENV area address */ +static uint32_t cur_load_area_addr = 0; +/* next save ENV area address */ +static uint32_t next_save_area_addr = 0; +#endif + +static uint32_t get_env_system_addr(void); +static uint32_t get_env_data_addr(void); +static uint32_t get_env_end_addr(void); +static void set_env_end_addr(uint32_t end_addr); +static EfErrCode write_env(const char *key, const char *value); +static char *find_env(const char *key); +static EfErrCode del_env(const char *key); +static size_t get_env_data_size(void); +static size_t get_env_user_used_size(void); +static EfErrCode create_env(const char *key, const char *value); +static uint32_t calc_env_crc(void); +static bool env_crc_is_ok(void); +#ifdef EF_ENV_AUTO_UPDATE +static EfErrCode env_auto_update(void); +#endif + +/** + * Flash ENV initialize. + * + * @param default_env default ENV set for user + * @param default_env_size default ENV set size + * + * @note user_size must equal with total_size in normal mode + * + * @return result + */ +EfErrCode ef_env_init(ef_env const *default_env, size_t default_env_size) { + EfErrCode result = EF_NO_ERR; + + EF_ASSERT(ENV_AREA_SIZE); + EF_ASSERT(ENV_USER_SETTING_SIZE); + EF_ASSERT(EF_ERASE_MIN_SIZE); + /* must be word alignment for ENV */ + EF_ASSERT(ENV_USER_SETTING_SIZE % 4 == 0); + EF_ASSERT(ENV_AREA_SIZE % 4 == 0); + EF_ASSERT(default_env); + EF_ASSERT(default_env_size < ENV_USER_SETTING_SIZE); + +#ifndef EF_ENV_USING_PFS_MODE + /* total_size must be aligned with erase_min_size */ + if (ENV_USER_SETTING_SIZE % EF_ERASE_MIN_SIZE == 0) { + EF_ASSERT(ENV_USER_SETTING_SIZE == ENV_AREA_SIZE); + } else { + EF_ASSERT((ENV_USER_SETTING_SIZE / EF_ERASE_MIN_SIZE + 1)*EF_ERASE_MIN_SIZE == ENV_AREA_SIZE); + } +#else + /* total_size must be aligned with erase_min_size */ + if (ENV_USER_SETTING_SIZE % EF_ERASE_MIN_SIZE == 0) { + /* it has double area when used power fail safeguard mode */ + EF_ASSERT(2 * ENV_USER_SETTING_SIZE == ENV_AREA_SIZE); + } else { + /* it has double area when used power fail safeguard mode */ + EF_ASSERT(2 * (ENV_USER_SETTING_SIZE / EF_ERASE_MIN_SIZE + 1)*EF_ERASE_MIN_SIZE == ENV_AREA_SIZE); + } +#endif + + env_start_addr = EF_START_ADDR; + default_env_set = default_env; + default_env_set_size = default_env_size; + + EF_DEBUG("ENV start address is 0x%08X, size is %d bytes.\n", EF_START_ADDR, ENV_AREA_SIZE); + + result = ef_load_env(); + +#ifdef EF_ENV_AUTO_UPDATE + if (result == EF_NO_ERR) { + env_auto_update(); + } +#endif + + if (result == EF_NO_ERR) { + init_ok = true; + } + + + return result; +} + +/** + * ENV set default. + * + * @return result + */ +EfErrCode ef_env_set_default(void) { + extern EfErrCode ef_env_ver_num_set_default(void); + + EfErrCode result = EF_NO_ERR; + size_t i; + + EF_ASSERT(default_env_set); + EF_ASSERT(default_env_set_size); + + /* lock the ENV cache */ + ef_port_env_lock(); + + /* set environment end address is at data section start address */ + set_env_end_addr(get_env_data_addr()); + +#ifdef EF_ENV_USING_PFS_MODE + /* set saved count to default 0 */ + env_cache[ENV_PARAM_INDEX_SAVED_COUNT] = 0; +#endif + +#ifdef EF_ENV_AUTO_UPDATE + /* initialize version number */ + env_cache[ENV_PARAM_INDEX_VER_NUM] = EF_ENV_VER_NUM; +#endif + + /* create default ENV */ + for (i = 0; i < default_env_set_size; i++) { + create_env(default_env_set[i].key, default_env_set[i].value); + } + + /* unlock the ENV cache */ + ef_port_env_unlock(); + + result = ef_save_env(); + +#ifdef EF_ENV_USING_PFS_MODE + /* reset other PFS area's data */ + if (result == EF_NO_ERR) { + env_cache_changed = true; + result = ef_save_env(); + } +#endif + + return result; +} + +/** + * Get ENV system section start address. + * + * @return system section start address + */ +static uint32_t get_env_system_addr(void) { +#ifndef EF_ENV_USING_PFS_MODE + return env_start_addr; +#else + return cur_load_area_addr; +#endif +} + +/** + * Get ENV data section start address. + * + * @return data section start address + */ +static uint32_t get_env_data_addr(void) { + return get_env_system_addr() + ENV_PARAM_BYTE_SIZE; +} + +/** + * Get ENV end address. + * It's the first word in ENV. + * + * @return ENV end address + */ +static uint32_t get_env_end_addr(void) { + /* it is the first word */ + return env_cache[ENV_PARAM_INDEX_END_ADDR]; +} + +/** + * Set ENV end address. + * It's the first word in ENV. + * + * @param end_addr ENV end address + */ +static void set_env_end_addr(uint32_t end_addr) { + env_cache[ENV_PARAM_INDEX_END_ADDR] = end_addr; +} + +/** + * Get current ENV data section size. + * + * @return size + */ +static size_t get_env_data_size(void) { + if (get_env_end_addr() > get_env_data_addr()) { + return get_env_end_addr() - get_env_data_addr(); + } else { + return 0; + } +} + +/** + * Get current user used ENV size. + * + * @return bytes + */ +static size_t get_env_user_used_size(void) { + if (get_env_end_addr() > get_env_system_addr()) { + return get_env_end_addr() - get_env_system_addr(); + } else { + return 0; + } +} + +/** + * Get current ENV already write bytes. + * + * @return write bytes + */ +size_t ef_get_env_write_bytes(void) { +#ifndef EF_ENV_USING_PFS_MODE + return get_env_user_used_size(); +#else + return get_env_user_used_size() * 2; +#endif +} + +/** + * Write an ENV at the end of cache. + * + * @param key ENV name + * @param value ENV value + * + * @return result + */ +static EfErrCode write_env(const char *key, const char *value) { + EfErrCode result = EF_NO_ERR; + size_t key_len = strlen(key), value_len = strlen(value), env_str_len; + char *env_cache_bak = (char *)env_cache; + + /* calculate ENV storage length, contain '=' and '\0'. */ + env_str_len = key_len + value_len + 2; + if (env_str_len % 4 != 0) { + env_str_len = (env_str_len / 4 + 1) * 4; + } + /* check capacity of ENV */ + if (env_str_len + get_env_user_used_size() >= ENV_USER_SETTING_SIZE) { + return EF_ENV_FULL; + } + + /* calculate current ENV ram cache end address */ + env_cache_bak += get_env_user_used_size(); + + /* copy key name */ + memcpy(env_cache_bak, key, key_len); + env_cache_bak += key_len; + /* copy equal sign */ + *env_cache_bak = '='; + env_cache_bak++; + /* copy value */ + memcpy(env_cache_bak, value, value_len); + env_cache_bak += value_len; + /* fill '\0' for string end sign */ + *env_cache_bak = '\0'; + env_cache_bak ++; + /* fill '\0' for word alignment */ + memset(env_cache_bak, 0, env_str_len - (key_len + value_len + 2)); + set_env_end_addr(get_env_end_addr() + env_str_len); + /* ENV ram cache has changed */ + env_cache_changed = true; + + return result; +} + +/** + * Find ENV. + * + * @param key ENV name + * + * @return found ENV in ram cache + */ +static char *find_env(const char *key) { + char *env_start, *env_end, *env, *found_env = NULL; + size_t key_len = strlen(key), env_len; + + if ((key == NULL) || *key == '\0') { + EF_INFO("Flash ENV name must be not empty!\n"); + return NULL; + } + + /* from data section start to data section end */ + env_start = (char *) ((char *) env_cache + ENV_PARAM_BYTE_SIZE); + env_end = (char *) ((char *) env_cache + get_env_user_used_size()); + + /* ENV is null */ + if (env_start == env_end) { + return NULL; + } + + env = env_start; + while (env < env_end) { + /* the key length must be equal */ + if (!strncmp(env, key, key_len) && (env[key_len] == '=')) { + found_env = env; + break; + } else { + /* calculate ENV length, contain '\0'. */ + env_len = strlen(env) + 1; + /* next ENV and word alignment */ + if (env_len % 4 == 0) { + env += env_len; + } else { + env += (env_len / 4 + 1) * 4; + } + } + } + return found_env; +} + +/** + * If the ENV is not exist, create it. + * @see flash_write_env + * + * @param key ENV name + * @param value ENV value + * + * @return result + */ +static EfErrCode create_env(const char *key, const char *value) { + EfErrCode result = EF_NO_ERR; + + EF_ASSERT(key); + EF_ASSERT(value); + + if ((key == NULL) || *key == '\0') { + EF_INFO("Flash ENV name must be not empty!\n"); + return EF_ENV_NAME_ERR; + } + + if (strchr(key, '=')) { + EF_INFO("Flash ENV name can't contain '='.\n"); + return EF_ENV_NAME_ERR; + } + + /* find ENV */ + if (find_env(key)) { + EF_INFO("The name of \"%s\" is already exist.\n", key); + return EF_ENV_NAME_EXIST; + } + /* write ENV at the end of cache */ + result = write_env(key, value); + + return result; +} + +/** + * Delete an ENV in cache. + * + * @param key ENV name + * + * @return result + */ +static EfErrCode del_env(const char *key) { + EfErrCode result = EF_NO_ERR; + char *del_env = NULL; + size_t del_env_length, remain_env_length; + + EF_ASSERT(key); + + if ((key == NULL) || *key == '\0') { + EF_INFO("Flash ENV name must be not NULL!\n"); + return EF_ENV_NAME_ERR; + } + + if (strchr(key, '=')) { + EF_INFO("Flash ENV name or value can't contain '='.\n"); + return EF_ENV_NAME_ERR; + } + + /* find ENV */ + del_env = find_env(key); + + if (!del_env) { + EF_INFO("Not find \"%s\" in ENV.\n", key); + return EF_ENV_NAME_ERR; + } + del_env_length = strlen(del_env); + /* '\0' also must be as ENV length */ + del_env_length ++; + /* the address must multiple of 4 */ + if (del_env_length % 4 != 0) { + del_env_length = (del_env_length / 4 + 1) * 4; + } + /* calculate remain ENV length */ + remain_env_length = get_env_data_size() + - (((uint32_t) del_env + del_env_length) - ((uint32_t) env_cache + ENV_PARAM_BYTE_SIZE)); + /* remain ENV move forward */ + memcpy(del_env, del_env + del_env_length, remain_env_length); + /* reset ENV end address */ + set_env_end_addr(get_env_end_addr() - del_env_length); + /* ENV ram cache has changed */ + env_cache_changed = true; + + return result; +} + +/** + * Set an ENV.If it value is NULL, delete it. + * If not find it in ENV table, then create it. + * + * @param key ENV name + * @param value ENV value + * + * @return result + */ +EfErrCode ef_set_env(const char *key, const char *value) { + EfErrCode result = EF_NO_ERR; + char *old_env, *old_value; + + if (!init_ok) { + EF_INFO("ENV isn't initialize OK.\n"); + return EF_ENV_INIT_FAILED; + } + + /* lock the ENV cache */ + ef_port_env_lock(); + + /* if ENV value is NULL, delete it */ + if (value == NULL) { + result = del_env(key); + } else { + old_env = find_env(key); + /* If find this ENV, then compare the new value and old value. */ + if (old_env) { + /* find the old value address */ + old_env = strchr(old_env, '='); + old_value = old_env + 1; + /* If it is changed then delete it and recreate it */ + if (strcmp(old_value, value)) { + result = del_env(key); + if (result == EF_NO_ERR) { + result = create_env(key, value); + } + } + } else { + result = create_env(key, value); + } + } + + /* unlock the ENV cache */ + ef_port_env_unlock(); + + return result; +} + +/** + * Del an ENV. + * + * @param key ENV name + * + * @return result + */ +EfErrCode ef_del_env(const char *key) { + EfErrCode result = EF_NO_ERR; + + if (!init_ok) { + EF_INFO("ENV isn't initialize OK.\n"); + return EF_ENV_INIT_FAILED; + } + + /* lock the ENV cache */ + ef_port_env_lock(); + + result = del_env(key); + + /* unlock the ENV cache */ + ef_port_env_unlock(); + + return result; +} + +/** + * Get an ENV value by key name. + * + * @param key ENV name + * + * @return value + */ +char *ef_get_env(const char *key) { + char *env = NULL, *value = NULL; + + if (!init_ok) { + EF_INFO("ENV isn't initialize OK.\n"); + return NULL; + } + + /* find ENV */ + env = find_env(key); + + if (env == NULL) { + return NULL; + } + /* get value address */ + value = strchr(env, '='); + if (value != NULL) { + /* the equal sign next character is value */ + value++; + } + return value; +} +/** + * Print ENV. + */ +void ef_print_env(void) { + uint32_t *env_cache_data_addr = env_cache + ENV_PARAM_WORD_SIZE, + *env_cache_end_addr = + (uint32_t *) (env_cache + ENV_PARAM_WORD_SIZE + get_env_data_size() / 4); + uint8_t j; + char c; + + if (!init_ok) { + EF_INFO("ENV isn't initialize OK.\n"); + return; + } + + for (; env_cache_data_addr < env_cache_end_addr; env_cache_data_addr += 1) { + for (j = 0; j < 4; j++) { + c = (*env_cache_data_addr) >> (8 * j); + ef_print("%c", c); + if (c == '\0') { + ef_print("\n"); + break; + } + } + } + +#ifndef EF_ENV_USING_PFS_MODE + ef_print("\nmode: normal\n"); + ef_print("size: %ld/%ld bytes.\n", get_env_user_used_size(), ENV_USER_SETTING_SIZE); +#else + ef_print("\nmode: power fail safeguard\n"); + ef_print("size: %ld/%ld bytes, write bytes %ld/%ld.\n", get_env_user_used_size(), + ENV_USER_SETTING_SIZE, ef_get_env_write_bytes(), ENV_AREA_SIZE); + ef_print("saved count: %ld\n", env_cache[ENV_PARAM_INDEX_SAVED_COUNT]); +#endif + +#ifdef EF_ENV_AUTO_UPDATE + ef_print("ver num: %d\n", env_cache[ENV_PARAM_INDEX_VER_NUM]); +#endif +} + +/** + * Load flash ENV to ram. + * + * @return result + */ +#ifndef EF_ENV_USING_PFS_MODE +EfErrCode ef_load_env(void) { + EfErrCode result = EF_NO_ERR; + uint32_t *env_cache_bak, env_end_addr; + + /* read ENV end address from flash */ + ef_port_read(get_env_system_addr() + ENV_PARAM_INDEX_END_ADDR * 4, &env_end_addr, 4); + /* if ENV is not initialize or flash has dirty data, set default for it */ + if ((env_end_addr == 0xFFFFFFFF) || (env_end_addr < env_start_addr) + || (env_end_addr > env_start_addr + ENV_USER_SETTING_SIZE)) { + result = ef_env_set_default(); + } else { + /* set ENV end address */ + set_env_end_addr(env_end_addr); + + env_cache_bak = env_cache + ENV_PARAM_WORD_SIZE; + /* read all ENV from flash */ + ef_port_read(get_env_data_addr(), env_cache_bak, get_env_data_size()); + /* read ENV CRC code from flash */ + ef_port_read(get_env_system_addr() + ENV_PARAM_INDEX_DATA_CRC * 4, + &env_cache[ENV_PARAM_INDEX_DATA_CRC] , 4); + /* if ENV CRC32 check is fault, set default for it */ + if (!env_crc_is_ok()) { + EF_INFO("Warning: ENV CRC check failed. Set it to default.\n"); + result = ef_env_set_default(); + } + } + return result; +} +#else +EfErrCode ef_load_env(void) { + EfErrCode result = EF_NO_ERR; + uint32_t area0_start_address = env_start_addr, area1_start_address = env_start_addr + + ENV_AREA_SIZE / 2; + uint32_t area0_end_addr, area1_end_addr, area0_crc, area1_crc, area0_saved_count, area1_saved_count; + bool area0_is_valid = true, area1_is_valid = true; + /* read ENV area end address from flash */ + ef_port_read(area0_start_address + ENV_PARAM_INDEX_END_ADDR * 4, &area0_end_addr, 4); + ef_port_read(area1_start_address + ENV_PARAM_INDEX_END_ADDR * 4, &area1_end_addr, 4); + if ((area0_end_addr == 0xFFFFFFFF) || (area0_end_addr < area0_start_address) + || (area0_end_addr > area0_start_address + ENV_USER_SETTING_SIZE)) { + area0_is_valid = false; + } + if ((area1_end_addr == 0xFFFFFFFF) || (area1_end_addr < area1_start_address) + || (area1_end_addr > area1_start_address + ENV_USER_SETTING_SIZE)) { + area1_is_valid = false; + } + /* check area0 CRC when it is valid */ + if (area0_is_valid) { + /* read ENV area0 crc32 code from flash */ + ef_port_read(area0_start_address + ENV_PARAM_INDEX_DATA_CRC * 4, &area0_crc, 4); + /* read ENV from ENV area0 */ + ef_port_read(area0_start_address, env_cache, area0_end_addr - area0_start_address); + /* current load ENV area address is area0 start address */ + cur_load_area_addr = area0_start_address; + if (!env_crc_is_ok()) { + area0_is_valid = false; + } + } + /* check area1 CRC when it is valid */ + if (area1_is_valid) { + /* read ENV area1 crc32 code from flash */ + ef_port_read(area1_start_address + ENV_PARAM_INDEX_DATA_CRC * 4, &area1_crc, 4); + /* read ENV from ENV area1 */ + ef_port_read(area1_start_address, env_cache, area1_end_addr - area1_start_address); + /* current load ENV area address is area1 start address */ + cur_load_area_addr = area1_start_address; + if (!env_crc_is_ok()) { + area1_is_valid = false; + } + } + /* all ENV area CRC is OK then compare saved count */ + if (area0_is_valid && area1_is_valid) { + /* read ENV area saved count from flash */ + ef_port_read(area0_start_address + ENV_PARAM_INDEX_SAVED_COUNT * 4, + &area0_saved_count, 4); + ef_port_read(area1_start_address + ENV_PARAM_INDEX_SAVED_COUNT * 4, + &area1_saved_count, 4); + /* the bigger saved count area is valid */ + if ((area0_saved_count > area1_saved_count) || ((area0_saved_count == 0) && (area1_saved_count == 0xFFFFFFFF))) { + area1_is_valid = false; + } else { + area0_is_valid = false; + } + } + if (area0_is_valid) { + /* current load ENV area address is area0 start address */ + cur_load_area_addr = area0_start_address; + /* next save ENV area address is area1 start address */ + next_save_area_addr = area1_start_address; + /* read all ENV from area0 */ + ef_port_read(area0_start_address, env_cache, area0_end_addr - area0_start_address); + } else if (area1_is_valid) { + /* next save ENV area address is area0 start address */ + next_save_area_addr = area0_start_address; + } else { + /* current load ENV area address is area1 start address */ + cur_load_area_addr = area1_start_address; + /* next save ENV area address is area0 start address */ + next_save_area_addr = area0_start_address; + /* set the ENV to default */ + result = ef_env_set_default(); + } + return result; +} +#endif + +/** + * Save ENV to flash. + */ +EfErrCode ef_save_env(void) { + EfErrCode result = EF_NO_ERR; + uint32_t write_addr, write_size; + + /* ENV ram cache has not changed don't need to save */ + if (!env_cache_changed) { + return result; + } + +#ifndef EF_ENV_USING_PFS_MODE + write_addr = get_env_system_addr(); + write_size = get_env_user_used_size(); + /* calculate and cache CRC32 code */ + env_cache[ENV_PARAM_INDEX_DATA_CRC] = calc_env_crc(); +#else + write_addr = next_save_area_addr; + write_size = get_env_user_used_size(); + /* replace next_save_area_addr with cur_load_area_addr */ + next_save_area_addr = cur_load_area_addr; + cur_load_area_addr = write_addr; + /* change the ENV end address to next save area address */ + set_env_end_addr(write_addr + write_size); + /* ENV area saved count +1 */ + env_cache[ENV_PARAM_INDEX_SAVED_COUNT]++; + /* calculate and cache CRC32 code */ + env_cache[ENV_PARAM_INDEX_DATA_CRC] = calc_env_crc(); +#endif + + /* erase ENV */ + result = ef_port_erase(write_addr, write_size); + switch (result) { + case EF_NO_ERR: { + EF_DEBUG("Erased ENV OK.\n"); + break; + } + case EF_ERASE_ERR: { + EF_INFO("Error: Erased ENV fault! Start address is 0x%08X, size is %ld.\n", write_addr, write_size); + /* will return when erase fault */ + return result; + } + } + + /* write ENV to flash */ + result = ef_port_write(write_addr, env_cache, write_size); + switch (result) { + case EF_NO_ERR: { + EF_DEBUG("Saved ENV OK.\n"); + break; + } + case EF_WRITE_ERR: { + EF_INFO("Error: Saved ENV fault! Start address is 0x%08X, size is %ld.\n", write_addr, write_size); + break; + } + } + + env_cache_changed = false; + + return result; +} + +/** + * Calculate the cached ENV CRC32 value. + * + * @return CRC32 value + */ +static uint32_t calc_env_crc(void) { + uint32_t crc32 = 0; + + /* Calculate the ENV end address CRC32. The 4 is ENV end address bytes size. */ + crc32 = ef_calc_crc32(crc32, &env_cache[ENV_PARAM_INDEX_END_ADDR], 4); + +#ifdef EF_ENV_USING_PFS_MODE + /* Calculate the ENV area saved count CRC32. */ + crc32 = ef_calc_crc32(crc32, &env_cache[ENV_PARAM_INDEX_SAVED_COUNT], 4); +#endif + + /* Calculate the all ENV data CRC32. */ + crc32 = ef_calc_crc32(crc32, &env_cache[ENV_PARAM_WORD_SIZE], get_env_data_size()); + + EF_DEBUG("Calculate ENV CRC32 number is 0x%08X.\n", crc32); + + return crc32; +} + +/** + * Check the ENV CRC32 + * + * @return true is ok + */ +static bool env_crc_is_ok(void) { + if (calc_env_crc() == env_cache[ENV_PARAM_INDEX_DATA_CRC]) { + EF_DEBUG("Verify ENV CRC32 result is OK.\n"); + return true; + } else { + return false; + } +} + +/** + * Set and save an ENV. If set ENV is success then will save it. + * + * @param key ENV name + * @param value ENV value + * + * @return result + */ +EfErrCode ef_set_and_save_env(const char *key, const char *value) { + EfErrCode result = EF_NO_ERR; + + result = ef_set_env(key, value); + + if (result == EF_NO_ERR) { + result = ef_save_env(); + } + + return result; +} + +/** + * Del and save an ENV. If del ENV is success then will save it. + * + * @param key ENV name + * + * @return result + */ +EfErrCode ef_del_and_save_env(const char *key) { + EfErrCode result = EF_NO_ERR; + + result = ef_del_env(key); + + if (result == EF_NO_ERR) { + result = ef_save_env(); + } + + return result; +} + +#ifdef EF_ENV_AUTO_UPDATE +/** + * Auto update ENV to latest default when current EF_ENV_VER is changed. + * + * @return result + */ +static EfErrCode env_auto_update(void) +{ + size_t i; + + /* lock the ENV cache */ + ef_port_env_lock(); + + /* read ENV version number from flash*/ + ef_port_read(get_env_system_addr() + ENV_PARAM_INDEX_VER_NUM * 4, + &env_cache[ENV_PARAM_INDEX_VER_NUM] , 4); + + /* check version number */ + if (env_cache[ENV_PARAM_INDEX_VER_NUM] != EF_ENV_VER_NUM) { + env_cache_changed = true; + /* update version number */ + env_cache[ENV_PARAM_INDEX_VER_NUM] = EF_ENV_VER_NUM; + /* add a new ENV when it's not found */ + for (i = 0; i < default_env_set_size; i++) { + if (find_env(default_env_set[i].key) == NULL) { + create_env(default_env_set[i].key, default_env_set[i].value); + } + } + } + + /* unlock the ENV cache */ + ef_port_env_unlock(); + + return ef_save_env(); +} +#endif /* EF_ENV_AUTO_UPDATE */ + +#endif /* EF_ENV_USING_WL_MODE */ + +#endif /* defined(EF_USING_ENV) && defined(EF_ENV_USING_LEGACY_MODE) */ diff --git a/demo/os/nuttx-spiflash/apps/system/easyflash/src/ef_env_legacy_wl.c b/demo/os/nuttx-spiflash/apps/system/easyflash/src/ef_env_legacy_wl.c new file mode 100644 index 0000000..1630890 --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/system/easyflash/src/ef_env_legacy_wl.c @@ -0,0 +1,1082 @@ +/* + * This file is part of the EasyFlash Library. + * + * Copyright (c) 2015-2018, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: Environment variables operating interface. (wear leveling mode) + * Created on: 2015-02-11 + */ + +#include +#include +#include + +#if defined(EF_USING_ENV) && defined(EF_ENV_USING_LEGACY_MODE) + +#ifdef EF_ENV_USING_WL_MODE + +#if defined(EF_USING_ENV) && (!defined(ENV_USER_SETTING_SIZE) || !defined(ENV_AREA_SIZE)) +#error "Please configure user setting ENV size or ENV area size (in ef_cfg.h)" +#endif + +/** + * ENV area has 2 sections + * 1. System section + * Storage ENV current using data section address. + * Units: Word. Total size: @see EF_ERASE_MIN_SIZE. + * 2. Data section + * The data section storage ENV's parameters and detail. + * When an exception has occurred on flash erase or write. The current using data section + * address will move to next available position. This position depends on EF_ERASE_MIN_SIZE. + * 2.1 ENV parameters part + * It storage ENV's parameters. + * 2.2 ENV detail part + * It storage all ENV. Storage format is key=value\0. + * All ENV must be 4 bytes alignment. The remaining part must fill '\0'. + * + * @note Word = 4 Bytes in this file + * @note It will has two ENV areas(Area0, Area1) in data section when used power fail safeguard mode. + */ + +/* flash ENV parameters part index and size */ +enum { + /* data section ENV detail part end address index */ + ENV_PARAM_PART_INDEX_END_ADDR = 0, + +#ifdef EF_ENV_USING_PFS_MODE + /* saved count for ENV area */ + ENV_PARAM_PART_INDEX_SAVED_COUNT, +#endif + +#ifdef EF_ENV_AUTO_UPDATE + /* current version number for ENV */ + ENV_PARAM_INDEX_VER_NUM, +#endif + + /* data section CRC32 code index */ + ENV_PARAM_PART_INDEX_DATA_CRC, + /* ENV parameters part word size */ + ENV_PARAM_PART_WORD_SIZE, + /* ENV parameters part byte size */ + ENV_PARAM_PART_BYTE_SIZE = ENV_PARAM_PART_WORD_SIZE * 4, +}; + +/* default ENV set, must be initialized by user */ +static ef_env const *default_env_set; +/* default ENV set size, must be initialized by user */ +static size_t default_env_set_size = 0; +/* flash ENV data section size */ +static size_t env_data_section_size = 0; +/* ENV ram cache */ +static uint32_t env_cache[ENV_USER_SETTING_SIZE / 4] = { 0 }; +/* ENV start address in flash */ +static uint32_t env_start_addr = 0; +/* current using data section address */ +static uint32_t cur_using_data_addr = 0; +/* ENV ram cache has changed when ENV created, deleted and changed value. */ +static bool env_cache_changed = false; +/* initialize OK flag */ +static bool init_ok = false; + +#ifdef EF_ENV_USING_PFS_MODE +/* next save ENV area address */ +static uint32_t next_save_area_addr = 0; +#endif + +static uint32_t get_env_start_addr(void); +static uint32_t get_cur_using_data_addr(void); +static uint32_t get_env_detail_addr(void); +static uint32_t get_env_detail_end_addr(void); +static void set_cur_using_data_addr(uint32_t using_data_addr); +static void set_env_detail_end_addr(uint32_t end_addr); +static EfErrCode write_env(const char *key, const char *value); +static char *find_env(const char *key); +static size_t get_env_detail_size(void); +static size_t get_env_user_used_size(void); +static EfErrCode create_env(const char *key, const char *value); +static EfErrCode del_env(const char *key); +static EfErrCode save_cur_using_data_addr(uint32_t cur_data_addr); +static uint32_t calc_env_crc(void); +static bool env_crc_is_ok(void); +#ifdef EF_ENV_AUTO_UPDATE +static EfErrCode env_auto_update(void); +#endif + +/** + * Flash ENV initialize. + * + * @param default_env default ENV set for user + * @param default_env_size default ENV set size + * + * @return result + */ +EfErrCode ef_env_init(ef_env const *default_env, size_t default_env_size) { + EfErrCode result = EF_NO_ERR; + + EF_ASSERT(ENV_AREA_SIZE); + EF_ASSERT(ENV_USER_SETTING_SIZE); + /* must be word alignment for ENV */ + EF_ASSERT(ENV_USER_SETTING_SIZE % 4 == 0); + EF_ASSERT(ENV_AREA_SIZE % 4 == 0); + EF_ASSERT(default_env); + EF_ASSERT(default_env_size < ENV_USER_SETTING_SIZE); + +#ifndef EF_ENV_USING_PFS_MODE + /* system section size is erase_min_size, so last part is data section */ + env_data_section_size = ENV_AREA_SIZE - EF_ERASE_MIN_SIZE; +#else + /* system section size is erase_min_size, so last part is data section */ + env_data_section_size = ENV_AREA_SIZE / 2 - EF_ERASE_MIN_SIZE; + EF_ASSERT((ENV_AREA_SIZE / EF_ERASE_MIN_SIZE) % 2 == 0); +#endif + EF_ASSERT(env_data_section_size >= ENV_USER_SETTING_SIZE); + /* the ENV data section size should be an integral multiple of erase minimum size. */ + EF_ASSERT(env_data_section_size % EF_ERASE_MIN_SIZE == 0); + + + env_start_addr = EF_START_ADDR; + default_env_set = default_env; + default_env_set_size = default_env_size; + + EF_DEBUG("ENV start address is 0x%08X, size is %d bytes.\n", EF_START_ADDR, ENV_AREA_SIZE); + + result = ef_load_env(); + +#ifdef EF_ENV_AUTO_UPDATE + if (result == EF_NO_ERR) { + env_auto_update(); + } +#endif + + if (result == EF_NO_ERR) { + init_ok = true; + } + + return result; +} + +/** + * ENV set default. + * + * @return result + */ +EfErrCode ef_env_set_default(void) { + EfErrCode result = EF_NO_ERR; + size_t i; + + EF_ASSERT(default_env_set); + EF_ASSERT(default_env_set_size); + + /* lock the ENV cache */ + ef_port_env_lock(); + + /* set ENV detail part end address is at ENV detail part start address */ + set_env_detail_end_addr(get_env_detail_addr()); + +#ifdef EF_ENV_USING_PFS_MODE + /* set saved count to default 0 */ + env_cache[ENV_PARAM_PART_INDEX_SAVED_COUNT] = 0; +#endif + +#ifdef EF_ENV_AUTO_UPDATE + /* initialize version number */ + env_cache[ENV_PARAM_INDEX_VER_NUM] = EF_ENV_VER_NUM; +#endif + + /* create default ENV */ + for (i = 0; i < default_env_set_size; i++) { + create_env(default_env_set[i].key, default_env_set[i].value); + } + + /* unlock the ENV cache */ + ef_port_env_unlock(); + + result = ef_save_env(); + +#ifdef EF_ENV_USING_PFS_MODE + /* reset other PFS area's data */ + if (result == EF_NO_ERR) { + env_cache_changed = true; + result = ef_save_env(); + } +#endif + + return result; +} + +/** + * Get ENV start address in flash. + * + * @return ENV start address in flash + */ +static uint32_t get_env_start_addr(void) { + return env_start_addr; +} +/** + * Get current using data section address. + * + * @return current using data section address + */ +static uint32_t get_cur_using_data_addr(void) { + return cur_using_data_addr; +} + +/** + * Set current using data section address. + * + * @param using_data_addr current using data section address + */ +static void set_cur_using_data_addr(uint32_t using_data_addr) { + cur_using_data_addr = using_data_addr; +} + +/** + * Get ENV detail part start address. + * + * @return detail part start address + */ +static uint32_t get_env_detail_addr(void) { + return get_cur_using_data_addr() + ENV_PARAM_PART_BYTE_SIZE; +} + +/** + * Get ENV detail part end address. + * It's the first word in ENV. + * + * @return ENV end address + */ +static uint32_t get_env_detail_end_addr(void) { + /* it is the first word */ + return env_cache[ENV_PARAM_PART_INDEX_END_ADDR]; +} + +/** + * Set ENV detail part end address. + * It's the first word in ENV. + * + * @param end_addr ENV end address + */ +static void set_env_detail_end_addr(uint32_t end_addr) { + env_cache[ENV_PARAM_PART_INDEX_END_ADDR] = end_addr; +} + +/** + * Get current ENV detail part size. + * + * @return size + */ +static size_t get_env_detail_size(void) { + if (get_env_detail_end_addr() > get_env_detail_addr()) { + return get_env_detail_end_addr() - get_env_detail_addr(); + } else { + return 0; + } +} + +/** + * Get current user used ENV size. + * + * @see ENV_USER_SETTING_SIZE + * + * @return size + */ +/* must be initialized */ +static size_t get_env_user_used_size(void) { + if (get_env_detail_end_addr() > get_cur_using_data_addr()) { + return get_env_detail_end_addr() - get_cur_using_data_addr(); + } else { + return 0; + } +} + +/** + * Get current ENV already write bytes. + * + * @return write bytes + */ +size_t ef_get_env_write_bytes(void) { +#ifndef EF_ENV_USING_PFS_MODE + return get_env_detail_end_addr() - get_env_start_addr(); +#else + return EF_ERASE_MIN_SIZE + get_env_detail_end_addr() - get_cur_using_data_addr(); +#endif +} + +/** + * Write an ENV at the end of cache. + * + * @param key ENV name + * @param value ENV value + * + * @return result + */ +static EfErrCode write_env(const char *key, const char *value) { + EfErrCode result = EF_NO_ERR; + size_t ker_len = strlen(key), value_len = strlen(value), env_str_len; + char *env_cache_bak = (char *)env_cache; + + /* calculate ENV storage length, contain '=' and '\0'. */ + env_str_len = ker_len + value_len + 2; + if (env_str_len % 4 != 0) { + env_str_len = (env_str_len / 4 + 1) * 4; + } + /* check capacity of ENV */ + if (env_str_len + get_env_user_used_size() >= ENV_USER_SETTING_SIZE) { + return EF_ENV_FULL; + } + /* calculate current ENV ram cache end address */ + env_cache_bak += ENV_PARAM_PART_BYTE_SIZE + get_env_detail_size(); + /* copy key name */ + memcpy(env_cache_bak, key, ker_len); + env_cache_bak += ker_len; + /* copy equal sign */ + *env_cache_bak = '='; + env_cache_bak++; + /* copy value */ + memcpy(env_cache_bak, value, value_len); + env_cache_bak += value_len; + /* fill '\0' for string end sign */ + *env_cache_bak = '\0'; + env_cache_bak ++; + /* fill '\0' for word alignment */ + memset(env_cache_bak, 0, env_str_len - (ker_len + value_len + 2)); + set_env_detail_end_addr(get_env_detail_end_addr() + env_str_len); + /* ENV ram cache has changed */ + env_cache_changed = true; + + return result; +} + +/** + * Find ENV. + * + * @param key ENV name + * + * @return found ENV in ram cache + */ +static char *find_env(const char *key) { + char *env_start, *env_end, *env, *found_env = NULL; + size_t key_len = strlen(key), env_len; + + if ((key == NULL) || (*key == '\0')) { + EF_INFO("Flash ENV name must be not empty!\n"); + return NULL; + } + + /* from data section start to data section end */ + env_start = (char *) ((char *) env_cache + ENV_PARAM_PART_BYTE_SIZE); + env_end = (char *) ((char *) env_cache + ENV_PARAM_PART_BYTE_SIZE + get_env_detail_size()); + + /* ENV is null */ + if (env_start == env_end) { + return NULL; + } + + env = env_start; + while (env < env_end) { + /* the key length must be equal */ + if (!strncmp(env, key, key_len) && (env[key_len] == '=')) { + found_env = env; + break; + } else { + /* calculate ENV length, contain '\0'. */ + env_len = strlen(env) + 1; + /* next ENV and word alignment */ + if (env_len % 4 == 0) { + env += env_len; + } else { + env += (env_len / 4 + 1) * 4; + } + } + } + + return found_env; +} + +/** + * If the ENV is not exist, create it. + * @see flash_write_env + * + * @param key ENV name + * @param value ENV value + * + * @return result + */ +static EfErrCode create_env(const char *key, const char *value) { + EfErrCode result = EF_NO_ERR; + + EF_ASSERT(key); + EF_ASSERT(value); + + if ((key == NULL) || (*key == '\0')) { + EF_INFO("Flash ENV name must be not empty!\n"); + return EF_ENV_NAME_ERR; + } + + if (strchr(key, '=')) { + EF_INFO("Flash ENV name can't contain '='.\n"); + return EF_ENV_NAME_ERR; + } + + /* find ENV */ + if (find_env(key)) { + EF_INFO("The name of \"%s\" is already exist.\n", key); + return EF_ENV_NAME_EXIST; + } + /* write ENV at the end of cache */ + result = write_env(key, value); + + return result; +} + +/** + * Delete an ENV in cache. + * + * @param key ENV name + * + * @return result + */ +static EfErrCode del_env(const char *key) { + EfErrCode result = EF_NO_ERR; + char *del_env = NULL; + size_t del_env_length, remain_env_length; + + EF_ASSERT(key); + + if ((key == NULL) || (*key == '\0')) { + EF_INFO("Flash ENV name must be not NULL!\n"); + return EF_ENV_NAME_ERR; + } + + if (strchr(key, '=')) { + EF_INFO("Flash ENV name or value can't contain '='.\n"); + return EF_ENV_NAME_ERR; + } + + /* find ENV */ + del_env = find_env(key); + + if (!del_env) { + EF_INFO("Not find \"%s\" in ENV.\n", key); + return EF_ENV_NAME_ERR; + } + del_env_length = strlen(del_env); + /* '\0' also must be as ENV length */ + del_env_length ++; + /* the address must multiple of 4 */ + if (del_env_length % 4 != 0) { + del_env_length = (del_env_length / 4 + 1) * 4; + } + /* calculate remain ENV length */ + remain_env_length = get_env_detail_size() + - (((uint32_t) del_env + del_env_length) - ((uint32_t) env_cache + ENV_PARAM_PART_BYTE_SIZE)); + /* remain ENV move forward */ + memcpy(del_env, del_env + del_env_length, remain_env_length); + /* reset ENV end address */ + set_env_detail_end_addr(get_env_detail_end_addr() - del_env_length); + /* ENV ram cache has changed */ + env_cache_changed = true; + + return result; +} + +/** + * Set an ENV.If it value is NULL, delete it. + * If not find it in ENV table, then create it. + * + * @param key ENV name + * @param value ENV value + * + * @return result + */ +EfErrCode ef_set_env(const char *key, const char *value) { + EfErrCode result = EF_NO_ERR; + char *old_env, *old_value; + + if (!init_ok) { + EF_INFO("ENV isn't initialize OK.\n"); + return EF_ENV_INIT_FAILED; + } + + /* lock the ENV cache */ + ef_port_env_lock(); + + /* if ENV value is NULL, delete it */ + if (value == NULL) { + result = del_env(key); + } else { + old_env = find_env(key); + /* If find this ENV, then compare the new value and old value. */ + if (old_env) { + /* find the old value address */ + old_env = strchr(old_env, '='); + old_value = old_env + 1; + /* If it is changed then delete it and recreate it */ + if (strcmp(old_value, value)) { + result = del_env(key); + if (result == EF_NO_ERR) { + result = create_env(key, value); + } + } + } else { + result = create_env(key, value); + } + } + /* unlock the ENV cache */ + ef_port_env_unlock(); + + return result; +} + +/** + * Del an ENV. + * + * @param key ENV name + * + * @return result + */ +EfErrCode ef_del_env(const char *key) { + EfErrCode result = EF_NO_ERR; + + if (!init_ok) { + EF_INFO("ENV isn't initialize OK.\n"); + return EF_ENV_INIT_FAILED; + } + + /* lock the ENV cache */ + ef_port_env_lock(); + + result = del_env(key); + + /* unlock the ENV cache */ + ef_port_env_unlock(); + + return result; +} + +/** + * Get an ENV value by key name. + * + * @param key ENV name + * + * @return value + */ +char *ef_get_env(const char *key) { + char *env = NULL, *value = NULL; + + if (!init_ok) { + EF_INFO("ENV isn't initialize OK.\n"); + return NULL; + } + + /* find ENV */ + env = find_env(key); + + if (env == NULL) { + return NULL; + } + /* get value address */ + value = strchr(env, '='); + if (value != NULL) { + /* the equal sign next character is value */ + value++; + } + return value; +} +/** + * Print ENV. + */ +void ef_print_env(void) { + uint32_t *env_cache_detail_addr = env_cache + ENV_PARAM_PART_WORD_SIZE, *env_cache_end_addr = + (uint32_t *) (env_cache + ENV_PARAM_PART_WORD_SIZE + get_env_detail_size() / 4); + uint8_t j; + char c; + + if (!init_ok) { + EF_INFO("ENV isn't initialize OK.\n"); + return; + } + + for (; env_cache_detail_addr < env_cache_end_addr; env_cache_detail_addr += 1) { + for (j = 0; j < 4; j++) { + c = (*env_cache_detail_addr) >> (8 * j); + ef_print("%c", c); + if (c == '\0') { + ef_print("\n"); + break; + } + } + } + +#ifndef EF_ENV_USING_PFS_MODE + ef_print("\nmode: wear leveling\n"); + ef_print("size: %ld/%ld bytes, write bytes %ld/%ld.\n", get_env_user_used_size(), ENV_USER_SETTING_SIZE, + ef_get_env_write_bytes(), ENV_AREA_SIZE); +#else + ef_print("\nmode: wear leveling and power fail safeguard\n"); + ef_print("size: %ld/%ld bytes, write bytes %ld/%ld.\n", get_env_user_used_size(), ENV_USER_SETTING_SIZE, + ef_get_env_write_bytes(), ENV_AREA_SIZE / 2); + ef_print("saved count: %ld\n", env_cache[ENV_PARAM_PART_INDEX_SAVED_COUNT]); +#endif + +#ifdef EF_ENV_AUTO_UPDATE + ef_print("ver num: %d\n", env_cache[ENV_PARAM_INDEX_VER_NUM]); +#endif +} + +/** + * Load flash ENV to ram. + * + * @return result + */ +#ifndef EF_ENV_USING_PFS_MODE +EfErrCode ef_load_env(void) { + EfErrCode result = EF_NO_ERR; + uint32_t *env_cache_bak, env_end_addr, using_data_addr; + + /* read current using data section address */ + ef_port_read(get_env_start_addr(), &using_data_addr, 4); + /* if ENV is not initialize or flash has dirty data, set default for it */ + if ((using_data_addr == 0xFFFFFFFF) + || (using_data_addr > get_env_start_addr() + ENV_AREA_SIZE) + || (using_data_addr < get_env_start_addr() + EF_ERASE_MIN_SIZE)) { + /* initialize current using data section address */ + set_cur_using_data_addr(get_env_start_addr() + EF_ERASE_MIN_SIZE); + /* save current using data section address to flash*/ + if ((result = save_cur_using_data_addr(get_cur_using_data_addr())) == EF_NO_ERR) { + /* set default ENV */ + result = ef_env_set_default(); + } + } else { + /* set current using data section address */ + set_cur_using_data_addr(using_data_addr); + /* read ENV detail part end address from flash */ + ef_port_read(get_cur_using_data_addr() + ENV_PARAM_PART_INDEX_END_ADDR * 4, &env_end_addr, 4); + /* if ENV end address has error, set default for ENV */ + if (env_end_addr > get_env_start_addr() + ENV_AREA_SIZE) { + /* initialize current using data section address */ + set_cur_using_data_addr(get_env_start_addr() + EF_ERASE_MIN_SIZE); + /* save current using data section address to flash*/ + if ((result = save_cur_using_data_addr(get_cur_using_data_addr())) == EF_NO_ERR) { + EF_INFO("Warning: ENV end address has error. Set it to default.\n"); + result = ef_env_set_default(); + } + } else { + /* set ENV detail part end address */ + set_env_detail_end_addr(env_end_addr); + + env_cache_bak = env_cache + ENV_PARAM_PART_WORD_SIZE; + /* read all ENV from flash */ + ef_port_read(get_env_detail_addr(), env_cache_bak, get_env_detail_size()); + /* read ENV CRC code from flash */ + ef_port_read(get_cur_using_data_addr() + ENV_PARAM_PART_INDEX_DATA_CRC * 4, + &env_cache[ENV_PARAM_PART_INDEX_DATA_CRC], 4); + /* if ENV CRC32 check is fault, set default for it */ + if (!env_crc_is_ok()) { + EF_INFO("Warning: ENV CRC check failed. Set it to default.\n"); + result = ef_env_set_default(); + } + } + + } + return result; +} +#else +EfErrCode ef_load_env(void) { + EfErrCode result = EF_NO_ERR; + /* ENV area0 current using address default value */ + uint32_t area0_default_cur_using_addr = get_env_start_addr() + EF_ERASE_MIN_SIZE; + /* ENV area1 current using address default value */ + uint32_t area1_default_cur_using_addr = area0_default_cur_using_addr + ENV_AREA_SIZE / 2; + uint32_t area0_cur_using_addr, area1_cur_using_addr, area0_end_addr, area1_end_addr; + uint32_t area0_crc, area1_crc, area0_saved_count, area1_saved_count; + bool area0_is_valid = true, area1_is_valid = true; + + /* read ENV area0 and area1 current using data section address */ + ef_port_read(get_env_start_addr(), &area0_cur_using_addr, 4); + ef_port_read(get_env_start_addr() + ENV_AREA_SIZE / 2, &area1_cur_using_addr, 4); + /* if ENV is not initialize or flash has dirty data, set it isn't valid */ + if ((area0_cur_using_addr == 0xFFFFFFFF) + || (area0_cur_using_addr > get_env_start_addr() + ENV_AREA_SIZE / 2) + || (area0_cur_using_addr < get_env_start_addr() + EF_ERASE_MIN_SIZE)) { + area0_is_valid = false; + } + if ((area1_cur_using_addr == 0xFFFFFFFF) + || (area1_cur_using_addr > get_env_start_addr() + ENV_AREA_SIZE) + || (area1_cur_using_addr < get_env_start_addr() + ENV_AREA_SIZE / 2 + EF_ERASE_MIN_SIZE)) { + area1_is_valid = false; + } + /* check area0 end address when it is valid */ + if (area0_is_valid) { + /* read ENV area end address from flash */ + ef_port_read(area0_cur_using_addr + ENV_PARAM_PART_INDEX_END_ADDR * 4, &area0_end_addr, 4); + if ((area0_end_addr == 0xFFFFFFFF) || (area0_end_addr < area0_cur_using_addr) + || (area0_end_addr > area0_cur_using_addr + ENV_USER_SETTING_SIZE)) { + area0_is_valid = false; + } + } + /* check area1 end address when it is valid */ + if (area1_is_valid) { + /* read ENV area end address from flash */ + ef_port_read(area1_cur_using_addr + ENV_PARAM_PART_INDEX_END_ADDR * 4, &area1_end_addr, 4); + if ((area1_end_addr == 0xFFFFFFFF) || (area1_end_addr < area1_cur_using_addr) + || (area1_end_addr > area1_cur_using_addr + ENV_USER_SETTING_SIZE)) { + area1_is_valid = false; + } + } + /* check area0 CRC when it is valid */ + if (area0_is_valid) { + /* read ENV area0 crc32 code from flash */ + ef_port_read(area0_cur_using_addr + ENV_PARAM_PART_INDEX_DATA_CRC * 4, &area0_crc, 4); + /* read ENV from ENV area0 */ + ef_port_read(area0_cur_using_addr, env_cache, area0_end_addr - area0_cur_using_addr); + /* current using data section address is area0 current using data section address */ + set_cur_using_data_addr(area0_cur_using_addr); + if (!env_crc_is_ok()) { + area0_is_valid = false; + } + } + /* check area1 CRC when it is valid */ + if (area1_is_valid) { + /* read ENV area1 crc32 code from flash */ + ef_port_read(area1_cur_using_addr + ENV_PARAM_PART_INDEX_DATA_CRC * 4, &area1_crc, 4); + /* read ENV from ENV area1 */ + ef_port_read(area1_cur_using_addr, env_cache, area1_end_addr - area1_cur_using_addr); + /* current using data section address is area1 current using data section address */ + set_cur_using_data_addr(area1_cur_using_addr); + if (!env_crc_is_ok()) { + area1_is_valid = false; + } + } + /* all ENV area CRC is OK then compare saved count */ + if (area0_is_valid && area1_is_valid) { + /* read ENV area saved count from flash */ + ef_port_read(area0_cur_using_addr + ENV_PARAM_PART_INDEX_SAVED_COUNT * 4, + &area0_saved_count, 4); + ef_port_read(area1_cur_using_addr + ENV_PARAM_PART_INDEX_SAVED_COUNT * 4, + &area1_saved_count, 4); + /* the bigger saved count area is valid */ + if ((area0_saved_count > area1_saved_count) || ((area0_saved_count == 0) && (area1_saved_count == 0xFFFFFFFF))) { + area1_is_valid = false; + } else { + area0_is_valid = false; + } + } + if (area0_is_valid) { + /* current using data section address is area0 current using data section address */ + set_cur_using_data_addr(area0_cur_using_addr); + /* next save ENV area address is area1 current using address value */ + next_save_area_addr = area1_cur_using_addr; + /* read all ENV from area0 */ + ef_port_read(area0_cur_using_addr, env_cache, area0_end_addr - area0_cur_using_addr); + } else if (area1_is_valid) { + /* already read data section and set_cur_using_data_addr above current code, + * so just set next save ENV area address is area0 current using address value */ + next_save_area_addr = area0_cur_using_addr; + } else { + /* current using data section address is area1 current using address default value */ + set_cur_using_data_addr(area1_default_cur_using_addr); + /* next save ENV area address default is area0 current using address default value */ + next_save_area_addr = area0_default_cur_using_addr; + /* save current using data section address to flash*/ + if (((result = save_cur_using_data_addr(area0_default_cur_using_addr)) == EF_NO_ERR) + && ((result = save_cur_using_data_addr(area1_default_cur_using_addr)) == EF_NO_ERR)) { + /* set the ENV to default */ + result = ef_env_set_default(); + } + } + return result; +} +#endif + +/** + * Save ENV to flash. + */ +EfErrCode ef_save_env(void) { + EfErrCode result = EF_NO_ERR; + uint32_t cur_using_addr_bak, move_offset_addr; + size_t env_used_size = get_env_user_used_size(); + uint32_t data_sec_end_addr; + + /* ENV ram cache has not changed don't need to save */ + if (!env_cache_changed) { + return result; + } + +#ifndef EF_ENV_USING_PFS_MODE + data_sec_end_addr = get_env_start_addr() + ENV_AREA_SIZE - 4; + cur_using_addr_bak = get_cur_using_data_addr(); +#else + cur_using_addr_bak = next_save_area_addr; + /* replace next_save_area_addr with cur_using_data_addr */ + next_save_area_addr = get_cur_using_data_addr(); + set_cur_using_data_addr(cur_using_addr_bak); + /* change the ENV detail end address to next save area address */ + set_env_detail_end_addr(get_cur_using_data_addr() + env_used_size); + /* area0 or area1 */ + if (get_cur_using_data_addr() < get_env_start_addr() + ENV_AREA_SIZE / 2) { + data_sec_end_addr = get_env_start_addr() + ENV_AREA_SIZE / 2 - 4; + } else { + data_sec_end_addr = get_env_start_addr() + ENV_AREA_SIZE - 4; + } + /* ENV area saved count +1 */ + env_cache[ENV_PARAM_PART_INDEX_SAVED_COUNT]++; +#endif + + /* wear leveling process, automatic move ENV to next available position */ + while (get_cur_using_data_addr() + env_used_size < data_sec_end_addr) { + /* calculate and cache CRC32 code */ + env_cache[ENV_PARAM_PART_INDEX_DATA_CRC] = calc_env_crc(); + /* erase ENV */ + result = ef_port_erase(get_cur_using_data_addr(), env_used_size); + switch (result) { + case EF_NO_ERR: { + EF_DEBUG("Erased ENV OK.\n"); + break; + } + case EF_ERASE_ERR: { + EF_INFO("Warning: Erased ENV fault! Start address is 0x%08X, size is %ld.\n", + get_cur_using_data_addr(), env_used_size); + EF_INFO("Moving ENV to next available position.\n"); + /* Calculate move offset address. + * Current strategy is optimistic. It will offset the flash erasure minimum size. + */ + move_offset_addr = EF_ERASE_MIN_SIZE; + /* calculate and set next available data section address */ + set_cur_using_data_addr(get_cur_using_data_addr() + move_offset_addr); + /* calculate and set next available ENV detail part end address */ + set_env_detail_end_addr(get_env_detail_end_addr() + move_offset_addr); + continue; + } + } + /* write ENV to flash */ + result = ef_port_write(get_cur_using_data_addr(), env_cache, env_used_size); + switch (result) { + case EF_NO_ERR: { + EF_DEBUG("Saved ENV OK.\n"); + break; + } + case EF_WRITE_ERR: { + EF_INFO("Warning: Saved ENV fault! Start address is 0x%08X, size is %ld.\n", + get_cur_using_data_addr(), env_used_size); + EF_INFO("Moving ENV to next available position.\n"); + /* Calculate move offset address. + * Current strategy is optimistic. It will offset the flash erasure minimum size. + */ + move_offset_addr = EF_ERASE_MIN_SIZE; + /* calculate and set next available data section address */ + set_cur_using_data_addr(get_cur_using_data_addr() + move_offset_addr); + /* calculate and set next available ENV detail part end address */ + set_env_detail_end_addr(get_env_detail_end_addr() + move_offset_addr); + continue; + } + } + /* save ENV success */ + if (result == EF_NO_ERR) { + break; + } + } + + if (get_cur_using_data_addr() + env_used_size < data_sec_end_addr) { + /* current using data section address has changed, save it */ + if (get_cur_using_data_addr() != cur_using_addr_bak) { + result = save_cur_using_data_addr(get_cur_using_data_addr()); + } + } else { + result = EF_ENV_FULL; + EF_INFO("Error: The flash has no available space to save ENV.\n"); + } + + env_cache_changed = false; + + return result; +} + +/** + * Calculate the cached ENV CRC32 value. + * + * @return CRC32 value + */ +static uint32_t calc_env_crc(void) { + uint32_t crc32 = 0; + + /* Calculate the ENV end address and all ENV data CRC32. + * The 4 is ENV end address bytes size. */ + crc32 = ef_calc_crc32(crc32, &env_cache[ENV_PARAM_PART_INDEX_END_ADDR], 4); + crc32 = ef_calc_crc32(crc32, &env_cache[ENV_PARAM_PART_WORD_SIZE], get_env_detail_size()); + EF_DEBUG("Calculate ENV CRC32 number is 0x%08X.\n", crc32); + + return crc32; +} + +/** + * Check the ENV CRC32 + * + * @return true is ok + */ +static bool env_crc_is_ok(void) { + if (calc_env_crc() == env_cache[ENV_PARAM_PART_INDEX_DATA_CRC]) { + EF_DEBUG("Verify ENV CRC32 result is OK.\n"); + return true; + } else { + return false; + } +} + +/** + * Save current using data section address to flash. + * + * @param cur_data_addr current using data section address + * + * @return result + */ +#ifndef EF_ENV_USING_PFS_MODE +static EfErrCode save_cur_using_data_addr(uint32_t cur_data_addr) { + EfErrCode result = EF_NO_ERR; + + /* erase ENV system section */ + result = ef_port_erase(get_env_start_addr(), 4); + if (result == EF_NO_ERR) { + /* write current using data section address to flash */ + result = ef_port_write(get_env_start_addr(), &cur_data_addr, 4); + if (result == EF_WRITE_ERR) { + EF_INFO("Error: Write system section fault! Start address is 0x%08X, size is %ld.\n", + get_env_start_addr(), 4); + EF_INFO("Note: The ENV can not be used.\n"); + } + } else { + EF_INFO("Error: Erased system section fault! Start address is 0x%08X, size is %ld.\n", + get_env_start_addr(), 4); + EF_INFO("Note: The ENV can not be used\n"); + } + return result; +} +#else +static EfErrCode save_cur_using_data_addr(uint32_t cur_data_addr) { + EfErrCode result = EF_NO_ERR; + uint32_t cur_system_sec_addr; + + if (cur_data_addr < get_env_start_addr() + ENV_AREA_SIZE / 2) { + /* current using system section is in ENV area0 */ + cur_system_sec_addr = get_env_start_addr(); + } else { + /* current using system section is in ENV area1 */ + cur_system_sec_addr = get_env_start_addr() + ENV_AREA_SIZE / 2; + } + /* erase ENV system section */ + result = ef_port_erase(cur_system_sec_addr, 4); + if (result == EF_NO_ERR) { + /* write area0 and area1 current using data section address to flash */ + result = ef_port_write(cur_system_sec_addr, &cur_data_addr, 4); + if (result == EF_WRITE_ERR) { + EF_INFO("Error: Write system section fault! Start address is 0x%08X, size is %ld.\n", + cur_system_sec_addr, 4); + EF_INFO("Note: The ENV can not be used.\n"); + } + } else { + EF_INFO("Error: Erased system section fault! Start address is 0x%08X, size is %ld.\n", + cur_system_sec_addr, 4); + EF_INFO("Note: The ENV can not be used\n"); + } + return result; +} +#endif + +/** + * Set and save an ENV. If set ENV is success then will save it. + * + * @param key ENV name + * @param value ENV value + * + * @return result + */ +EfErrCode ef_set_and_save_env(const char *key, const char *value) { + EfErrCode result = EF_NO_ERR; + + result = ef_set_env(key, value); + + if (result == EF_NO_ERR) { + result = ef_save_env(); + } + + return result; +} + +/** + * Del and save an ENV. If del ENV is success then will save it. + * + * @param key ENV name + * + * @return result + */ +EfErrCode ef_del_and_save_env(const char *key) { + EfErrCode result = EF_NO_ERR; + + result = ef_del_env(key); + + if (result == EF_NO_ERR) { + result = ef_save_env(); + } + + return result; +} + +#ifdef EF_ENV_AUTO_UPDATE +/** + * Auto update ENV to latest default when current EF_ENV_VER is changed. + * + * @return result + */ +static EfErrCode env_auto_update(void) +{ + size_t i; + + /* lock the ENV cache */ + ef_port_env_lock(); + + /* read ENV version number from flash*/ + ef_port_read(get_cur_using_data_addr() + ENV_PARAM_INDEX_VER_NUM * 4, + &env_cache[ENV_PARAM_INDEX_VER_NUM] , 4); + + /* check version number */ + if (env_cache[ENV_PARAM_INDEX_VER_NUM] != EF_ENV_VER_NUM) { + env_cache_changed = true; + /* update version number */ + env_cache[ENV_PARAM_INDEX_VER_NUM] = EF_ENV_VER_NUM; + /* add a new ENV when it's not found */ + for (i = 0; i < default_env_set_size; i++) { + if (find_env(default_env_set[i].key) == NULL) { + create_env(default_env_set[i].key, default_env_set[i].value); + } + } + } + + /* unlock the ENV cache */ + ef_port_env_unlock(); + + return ef_save_env(); +} +#endif /* EF_ENV_AUTO_UPDATE */ + +#endif /* EF_ENV_USING_WL_MODE */ + +#endif /* defined(EF_USING_ENV) && defined(EF_ENV_USING_LEGACY_MODE) */ diff --git a/demo/os/nuttx-spiflash/apps/system/easyflash/src/ef_iap.c b/demo/os/nuttx-spiflash/apps/system/easyflash/src/ef_iap.c new file mode 100644 index 0000000..b51a483 --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/system/easyflash/src/ef_iap.c @@ -0,0 +1,289 @@ +/* + * This file is part of the EasyFlash Library. + * + * Copyright (c) 2015-2017, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: IAP(In-Application Programming) operating interface. + * Created on: 2015-01-05 + */ + +#include + +#ifdef EF_USING_IAP + +/* IAP section backup application section start address in flash */ +static uint32_t bak_app_start_addr = 0; + +/** + * Flash IAP function initialize. + * + * @return result + */ +EfErrCode ef_iap_init(void) { + EfErrCode result = EF_NO_ERR; + + bak_app_start_addr = EF_START_ADDR ; + +#if defined(EF_USING_ENV) + bak_app_start_addr += ENV_AREA_SIZE; +#endif + +#if defined(EF_USING_LOG) + bak_app_start_addr += LOG_AREA_SIZE; +#endif + + return result; +} + +/** + * Erase backup area application data. + * + * @param app_size application size + * + * @return result + */ +EfErrCode ef_erase_bak_app(size_t app_size) { + EfErrCode result = EF_NO_ERR; + + result = ef_port_erase(ef_get_bak_app_start_addr(), app_size); + switch (result) { + case EF_NO_ERR: { + EF_INFO("Erased backup area application OK.\n"); + break; + } + case EF_ERASE_ERR: { + EF_INFO("Warning: Erase backup area application fault!\n"); + /* will return when erase fault */ + return result; + } + } + + return result; +} + +/** + * Erase user old application by using specified erase function. + * + * @param user_app_addr application entry address + * @param app_size application size + * @param app_erase user specified application erase function + * + * @return result + */ +EfErrCode ef_erase_spec_user_app(uint32_t user_app_addr, size_t app_size, + EfErrCode (*app_erase)(uint32_t addr, size_t size)) { + EfErrCode result = EF_NO_ERR; + + result = app_erase(user_app_addr, app_size); + switch (result) { + case EF_NO_ERR: { + EF_INFO("Erased user application OK.\n"); + break; + } + case EF_ERASE_ERR: { + EF_INFO("Warning: Erase user application fault!\n"); + /* will return when erase fault */ + return result; + } + } + + return result; +} + +/** + * Erase user old application by using default `ef_port_erase` function. + * + * @param user_app_addr application entry address + * @param app_size application size + * + * @return result + */ +EfErrCode ef_erase_user_app(uint32_t user_app_addr, size_t app_size) { + return ef_erase_spec_user_app(user_app_addr, app_size, ef_port_erase); +} + +/** + * Erase old bootloader + * + * @param bl_addr bootloader entry address + * @param bl_size bootloader size + * + * @return result + */ +EfErrCode ef_erase_bl(uint32_t bl_addr, size_t bl_size) { + EfErrCode result = EF_NO_ERR; + + result = ef_port_erase(bl_addr, bl_size); + switch (result) { + case EF_NO_ERR: { + EF_INFO("Erased bootloader OK.\n"); + break; + } + case EF_ERASE_ERR: { + EF_INFO("Warning: Erase bootloader fault!\n"); + /* will return when erase fault */ + return result; + } + } + + return result; +} + +/** + * Write data of application to backup area. + * + * @param data a part of application + * @param size data size + * @param cur_size current write application size + * @param total_size application total size + * + * @return result + */ +EfErrCode ef_write_data_to_bak(uint8_t *data, size_t size, size_t *cur_size, + size_t total_size) { + EfErrCode result = EF_NO_ERR; + + /* make sure don't write excess data */ + if (*cur_size + size > total_size) { + size = total_size - *cur_size; + } + + result = ef_port_write(ef_get_bak_app_start_addr() + *cur_size, (uint32_t *) data, size); + switch (result) { + case EF_NO_ERR: { + *cur_size += size; + EF_DEBUG("Write data to backup area OK.\n"); + break; + } + case EF_WRITE_ERR: { + EF_INFO("Warning: Write data to backup area fault!\n"); + break; + } + } + + return result; +} + +/** + * Copy backup area application to application entry by using specified write function. + * + * @param user_app_addr application entry address + * @param app_size application size + * @param app_write user specified application write function + * + * @return result + */ +EfErrCode ef_copy_spec_app_from_bak(uint32_t user_app_addr, size_t app_size, + EfErrCode (*app_write)(uint32_t addr, const uint32_t *buf, size_t size)) { + size_t cur_size; + uint32_t app_cur_addr, bak_cur_addr; + EfErrCode result = EF_NO_ERR; + /* 32 words size buffer */ + uint32_t buff[32]; + + /* cycle copy data */ + for (cur_size = 0; cur_size < app_size; cur_size += sizeof(buff)) { + app_cur_addr = user_app_addr + cur_size; + bak_cur_addr = ef_get_bak_app_start_addr() + cur_size; + ef_port_read(bak_cur_addr, buff, sizeof(buff)); + result = app_write(app_cur_addr, buff, sizeof(buff)); + if (result != EF_NO_ERR) { + break; + } + } + + switch (result) { + case EF_NO_ERR: { + EF_INFO("Write data to application entry OK.\n"); + break; + } + case EF_WRITE_ERR: { + EF_INFO("Warning: Write data to application entry fault!\n"); + break; + } + } + + return result; +} + +/** + * Copy backup area application to application entry by using default `ef_port_write` function. + * + * @param user_app_addr application entry address + * @param app_size application size + * + * @return result + */ +EfErrCode ef_copy_app_from_bak(uint32_t user_app_addr, size_t app_size) { + return ef_copy_spec_app_from_bak(user_app_addr, app_size, ef_port_write); +} + +/** + * Copy backup area bootloader to bootloader entry. + * + * @param bl_addr bootloader entry address + * @param bl_size bootloader size + * + * @return result + */ +EfErrCode ef_copy_bl_from_bak(uint32_t bl_addr, size_t bl_size) { + size_t cur_size; + uint32_t bl_cur_addr, bak_cur_addr; + EfErrCode result = EF_NO_ERR; + /* 32 words buffer */ + uint32_t buff[32]; + + /* cycle copy data by 32bytes buffer */ + for (cur_size = 0; cur_size < bl_size; cur_size += sizeof(buff)) { + bl_cur_addr = bl_addr + cur_size; + bak_cur_addr = ef_get_bak_app_start_addr() + cur_size; + ef_port_read(bak_cur_addr, buff, sizeof(buff)); + result = ef_port_write(bl_cur_addr, buff, sizeof(buff)); + if (result != EF_NO_ERR) { + break; + } + } + + switch (result) { + case EF_NO_ERR: { + EF_INFO("Write data to bootloader entry OK.\n"); + break; + } + case EF_WRITE_ERR: { + EF_INFO("Warning: Write data to bootloader entry fault!\n"); + break; + } + } + + return result; +} + +/** + * Get IAP section start address in flash. + * + * @return size + */ +uint32_t ef_get_bak_app_start_addr(void) { + return bak_app_start_addr; +} + +#endif /* EF_USING_IAP */ diff --git a/demo/os/nuttx-spiflash/apps/system/easyflash/src/ef_log.c b/demo/os/nuttx-spiflash/apps/system/easyflash/src/ef_log.c new file mode 100644 index 0000000..9d2b8c6 --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/system/easyflash/src/ef_log.c @@ -0,0 +1,731 @@ +/* + * This file is part of the EasyFlash Library. + * + * Copyright (c) 2015-2019, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: Save logs to flash. + * Created on: 2015-06-04 + */ + +#include + +#ifdef EF_USING_LOG + +#if defined(EF_USING_LOG) && !defined(LOG_AREA_SIZE) +#error "Please configure log area size (in ef_cfg.h)" +#endif + +/* magic code on every sector header. 'EF' is 0xEF30EF30 */ +#define LOG_SECTOR_MAGIC 0xEF30EF30 +/* sector header size, includes the sector magic code and status magic code */ +#define LOG_SECTOR_HEADER_SIZE 12 +/* sector header word size,what is equivalent to the total number of sectors header index */ +#define LOG_SECTOR_HEADER_WORD_SIZE 3 + +/** + * Sector status magic code + * The sector status is 8B after LOG_SECTOR_MAGIC at every sector header. + * ============================================== + * | header(12B) | status | + * ---------------------------------------------- + * | 0xEF30EF30 0xFFFFFFFF 0xFFFFFFFF | empty | + * | 0xEF30EF30 0xFEFEFEFE 0xFFFFFFFF | using | + * | 0xEF30EF30 0xFEFEFEFE 0xFCFCFCFC | full | + * ============================================== + * + * State transition relationship: empty->using->full + * The FULL status will change to EMPTY after sector clean. + */ +#define SECTOR_STATUS_MAGIC_EMPUT 0xFFFFFFFF +#define SECTOR_STATUS_MAGIC_USING 0xFEFEFEFE +#define SECTOR_STATUS_MAGIC_FULL 0xFCFCFCFC + +typedef enum { + SECTOR_STATUS_EMPUT, + SECTOR_STATUS_USING, + SECTOR_STATUS_FULL, + SECTOR_STATUS_HEADER_ERROR, +} SectorStatus; + +typedef enum { + SECTOR_HEADER_MAGIC_INDEX, + SECTOR_HEADER_USING_INDEX, + SECTOR_HEADER_FULL_INDEX, +} SectorHeaderIndex; + +/* the stored logs start address and end address. It's like a ring buffer implemented on flash. */ +static uint32_t log_start_addr = 0, log_end_addr = 0; +/* saved log area address for flash */ +static uint32_t log_area_start_addr = 0; +/* initialize OK flag */ +static bool init_ok = false; + +static void find_start_and_end_addr(void); +static uint32_t get_next_flash_sec_addr(uint32_t cur_addr); + +/** + * The flash save log function initialize. + * + * @return result + */ +EfErrCode ef_log_init(void) { + EfErrCode result = EF_NO_ERR; + + EF_ASSERT(LOG_AREA_SIZE); + EF_ASSERT(EF_ERASE_MIN_SIZE); + /* the log area size must be an integral multiple of erase minimum size. */ + EF_ASSERT(LOG_AREA_SIZE % EF_ERASE_MIN_SIZE == 0); + /* the log area size must be more than twice of EF_ERASE_MIN_SIZE */ + EF_ASSERT(LOG_AREA_SIZE / EF_ERASE_MIN_SIZE >= 2); + +#ifdef EF_USING_ENV + log_area_start_addr = EF_START_ADDR + ENV_AREA_SIZE; +#else + log_area_start_addr = EF_START_ADDR; +#endif + + /* find the log store start address and end address */ + find_start_and_end_addr(); + /* initialize OK */ + init_ok = true; + + return result; +} + +/** + * Get flash sector current status. + * + * @param addr sector address, this function will auto calculate the sector header address by this address. + * + * @return the flash sector current status + */ +static SectorStatus get_sector_status(uint32_t addr) { + uint32_t header_buf[LOG_SECTOR_HEADER_WORD_SIZE] = {0}, header_addr = 0; + uint32_t sector_header_magic = 0; + uint32_t status_full_magic = 0, status_use_magic = 0; + + /* calculate the sector header address */ + header_addr = addr & (~(EF_ERASE_MIN_SIZE - 1)); + + if (ef_port_read(header_addr, header_buf, sizeof(header_buf)) == EF_NO_ERR) { + sector_header_magic = header_buf[SECTOR_HEADER_MAGIC_INDEX]; + status_use_magic = header_buf[SECTOR_HEADER_USING_INDEX]; + status_full_magic = header_buf[SECTOR_HEADER_FULL_INDEX]; + } else { + EF_DEBUG("Error: Read sector header data error.\n"); + return SECTOR_STATUS_HEADER_ERROR; + } + + /* compare header magic code */ + if(sector_header_magic == LOG_SECTOR_MAGIC){ + if((status_use_magic == SECTOR_STATUS_MAGIC_EMPUT) && (status_full_magic == SECTOR_STATUS_MAGIC_EMPUT)) { + return SECTOR_STATUS_EMPUT; + } else if((status_use_magic == SECTOR_STATUS_MAGIC_USING) && (status_full_magic == SECTOR_STATUS_MAGIC_EMPUT)) { + return SECTOR_STATUS_USING; + } else if((status_use_magic == SECTOR_STATUS_MAGIC_USING) && (status_full_magic == SECTOR_STATUS_MAGIC_FULL)) { + return SECTOR_STATUS_FULL; + } else { + return SECTOR_STATUS_HEADER_ERROR; + } + } else { + return SECTOR_STATUS_HEADER_ERROR; + } + +} + +/** + * Write flash sector current status. + * + * @param addr sector address, this function will auto calculate the sector header address by this address. + * @param status sector cur status + * + * @return result + */ +static EfErrCode write_sector_status(uint32_t addr, SectorStatus status) { + uint32_t header, header_addr = 0; + + /* calculate the sector header address */ + header_addr = addr & (~(EF_ERASE_MIN_SIZE - 1)); + + /* calculate the sector staus magic */ + switch (status) { + case SECTOR_STATUS_EMPUT: { + header = LOG_SECTOR_MAGIC; + return ef_port_write(header_addr, &header, sizeof(header)); + } + case SECTOR_STATUS_USING: { + header = SECTOR_STATUS_MAGIC_USING; + return ef_port_write(header_addr + sizeof(header), &header, sizeof(header)); + } + case SECTOR_STATUS_FULL: { + header = SECTOR_STATUS_MAGIC_FULL; + return ef_port_write(header_addr + sizeof(header) * 2, &header, sizeof(header)); + } + default: + return EF_WRITE_ERR; + } +} + +/** + * Find the current flash sector using end address by continuous 0xFF. + * + * @param addr sector address + * + * @return current flash sector using end address + */ +static uint32_t find_sec_using_end_addr(uint32_t addr) { +/* read section data buffer size */ +#define READ_BUF_SIZE 32 + + uint32_t sector_start = addr, data_start = addr, continue_ff = 0, read_buf_size = 0, i; + uint8_t buf[READ_BUF_SIZE]; + + EF_ASSERT(READ_BUF_SIZE % 4 == 0); + /* calculate the sector start and data start address */ + sector_start = addr & (~(EF_ERASE_MIN_SIZE - 1)); + data_start = sector_start + LOG_SECTOR_HEADER_SIZE; + + /* counts continuous 0xFF which is end of sector */ + while (data_start < sector_start + EF_ERASE_MIN_SIZE) { + if (data_start + READ_BUF_SIZE < sector_start + EF_ERASE_MIN_SIZE) { + read_buf_size = READ_BUF_SIZE; + } else { + read_buf_size = sector_start + EF_ERASE_MIN_SIZE - data_start; + } + ef_port_read(data_start, (uint32_t *)buf, read_buf_size); + for (i = 0; i < read_buf_size; i++) { + if (buf[i] == 0xFF) { + continue_ff++; + } else { + continue_ff = 0; + } + } + data_start += read_buf_size; + } + /* calculate current flash sector using end address */ + if (continue_ff >= EF_ERASE_MIN_SIZE - LOG_SECTOR_HEADER_SIZE) { + /* from 0 to sec_size all sector is 0xFF, so the sector is empty */ + return sector_start + LOG_SECTOR_HEADER_SIZE; + } else if (continue_ff >= 4) { + /* form end_addr - 4 to sec_size length all area is 0xFF, so it's used part of the sector. + * the address must be word alignment. */ + if (continue_ff % 4 != 0) { + continue_ff = (continue_ff / 4 + 1) * 4; + } + return sector_start + EF_ERASE_MIN_SIZE - continue_ff; + } else { + /* all sector not has continuous 0xFF, so the sector is full */ + return sector_start + EF_ERASE_MIN_SIZE; + } +} + +/** + * Find the log store start address and end address. + * It's like a ring buffer implemented on flash. + * The flash log area can be in two states depending on start address and end address: + * state 1 state 2 + * |============| |============| + * log area start--> |############| <-- start address |############| <-- end address + * |############| | empty | + * |------------| |------------| + * |############| |############| <-- start address + * |############| |############| + * |------------| |------------| + * | . | | . | + * | . | | . | + * | . | | . | + * |------------| |------------| + * |############| <-- end address |############| + * | empty | |############| + * log area end --> |============| |============| + * + * LOG_AREA_SIZE = log area end - log area star + * + */ +static void find_start_and_end_addr(void) { + size_t cur_size = 0; + SectorStatus cur_sec_status, last_sec_status; + uint32_t cur_using_sec_addr = 0; + /* all status sector counts */ + size_t empty_sec_counts = 0, using_sec_counts = 0, full_sector_counts = 0; + /* total sector number */ + size_t total_sec_num = LOG_AREA_SIZE / EF_ERASE_MIN_SIZE; + /* see comment of find_start_and_end_addr function */ + uint8_t cur_log_sec_state = 0; + + /* get the first sector status */ + cur_sec_status = get_sector_status(log_area_start_addr); + last_sec_status = cur_sec_status; + + for (cur_size = EF_ERASE_MIN_SIZE; cur_size < LOG_AREA_SIZE; cur_size += EF_ERASE_MIN_SIZE) { + /* get current sector status */ + cur_sec_status = get_sector_status(log_area_start_addr + cur_size); + /* compare last and current status */ + switch (last_sec_status) { + case SECTOR_STATUS_EMPUT: { + switch (cur_sec_status) { + case SECTOR_STATUS_EMPUT: + break; + case SECTOR_STATUS_USING: + EF_DEBUG("Error: Log area error! Now will clean all log area.\n"); + ef_log_clean(); + return; + case SECTOR_STATUS_FULL: + EF_DEBUG("Error: Log area error! Now will clean all log area.\n"); + ef_log_clean(); + return; + } + empty_sec_counts++; + break; + } + case SECTOR_STATUS_USING: { + switch (cur_sec_status) { + case SECTOR_STATUS_EMPUT: + /* like state 1 */ + cur_log_sec_state = 1; + log_start_addr = log_area_start_addr; + cur_using_sec_addr = log_area_start_addr + cur_size - EF_ERASE_MIN_SIZE; + break; + case SECTOR_STATUS_USING: + EF_DEBUG("Error: Log area error! Now will clean all log area.\n"); + ef_log_clean(); + return; + case SECTOR_STATUS_FULL: + /* like state 2 */ + cur_log_sec_state = 2; + log_start_addr = log_area_start_addr + cur_size; + cur_using_sec_addr = log_area_start_addr + cur_size - EF_ERASE_MIN_SIZE; + break; + } + using_sec_counts++; + break; + } + case SECTOR_STATUS_FULL: { + switch (cur_sec_status) { + case SECTOR_STATUS_EMPUT: + /* like state 1 */ + if (cur_log_sec_state == 2) { + EF_DEBUG("Error: Log area error! Now will clean all log area.\n"); + ef_log_clean(); + return; + } else { + cur_log_sec_state = 1; + log_start_addr = log_area_start_addr; + log_end_addr = log_area_start_addr + cur_size; + cur_using_sec_addr = log_area_start_addr + cur_size - EF_ERASE_MIN_SIZE; + } + break; + case SECTOR_STATUS_USING: + if(total_sec_num <= 2) { + /* like state 1 */ + cur_log_sec_state = 1; + log_start_addr = log_area_start_addr; + cur_using_sec_addr = log_area_start_addr + cur_size; + } else { + /* like state 2 when the sector is the last one */ + if (cur_size + EF_ERASE_MIN_SIZE >= LOG_AREA_SIZE) { + cur_log_sec_state = 2; + log_start_addr = get_next_flash_sec_addr(log_area_start_addr + cur_size); + cur_using_sec_addr = log_area_start_addr + cur_size; + } + } + break; + case SECTOR_STATUS_FULL: + break; + } + full_sector_counts++; + break; + } + case SECTOR_STATUS_HEADER_ERROR: + EF_DEBUG("Error: Log sector header error! Now will clean all log area.\n"); + ef_log_clean(); + return; + } + last_sec_status = cur_sec_status; + } + + /* the last sector status counts */ + if (cur_sec_status == SECTOR_STATUS_EMPUT) { + empty_sec_counts++; + } else if (cur_sec_status == SECTOR_STATUS_USING) { + using_sec_counts++; + } else if (cur_sec_status == SECTOR_STATUS_FULL) { + full_sector_counts++; + } else if (cur_sec_status == SECTOR_STATUS_HEADER_ERROR) { + EF_DEBUG("Error: Log sector header error! Now will clean all log area.\n"); + ef_log_clean(); + return; + } + + if (using_sec_counts != 1) { + /* this state is almost impossible */ + EF_DEBUG("Error: There must be only one sector status is USING! Now will clean all log area.\n"); + ef_log_clean(); + } else { + /* find the end address */ + log_end_addr = find_sec_using_end_addr(cur_using_sec_addr); + } + +} + +/** + * Get log used flash total size. + * + * @return log used flash total size. @note NOT contain sector headers + */ +size_t ef_log_get_used_size(void) { + size_t header_total_num = 0, physical_size = 0; + /* must be call this function after initialize OK */ + if (!init_ok) { + return 0; + } + + if (log_start_addr < log_end_addr) { + physical_size = log_end_addr - log_start_addr; + } else { + physical_size = LOG_AREA_SIZE - (log_start_addr - log_end_addr); + } + + header_total_num = physical_size / EF_ERASE_MIN_SIZE + 1; + + return physical_size - header_total_num * LOG_SECTOR_HEADER_SIZE; +} + +/** + * Sequential reading log data. It will ignore sector headers. + * + * @param addr address + * @param log log buffer + * @param size log size, not contain sector headers. + * + * @return result + */ +static EfErrCode log_seq_read(uint32_t addr, uint32_t *log, size_t size) { + EfErrCode result = EF_NO_ERR; + size_t read_size = 0, read_size_temp = 0; + + while (size) { + /* move to sector data address */ + if ((addr + read_size) % EF_ERASE_MIN_SIZE == 0) { + addr += LOG_SECTOR_HEADER_SIZE; + } + /* calculate current sector last data size */ + read_size_temp = EF_ERASE_MIN_SIZE - (addr % EF_ERASE_MIN_SIZE); + if (size < read_size_temp) { + read_size_temp = size; + } + result = ef_port_read(addr + read_size, log + read_size / 4, read_size_temp); + if (result != EF_NO_ERR) { + return result; + } + read_size += read_size_temp; + size -= read_size_temp; + } + + return result; +} + +/** + * Calculate flash physical address by log index. + * + * @param index log index + * + * @return flash physical address + */ +static uint32_t log_index2addr(size_t index) { + size_t header_total_offset = 0; + /* total include sector number */ + size_t sector_num = index / (EF_ERASE_MIN_SIZE - LOG_SECTOR_HEADER_SIZE) + 1; + + header_total_offset = sector_num * LOG_SECTOR_HEADER_SIZE; + if (log_start_addr < log_end_addr) { + return log_start_addr + index + header_total_offset; + } else { + if (log_start_addr + index + header_total_offset < log_area_start_addr + LOG_AREA_SIZE) { + return log_start_addr + index + header_total_offset; + } else { + return log_start_addr + index + header_total_offset - LOG_AREA_SIZE; + + } + } +} + +/** + * Read log from flash. + * + * @param index index for saved log. + * Minimum index is 0. + * Maximum index is ef_log_get_used_size() - 1. + * @param log the log which will read from flash + * @param size read bytes size + * + * @return result + */ +EfErrCode ef_log_read(size_t index, uint32_t *log, size_t size) { + EfErrCode result = EF_NO_ERR; + size_t cur_using_size = ef_log_get_used_size(); + size_t read_size_temp = 0; + size_t header_total_num = 0; + + if (!size) { + return result; + } + + EF_ASSERT(size % 4 == 0); + EF_ASSERT(index < cur_using_size); + + if (index + size > cur_using_size) { + EF_DEBUG("Warning: Log read size out of bound. Cut read size.\n"); + size = cur_using_size - index; + } + /* must be call this function after initialize OK */ + if (!init_ok) { + return EF_ENV_INIT_FAILED; + } + + if (log_start_addr < log_end_addr) { + log_seq_read(log_index2addr(index), log, size); + } else { + if (log_index2addr(index) + size <= log_area_start_addr + LOG_AREA_SIZE) { + /* Flash log area + * |--------------| + * log_area_start_addr --> |##############| + * |##############| + * |##############| + * |--------------| + * |##############| + * |##############| + * |##############| <-- log_end_addr + * |--------------| + * log_start_addr --> |##############| + * read start --> |**************| <-- read end + * |##############| + * |--------------| + * + * read from (log_start_addr + log_index2addr(index)) to (log_start_addr + index + log_index2addr(index)) + */ + result = log_seq_read(log_index2addr(index), log, size); + } else if (log_index2addr(index) < log_area_start_addr + LOG_AREA_SIZE) { + /* Flash log area + * |--------------| + * log_area_start_addr --> |**************| <-- read end + * |##############| + * |##############| + * |--------------| + * |##############| + * |##############| + * |##############| <-- log_end_addr + * |--------------| + * log_start_addr --> |##############| + * read start --> |**************| + * |**************| + * |--------------| + * read will by 2 steps + * step1: read from (log_start_addr + log_index2addr(index)) to flash log area end address + * step2: read from flash log area start address to read size's end address + */ + read_size_temp = (log_area_start_addr + LOG_AREA_SIZE) - log_index2addr(index); + header_total_num = read_size_temp / EF_ERASE_MIN_SIZE; + /* Minus some ignored bytes */ + read_size_temp -= header_total_num * LOG_SECTOR_HEADER_SIZE; + result = log_seq_read(log_index2addr(index), log, read_size_temp); + if (result == EF_NO_ERR) { + result = log_seq_read(log_area_start_addr, log + read_size_temp / 4, size - read_size_temp); + } + } else { + /* Flash log area + * |--------------| + * log_area_start_addr --> |##############| + * read start --> |**************| + * |**************| <-- read end + * |--------------| + * |##############| + * |##############| + * |##############| <-- log_end_addr + * |--------------| + * log_start_addr --> |##############| + * |##############| + * |##############| + * |--------------| + * read from (log_start_addr + log_index2addr(index) - LOG_AREA_SIZE) to read size's end address + */ + result = log_seq_read(log_index2addr(index) - LOG_AREA_SIZE, log, size); + } + } + + return result; +} + +/** + * Write log to flash. + * + * @param log the log which will be write to flash + * @param size write bytes size + * + * @return result + */ +EfErrCode ef_log_write(const uint32_t *log, size_t size) { + EfErrCode result = EF_NO_ERR; + size_t write_size = 0, writable_size = 0; + uint32_t write_addr = log_end_addr, erase_addr; + SectorStatus sector_status; + + EF_ASSERT(size % 4 == 0); + /* must be call this function after initialize OK */ + if (!init_ok) { + return EF_ENV_INIT_FAILED; + } + + if ((sector_status = get_sector_status(write_addr)) == SECTOR_STATUS_HEADER_ERROR) { + return EF_WRITE_ERR; + } + /* write some log when current sector status is USING and EMPTY */ + if ((sector_status == SECTOR_STATUS_USING) || (sector_status == SECTOR_STATUS_EMPUT)) { + /* write the already erased but not used area */ + writable_size = EF_ERASE_MIN_SIZE - ((write_addr - log_area_start_addr) % EF_ERASE_MIN_SIZE); + if (size >= writable_size) { + result = ef_port_write(write_addr, log, writable_size); + if (result != EF_NO_ERR) { + goto exit; + } + /* change the current sector status to FULL */ + result = write_sector_status(write_addr, SECTOR_STATUS_FULL); + if (result != EF_NO_ERR) { + goto exit; + } + write_size += writable_size; + } else { + result = ef_port_write(write_addr, log, size); + log_end_addr = write_addr + size; + goto exit; + } + } + /* erase and write remain log */ + while (true) { + /* calculate next available sector address */ + erase_addr = write_addr = get_next_flash_sec_addr(write_addr - 4); + /* move the flash log start address to next available sector address */ + if (log_start_addr == erase_addr) { + log_start_addr = get_next_flash_sec_addr(log_start_addr); + } + /* erase sector */ + result = ef_port_erase(erase_addr, EF_ERASE_MIN_SIZE); + if (result != EF_NO_ERR) { + goto exit; + } + /* change the sector status to EMPTY and USING when write begin sector start address */ + result = write_sector_status(write_addr, SECTOR_STATUS_EMPUT); + result = write_sector_status(write_addr, SECTOR_STATUS_USING); + if (result == EF_NO_ERR) { + write_addr += LOG_SECTOR_HEADER_SIZE; + } else { + goto exit; + } + /* calculate current sector writable data size */ + writable_size = EF_ERASE_MIN_SIZE - LOG_SECTOR_HEADER_SIZE; + if (size - write_size >= writable_size) { + result = ef_port_write(write_addr, log + write_size / 4, writable_size); + if (result != EF_NO_ERR) { + goto exit; + } + /* change the current sector status to FULL */ + result = write_sector_status(write_addr, SECTOR_STATUS_FULL); + if (result != EF_NO_ERR) { + goto exit; + } + log_end_addr = write_addr + writable_size; + write_size += writable_size; + write_addr += writable_size; + } else { + result = ef_port_write(write_addr, log + write_size / 4, size - write_size); + if (result != EF_NO_ERR) { + goto exit; + } + log_end_addr = write_addr + (size - write_size); + break; + } + } + +exit: + return result; +} + +/** + * Get next flash sector address.The log total sector like ring buffer which implement by flash. + * + * @param cur_addr cur flash address + * + * @return next flash sector address + */ +static uint32_t get_next_flash_sec_addr(uint32_t cur_addr) { + size_t cur_sec_id = (cur_addr - log_area_start_addr) / EF_ERASE_MIN_SIZE; + size_t sec_total_num = LOG_AREA_SIZE / EF_ERASE_MIN_SIZE; + + if (cur_sec_id + 1 >= sec_total_num) { + /* return to ring head */ + return log_area_start_addr; + } else { + return log_area_start_addr + (cur_sec_id + 1) * EF_ERASE_MIN_SIZE; + } +} + +/** + * Clean all log which in flash. + * + * @return result + */ +EfErrCode ef_log_clean(void) { + EfErrCode result = EF_NO_ERR; + uint32_t write_addr = log_area_start_addr; + + /* clean address */ + log_start_addr = log_area_start_addr; + log_end_addr = log_start_addr + LOG_SECTOR_HEADER_SIZE; + /* erase log flash area */ + result = ef_port_erase(log_area_start_addr, LOG_AREA_SIZE); + if (result != EF_NO_ERR) { + goto exit; + } + /* setting first sector is EMPTY to USING */ + write_sector_status(write_addr, SECTOR_STATUS_EMPUT); + write_sector_status(write_addr, SECTOR_STATUS_USING); + if (result != EF_NO_ERR) { + goto exit; + } + write_addr += EF_ERASE_MIN_SIZE; + /* add sector header */ + while (true) { + write_sector_status(write_addr, SECTOR_STATUS_EMPUT); + if (result != EF_NO_ERR) { + goto exit; + } + write_addr += EF_ERASE_MIN_SIZE; + if (write_addr >= log_area_start_addr + LOG_AREA_SIZE) { + break; + } + } + +exit: + return result; +} + +#endif /* EF_USING_LOG */ diff --git a/demo/os/nuttx-spiflash/apps/system/easyflash/src/ef_utils.c b/demo/os/nuttx-spiflash/apps/system/easyflash/src/ef_utils.c new file mode 100644 index 0000000..c6c9571 --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/system/easyflash/src/ef_utils.c @@ -0,0 +1,99 @@ +/* + * This file is part of the EasyFlash Library. + * + * Copyright (c) 2015-2017, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: Some utils for this library. + * Created on: 2015-01-14 + */ + +#include + +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 ef_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; +} diff --git a/demo/os/nuttx-spiflash/apps/system/easylogger/Kconfig b/demo/os/nuttx-spiflash/apps/system/easylogger/Kconfig new file mode 100644 index 0000000..8dcd6c2 --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/system/easylogger/Kconfig @@ -0,0 +1,27 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +menuconfig SYSTEM_EASYLOGGER + tristate "Easylogger" + default n + ---help--- + Enable support for the Easylogger + +if SYSTEM_EASYLOGGER + +config SYSTEM_EASYLOGGER_FILE + bool "Enable EasyLogger File" + default n + ---help--- + Add EasyLogger File support. + +config SYSTEM_EASYLOGGER_FLASH + bool "Enable EasyLogger Flash" + default n + select SYSTEM_EASYFLASH + ---help--- + Add EasyLogger Flash support. + +endif # SYSTEM_EASYLOGGER diff --git a/demo/os/nuttx-spiflash/apps/system/easylogger/Make.defs b/demo/os/nuttx-spiflash/apps/system/easylogger/Make.defs new file mode 100644 index 0000000..f78b24c --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/system/easylogger/Make.defs @@ -0,0 +1,23 @@ +############################################################################ +# apps/system/easylogger/Make.defs +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_SYSTEM_EASYLOGGER),) +CONFIGURED_APPS += $(APPDIR)/system/easylogger +endif diff --git a/demo/os/nuttx-spiflash/apps/system/easylogger/Makefile b/demo/os/nuttx-spiflash/apps/system/easylogger/Makefile new file mode 100644 index 0000000..6d18bd6 --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/system/easylogger/Makefile @@ -0,0 +1,45 @@ +############################################################################ +# apps/system/easylogger/Makefile +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +# easylogger Application + +CSRCS = elog_port.c elog.c elog_utils.c +CSRCS += elog_async.c elog_buf.c + +CFLAGS += ${shell $(INCDIR) "$(CC)" $(APPDIR)/system/easylogger/inc} + +VPATH += :src port + +ifeq ($(CONFIG_SYSTEM_EASYLOGGER_FILE),y) +CSRCS += elog_file.c elog_file_port.c +CFLAGS += ${shell $(INCDIR) "$(CC)" $(APPDIR)/system/easylogger/plugins/file} +VPATH += :plugins/file +endif + +ifeq ($(CONFIG_SYSTEM_EASYLOGGER_FLASH),y) +CSRCS += elog_flash.c elog_flash_port.c +CFLAGS += ${shell $(INCDIR) "$(CC)" $(APPDIR)/system/easylogger/plugins/flash} +VPATH += :plugins/flash +CFLAGS += ${shell $(INCDIR) "$(CC)" $(APPDIR)/system/easyflash/inc} +endif + +include $(APPDIR)/Application.mk diff --git a/demo/os/nuttx-spiflash/apps/system/easylogger/inc/elog_cfg.h b/demo/os/nuttx-spiflash/apps/system/easylogger/inc/elog_cfg.h new file mode 100644 index 0000000..b484204 --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/system/easylogger/inc/elog_cfg.h @@ -0,0 +1,86 @@ +/* + * This file is part of the EasyLogger Library. + * + * Copyright (c) 2015-2016, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: It is the configure head file for this library. + * Created on: 2015-07-30 + */ + +#ifndef _ELOG_CFG_H_ +#define _ELOG_CFG_H_ +/*---------------------------------------------------------------------------*/ +/* enable log output. */ +#define ELOG_OUTPUT_ENABLE +/* enable terminal output. default open this macro */ +#define ELOG_TERMINAL_ENABLE +/*---------------------------------------------------------------------------*/ +/* enable log write file. default open this macro */ +// #define ELOG_FILE_ENABLE +/* enable flush file cache. default open this macro */ +// #define ELOG_FILE_FLUSH_CACHE_ENABLE +/*---------------------------------------------------------------------------*/ +/* setting static output log level. range: from ELOG_LVL_ASSERT to ELOG_LVL_VERBOSE */ +#define ELOG_OUTPUT_LVL ELOG_LVL_VERBOSE +/* enable assert check */ +#define ELOG_ASSERT_ENABLE +/* buffer size for every line's log */ +#define ELOG_LINE_BUF_SIZE 160 +/* output line number max length */ +#define ELOG_LINE_NUM_MAX_LEN 5 +/* output filter's tag max length */ +#define ELOG_FILTER_TAG_MAX_LEN 16 +/* output filter's keyword max length */ +#define ELOG_FILTER_KW_MAX_LEN 16 +/* output filter's tag level max num */ +#define ELOG_FILTER_TAG_LVL_MAX_NUM 5 +/* output newline sign */ +#define ELOG_NEWLINE_SIGN "\n" +/*---------------------------------------------------------------------------*/ +/* enable log color */ +// #define ELOG_COLOR_ENABLE +/* change the some level logs to not default color if you want */ +#define ELOG_COLOR_ASSERT (F_MAGENTA B_NULL S_NORMAL) +#define ELOG_COLOR_ERROR (F_RED B_NULL S_NORMAL) +#define ELOG_COLOR_WARN (F_YELLOW B_NULL S_NORMAL) +#define ELOG_COLOR_INFO (F_CYAN B_NULL S_NORMAL) +#define ELOG_COLOR_DEBUG (F_GREEN B_NULL S_NORMAL) +#define ELOG_COLOR_VERBOSE (F_BLUE B_NULL S_NORMAL) +/*---------------------------------------------------------------------------*/ +/* enable asynchronous output mode */ +#define ELOG_ASYNC_OUTPUT_ENABLE +/* the highest output level for async mode, other level will sync output */ +#define ELOG_ASYNC_OUTPUT_LVL ELOG_LVL_DEBUG +/* buffer size for asynchronous output mode */ +#define ELOG_ASYNC_OUTPUT_BUF_SIZE (ELOG_LINE_BUF_SIZE * 10) +/* each asynchronous output's log which must end with newline sign */ +//#define ELOG_ASYNC_LINE_OUTPUT +/* asynchronous output mode using POSIX pthread implementation */ +#define ELOG_ASYNC_OUTPUT_USING_PTHREAD +/*---------------------------------------------------------------------------*/ +/* enable buffered output mode */ +// #define ELOG_BUF_OUTPUT_ENABLE +/* buffer size for buffered output mode */ +// #define ELOG_BUF_OUTPUT_BUF_SIZE (ELOG_LINE_BUF_SIZE * 10) +/*---------------------------------------------------------------------------*/ + +#endif /* _ELOG_CFG_H_ */ diff --git a/demo/os/nuttx-spiflash/apps/system/easylogger/plugins/flash/elog_flash_cfg.h b/demo/os/nuttx-spiflash/apps/system/easylogger/plugins/flash/elog_flash_cfg.h new file mode 100644 index 0000000..ec750e6 --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/system/easylogger/plugins/flash/elog_flash_cfg.h @@ -0,0 +1,37 @@ +/* + * This file is part of the EasyLogger Library. + * + * Copyright (c) 2015, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: It is the configure head file for this flash log plugin. + * Created on: 2015-07-30 + */ + +#ifndef _ELOG_FLASH_CFG_H_ +#define _ELOG_FLASH_CFG_H_ + +/* EasyLogger flash log plugin's using buffer mode */ +#define ELOG_FLASH_USING_BUF_MODE +/* EasyLogger flash log plugin's RAM buffer size */ +#define ELOG_FLASH_BUF_SIZE 1024 /* @note you must define it for a value */ + +#endif /* _ELOG_FLASH_CFG_H_ */ diff --git a/demo/os/nuttx-spiflash/apps/system/easylogger/plugins/flash/elog_flash_port.c b/demo/os/nuttx-spiflash/apps/system/easylogger/plugins/flash/elog_flash_port.c new file mode 100644 index 0000000..45ba4d4 --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/system/easylogger/plugins/flash/elog_flash_port.c @@ -0,0 +1,80 @@ +/* + * This file is part of the EasyLogger Library. + * + * Copyright (c) 2015, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: Portable interface for EasyLogger's flash log pulgin. + * Created on: 2015-07-28 + */ + +#include + +#include "elog_flash.h" + +static pthread_mutex_t flash_log_lock; + +/** + * EasyLogger flash log pulgin port initialize + * + * @return result + */ +ElogErrCode elog_flash_port_init(void) { + ElogErrCode result = ELOG_NO_ERR; + + /* add your code here */ + pthread_mutex_init(&flash_log_lock, NULL); + + return result; +} + +/** + * output flash saved log port interface + * + * @param log flash saved log + * @param size log size + */ +void elog_flash_port_output(const char *log, size_t size) { + + /* add your code here */ + printf("%.*s", size, log); + +} + +/** + * flash log lock + */ +void elog_flash_port_lock(void) { + + /* add your code here */ + pthread_mutex_lock(&flash_log_lock); + +} + +/** + * flash log unlock + */ +void elog_flash_port_unlock(void) { + + /* add your code here */ + pthread_mutex_unlock(&flash_log_lock); + +} \ No newline at end of file diff --git a/demo/os/nuttx-spiflash/apps/system/easylogger/port/elog_port.c b/demo/os/nuttx-spiflash/apps/system/easylogger/port/elog_port.c new file mode 100644 index 0000000..412c5c5 --- /dev/null +++ b/demo/os/nuttx-spiflash/apps/system/easylogger/port/elog_port.c @@ -0,0 +1,168 @@ +/* + * This file is part of the EasyLogger Library. + * + * Copyright (c) 2015, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: Portable interface for linux. + * Created on: 2015-04-28 + */ + +#include +#include +#include +#include +#include +#include + +#ifdef ELOG_FILE_ENABLE +#include +#endif + +static pthread_mutex_t output_lock; + +/** + * EasyLogger port initialize + * + * @return result + */ +ElogErrCode elog_port_init(void) { + ElogErrCode result = ELOG_NO_ERR; + + /* add your code here */ + pthread_mutex_init(&output_lock, NULL); + +#ifdef ELOG_FILE_ENABLE + elog_file_init(); +#endif + + return result; +} + +/** + * EasyLogger port deinitialize + * + */ +void elog_port_deinit(void) { + + /* add your code here */ + +#ifdef ELOG_FILE_ENABLE + elog_file_deinit(); +#endif + + pthread_mutex_destroy(&output_lock); +} + +/** + * output log port interface + * + * @param log output of log + * @param size log size + */ +void elog_port_output(const char *log, size_t size) { + + /* add your code here */ + + /* output to terminal */ +#ifdef ELOG_TERMINAL_ENABLE + printf("%.*s", (int)size, log); +#endif + +#ifdef ELOG_FILE_ENABLE + /* write the file */ + elog_file_write(log, size); +#endif +} + +/** + * output lock + */ +void elog_port_output_lock(void) { + + /* add your code here */ + + pthread_mutex_lock(&output_lock); +} + +/** + * output unlock + */ +void elog_port_output_unlock(void) { + + /* add your code here */ + + pthread_mutex_unlock(&output_lock); +} + +/** + * get current time interface + * + * @return current time + */ +const char *elog_port_get_time(void) { + + /* add your code here */ + + static char cur_system_time[24] = { 0 }; + + time_t cur_t; + struct tm cur_tm; + + time(&cur_t); + localtime_r(&cur_t, &cur_tm); + + strftime(cur_system_time, sizeof(cur_system_time), "%Y-%m-%d %T", &cur_tm); + + return cur_system_time; +} + +/** + * get current process name interface + * + * @return current process name + */ +const char *elog_port_get_p_info(void) { + + /* add your code here */ + + static char cur_process_info[10] = { 0 }; + + snprintf(cur_process_info, 10, "pid:%04d", getpid()); + + return cur_process_info; +} + +/** + * get current thread name interface + * + * @return current thread name + */ +const char *elog_port_get_t_info(void) { + + /* add your code here */ + + static char cur_thread_info[10] = { 0 }; + + snprintf(cur_thread_info, 10, "tid:%04d", gettid()); + + return cur_thread_info; +} diff --git a/docs/zh/images/ElogNuttxSpiFlashDemo.png b/docs/zh/images/ElogNuttxSpiFlashDemo.png new file mode 100644 index 0000000..a8bf037 Binary files /dev/null and b/docs/zh/images/ElogNuttxSpiFlashDemo.png differ