├── .gitattributes ├── LICENSE ├── README.md ├── README_ZH.md ├── SConscript ├── docs ├── en │ ├── api.md │ ├── images │ │ ├── EnvDemo.gif │ │ ├── IapDemo.gif │ │ └── LogDemo.gif │ └── readme.md ├── readme.md └── zh │ ├── api.md │ ├── design.md │ ├── images │ ├── BackupAreaPartition.jpg │ ├── EnvDemo.gif │ ├── IapDemo.gif │ ├── LogDemo.gif │ ├── env_op1_step1.png │ ├── env_op1_step2.png │ ├── env_op1_step3.png │ ├── env_op1_step4.png │ ├── ng_mode_data_structure.png │ └── wechat_support.png │ ├── port.md │ ├── readme.md │ └── v4_migrate.md ├── inc ├── easyflash.h ├── ef_cfg.h └── ef_def.h ├── ports ├── README.md ├── ef_fal_port.c └── ef_sfud_port.c └── src ├── easyflash.c ├── ef_cmd.c ├── ef_env.c ├── ef_env_legacy.c ├── ef_env_legacy_wl.c ├── ef_iap.c ├── ef_log.c └── ef_utils.c /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | 3 | *.S text 4 | *.asm text 5 | *.c text 6 | *.cc text 7 | *.cpp text 8 | *.cxx text 9 | *.h text 10 | *.htm text 11 | *.html text 12 | *.in text 13 | *.ld text 14 | *.m4 text 15 | *.mak text 16 | *.mk text 17 | *.py text 18 | *.rb text 19 | *.s text 20 | *.sct text 21 | *.sh text 22 | *.txt text 23 | *.xml text 24 | Makefile text 25 | AUTHORS text 26 | COPYING text 27 | 28 | *.LZO -text 29 | *.Opt -text 30 | *.Uv2 -text 31 | *.ewp -text 32 | *.eww -text 33 | *.vcproj -text 34 | *.bat -text 35 | *.dos -text 36 | *.icf -text 37 | *.inf -text 38 | *.ini -text 39 | *.sct -text 40 | *.xsd -text 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2020 Armink (armink.ztl@gmail.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EasyFlash 2 | 3 | [中文页](README_ZH.md) | English 4 | 5 | [![GitHub release](https://img.shields.io/github/release/armink/EasyFlash.svg)](https://github.com/armink/EasyFlash/releases/latest) [![GitHub commits](https://img.shields.io/github/commits-since/armink/EasyFlash/4.1.0.svg)](https://github.com/armink/EasyFlash/compare/4.1.0...master) [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/armink/EasyFlash/master/LICENSE) 6 | 7 | ## 1. Introduction 8 | 9 | [EasyFlash](https://github.com/armink/EasyFlash) is an open source, lightweight embedded Flash memory library, which facilitates developers to easily develop common applications based on Flash memory. It is very suitable for products that require power-off storage functions such as smart homes, wearables, industrial control, medical, Internet of Things, etc., with extremely low resource consumption and support for various MCU on-chip memories. The library mainly includes **three useful functions**: 10 | 11 | - **ENV** quickly save product parameters, support **write balance (wear balance)** and **power failure protection** functions 12 | 13 | EasyFlash can not only realize the power-down saving function of the product's **set parameters** or **run log** information, but also encapsulates the concise **add, delete, modify and query** methods, which reduces development The difficulty of processing product parameters also ensures that the product has better scalability during later upgrades. Turn Flash into a small key-value storage database of the NoSQL (non-relational database) model. 14 | 15 | - **IAP** Online upgrade is no longer difficult 16 | 17 | The library encapsulates the commonly used interface of IAP (In-Application Programming) function, supports CRC32 check, and supports the upgrade of Bootloader and Application. 18 | 19 | - **Log** No file system required, logs can be stored directly on Flash 20 | 21 | It is very suitable for application in small products without file system, which is convenient for developers to quickly locate and find the cause of system crash or crash. At the same time with [EasyLogger](https://github.com/armink/EasyLogger) (my open source ultra-lightweight, high-performance C log library, it provides a seamless interface with EasyFlash), easy to achieve C log Flash storage function. 22 | 23 | ### 1.1, V4.0 NG mode 24 | 25 | Since the Spring Festival in 2019, EasyFlash has been iterating for more than 4 years, combining the needs and suggestions of many developers, and finally released the V4.0 version. The ENV function in this version is named **NG** (Next Generation) mode , This is a completely refactored new version with the following new features: 26 | 27 | - Smaller resource footprint, memory footprint **almost 0**; (Versions before V4.0 will use additional RAM space for cache) 28 | - The value type of ENV supports **any type**, any length, which is equivalent to direct memcpy variable to flash; (only supports storing strings before V4.0) 29 | - ENV operation efficiency is higher than the previous mode, the remaining free area is fully utilized, and the number of erasing and operation time are significantly reduced; 30 | - **Native support** wear leveling, power-down protection function (extra Flash sector is required before V4.0); 31 | - ENV supports **incremental upgrade**, ENV also supports upgrade after firmware upgrade; 32 | - Support large data storage mode, **length unlimited**, data can be stored sequentially on multiple Flash sectors. Resources such as script programs and audio that occupy more than 1 sector of Flash can also be stored in ENV (supported in V4.2 soon); 33 | - Support **data encryption**, improve storage security, a must-have function in the Internet of Things era (coming soon in V4.3); 34 | - Support **data compression** to reduce Flash usage (coming soon in V4.4); 35 | 36 | V4.0 design and internal principles, V4.0 migration guide and more, please continue to read the following [document chapter](#2. Documentation) 37 | 38 | ### 1.2, resource occupation 39 | 40 | ``` 41 | Minimum requirements: ROM: 6K bytes RAM: 0.1K bytes 42 | ``` 43 | 44 | ## 2. Documentation 45 | 46 | - Porting instructions based on RT-Thread: [`ports/README.md`](ports/README.md) 47 | - API documentation: [`docs/zh/api.md`](docs/zh/api.md) 48 | - General porting document: [`docs/zh/port.md`](docs/zh/port.md) 49 | - V4.0 Migration Guide: [`\docs\zh\v4_migrate.md`](/docs/zh/v4_migrate.md) 50 | - V4.0 ENV function design and implementation: [`\docs\zh\design.md`](/docs/zh/design.md) 51 | 52 | Be sure to **read the document** before porting. 53 | 54 | ## 3. Configuration instructions 55 | 56 | ```shell 57 | [*] ENV: Environment variables 58 | [*] Auto update ENV to latest default when current ENV version number is changed. 59 | (0) Setting current ENV version number 60 | [*] LOG: Save logs on flash 61 | (262144) Saved log area size. MUST be aligned by erase minimum granularity 62 | [*] IAP: In Application Programming 63 | (4096) Erase minimum granularity 64 | Write minimum granularity (1bit such as Nor Flash) ---> 65 | (0) Start addr on flash or partition 66 | [*] Enable debug log output 67 | ``` 68 | 69 | - `ENV: Environment variables`: Whether to enable the environment variable function 70 | - `Auto update ENV to latest default when current ENV version number is changed.`: Whether to enable automatic update of environment variables. After starting this function, the environment variable will be automatically updated when its version number changes. 71 | - `Setting current ENV version number`: current environment variable version number 72 | - `LOG: Save logs on flash`: log function, you can save the log sequence to Flash. It can also cooperate with EasyLogger to complete the power-down storage of product logs. 73 | - `IAP: In Application Programming`: IAP online upgrade function, after opening it will provide some commonly used APIs in IAP functions. 74 | - `Erase minimum granularity`: The minimum granularity of erasing, the general SPI Flash is usually 4KB, and the STM32F4 on-chip Flash is usually 128KB. 75 | - `Write minimum granularity`: The minimum granularity of writing data. Generally, SPI Flash is usually 1bit, and STM32F4 on-chip Flash is usually 8bit. Please refer to the specific options for details. 76 | - `Start addr on flash or partition`: The offset address of the entire storage area of ​​EasyFlash relative to Flash or partition, depending on the transplant code. 77 | - `Enable debug log output`: Whether to enable debug log output. After opening, you will see more debug log information. 78 | 79 | ## 4. Support 80 | 81 | ![support](./docs/zh/images/wechat_support.png) 82 | 83 | If EasyFlash solves your problem, you might as well scan the QR code above and ask me **a cup of coffee**~ -------------------------------------------------------------------------------- /README_ZH.md: -------------------------------------------------------------------------------- 1 | # EasyFlash 2 | 3 | 中文页 | [English](README.md) 4 | 5 | [![GitHub release](https://img.shields.io/github/release/armink/EasyFlash.svg)](https://github.com/armink/EasyFlash/releases/latest) [![GitHub commits](https://img.shields.io/github/commits-since/armink/EasyFlash/4.1.0.svg)](https://github.com/armink/EasyFlash/compare/4.1.0...master) [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/armink/EasyFlash/master/LICENSE) 6 | 7 | ## 1、介绍 8 | 9 | [EasyFlash](https://github.com/armink/EasyFlash)是一款开源的轻量级嵌入式Flash存储器库,方便开发者更加轻松的实现基于Flash存储器的常见应用开发。非常适合智能家居、可穿戴、工控、医疗、物联网等需要断电存储功能的产品,资源占用极低,支持各种 MCU 片上存储器。该库主要包括 **三大实用功能** : 10 | 11 | - **ENV** 快速保存产品参数,支持 **写平衡(磨损平衡)** 及 **掉电保护** 功能 12 | 13 | EasyFlash不仅能够实现对产品的 **设定参数** 或 **运行日志** 等信息的掉电保存功能,还封装了简洁的 **增加、删除、修改及查询** 方法, 降低了开发者对产品参数的处理难度,也保证了产品在后期升级时拥有更好的扩展性。让Flash变为NoSQL(非关系型数据库)模型的小型键值(Key-Value)存储数据库。 14 | 15 | - **IAP** 在线升级再也不是难事儿 16 | 17 | 该库封装了IAP(In-Application Programming)功能常用的接口,支持CRC32校验,同时支持Bootloader及Application的升级。 18 | 19 | - **Log** 无需文件系统,日志可直接存储在Flash上 20 | 21 | 非常适合应用在小型的不带文件系统的产品中,方便开发人员快速定位、查找系统发生崩溃或死机的原因。同时配合[EasyLogger](https://github.com/armink/EasyLogger)(我开源的超轻量级、高性能C日志库,它提供与EasyFlash的无缝接口)一起使用,轻松实现C日志的Flash存储功能。 22 | 23 | ### 1.1、V4.0 NG 模式 24 | 25 | 自 2019 年春节后,EasyFlash 经过 4 年多的迭代,结合众多开发者的需求及建议,终于发布了 V4.0 版本,该版本中的 ENV 功能被命名为 **NG** (Next Generation) 模式,这是一个完全重构的新版本,具有以下新特性: 26 | 27 | - 更小的资源占用,内存占用 **几乎为 0** ;(V4.0 以前版本会使用额外的 RAM 空间进行缓存) 28 | - ENV 的值类型支持 **任意类型** 、任意长度,相当于直接 memcpy 变量至 flash ;(V4.0 之前只支持存储字符串) 29 | - ENV 操作效率比以前的模式高,充分利用剩余空闲区域,擦除次数及操作时间显著降低; 30 | - **原生支持** 磨损平衡、掉电保护功能 (V4.0 之前需要占用额外的 Flash 扇区); 31 | - ENV 支持 **增量升级** ,固件升级后 ENV 也支持升级; 32 | - 支持大数据存储模式,**长度无限制**,数据可在多个 Flash 扇区上顺序存储。像脚本程序、音频等占用 Flash 超过 1 个扇区的资源也都可以存入 ENV(即将在 V4.2 支持); 33 | - 支持 **数据加密** ,提升存储的安全性,物联网时代的必备功能(即将在 V4.3 支持); 34 | - 支持 **数据压缩** ,减低 Flash 占用(即将在 V4.4 支持); 35 | 36 | V4.0 设计及内部原理,V4.0 迁移指南等更多内容请继续阅读下面的 [文档章节](#2、文档) 37 | 38 | ### 1.2、资源占用 39 | 40 | ``` 41 | 最低要求: ROM: 6K bytes RAM: 0.1K bytes 42 | ``` 43 | 44 | ## 2、文档 45 | 46 | - 基于 RT-Thread 的移植说明文档:[`ports/README.md`](ports/README.md) 47 | - API 说明文档:[ `docs/zh/api.md`](docs/zh/api.md) 48 | - 通用移植文档:[ `docs/zh/port.md`](docs/zh/port.md) 49 | - V4.0 迁移指南:[`\docs\zh\v4_migrate.md`](/docs/zh/v4_migrate.md) 50 | - V4.0 ENV 功能设计与实现:[`\docs\zh\design.md`](/docs/zh/design.md) 51 | 52 | 务必保证在 **阅读文档** 后再移植使用。 53 | 54 | ## 3、配置说明 55 | 56 | ```shell 57 | [*] ENV: Environment variables 58 | [*] Auto update ENV to latest default when current ENV version number is changed. 59 | (0) Setting current ENV version number 60 | [*] LOG: Save logs on flash 61 | (262144) Saved log area size. MUST be aligned by erase minimum granularity 62 | [*] IAP: In Application Programming 63 | (4096) Erase minimum granularity 64 | Write minimum granularity (1bit such as Nor Flash) ---> 65 | (0) Start addr on flash or partition 66 | [*] Enable debug log output 67 | ``` 68 | 69 | - `ENV: Environment variables`: 是否使能环境变量功能 70 | - `Auto update ENV to latest default when current ENV version number is changed.`:是否启用环境变量自动更新功能。启动这个功能后,环境变量将在其版本号发生变化时自动更新。 71 | - `Setting current ENV version number` : 当前环境变量版本号 72 | - `LOG: Save logs on flash`:日志功能,可以将日志顺序保存至 Flash 中。还可以配合 EasyLogger 完成产品日志的掉电存储。 73 | - `IAP: In Application Programming`:IAP 在线升级功能,开启后将提供一些 IAP 功能里常用的 API 。 74 | - `Erase minimum granularity`:擦除的最小粒度,一般 SPI Flash 通常为 4KB,STM32F4 片内 Flash 通常为 128KB。 75 | - `Write minimum granularity`:写数据的最小粒度,一般 SPI Flash 通常为 1bit,STM32F4 片内 Flash 通常为 8bit,详见具体选项。 76 | - `Start addr on flash or partition`:EasyFlash 的整个存储区相对于 Flash 或者 分区 的偏移地址,视移植代码而定。 77 | - `Enable debug log output`:是否使能调试日志输出。开启后将会看到更多调试日志信息。 78 | 79 | ## 4、支持 80 | 81 | ![support](./docs/zh/images/wechat_support.png) 82 | 83 | 84 | 如果 EasyFlash 解决了你的问题,不妨扫描上面二维码请我 **喝杯咖啡**~ -------------------------------------------------------------------------------- /SConscript: -------------------------------------------------------------------------------- 1 | from building import * 2 | 3 | # get current directory 4 | cwd = GetCurrentDir() 5 | # The set of source files associated with this SConscript file. 6 | src = Glob('src/*.c') 7 | 8 | path = [cwd + '/inc'] 9 | 10 | group = DefineGroup('EasyFlash', src, depend = ['PKG_USING_EASYFLASH'], CPPPATH = path) 11 | 12 | Return('group') 13 | -------------------------------------------------------------------------------- /docs/en/api.md: -------------------------------------------------------------------------------- 1 | # Coming soon... -------------------------------------------------------------------------------- /docs/en/images/EnvDemo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armink-rtt-pkgs/EasyFlash/e8bf0313604ff4da91730c789800ae007b28f56a/docs/en/images/EnvDemo.gif -------------------------------------------------------------------------------- /docs/en/images/IapDemo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armink-rtt-pkgs/EasyFlash/e8bf0313604ff4da91730c789800ae007b28f56a/docs/en/images/IapDemo.gif -------------------------------------------------------------------------------- /docs/en/images/LogDemo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armink-rtt-pkgs/EasyFlash/e8bf0313604ff4da91730c789800ae007b28f56a/docs/en/images/LogDemo.gif -------------------------------------------------------------------------------- /docs/en/readme.md: -------------------------------------------------------------------------------- 1 | |File name |Description| 2 | |:----- |:----| 3 | |api.md |API description| -------------------------------------------------------------------------------- /docs/readme.md: -------------------------------------------------------------------------------- 1 | |File or folder name |Description| 2 | |:----- |:----| 3 | |en |English documents| 4 | |zh |中文文档(简体)| -------------------------------------------------------------------------------- /docs/zh/api.md: -------------------------------------------------------------------------------- 1 | # EasyFlash API 说明 2 | 3 | --- 4 | 5 | 所有支持的API接口都在[`\easyflash\inc\easyflash.h`](https://github.com/armink/EasyFlash/blob/master/easyflash/inc/easyflash.h)中声明。以下内容较多,建议使用 **CTRL+F** 搜索。 6 | 7 | 名词介绍: 8 | 9 | - **备份区** :是EasyFlash定义的一个存放环境变量、已下载程序及日志的Flash区域,详细存储架构可以参考[`\easyflash\src\easyflash.c`](https://github.com/armink/EasyFlash/blob/master/easyflash/src/easyflash.c#L29-L58)文件头位置的注释说明或[移植文档中关于备份区参数配置](https://github.com/armink/EasyFlash/blob/master/docs/zh/port.md#55-备份区)。 10 | 11 | ## 1、用户使用接口 12 | 13 | ### 1.1 初始化 14 | 15 | 初始化的EasyFlash的各个组件,初始化后才可以使用下面的API。 16 | 17 | 针对环境变量组件,如果 flash 第一次初始化,会自动调用 `ef_env_set_default` ,恢复移植文件中定义的默认环境变量(详见移植文档),之后每次启动会自动检查 flash 中的环境变量状态,flash 出现严重损坏时也会恢复环境变量至默认。如果开启增量升级功能,还会做增量升级的检查,版本号不一致时执行增量升级。 18 | 19 | ```C 20 | EfErrCode easyflash_init(void) 21 | ``` 22 | 23 | ### 1.2 环境变量 24 | 25 | 在 V4.0 以后,环境变量在 EasyFlash 底层都是按照二进制数据格式进行存储,即 **blob 格式** ,这样上层支持传入任意类型。而在 V4.0 之前底层使用的是字符串格式进行存储,字符串格式的环境变量也可以转换为 blob 格式,所以 V4.0 能做到完全兼容以前的 API ,例如:`ef_get_env/ef_set_env` ,这些 API **只能** 给字符串 ENV 使用。 26 | 27 | 另外字符串 ENV 也有长度的限制,默认 `EF_STR_ENV_VALUE_MAX_SIZE` 配置为 128 字节,超过长度的可能无法使用 `ef_get_env` 获取,只能使用 `ef_get_env_blob` 获取。 28 | 29 | #### 1.2.1 获取环境变量 30 | 31 | 通过环境变量的名字来获取其对应的值。支持两种接口 32 | 33 | ##### 1.2.1.1 获取 blob 类型环境变量 34 | 35 | ```C 36 | size_t ef_get_env_blob(const char *key, void *value_buf, size_t buf_len, size_t *save_value_len) 37 | ``` 38 | 39 | |参数 |描述| 40 | |:----- |:----| 41 | |key |环境变量名称| 42 | |value_buf |存放环境变量的缓冲区| 43 | |buf_len |该缓冲区的大小| 44 | |save_value_len |返回该环境变量实际存储在 flash 中的大小| 45 | |返回 |成功存放至缓冲区中的数据长度| 46 | 47 | 示例: 48 | 49 | ```C 50 | char value[32]; 51 | size_t len; 52 | /* 如果环境变量长度未知,可以先获取 Flash 上存储的实际长度,将通过 len 返回 */ 53 | ef_get_env_blob("key", NULL, 0, &len); 54 | /* 如果长度已知,使用 value 缓冲区,存放读取回来的环境变量值数据,并将实际长度返回 */ 55 | len = ef_get_env_blob("key", value, sizeof(value) , NULL); 56 | ``` 57 | 58 | ##### 1.2.1.2 获取字符串类型环境变量 59 | 60 | **注意** : 61 | 62 | - 该函数**已废弃**,不推荐继续使用,可以使用上面的函数替代; 63 | - 该函数不允许连续使用,使用时需使用 strdup 包裹,确保每次返回回来的字符串内存空间独立; 64 | - 该函数不支持可重入,返回的值位于函数内部缓冲区,出于安全考虑,请加锁保护。 65 | 66 | ```C 67 | char *ef_get_env(const char *key) 68 | ``` 69 | 70 | |参数 |描述| 71 | |:----- |:----| 72 | |key |环境变量名称| 73 | |返回 |环境变量值| 74 | 75 | 76 | #### 1.2.2 设置环境变量 77 | 78 | 使用此方法可以实现对环境变量的增加、修改及删除功能。 79 | 80 | - **增加** :当环境变量表中不存在该名称的环境变量时,则会执行新增操作; 81 | - **修改** :入参中的环境变量名称在当前环境变量表中存在,则把该环境变量值修改为入参中的值; 82 | - **删除**:当入参中的value为NULL时,则会删除入参名对应的环境变量。 83 | 84 | ##### 1.2.1.1 设置 blob 类型环境变量 85 | 86 | ```C 87 | EfErrCode ef_set_env_blob(const char *key, const void *value_buf, size_t buf_len) 88 | ``` 89 | 90 | |参数 |描述| 91 | |:----- |:----| 92 | |key |环境变量名称| 93 | |value_buf |环境变量值缓冲区| 94 | |buf_len |缓冲区长度,即值的长度| 95 | 96 | ##### 1.2.1.2 设置字符串类型环境变量 97 | 98 | 99 | ```C 100 | EfErrCode ef_set_env(const char *key, const char *value) 101 | ``` 102 | 103 | |参数 |描述| 104 | |:----- |:----| 105 | |key |环境变量名称| 106 | |value |环境变量值| 107 | 108 | #### 1.2.3 删除环境变量 109 | 110 | 使用此方法可以实现对环境变量的删除功能。 111 | 112 | ```c 113 | EfErrCode ef_del_env(const char *key) 114 | ``` 115 | 116 | | 参数 | 描述 | 117 | | :--- | :----------- | 118 | | key | 环境变量名称 | 119 | 120 | 121 | #### 1.2.4 重置环境变量 122 | 将内存中的环境变量表重置为默认值。关于默认环境变量的设定方法可以参考移植文档。 123 | 124 | ```C 125 | EfErrCode ef_env_set_default(void) 126 | ``` 127 | 128 | #### 1.2.5 打印环境变量 129 | 130 | 通过在移植接口([`\easyflash\port\ef_port.c`](https://github.com/armink/EasyFlash/blob/master/easyflash/port/ef_port.c))中定义的`ef_print`打印方法,来将Flash中的所有环境变量输出出来。 131 | 132 | ```C 133 | void ef_print_env(void) 134 | ``` 135 | 136 | 137 | ### 1.3 在线升级 138 | 139 | #### 1.3.1 擦除备份区中的应用程序 140 | 141 | ```C 142 | EfErrCode ef_erase_bak_app(size_t app_size) 143 | ``` 144 | 145 | #### 1.3.2 擦除用户的应用程序 146 | 147 | 注意:请不要在应用程序中调用该方法 148 | 149 | ```C 150 | EfErrCode ef_erase_user_app(uint32_t user_app_addr, size_t user_app_size) 151 | ``` 152 | 153 | |参数 |描述| 154 | |:----- |:----| 155 | |user_app_addr |用户应用程序入口地址| 156 | |user_app_size |用户应用程序大小| 157 | 158 | #### 1.3.3 通过用户指定的擦除方法来擦除应用程序 159 | 160 | 当用户的应用程序与备份区 **不在同一个** Flash 时,则需要用户额外指定擦除应用程序的方法。而 `ef_erase_user_app` 会使用移植文件中的 `ef_port_erase` 方法进行擦除,除此之外的其余功能,两个方法均一致。 161 | 162 | 注意:请不要在应用程序中调用该方法 163 | 164 | ```C 165 | EfErrCode ef_erase_spec_user_app(uint32_t user_app_addr, size_t app_size, 166 | EfErrCode (*app_erase)(uint32_t addr, size_t size)); 167 | ``` 168 | 169 | |参数 |描述| 170 | |:----- |:----| 171 | |user_app_addr |用户应用程序入口地址| 172 | |user_app_size |用户应用程序大小| 173 | |app_erase |用户指定应用程序擦写方法| 174 | 175 | #### 1.3.4 擦除Bootloader 176 | 177 | 注意:请不要在Bootloader中调用该方法 178 | 179 | ```C 180 | EfErrCode ef_erase_bl(uint32_t bl_addr, size_t bl_size) 181 | ``` 182 | 183 | |参数 |描述| 184 | |:----- |:----| 185 | |bl_addr |Bootloader入口地址| 186 | |bl_size |Bootloader大小| 187 | 188 | #### 1.3.5 写数据到备份区 189 | 190 | 为下载程序到备份区定制的Flash连续写方法。 191 | 注意:写之前请先确认Flash已进行擦除。 192 | 193 | ```C 194 | EfErrCode ef_write_data_to_bak(uint8_t *data, 195 | size_t size, 196 | size_t *cur_size, 197 | size_t total_size) 198 | ``` 199 | 200 | |参数 |描述| 201 | |:----- |:----| 202 | |data |需要写入到备份区中的数据存储地址| 203 | |size |此次写入数据的大小(字节)| 204 | |cur_size |之前已写入到备份区中的数据大小(字节)| 205 | |total_size |需要写入到备份区的数据总大小(字节)| 206 | 207 | #### 1.3.6 从备份拷贝应用程序 208 | 209 | 将备份区已下载好的应用程序拷贝至用户应用程序起始地址。 210 | 注意: 211 | 1、拷贝前必须对原有的应用程序进行擦除 212 | 2、不要在应用程序中调用该方法 213 | 214 | ```C 215 | EfErrCode ef_copy_app_from_bak(uint32_t user_app_addr, size_t app_size) 216 | ``` 217 | 218 | |参数 |描述| 219 | |:----- |:----| 220 | |user_app_addr |用户应用程序入口地址| 221 | |user_app_size |用户应用程序大小| 222 | 223 | #### 1.3.7 通过用户指定的写操作方法来拷贝应用程序 224 | 225 | 当用户的应用程序与备份区 **不在同一个** Flash 时,则需要用户额外指定写应用程序的方法。而 `ef_copy_app_from_bak` 会使用移植文件中的 `ef_port_write` 方法进行写操作,除此之外的其余功能,两个方法均一致。 226 | 227 | ```C 228 | EfErrCode ef_copy_spec_app_from_bak(uint32_t user_app_addr, size_t app_size, 229 | EfErrCode (*app_write)(uint32_t addr, const uint32_t *buf, size_t size)) 230 | ``` 231 | 232 | |参数 |描述| 233 | |:----- |:----| 234 | |user_app_addr |用户应用程序入口地址| 235 | |user_app_size |用户应用程序大小| 236 | |app_write |用户指定应用程序写操作方法| 237 | 238 | #### 1.3.8 从备份拷贝Bootloader 239 | 240 | 将备份区已下载好的Bootloader拷贝至Bootloader起始地址。 241 | 注意: 242 | 1、拷贝前必须对原有的Bootloader进行擦除 243 | 2、不要在Bootloader中调用该方法 244 | 245 | ```C 246 | EfErrCode ef_copy_bl_from_bak(uint32_t bl_addr, size_t bl_size) 247 | ``` 248 | 249 | |参数 |描述| 250 | |:----- |:----| 251 | |bl_addr |Bootloader入口地址| 252 | |bl_size |Bootloader大小| 253 | 254 | ### 1.4 日志存储 255 | 256 | #### 1.4.1 从Flash中读取已存在的日志 257 | 258 | ```C 259 | EfErrCode ef_log_read(size_t index, uint32_t *log, size_t size); 260 | ``` 261 | 262 | |参数 |描述| 263 | |:----- |:----| 264 | |index |日志读取的索引顺序| 265 | |log |存储待读取日志的缓冲区| 266 | |size |读取日志的大小| 267 | 268 | #### 1.4.2 往Flash中保存日志 269 | 270 | ```C 271 | EfErrCode ef_log_write(const uint32_t *log, size_t size); 272 | ``` 273 | 274 | |参数 |描述| 275 | |:----- |:----| 276 | |log |存储待保存的日志| 277 | |size |待保存日志的大小| 278 | 279 | #### 1.4.3 清空存储在Flash中全部日志 280 | 281 | ```C 282 | EfErrCode ef_log_clean(void); 283 | ``` 284 | 285 | #### 1.4.4 获取已存储在Flash中的日志大小 286 | 287 | ```C 288 | size_t ef_log_get_used_size(void); 289 | ``` 290 | 291 | ## 2、配置 292 | 293 | 参照EasyFlash 移植说明([`\docs\zh\port.md`](https://github.com/armink/EasyFlash/blob/master/docs/zh/port.md#5设置参数))中的 `设置参数` 章节 294 | 295 | ## 3、注意 296 | 297 | - 写数据前务必记得先擦除 298 | - 不要在应用程序及Bootloader中执行擦除及拷贝自身的动作 299 | - Log功能对Flash擦除和写入要求4个字节对齐,擦除的最小单位则需根据用户的平台来确定 300 | -------------------------------------------------------------------------------- /docs/zh/design.md: -------------------------------------------------------------------------------- 1 | # EasyFlash V4.0 ENV 功能设计与实现 2 | 3 | ## 1、为什么要开发 V4.0 4 | 5 | EasyFlash 是我个人开发的第二款开源软件,自 2015 年初正式开源出来,至今(2019.02)已经经历了 4 年多时间。期间有很多其他行业的嵌入式开发者与我取得联系,得知他们已经将 EasyFlash 应用于自己的产品上,我心里也倍感欣慰,可见 EasyFlash 的成熟性已经得到了很多行业的认可。 6 | 7 | ### 1.1 功能简洁,但性能差强人意 8 | 9 | 大家普遍的感觉是 EasyFlash 功能简洁,可以很容易的应用于产品上。但随着技术的演进,大家对于 KV 需求的多样化,对于 MCU 资源(主要是 RAM)、Flash 存储资源、Flash 寿命等性能指标越来越高,旧版本的 EasyFlash 在这些方面还是有提升的空间。比如: 10 | 11 | ### 1.2 旧版本的痛点 12 | 13 | - 每个存储在 Flash 上的 ENV 都会在 RAM 中缓存一份,这样做虽然能够简化实现,但确实会占用很多 RAM 资源; 14 | - ENV 的值类型只支持字符串,如果想要保存其他类型的值(比如:数组、结构体)就比较麻烦了,虽然我后来为此又专门开发了 [struct2json](https://github.com/armink/struct2json) 开源软件,但还是不够便捷; 15 | - 每次保存 ENV 都需要重新擦写整个 Flash 扇区,那么位于扇区尾部未使用的区域始终无法得到利用,降低了 Flash 的使用效率,也就降低了 Flash 的使用寿命 16 | 17 | ### 1.3 从 0 开始的 NG 版本 18 | 19 | 也就是从 2017 年初开始,我便开始准备 EasyFlash 的性能优化工作,结合大家的需求,不断的整理、迭代设计文档,也与一些社区爱好者做过非常深入的交流。最终确定下来,如果单纯的在原有基础上进行完善,那么会有太多的功能实现受到限制,所以干脆重新开发全新一代 ENV 功能组件,这个版本被命名为 NG(Next Generation) 版本。 20 | 21 | NG 版本差不多在 2017 年底就已经设计完毕,但一直没时间去开发。后来在亲人的支持下,终于利用 2019 年猪年春节的假期,在岳父母家完成了 V4.0 NG 版本的开发(在此感谢岳父母、爱人的支持)。 22 | 23 | ## 2、V4.0 的特色有哪些 24 | 25 | - 更小的资源占用,内存占用 **几乎为 0** ; 26 | - ENV 的值类型支持 **任意类型** 、任意长度,相当于直接 memcpy 变量至 flash ; 27 | - ENV 操作效率比以前的模式高,充分利用剩余空闲区域,擦除次数及操作时间显著降低; 28 | - **原生支持** 磨损平衡、掉电保护功能 (V4.0 之前需要占用额外的 Flash 扇区); 29 | - ENV 支持 **增量升级** ,固件升级后 ENV 也支持升级; 30 | - 支持大数据存储模式,**长度无限制**,数据可在多个 Flash 扇区上顺序存储。像脚本程序、音频等占用 Flash 超过 1 个扇区的资源也都可以存入 ENV; 31 | - 支持 **数据加密** ,提升存储的安全性,物联网时代的必备功能; 32 | - 支持 **数据压缩** ,减低 Flash 占用; 33 | 34 | ## 3、如何实现 35 | 36 | ### 3.1 算法 37 | 38 | 假定 ENV 分区里有 4 个扇区,以下将按照操作 ENV 的方式,逐一举例讲解不同操作下,对应的 Flash 状态及数据变化。 39 | 40 | #### 3.1.1 ENV 操作过程1(常规模式) 41 | 42 | ##### 3.1.1.1 首次使用 43 | 44 | ![env_op1_step1](images/env_op1_step1.png) 45 | 46 | 首次使用时,EasyFlash 会检查各个扇区的 header,如果不符合规定的格式将执行全部格式化操作,格式化后,每个扇区的顶部将被存入 header ,负责记录当前扇区的状态、魔数等信息。格式化的初始化状态为空状态。 47 | 48 | ##### 3.1.1.2 添加 KV1、KV2、KV3 49 | 50 | ![env_op1_step2](images/env_op1_step2.png) 51 | 52 | 在执行添加操作前,会先检索合适地址来存放即将添加的新 KV,这里检索策略主要是: 53 | 54 | - 确定当前选择的扇区剩余容量充足 55 | - 优选选择正在使用状态的扇区,最后使用空状态扇区 56 | - 检查新 KV 是否有同名的 KV 存在,存在还需要额外执行删除旧值的动作 57 | 58 | 通过上图可以看出, KV1、KV2 及 KV3 已经被放入 sector1 ,添加后,扇区状态也被修改为正在使用 59 | 60 | ##### 3.1.1.3 修改 KV2 KV3,删除 KV1,添加 KV4 61 | 62 | ![env_op1_step3](images/env_op1_step3.png) 63 | 64 | 修改 ENV 时,旧的 ENV 将被删除,扇区的状态也将被修改为脏状态,然后再执行新增 ENV 的操作。 65 | 66 | - 执行修改 KV2 时,已经存在的 KV2 旧值被修改为已删除,sector1 状态被修改为脏状态,此后将 KV2 新值放入 sector1,发现 sector1 已经没有空间了,sector1 的状态还会被修改为已满状态; 67 | 68 | - 执行修改 KV3 时,已经存在的 KV3 旧值被修改为已删除,sector1 状态已经为脏状态,无需再做修改。经过查找发现 KV3 的新值只能放到 sector2,放到 sector2 后将其修改为正在使用状态; 69 | - 执行删除 KV1 时,找到 KV1 的位置,将其修改为已删除状态,sector1 状态已经为脏状态,无需再做修改; 70 | - 执行添加 KV4 时,经过查找在 sector2 找到合适的存储位置,将其添加后,sector2 状态已经为正在使用状态,无需再做修改。 71 | 72 | ##### 3.1.1.4 添加 KV5 KV6,触发 GC 73 | 74 | 75 | ![env_op1_step4](images/env_op1_step4.png) 76 | 77 | - 执行添加 KV5 操作,由于 KV5 体积较大,sector2 放不下,所以只能放在一个新扇区 sector3 上,添加后,修改 sector3 状态为正在使用 78 | - 执行添加 KV6 操作,KV6 也只能放在 sector3 下,将其放入 sector 3 后,发现 sector3 空间已满,所以将其修改已满状态。执行完成后,发现整个 ENV 的 4 个扇区只有 1 个状态为空的扇区了,这个扇区如果再继续使用就没法再执行 GC 操作了,所以此时触发了 GC 请求; 79 | - 执行 GC 请求,EasyFlash 会找到所有被标记为已满并且为脏状态的扇区,并将其内部的 ENV 搬运至其他位置。就这样 sector1 上的 KV2 被搬运至了 sector2,腾空 sector1 后,又对其执行了格式化操作,这样整个 ENV 分区里又多了一个空状态的扇区。 80 | 81 | #### 3.1.2 ENV 操作过程2(开启大数据存储模式) 82 | 83 | 马上就来…… 84 | 85 | ### 3.2 数据结构 86 | 87 | 结合上面的算法不难发现,其实所有的操作都围绕着 **扇区状态** 及 **ENV状态** ,这些状态将被存放在扇区及 ENV 头部,并且保证在不擦除扇区数据的前提下进行单向修改,在程序代码实现上称这些状态及其他一些数据信息为 **元数据**。 88 | 89 | 除了常规功能外,还有一项重要指标是 EasyFlash 非常看重的,那就是掉电保护能力,相当于在任何操作出现掉电异常,整个 EasyFlash 的容错能力是否过硬,是否可以进行掉电恢复。像 准备写入、准备删除这些中间状态就是为了掉电保护功能而设计。 90 | 91 | 出于后期扩展性的考虑这里也预留了一些保留属性,还有一些提前规划好的状态及属性后面将用过多扇区存储、加密、压缩功能的实现。 92 | 93 | 设计完成后,整个 ENV 的数据结构如下图,该图最终也可转换为对应的结构体。 94 | 95 | ![ng_mode_data_structure](images/ng_mode_data_structure.png) -------------------------------------------------------------------------------- /docs/zh/images/BackupAreaPartition.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armink-rtt-pkgs/EasyFlash/e8bf0313604ff4da91730c789800ae007b28f56a/docs/zh/images/BackupAreaPartition.jpg -------------------------------------------------------------------------------- /docs/zh/images/EnvDemo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armink-rtt-pkgs/EasyFlash/e8bf0313604ff4da91730c789800ae007b28f56a/docs/zh/images/EnvDemo.gif -------------------------------------------------------------------------------- /docs/zh/images/IapDemo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armink-rtt-pkgs/EasyFlash/e8bf0313604ff4da91730c789800ae007b28f56a/docs/zh/images/IapDemo.gif -------------------------------------------------------------------------------- /docs/zh/images/LogDemo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armink-rtt-pkgs/EasyFlash/e8bf0313604ff4da91730c789800ae007b28f56a/docs/zh/images/LogDemo.gif -------------------------------------------------------------------------------- /docs/zh/images/env_op1_step1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armink-rtt-pkgs/EasyFlash/e8bf0313604ff4da91730c789800ae007b28f56a/docs/zh/images/env_op1_step1.png -------------------------------------------------------------------------------- /docs/zh/images/env_op1_step2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armink-rtt-pkgs/EasyFlash/e8bf0313604ff4da91730c789800ae007b28f56a/docs/zh/images/env_op1_step2.png -------------------------------------------------------------------------------- /docs/zh/images/env_op1_step3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armink-rtt-pkgs/EasyFlash/e8bf0313604ff4da91730c789800ae007b28f56a/docs/zh/images/env_op1_step3.png -------------------------------------------------------------------------------- /docs/zh/images/env_op1_step4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armink-rtt-pkgs/EasyFlash/e8bf0313604ff4da91730c789800ae007b28f56a/docs/zh/images/env_op1_step4.png -------------------------------------------------------------------------------- /docs/zh/images/ng_mode_data_structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armink-rtt-pkgs/EasyFlash/e8bf0313604ff4da91730c789800ae007b28f56a/docs/zh/images/ng_mode_data_structure.png -------------------------------------------------------------------------------- /docs/zh/images/wechat_support.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armink-rtt-pkgs/EasyFlash/e8bf0313604ff4da91730c789800ae007b28f56a/docs/zh/images/wechat_support.png -------------------------------------------------------------------------------- /docs/zh/port.md: -------------------------------------------------------------------------------- 1 | # EasyFlash 移植说明 2 | 3 | --- 4 | 5 | ## 1、下载源码 6 | 7 | [点击此链接](https://github.com/armink/EasyFlash/archive/master.zip)即可直接下载位于Github上的最新源码。 8 | 9 | > 建议:点击项目主页 https://github.com/armink/EasyFlash 右上角 **Watch & Star**,这样项目有更新时,会及时以邮件形式通知你。 10 | 11 | 如果Github下载太慢,也可以点击项目位于的国内仓库下载的链接([OSChina](https://git.oschina.net/Armink/EasyFlash/repository/archive?ref=master)|[Coding](https://coding.net/u/armink/p/EasyFlash/git/archive/master))。 12 | 13 | ## 2、导入项目 14 | 15 | 在导入到项目前,先打开[`\demo\`](https://github.com/armink/EasyFlash/tree/master/demo)文件夹,检查下有没有与项目Flash规格一致的Demo。如果有则先直接跳过2、3、4章节,按照第5章的要求设置参数,并运行、验证Demo。验证通过再按照下面的导入项目要求,将Demo中的移植文件直接导入到项目中即可。 16 | 17 | - 1、先解压下载好的源码包,文件的目录结构大致如下: 18 | 19 | |源文件 |描述 | 20 | |:------------------------------ |:----- | 21 | |\easyflash\src\ef_env.c |Env(常规模式)相关操作接口及实现源码| 22 | |\easyflash\src\ef_iap.c |IAP 相关操作接口及实现源码| 23 | |\easyflash\src\ef_log.c |Log 相关操作接口及实现源码| 24 | |\easyflash\src\ef_utils.c |EasyFlash常用小工具,例如:CRC32| 25 | |\easyflash\src\easyflash.c |目前只包含EasyFlash初始化方法| 26 | |\easyflash\port\ef_port.c |不同平台下的EasyFlash移植接口| 27 | |\demo\env\stm32f10x\non_os |stm32f10x裸机片内Flash的Env demo| 28 | |\demo\env\stm32f10x\non_os_spi_flash |stm32f10x裸机SPI Flash的Env demo| 29 | |\demo\env\stm32f10x\rtt |stm32f10x基于[RT-Thread](http://www.rt-thread.org/)的片内Flash Env demo| 30 | |\demo\env\stm32f4xx |stm32f4xx基于[RT-Thread](http://www.rt-thread.org/)的片内Flash Env demo| 31 | |\demo\iap\ymodem+rtt.c |使用[RT-Thread](http://www.rt-thread.org/)+[Ymodem](https://github.com/RT-Thread/rt-thread/tree/master/components/utilities/ymodem)的IAP Demo| 32 | |\demo\log\easylogger.c |基于[EasyLogger](https://github.com/armink/EasyLogger)的Log Demo| 33 | 34 | 35 | - 2、将`\easyflash\`(里面包含`inc`、`src`及`port`的那个)文件夹拷贝到项目中; 36 | - 3、添加`\easyflash\src\easyflash.c`、`\easyflash\src\ef_utils.c`及`\easyflash\port\ef_port.c`这些文件到项目的编译路径中; 37 | - 4、根据项目需求,选择性添加`\easyflash\src\`中的其他源码文件到项目的编译路径中; 38 | - 5、添加`\easyflash\inc\`文件夹到编译的头文件目录列表中; 39 | 40 | ## 3、Flash规格 41 | 42 | 在移植时务必先要了解项目的Flash规格,这里需要了解是规格是 **擦除粒度(擦除最小单元)** 、**写入粒度**及 **内部存储结构** ,各个厂家的Flash规格都有差异,同一厂家不同系列的规格也有差异。以下是一些常见 Flash 的规格 43 | 44 | | Flash 类型 | 写入粒度 | 擦除粒度 | 内部存储结构 | 45 | | ---------------------- | -------- | ------------ | ------------------------- | 46 | | STM32F1 片内 Flash | 4bytes | 1K/2K | 均匀分布 | 47 | | STM32F2/F4 片内 Flash | 1byte | 16K/64K/128K | 最大的有128K,最小的有16K | 48 | | Nor Flash(SPI-Flash) | 1bit | 4K | 均匀分布 | 49 | 50 | > **注意** : 51 | > - 1、务必保证熟悉Flash规格后,再继续下章节; 52 | > - 2、V4.0 新模式暂时无法使用在 STM32L4 片内 Flash 上,L4 只能使用 V3.0 版本或者 V4.0 的 EF_ENV_USING_LEGACY_MODE 模式 53 | 54 | ## 4、移植接口 55 | 56 | ### 4.1 移植初始化 57 | 58 | EasyFlash移植初始化。可以传递默认环境变量,初始化EasyFlash移植所需的资源等等。 59 | 60 | ```C 61 | EfErrCode ef_port_init(ef_env const **default_env, size_t *default_env_size) 62 | ``` 63 | 64 | |参数 |描述| 65 | |:----- |:----| 66 | |default_env |默认的环境变量| 67 | |default_env_size |默认环境变量的数量| 68 | 69 | ### 4.2 读取Flash 70 | 71 | 最小单位为4个字节。 72 | 73 | ```C 74 | EfErrCode ef_port_read(uint32_t addr, uint32_t *buf, size_t size) 75 | ``` 76 | 77 | |参数 |描述| 78 | |:----- |:----| 79 | |addr |读取起始地址(4字节对齐)| 80 | |buf |存放读取数据的缓冲区| 81 | |size |读取数据的大小(字节)| 82 | 83 | ### 4.3 擦除Flash 84 | 85 | ```C 86 | EfErrCode ef_port_erase(uint32_t addr, size_t size) 87 | ``` 88 | 89 | |参数 |描述| 90 | |:----- |:----| 91 | |addr |擦除起始地址| 92 | |size |擦除数据的大小(字节)| 93 | 94 | ### 4.4 写入Flash 95 | 96 | 最小单位为4个字节。 97 | 98 | ```C 99 | EfErrCode ef_port_write(uint32_t addr, const uint32_t *buf, size_t size) 100 | ``` 101 | 102 | |参数 |描述| 103 | |:----- |:----| 104 | |addr |写入的起始地址(4字节对齐)| 105 | |buf |源数据的缓冲区| 106 | |size |写入数据的大小(字节)| 107 | 108 | ### 4.5 对环境变量缓冲区加锁 109 | 110 | 为了保证RAM缓冲区在并发执行的安全性,所以需要对其进行加锁(如果项目的使用场景不存在并发情况,则可以忽略)。有操作系统时可以使用获取信号量来加锁,裸机时可以通过关闭全局中断来加锁。 111 | 112 | ```C 113 | void ef_port_env_lock(void) 114 | ``` 115 | 116 | ### 4.6 对环境变量缓冲区解锁 117 | 118 | 有操作系统是可以使用释放信号量来解锁,裸机时可以通过开启全局中断来解锁。 119 | 120 | ```C 121 | void ef_port_env_unlock(void) 122 | ``` 123 | 124 | ### 4.7 打印调试日志信息 125 | 126 | 在定义`PRINT_DEBUG`宏后,打印调试日志信息。 127 | 128 | ```C 129 | void ef_log_debug(const char *file, const long line, const char *format, ...) 130 | ``` 131 | 132 | |参数 |描述| 133 | |:----- |:----| 134 | |file |调用该方法的文件| 135 | |line |调用该方法的行号| 136 | |format |打印格式| 137 | |... |不定参| 138 | 139 | ### 4.8 打印普通日志信息 140 | 141 | ```C 142 | void ef_log_info(const char *format, ...) 143 | ``` 144 | 145 | |参数 |描述| 146 | |:----- |:----| 147 | |format |打印格式| 148 | |... |不定参| 149 | 150 | ### 4.9 无格式打印信息 151 | 152 | 该方法输出无固定格式的打印信息,为`ef_print_env`方法所用(如果不使用`ef_print_env`则可以忽略)。而`ef_log_debug`及`ef_log_info`可以输出带指定前缀及格式的打印日志信息。 153 | 154 | ```C 155 | void ef_print(const char *format, ...) 156 | ``` 157 | 158 | |参数 |描述| 159 | |:----- |:----| 160 | |format |打印格式| 161 | |... |不定参| 162 | 163 | ### 4.10 默认环境变量集合 164 | 165 | 在 ef_port.c 文件顶部定义有 `static const ef_env default_env_set[]` ,我们可以将产品上需要的默认环境变量集中定义在这里。当 flash 第一次初始化时会将默认的环境变量写入。 166 | 167 | 默认环境变量内部采用 void * 类型,所以支持任意类型,可参考如下示例: 168 | 169 | ```C 170 | static uint32_t boot_count = 0; 171 | static time_t boot_time[10] = {0, 1, 2, 3}; 172 | static const ef_env default_env_set[] = { 173 | // { key , value, value_len }, 174 | {"username", "armink", 0}, //类型为字符串的环境变量可以设定值的长度为 0 ,此时会在初始化时会自动检测其长度 175 | {"password", "123456", 0}, 176 | {"boot_count", &boot_count, sizeof(boot_count)}, //整形 177 | {"boot_time", &boot_time, sizeof(boot_time)}, //数组类型,其他类型使用方式类似 178 | }; 179 | ``` 180 | 181 | ## 5、设置参数 182 | 183 | 配置时需要修改项目中的`ef_cfg.h`文件,开启、关闭、修改对应的宏即可。 184 | 185 | ### 5.1 环境变量功能 186 | 187 | - 默认状态:开启 188 | - 操作方法:开启、关闭`EF_USING_ENV`宏即可 189 | 190 | #### 5.1.1 自动更新(增量更新) 191 | 192 | 可以对 ENV 设置版本号(参照 5.1.2)。当 ENV 初始化时,如果检测到产品存储的版本号与设定版本号不一致,会自动追加默认环境变量集合中新增的环境变量。 193 | 194 | 该功能非常适用于经常升级的产品中,当产品功能变更时,有可能会新增环境变量,此时只需要增大当前设定的 ENV 版本号,下次固件升级后,新增的环境变量将会自动追加上去。 195 | 196 | - 默认状态:关闭 197 | - 操作方法:开启、关闭`EF_ENV_AUTO_UPDATE`宏即可 198 | 199 | #### 5.1.2 环境变量版本号 200 | 201 | 该配置依赖于 5.1.1 配置。设置的环境变量版本号为整形数值,可以从 0 开始。如果在默认环境变量表中增加了环境变量,此时需要对该配置进行修改(通常加 1 )。 202 | 203 | - 操作方法:修改`EF_ENV_VER_NUM`宏对应值即可 204 | 205 | ### 5.2 在线升级功能 206 | 207 | - 默认状态:开启 208 | - 操作方法:开启、关闭`EF_USING_IAP`宏即可 209 | 210 | ### 5.3 日志功能 211 | 212 | - 默认状态:开启 213 | - 操作方法:开启、关闭`EF_USING_LOG`宏即可 214 | 215 | ### 5.4 Flash 擦除粒度(最小擦除单位) 216 | 217 | - 操作方法:修改`EF_ERASE_MIN_SIZE`宏对应值即可,单位:byte 218 | 219 | ### 5.5 Flash 写入粒度 220 | 221 | - 操作方法:修改`EF_WRITE_GRAN`宏对应值即可,单位:bit,仅支持:1/8/32 222 | 223 | ### 5.5 备份区 224 | 225 | 备份区共计包含3个区域,依次为:环境变量区、日志区及在线升级区。分区方式如下图所示 226 | 227 | ![backup_area_partiton](images/BackupAreaPartition.jpg) 228 | 229 | 在配置时需要注意以下几点: 230 | 231 | - 1、所有的区域必须按照`EF_ERASE_MIN_SIZE`对齐; 232 | - 2、环境变量分区大小至少为两倍以上 `EF_ERASE_MIN_SIZE`; 233 | - 3、从 V4.0 开始 ENV 的模式命名为 NG 模式,V4.0 之前的称之为 LEGACY 遗留模式; 234 | - 遗留模式已经被废弃,不再建议继续使用; 235 | - 如果需要继续使用遗留模式,请 EasyFlash 的 V3.X 版本。 236 | 237 | #### 5.5.1 备份区起始地址 238 | 239 | - 操作方法:修改`EF_START_ADDR`宏对应值即可 240 | 241 | #### 5.5.2 环境变量区总容量 242 | 243 | - 操作方法:修改`ENV_AREA_SIZE`宏对应值即可 244 | 245 | > 注意:不使用环境变量功能时,可以不定义此宏。 246 | 247 | #### 5.5.3 日志区总容量 248 | 249 | - 操作方法:修改`LOG_AREA_SIZE`宏对应值即可 250 | 251 | > 注意:不使用日志功能时,可以不定义此宏。 252 | 253 | ### 5.6 调试日志 254 | 255 | 开启后,将会库运行时自动输出调试日志 256 | 257 | - 默认状态:开启 258 | - 操作方法:开启、关闭`PRINT_DEBUG`宏即可 259 | 260 | ## 6、测试验证 261 | 262 | 如果`\demo\`文件夹下有与项目Flash规格一致的Demo,则直接编译运行,观察测试结果即可。无需关注下面的步骤。 263 | 264 | 每次使用前,务必先执行`easyflash_init()`方法对EasyFlash库及所使用的Flash进行初始化,保证初始化没问题后,再使用各功能的API方法。如果出现错误或断言,需根据提示信息检查移植配置及接口。 265 | 266 | ### 6.1 环境变量 267 | 268 | 查看[`\demo\env\`](https://github.com/armink/EasyFlash/tree/master/demo/env)子文件夹中例子的`README.md`说明文档。测试时可以将`\demo\env\stm32f10x\non_os\app\src\app.c`中的`static void test_env(void)`方法体复制到项目中,然后运行测试。 269 | 270 | ### 6.2 在线升级 271 | 272 | 查看[`\demo\iap\README.md`](https://github.com/armink/EasyFlash/tree/master/demo/iap)说明文档。 273 | 274 | ### 6.3 日志 275 | 276 | 查看[`\demo\log\README.md`](https://github.com/armink/EasyFlash/tree/master/demo/log)说明文档。 277 | 278 | > 注意:`easylogger.c`是使用[EasyLogger](https://github.com/armink/EasyLogger)与EasyFlash的无缝接口的例子,EasyLogger提供针对日志的很多常用功能封装,详细功能可以查看其介绍。使用这个例子时,务必记得将EasyLogger一并导入到项目中。 -------------------------------------------------------------------------------- /docs/zh/readme.md: -------------------------------------------------------------------------------- 1 | |文件名 |描述| 2 | |:----- |:----| 3 | |api.md |API 说明| 4 | |port.md |移植说明| -------------------------------------------------------------------------------- /docs/zh/v4_migrate.md: -------------------------------------------------------------------------------- 1 | # V4.0 迁移指南 2 | 3 | ## 1、V3.0 与 V4.0 差异 4 | 5 | ### 1.1 API 接口方面 6 | 7 | #### 1.1.1 完全兼容旧版 8 | 9 | V4.0 在设计时,已经做到接口完全兼容旧版本,所以如果你的应用使用的是旧版本,那么无需修改任何源代码即可做到无缝迁移。 10 | 11 | #### 1.1.2 新增接口 12 | 13 | V4.0 底层对于 ENV 的存储使用的 blob 格式,所以增加如下 blob 操作接口,替代 V3.0 的基于字符串的接口 14 | 15 | - `size_t ef_get_env_blob(const char *key, void *value_buf, size_t buf_len, size_t *value_len)` 16 | - `EfErrCode ef_set_env_blob(const char *key, const void *value_buf, size_t buf_len)` 17 | 18 | #### 1.1.3 废弃接口 19 | 20 | 以下接口在 V4.0 中仍然可用,但已经由于种种原因被废弃,可能将会在 V5.0 版本中被正式删除 21 | 22 | - `char *ef_get_env(const char *key)` 23 | 24 | - 注意:由于 V4.0 版本开始,在该函数内部具有环境变量的缓冲区,不允许连续多次同时使用该函数,例如如下代码: 25 | 26 | ```C 27 | // 错误的使用方法 28 | ssid = ef_get_env("ssid"); 29 | password = ef_get_env("password"); // 由于 buf 共用,password 与 ssid 会返回相同的 buf 地址 30 | 31 | // 建议改为下面的方式 32 | ssid = strdup(ef_get_env("ssid")); // 克隆获取回来的环境变量 33 | password = strdup(ef_get_env("password")); 34 | 35 | // 使用完成后,释放资源 36 | free(ssid); // 与 strdup 成对 37 | free(password); 38 | ``` 39 | 40 | 41 | 42 | - `EfErrCode ef_save_env(void)` 43 | 44 | - `EfErrCode ef_set_and_save_env(const char *key, const char *value)` 45 | 46 | - `EfErrCode ef_del_and_save_env(const char *key)` 47 | 48 | - `size_t ef_get_env_write_bytes(void)` 49 | 50 | ## 2、主要修改项 51 | 52 | ### 2.1 配置方面 53 | 54 | - 删除 EF_ENV_USING_WL_MODE:V4.0 原生支持磨损平衡,无需额外开启 55 | - 删除 EF_ENV_USING_PFS_MODE:V4.0 自带掉电保护功能,无需额外开启 56 | - 删除 ENV_USER_SETTING_SIZE:V4.0 无需 RAM 缓存 57 | - 增加 EF_WRITE_GRAN :详见移植文档 58 | 59 | ### 2.2 接口改进 60 | 61 | - 建议使用 ef_get_env_blob 接口替代 ef_get_env,使用方法详见 API 文档 62 | - V4.0 无需额外执行保存动作,所以使用这些接口的代码在 V4..0 无意义,可以移除 63 | - ef_save_env 64 | - ef_set_and_save_env 65 | - ef_del_and_save_env 66 | 67 | -------------------------------------------------------------------------------- /inc/easyflash.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the EasyFlash Library. 3 | * 4 | * Copyright (c) 2014-2019, Armink, 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files (the 8 | * 'Software'), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | * Function: It is an head file for this library. You can see all be called functions. 26 | * Created on: 2014-09-10 27 | */ 28 | 29 | 30 | #ifndef EASYFLASH_H_ 31 | #define EASYFLASH_H_ 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #ifdef __cplusplus 40 | extern "C" { 41 | #endif 42 | 43 | /* easyflash.c */ 44 | EfErrCode easyflash_init(void); 45 | 46 | #ifdef EF_USING_ENV 47 | /* only supported on ef_env.c */ 48 | size_t ef_get_env_blob(const char *key, void *value_buf, size_t buf_len, size_t *saved_value_len); 49 | bool ef_get_env_obj(const char *key, env_node_obj_t env); 50 | size_t ef_read_env_value(env_node_obj_t env, uint8_t *value_buf, size_t buf_len); 51 | EfErrCode ef_set_env_blob(const char *key, const void *value_buf, size_t buf_len); 52 | 53 | /* ef_env.c, ef_env_legacy_wl.c and ef_env_legacy.c */ 54 | EfErrCode ef_load_env(void); 55 | void ef_print_env(void); 56 | char *ef_get_env(const char *key); 57 | EfErrCode ef_set_env(const char *key, const char *value); 58 | EfErrCode ef_del_env(const char *key); 59 | EfErrCode ef_save_env(void); 60 | EfErrCode ef_env_set_default(void); 61 | size_t ef_get_env_write_bytes(void); 62 | EfErrCode ef_set_and_save_env(const char *key, const char *value); 63 | EfErrCode ef_del_and_save_env(const char *key); 64 | #endif 65 | 66 | #ifdef EF_USING_IAP 67 | /* ef_iap.c */ 68 | EfErrCode ef_erase_bak_app(size_t app_size); 69 | EfErrCode ef_erase_user_app(uint32_t user_app_addr, size_t user_app_size); 70 | EfErrCode ef_erase_spec_user_app(uint32_t user_app_addr, size_t app_size, 71 | EfErrCode (*app_erase)(uint32_t addr, size_t size)); 72 | EfErrCode ef_erase_bl(uint32_t bl_addr, size_t bl_size); 73 | EfErrCode ef_write_data_to_bak(uint8_t *data, size_t size, size_t *cur_size, 74 | size_t total_size); 75 | EfErrCode ef_copy_app_from_bak(uint32_t user_app_addr, size_t app_size); 76 | EfErrCode ef_copy_spec_app_from_bak(uint32_t user_app_addr, size_t app_size, 77 | EfErrCode (*app_write)(uint32_t addr, const uint32_t *buf, size_t size)); 78 | EfErrCode ef_copy_bl_from_bak(uint32_t bl_addr, size_t bl_size); 79 | uint32_t ef_get_bak_app_start_addr(void); 80 | #endif 81 | 82 | #ifdef EF_USING_LOG 83 | /* ef_log.c */ 84 | EfErrCode ef_log_read(size_t index, uint32_t *log, size_t size); 85 | EfErrCode ef_log_write(const uint32_t *log, size_t size); 86 | EfErrCode ef_log_clean(void); 87 | size_t ef_log_get_used_size(void); 88 | #endif 89 | 90 | /* ef_utils.c */ 91 | uint32_t ef_calc_crc32(uint32_t crc, const void *buf, size_t size); 92 | 93 | /* ef_port.c */ 94 | EfErrCode ef_port_read(uint32_t addr, uint32_t *buf, size_t size); 95 | EfErrCode ef_port_erase(uint32_t addr, size_t size); 96 | EfErrCode ef_port_write(uint32_t addr, const uint32_t *buf, size_t size); 97 | void ef_port_env_lock(void); 98 | void ef_port_env_unlock(void); 99 | void ef_log_debug(const char *file, const long line, const char *format, ...); 100 | void ef_log_info(const char *format, ...); 101 | void ef_print(const char *format, ...); 102 | 103 | #ifdef __cplusplus 104 | } 105 | #endif 106 | 107 | #endif /* EASYFLASH_H_ */ 108 | -------------------------------------------------------------------------------- /inc/ef_cfg.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the EasyFlash Library. 3 | * 4 | * Copyright (c) 2015-2019, Armink, 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files (the 8 | * 'Software'), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | * Function: It is the configure head file for this library. 26 | * Created on: 2015-07-14 27 | */ 28 | 29 | 30 | #ifndef EF_CFG_H_ 31 | #define EF_CFG_H_ 32 | 33 | #include 34 | 35 | /* using ENV function */ 36 | #ifdef PKG_EASYFLASH_ENV 37 | #define EF_USING_ENV 38 | 39 | #ifdef PKG_EASYFLASH_ENV_AUTO_UPDATE 40 | /* Auto update ENV to latest default when current ENV version number is changed. */ 41 | #define EF_ENV_AUTO_UPDATE 42 | /** 43 | * ENV version number defined by user. 44 | * Please change it when your firmware add a new ENV to default_env_set. 45 | */ 46 | #define EF_ENV_VER_NUM PKG_EASYFLASH_ENV_VER_NUM 47 | #endif 48 | 49 | #endif /* PKG_EASYFLASH_ENV */ 50 | 51 | /* using IAP function */ 52 | #ifdef PKG_EASYFLASH_IAP 53 | #define EF_USING_IAP 54 | #endif 55 | 56 | /* using save log function */ 57 | #ifdef PKG_EASYFLASH_LOG 58 | #define EF_USING_LOG 59 | /* saved log area size */ 60 | #define LOG_AREA_SIZE (PKG_EASYFLASH_LOG_AREA_SIZE) 61 | #endif 62 | 63 | /* the minimum size of flash erasure */ 64 | #define EF_ERASE_MIN_SIZE PKG_EASYFLASH_ERASE_GRAN 65 | 66 | /* the flash write granularity, unit: bit 67 | * only support 1(nor flash)/ 8(stm32f4)/ 32(stm32f1)/ 64(stm32l4) */ 68 | #define EF_WRITE_GRAN (PKG_EASYFLASH_WRITE_GRAN) 69 | 70 | /* 71 | * 72 | * This all Backup Area Flash storage index. All used flash area configure is under here. 73 | * |----------------------------| Storage Size 74 | * | Environment variables area | ENV area size @see ENV_AREA_SIZE 75 | * |----------------------------| 76 | * | Saved log area | Log area size @see LOG_AREA_SIZE 77 | * |----------------------------| 78 | * |(IAP)Downloaded application | IAP already downloaded application, unfixed size 79 | * |----------------------------| 80 | * 81 | * @note all area sizes must be aligned with EF_ERASE_MIN_SIZE 82 | * 83 | * The EasyFlash add the NG (Next Generation) mode start from V4.0. All old mode before V4.0, called LEGACY mode. 84 | * 85 | * - NG (Next Generation) mode is default mode from V4.0. It's easy to settings, only defined the ENV_AREA_SIZE. 86 | * - The LEGACY mode has been DEPRECATED. It is NOT RECOMMENDED to continue using. 87 | * Beacuse it will use ram to buffer the ENV and spend more flash erase times. 88 | * If you want use it please using the V3.X version. 89 | */ 90 | 91 | /* backup area start address */ 92 | #define EF_START_ADDR PKG_EASYFLASH_START_ADDR 93 | 94 | /* ENV area size. It's at least one empty sector for GC. So it's definition must more then or equal 2 flash sector size. */ 95 | #define ENV_AREA_SIZE (EF_ERASE_MIN_SIZE * 2) /* default is the double erase min size */ 96 | 97 | /* print debug information of flash */ 98 | #ifdef PKG_EASYFLASH_DEBUG 99 | #define PRINT_DEBUG 100 | #endif 101 | 102 | #endif /* EF_CFG_H_ */ 103 | -------------------------------------------------------------------------------- /inc/ef_def.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the EasyFlash Library. 3 | * 4 | * Copyright (c) 2019-2020, Armink, 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files (the 8 | * 'Software'), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | * Function: It is the definitions head file for this library. 26 | * Created on: 2019-11-20 27 | */ 28 | 29 | #ifndef EF_DEF_H_ 30 | #define EF_DEF_H_ 31 | 32 | #ifdef __cplusplus 33 | extern "C" { 34 | #endif 35 | 36 | /* EasyFlash software version number */ 37 | #define EF_SW_VERSION "4.1.99" 38 | #define EF_SW_VERSION_NUM 0x40199 39 | 40 | /* 41 | * ENV version number defined by user. 42 | * Please change it when your firmware add a new ENV to default_env_set. 43 | */ 44 | #ifndef EF_ENV_VER_NUM 45 | #define EF_ENV_VER_NUM 0 46 | #endif 47 | 48 | /* the ENV max name length must less then it */ 49 | #ifndef EF_ENV_NAME_MAX 50 | #define EF_ENV_NAME_MAX 32 51 | #endif 52 | 53 | /* EasyFlash debug print function. Must be implement by user. */ 54 | #ifdef PRINT_DEBUG 55 | #define EF_DEBUG(...) ef_log_debug(__FILE__, __LINE__, __VA_ARGS__) 56 | #else 57 | #define EF_DEBUG(...) 58 | #endif 59 | /* EasyFlash routine print function. Must be implement by user. */ 60 | #define EF_INFO(...) ef_log_info(__VA_ARGS__) 61 | /* EasyFlash assert for developer. */ 62 | #define EF_ASSERT(EXPR) \ 63 | if (!(EXPR)) \ 64 | { \ 65 | EF_DEBUG("(%s) has assert failed at %s.\n", #EXPR, __FUNCTION__); \ 66 | while (1); \ 67 | } 68 | 69 | typedef struct _ef_env { 70 | char *key; 71 | void *value; 72 | size_t value_len; 73 | } ef_env, *ef_env_t; 74 | 75 | /* EasyFlash error code */ 76 | typedef enum { 77 | EF_NO_ERR, 78 | EF_ERASE_ERR, 79 | EF_READ_ERR, 80 | EF_WRITE_ERR, 81 | EF_ENV_NAME_ERR, 82 | EF_ENV_NAME_EXIST, 83 | EF_ENV_FULL, 84 | EF_ENV_INIT_FAILED, 85 | } EfErrCode; 86 | 87 | /* the flash sector current status */ 88 | typedef enum { 89 | EF_SECTOR_EMPTY, 90 | EF_SECTOR_USING, 91 | EF_SECTOR_FULL, 92 | } EfSecrorStatus; 93 | 94 | enum env_status { 95 | ENV_UNUSED, 96 | ENV_PRE_WRITE, 97 | ENV_WRITE, 98 | ENV_PRE_DELETE, 99 | ENV_DELETED, 100 | ENV_ERR_HDR, 101 | ENV_STATUS_NUM, 102 | }; 103 | typedef enum env_status env_status_t; 104 | 105 | struct env_node_obj { 106 | env_status_t status; /**< ENV node status, @see node_status_t */ 107 | bool crc_is_ok; /**< ENV node CRC32 check is OK */ 108 | uint8_t name_len; /**< name length */ 109 | uint32_t magic; /**< magic word(`K`, `V`, `4`, `0`) */ 110 | uint32_t len; /**< ENV node total length (header + name + value), must align by EF_WRITE_GRAN */ 111 | uint32_t value_len; /**< value length */ 112 | char name[EF_ENV_NAME_MAX]; /**< name */ 113 | struct { 114 | uint32_t start; /**< ENV node start address */ 115 | uint32_t value; /**< value start address */ 116 | } addr; 117 | }; 118 | typedef struct env_node_obj *env_node_obj_t; 119 | 120 | #ifdef __cplusplus 121 | } 122 | #endif 123 | 124 | #endif /* EF_DEF_H_ */ 125 | -------------------------------------------------------------------------------- /ports/README.md: -------------------------------------------------------------------------------- 1 | # 基于 RT-Thread 的 EasyFlash 移植参考示例 2 | 3 | ## 1、简介 4 | 5 | 本目录下主要存放了基于 RT-Thread 的移植参考示例。主要基于以下两种底层 Flash 驱动 6 | 7 | - [fal : Flash 抽象层](https://github.com/RT-Thread-packages/fal) 8 | - [SFUD : 万能 SPI Flash 驱动库](https://github.com/RT-Thread/rt-thread/tree/master/components/drivers/spi/sfud) 9 | 10 | 如果你的 Flash 驱动使用的是上面中的一种,那么移植 EasyFlash 将会非常简单,具体参考下面的文档。如果没有使用上面的驱动,请参考 EasyFlash 的 [移植文档](https://github.com/armink/EasyFlash/blob/master/docs/zh/port.md) 进行移植。在 EasyFlash [官方仓库](https://github.com/armink/EasyFlash) 下有很多 demo,也可以参考。 11 | 12 | ## 2、基于 fal : Flash 抽象层 13 | 14 | 移植参考文件位于 [/ports/ef_fal_port.c](ef_fal_port.c) 。先将该文件拷贝至项目中,在文件顶部有如下宏定义 15 | 16 | ```c 17 | /* EasyFlash partition name on FAL partition table */ 18 | #define FAL_EF_PART_NAME "ef" 19 | ``` 20 | 21 | 再修改该宏定义为 EasyFlash 备份区位于 fal 分区表中的分区名即可,默认是 "ef"。 22 | 23 | > 注意:在使用 fal 时,由于上面指定好了会使用整个 `"ef"` 分区给 EasyFlash,同时 `ef_fal_port.c` 中的移植也是基于分区读写,所以配置的 EasyFlash 起始地址应从 **0** 地址开始,即`PKG_EASYFLASH_START_ADDR` 配置应为 0。 24 | 25 | ## 3、基于 SFUD : 万能 SPI Flash 驱动库 26 | 27 | 移植参考文件位于 [/ports/ef_sfud_port.c](ef_sfud_port.c) 。先将该文件拷贝至项目中,在文件中的 `ef_port_init` 函数中有关于 SFUD Flash 设备的获取的相关的代码,如下所示 28 | 29 | ```c 30 | static const sfud_flash *flash; 31 | 32 | EfErrCode ef_port_init(ef_env const **default_env, size_t *default_env_size) { 33 | EfErrCode result = EF_NO_ERR; 34 | 35 | ... 36 | 37 | /* 从 RT-Thread 的 SPI Flash 设备中获取 SFUD Flash 设备对象 */ 38 | extern rt_spi_flash_device_t w25q64; 39 | flash = (sfud_flash_t)(w25q64->user_data); 40 | 41 | return result; 42 | } 43 | ``` 44 | 45 | 上述代码中的 `w25q64` ,是在 Flash 设备初始化时,执行 `w25q64 = rt_sfud_flash_probe("w25q64", "spi10");` 成功的返回值。该对象是 RT-Thread 的 SPI Flash 对象,其内部元素 `user_data` 里存放了 SFUD Flash 对象,将其赋值给静态的 `flash` 变量即可。 -------------------------------------------------------------------------------- /ports/ef_fal_port.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the EasyFlash Library. 3 | * 4 | * Copyright (c) 2015, Armink, 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files (the 8 | * 'Software'), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | * Function: Portable interface for FAL (Flash Abstraction Layer) partition. 26 | * Created on: 2018-05-19 27 | */ 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | /* EasyFlash partition name on FAL partition table */ 38 | #define FAL_EF_PART_NAME "ef" 39 | 40 | /* default ENV set for user */ 41 | static const ef_env default_env_set[] = { 42 | {"iap_need_copy_app", "0"}, 43 | {"iap_need_crc32_check", "0"}, 44 | {"iap_copy_app_size", "0"}, 45 | {"stop_in_bootloader", "0"}, 46 | }; 47 | 48 | static char log_buf[RT_CONSOLEBUF_SIZE]; 49 | static struct rt_semaphore env_cache_lock; 50 | static const struct fal_partition *part = NULL; 51 | 52 | /** 53 | * Flash port for hardware initialize. 54 | * 55 | * @param default_env default ENV set for user 56 | * @param default_env_size default ENV size 57 | * 58 | * @return result 59 | */ 60 | EfErrCode ef_port_init(ef_env const **default_env, size_t *default_env_size) { 61 | EfErrCode result = EF_NO_ERR; 62 | 63 | *default_env = default_env_set; 64 | *default_env_size = sizeof(default_env_set) / sizeof(default_env_set[0]); 65 | 66 | rt_sem_init(&env_cache_lock, "env lock", 1, RT_IPC_FLAG_PRIO); 67 | 68 | part = fal_partition_find(FAL_EF_PART_NAME); 69 | EF_ASSERT(part); 70 | 71 | return result; 72 | } 73 | 74 | /** 75 | * Read data from flash. 76 | * @note This operation's units is word. 77 | * 78 | * @param addr flash address 79 | * @param buf buffer to store read data 80 | * @param size read bytes size 81 | * 82 | * @return result 83 | */ 84 | EfErrCode ef_port_read(uint32_t addr, uint32_t *buf, size_t size) { 85 | EfErrCode result = EF_NO_ERR; 86 | 87 | fal_partition_read(part, addr, (uint8_t *)buf, size); 88 | 89 | return result; 90 | } 91 | 92 | /** 93 | * Erase data on flash. 94 | * @note This operation is irreversible. 95 | * @note This operation's units is different which on many chips. 96 | * 97 | * @param addr flash address 98 | * @param size erase bytes size 99 | * 100 | * @return result 101 | */ 102 | EfErrCode ef_port_erase(uint32_t addr, size_t size) { 103 | EfErrCode result = EF_NO_ERR; 104 | 105 | /* make sure the start address is a multiple of FLASH_ERASE_MIN_SIZE */ 106 | EF_ASSERT(addr % EF_ERASE_MIN_SIZE == 0); 107 | 108 | if (fal_partition_erase(part, addr, size) < 0) 109 | { 110 | result = EF_ERASE_ERR; 111 | } 112 | 113 | return result; 114 | } 115 | /** 116 | * Write data to flash. 117 | * @note This operation's units is word. 118 | * @note This operation must after erase. @see flash_erase. 119 | * 120 | * @param addr flash address 121 | * @param buf the write data buffer 122 | * @param size write bytes size 123 | * 124 | * @return result 125 | */ 126 | EfErrCode ef_port_write(uint32_t addr, const uint32_t *buf, size_t size) { 127 | EfErrCode result = EF_NO_ERR; 128 | 129 | if (fal_partition_write(part, addr, (uint8_t *)buf, size) < 0) 130 | { 131 | result = EF_WRITE_ERR; 132 | } 133 | 134 | return result; 135 | } 136 | 137 | /** 138 | * lock the ENV ram cache 139 | */ 140 | void ef_port_env_lock(void) { 141 | rt_sem_take(&env_cache_lock, RT_WAITING_FOREVER); 142 | } 143 | 144 | /** 145 | * unlock the ENV ram cache 146 | */ 147 | void ef_port_env_unlock(void) { 148 | rt_sem_release(&env_cache_lock); 149 | } 150 | 151 | /** 152 | * This function is print flash debug info. 153 | * 154 | * @param file the file which has call this function 155 | * @param line the line number which has call this function 156 | * @param format output format 157 | * @param ... args 158 | * 159 | */ 160 | void ef_log_debug(const char *file, const long line, const char *format, ...) { 161 | 162 | #ifdef PRINT_DEBUG 163 | 164 | va_list args; 165 | 166 | /* args point to the first variable parameter */ 167 | va_start(args, format); 168 | ef_print("[Flash] (%s:%ld) ", file, line); 169 | /* must use vprintf to print */ 170 | rt_vsprintf(log_buf, format, args); 171 | ef_print("%s", log_buf); 172 | va_end(args); 173 | 174 | #endif 175 | 176 | } 177 | 178 | /** 179 | * This function is print flash routine info. 180 | * 181 | * @param format output format 182 | * @param ... args 183 | */ 184 | void ef_log_info(const char *format, ...) { 185 | va_list args; 186 | 187 | /* args point to the first variable parameter */ 188 | va_start(args, format); 189 | ef_print("[Flash] "); 190 | /* must use vprintf to print */ 191 | rt_vsprintf(log_buf, format, args); 192 | ef_print("%s", log_buf); 193 | va_end(args); 194 | } 195 | /** 196 | * This function is print flash non-package info. 197 | * 198 | * @param format output format 199 | * @param ... args 200 | */ 201 | void ef_print(const char *format, ...) { 202 | va_list args; 203 | 204 | /* args point to the first variable parameter */ 205 | va_start(args, format); 206 | /* must use vprintf to print */ 207 | rt_vsprintf(log_buf, format, args); 208 | rt_kprintf("%s", log_buf); 209 | va_end(args); 210 | } 211 | -------------------------------------------------------------------------------- /ports/ef_sfud_port.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the EasyFlash Library. 3 | * 4 | * Copyright (c) 2015, Armink, 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files (the 8 | * 'Software'), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | * Function: Portable interface for SFUD flash driver. 26 | * Created on: 2015-01-16 27 | */ 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | /* default ENV set for user */ 39 | static const ef_env default_env_set[] = { 40 | {"iap_need_copy_app", "0"}, 41 | {"iap_need_crc32_check", "0"}, 42 | {"iap_copy_app_size", "0"}, 43 | {"stop_in_bootloader", "0"}, 44 | }; 45 | 46 | static char log_buf[RT_CONSOLEBUF_SIZE]; 47 | static struct rt_semaphore env_cache_lock; 48 | 49 | static const sfud_flash *flash; 50 | 51 | /** 52 | * Flash port for hardware initialize. 53 | * 54 | * @param default_env default ENV set for user 55 | * @param default_env_size default ENV size 56 | * 57 | * @return result 58 | */ 59 | EfErrCode ef_port_init(ef_env const **default_env, size_t *default_env_size) { 60 | EfErrCode result = EF_NO_ERR; 61 | 62 | *default_env = default_env_set; 63 | *default_env_size = sizeof(default_env_set) / sizeof(default_env_set[0]); 64 | 65 | rt_sem_init(&env_cache_lock, "env lock", 1, RT_IPC_FLAG_PRIO); 66 | 67 | extern rt_spi_flash_device_t w25q64; 68 | flash = (sfud_flash_t)(w25q64->user_data); 69 | 70 | return result; 71 | } 72 | 73 | /** 74 | * Read data from flash. 75 | * @note This operation's units is word. 76 | * 77 | * @param addr flash address 78 | * @param buf buffer to store read data 79 | * @param size read bytes size 80 | * 81 | * @return result 82 | */ 83 | EfErrCode ef_port_read(uint32_t addr, uint32_t *buf, size_t size) { 84 | EfErrCode result = EF_NO_ERR; 85 | 86 | sfud_read(flash, addr, size, (uint8_t *)buf); 87 | 88 | return result; 89 | } 90 | 91 | /** 92 | * Erase data on flash. 93 | * @note This operation is irreversible. 94 | * @note This operation's units is different which on many chips. 95 | * 96 | * @param addr flash address 97 | * @param size erase bytes size 98 | * 99 | * @return result 100 | */ 101 | EfErrCode ef_port_erase(uint32_t addr, size_t size) { 102 | EfErrCode result = EF_NO_ERR; 103 | sfud_err sfud_result = SFUD_SUCCESS; 104 | 105 | /* make sure the start address is a multiple of FLASH_ERASE_MIN_SIZE */ 106 | EF_ASSERT(addr % EF_ERASE_MIN_SIZE == 0); 107 | 108 | sfud_result = sfud_erase(flash, addr, size); 109 | 110 | if(sfud_result != SFUD_SUCCESS) { 111 | result = EF_ERASE_ERR; 112 | } 113 | return result; 114 | } 115 | /** 116 | * Write data to flash. 117 | * @note This operation's units is word. 118 | * @note This operation must after erase. @see flash_erase. 119 | * 120 | * @param addr flash address 121 | * @param buf the write data buffer 122 | * @param size write bytes size 123 | * 124 | * @return result 125 | */ 126 | EfErrCode ef_port_write(uint32_t addr, const uint32_t *buf, size_t size) { 127 | EfErrCode result = EF_NO_ERR; 128 | sfud_err sfud_result = SFUD_SUCCESS; 129 | 130 | sfud_result = sfud_write(flash, addr, size, (const uint8_t *)buf); 131 | 132 | if(sfud_result != SFUD_SUCCESS) { 133 | result = EF_WRITE_ERR; 134 | } 135 | 136 | return result; 137 | } 138 | 139 | /** 140 | * lock the ENV ram cache 141 | */ 142 | void ef_port_env_lock(void) { 143 | rt_sem_take(&env_cache_lock, RT_WAITING_FOREVER); 144 | } 145 | 146 | /** 147 | * unlock the ENV ram cache 148 | */ 149 | void ef_port_env_unlock(void) { 150 | rt_sem_release(&env_cache_lock); 151 | } 152 | 153 | /** 154 | * This function is print flash debug info. 155 | * 156 | * @param file the file which has call this function 157 | * @param line the line number which has call this function 158 | * @param format output format 159 | * @param ... args 160 | * 161 | */ 162 | void ef_log_debug(const char *file, const long line, const char *format, ...) { 163 | 164 | #ifdef PRINT_DEBUG 165 | 166 | va_list args; 167 | 168 | /* args point to the first variable parameter */ 169 | va_start(args, format); 170 | ef_print("[Flash] (%s:%ld) ", file, line); 171 | /* must use vprintf to print */ 172 | rt_vsprintf(log_buf, format, args); 173 | ef_print("%s", log_buf); 174 | va_end(args); 175 | 176 | #endif 177 | 178 | } 179 | 180 | /** 181 | * This function is print flash routine info. 182 | * 183 | * @param format output format 184 | * @param ... args 185 | */ 186 | void ef_log_info(const char *format, ...) { 187 | va_list args; 188 | 189 | /* args point to the first variable parameter */ 190 | va_start(args, format); 191 | ef_print("[Flash] "); 192 | /* must use vprintf to print */ 193 | rt_vsprintf(log_buf, format, args); 194 | ef_print("%s", log_buf); 195 | va_end(args); 196 | } 197 | /** 198 | * This function is print flash non-package info. 199 | * 200 | * @param format output format 201 | * @param ... args 202 | */ 203 | void ef_print(const char *format, ...) { 204 | va_list args; 205 | 206 | /* args point to the first variable parameter */ 207 | va_start(args, format); 208 | /* must use vprintf to print */ 209 | rt_vsprintf(log_buf, format, args); 210 | rt_kprintf("%s", log_buf); 211 | va_end(args); 212 | } 213 | -------------------------------------------------------------------------------- /src/easyflash.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the EasyFlash Library. 3 | * 4 | * Copyright (c) 2014-2019, Armink, 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files (the 8 | * 'Software'), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | * Function: Initialize interface for this library. 26 | * Created on: 2014-09-09 27 | */ 28 | 29 | /* 30 | * 31 | * This all Backup Area Flash storage index. All used flash area configure is under here. 32 | * |----------------------------| Storage Size 33 | * | Environment variables area | ENV area size @see ENV_AREA_SIZE 34 | * |----------------------------| 35 | * | Saved log area | Log area size @see LOG_AREA_SIZE 36 | * |----------------------------| 37 | * |(IAP)Downloaded application | IAP already downloaded application, unfixed size 38 | * |----------------------------| 39 | * 40 | * @note all area sizes must be aligned with EF_ERASE_MIN_SIZE 41 | * 42 | * The EasyFlash add the NG (Next Generation) mode start from V4.0. All old mode before V4.0, called LEGACY mode. 43 | * 44 | * - NG (Next Generation) mode is default mode from V4.0. It's easy to settings, only defined the ENV_AREA_SIZE. 45 | * - The LEGACY mode has been DEPRECATED. It is NOT RECOMMENDED to continue using. 46 | * Beacuse it will use ram to buffer the ENV and spend more flash erase times. 47 | * If you want use it please using the V3.X version. 48 | */ 49 | 50 | #include 51 | 52 | #if !defined(EF_START_ADDR) 53 | #error "Please configure backup area start address (in ef_cfg.h)" 54 | #endif 55 | 56 | #if !defined(EF_ERASE_MIN_SIZE) 57 | #error "Please configure minimum size of flash erasure (in ef_cfg.h)" 58 | #endif 59 | 60 | /** 61 | * EasyFlash system initialize. 62 | * 63 | * @return result 64 | */ 65 | EfErrCode easyflash_init(void) { 66 | extern EfErrCode ef_port_init(ef_env const **default_env, size_t *default_env_size); 67 | extern EfErrCode ef_env_init(ef_env const *default_env, size_t default_env_size); 68 | extern EfErrCode ef_iap_init(void); 69 | extern EfErrCode ef_log_init(void); 70 | 71 | size_t default_env_set_size = 0; 72 | const ef_env *default_env_set; 73 | EfErrCode result = EF_NO_ERR; 74 | static bool init_ok = false; 75 | 76 | if (init_ok) { 77 | return EF_NO_ERR; 78 | } 79 | 80 | result = ef_port_init(&default_env_set, &default_env_set_size); 81 | 82 | #ifdef EF_USING_ENV 83 | if (result == EF_NO_ERR) { 84 | result = ef_env_init(default_env_set, default_env_set_size); 85 | } 86 | #endif 87 | 88 | #ifdef EF_USING_IAP 89 | if (result == EF_NO_ERR) { 90 | result = ef_iap_init(); 91 | } 92 | #endif 93 | 94 | #ifdef EF_USING_LOG 95 | if (result == EF_NO_ERR) { 96 | result = ef_log_init(); 97 | } 98 | #endif 99 | 100 | if (result == EF_NO_ERR) { 101 | init_ok = true; 102 | EF_INFO("EasyFlash V%s is initialize success.\n", EF_SW_VERSION); 103 | } else { 104 | EF_INFO("EasyFlash V%s is initialize fail.\n", EF_SW_VERSION); 105 | } 106 | EF_INFO("You can get the latest version on https://github.com/armink/EasyFlash .\n"); 107 | 108 | return result; 109 | } 110 | -------------------------------------------------------------------------------- /src/ef_cmd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the EasyFlash Library. 3 | * 4 | * Copyright (c) 2015, Armink, 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files (the 8 | * 'Software'), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | * Function: RT-Thread Finsh/MSH command for EasyFlash. 26 | * Created on: 2018-05-19 27 | */ 28 | 29 | #include 30 | #include 31 | 32 | #if defined(RT_USING_FINSH) && defined(FINSH_USING_MSH) && defined(EF_USING_ENV) 33 | #include 34 | #if defined(EF_USING_ENV) 35 | static void __setenv(uint8_t argc, char **argv) { 36 | uint8_t i; 37 | 38 | if (argc > 3) { 39 | /* environment variable value string together */ 40 | for (i = 0; i < argc - 2; i++) { 41 | argv[2 + i][rt_strlen(argv[2 + i])] = ' '; 42 | } 43 | } 44 | if (argc == 1) { 45 | rt_kprintf("Please input: setenv [value]\n"); 46 | } else if (argc == 2) { 47 | ef_set_env(argv[1], NULL); 48 | } else { 49 | ef_set_env(argv[1], argv[2]); 50 | } 51 | } 52 | MSH_CMD_EXPORT_ALIAS(__setenv, setenv, Set an envrionment variable.); 53 | 54 | static void printenv(uint8_t argc, char **argv) { 55 | ef_print_env(); 56 | } 57 | MSH_CMD_EXPORT(printenv, Print all envrionment variables.); 58 | 59 | static void saveenv(uint8_t argc, char **argv) { 60 | ef_save_env(); 61 | } 62 | MSH_CMD_EXPORT(saveenv, Save all envrionment variables to flash.); 63 | 64 | static void getvalue(uint8_t argc, char **argv) { 65 | char *value = NULL; 66 | value = ef_get_env(argv[1]); 67 | if (value) { 68 | rt_kprintf("The %s value is %s.\n", argv[1], value); 69 | } else { 70 | rt_kprintf("Can't find %s.\n", argv[1]); 71 | } 72 | } 73 | MSH_CMD_EXPORT(getvalue, Get an envrionment variable by name.); 74 | 75 | static void resetenv(uint8_t argc, char **argv) { 76 | ef_env_set_default(); 77 | } 78 | MSH_CMD_EXPORT(resetenv, Reset all envrionment variable to default.); 79 | #endif /* defined(EF_USING_ENV) */ 80 | #endif /* defined(RT_USING_FINSH) && defined(FINSH_USING_MSH) */ 81 | -------------------------------------------------------------------------------- /src/ef_env.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armink-rtt-pkgs/EasyFlash/e8bf0313604ff4da91730c789800ae007b28f56a/src/ef_env.c -------------------------------------------------------------------------------- /src/ef_env_legacy.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the EasyFlash Library. 3 | * 4 | * Copyright (c) 2014-2018, Armink, 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files (the 8 | * 'Software'), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | * Function: Environment variables operating interface. (normal mode) 26 | * Created on: 2014-10-06 27 | */ 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | #if defined(EF_USING_ENV) && defined(EF_ENV_USING_LEGACY_MODE) 34 | 35 | #ifndef EF_ENV_USING_WL_MODE 36 | 37 | #if defined(EF_USING_ENV) && (!defined(ENV_USER_SETTING_SIZE) || !defined(ENV_AREA_SIZE)) 38 | #error "Please configure user setting ENV size or ENV area size (in ef_cfg.h)" 39 | #endif 40 | 41 | /** 42 | * ENV area has 2 sections 43 | * 1. System section 44 | * It storages ENV parameters. (Units: Word) 45 | * 2. Data section 46 | * It storages all ENV. Storage format is key=value\0. 47 | * All ENV must be 4 bytes alignment. The remaining part must fill '\0'. 48 | * 49 | * @note Word = 4 Bytes in this file 50 | * @note When using power fail safeguard mode, it has two ENV areas(Area0, Area1). 51 | */ 52 | 53 | /* flash ENV parameters index and size in system section */ 54 | enum { 55 | /* data section ENV end address index in system section */ 56 | ENV_PARAM_INDEX_END_ADDR = 0, 57 | 58 | #ifdef EF_ENV_USING_PFS_MODE 59 | /* saved count for ENV area */ 60 | ENV_PARAM_INDEX_SAVED_COUNT, 61 | #endif 62 | 63 | #ifdef EF_ENV_AUTO_UPDATE 64 | /* current version number for ENV */ 65 | ENV_PARAM_INDEX_VER_NUM, 66 | #endif 67 | 68 | /* data section CRC32 code index in system section */ 69 | ENV_PARAM_INDEX_DATA_CRC, 70 | /* flash ENV parameters word size */ 71 | ENV_PARAM_WORD_SIZE, 72 | /* flash ENV parameters byte size */ 73 | ENV_PARAM_BYTE_SIZE = ENV_PARAM_WORD_SIZE * 4, 74 | }; 75 | 76 | /* default ENV set, must be initialized by user */ 77 | static ef_env const *default_env_set; 78 | /* default ENV set size, must be initialized by user */ 79 | static size_t default_env_set_size = 0; 80 | /* ENV ram cache */ 81 | static uint32_t env_cache[ENV_USER_SETTING_SIZE / 4] = { 0 }; 82 | /* ENV start address in flash */ 83 | static uint32_t env_start_addr = 0; 84 | /* ENV ram cache has changed when ENV created, deleted and changed value. */ 85 | static bool env_cache_changed = false; 86 | /* initialize OK flag */ 87 | static bool init_ok = false; 88 | 89 | #ifdef EF_ENV_USING_PFS_MODE 90 | /* current load ENV area address */ 91 | static uint32_t cur_load_area_addr = 0; 92 | /* next save ENV area address */ 93 | static uint32_t next_save_area_addr = 0; 94 | #endif 95 | 96 | static uint32_t get_env_system_addr(void); 97 | static uint32_t get_env_data_addr(void); 98 | static uint32_t get_env_end_addr(void); 99 | static void set_env_end_addr(uint32_t end_addr); 100 | static EfErrCode write_env(const char *key, const char *value); 101 | static char *find_env(const char *key); 102 | static EfErrCode del_env(const char *key); 103 | static size_t get_env_data_size(void); 104 | static size_t get_env_user_used_size(void); 105 | static EfErrCode create_env(const char *key, const char *value); 106 | static uint32_t calc_env_crc(void); 107 | static bool env_crc_is_ok(void); 108 | #ifdef EF_ENV_AUTO_UPDATE 109 | static EfErrCode env_auto_update(void); 110 | #endif 111 | 112 | /** 113 | * Flash ENV initialize. 114 | * 115 | * @param default_env default ENV set for user 116 | * @param default_env_size default ENV set size 117 | * 118 | * @note user_size must equal with total_size in normal mode 119 | * 120 | * @return result 121 | */ 122 | EfErrCode ef_env_init(ef_env const *default_env, size_t default_env_size) { 123 | EfErrCode result = EF_NO_ERR; 124 | 125 | EF_ASSERT(ENV_AREA_SIZE); 126 | EF_ASSERT(ENV_USER_SETTING_SIZE); 127 | EF_ASSERT(EF_ERASE_MIN_SIZE); 128 | /* must be word alignment for ENV */ 129 | EF_ASSERT(ENV_USER_SETTING_SIZE % 4 == 0); 130 | EF_ASSERT(ENV_AREA_SIZE % 4 == 0); 131 | EF_ASSERT(default_env); 132 | EF_ASSERT(default_env_size < ENV_USER_SETTING_SIZE); 133 | 134 | #ifndef EF_ENV_USING_PFS_MODE 135 | /* total_size must be aligned with erase_min_size */ 136 | if (ENV_USER_SETTING_SIZE % EF_ERASE_MIN_SIZE == 0) { 137 | EF_ASSERT(ENV_USER_SETTING_SIZE == ENV_AREA_SIZE); 138 | } else { 139 | EF_ASSERT((ENV_USER_SETTING_SIZE / EF_ERASE_MIN_SIZE + 1)*EF_ERASE_MIN_SIZE == ENV_AREA_SIZE); 140 | } 141 | #else 142 | /* total_size must be aligned with erase_min_size */ 143 | if (ENV_USER_SETTING_SIZE % EF_ERASE_MIN_SIZE == 0) { 144 | /* it has double area when used power fail safeguard mode */ 145 | EF_ASSERT(2 * ENV_USER_SETTING_SIZE == ENV_AREA_SIZE); 146 | } else { 147 | /* it has double area when used power fail safeguard mode */ 148 | EF_ASSERT(2 * (ENV_USER_SETTING_SIZE / EF_ERASE_MIN_SIZE + 1)*EF_ERASE_MIN_SIZE == ENV_AREA_SIZE); 149 | } 150 | #endif 151 | 152 | env_start_addr = EF_START_ADDR; 153 | default_env_set = default_env; 154 | default_env_set_size = default_env_size; 155 | 156 | EF_DEBUG("ENV start address is 0x%08X, size is %d bytes.\n", EF_START_ADDR, ENV_AREA_SIZE); 157 | 158 | result = ef_load_env(); 159 | 160 | #ifdef EF_ENV_AUTO_UPDATE 161 | if (result == EF_NO_ERR) { 162 | env_auto_update(); 163 | } 164 | #endif 165 | 166 | if (result == EF_NO_ERR) { 167 | init_ok = true; 168 | } 169 | 170 | 171 | return result; 172 | } 173 | 174 | /** 175 | * ENV set default. 176 | * 177 | * @return result 178 | */ 179 | EfErrCode ef_env_set_default(void) { 180 | extern EfErrCode ef_env_ver_num_set_default(void); 181 | 182 | EfErrCode result = EF_NO_ERR; 183 | size_t i; 184 | 185 | EF_ASSERT(default_env_set); 186 | EF_ASSERT(default_env_set_size); 187 | 188 | /* lock the ENV cache */ 189 | ef_port_env_lock(); 190 | 191 | /* set environment end address is at data section start address */ 192 | set_env_end_addr(get_env_data_addr()); 193 | 194 | #ifdef EF_ENV_USING_PFS_MODE 195 | /* set saved count to default 0 */ 196 | env_cache[ENV_PARAM_INDEX_SAVED_COUNT] = 0; 197 | #endif 198 | 199 | #ifdef EF_ENV_AUTO_UPDATE 200 | /* initialize version number */ 201 | env_cache[ENV_PARAM_INDEX_VER_NUM] = EF_ENV_VER_NUM; 202 | #endif 203 | 204 | /* create default ENV */ 205 | for (i = 0; i < default_env_set_size; i++) { 206 | create_env(default_env_set[i].key, default_env_set[i].value); 207 | } 208 | 209 | /* unlock the ENV cache */ 210 | ef_port_env_unlock(); 211 | 212 | result = ef_save_env(); 213 | 214 | #ifdef EF_ENV_USING_PFS_MODE 215 | /* reset other PFS area's data */ 216 | if (result == EF_NO_ERR) { 217 | env_cache_changed = true; 218 | result = ef_save_env(); 219 | } 220 | #endif 221 | 222 | return result; 223 | } 224 | 225 | /** 226 | * Get ENV system section start address. 227 | * 228 | * @return system section start address 229 | */ 230 | static uint32_t get_env_system_addr(void) { 231 | #ifndef EF_ENV_USING_PFS_MODE 232 | return env_start_addr; 233 | #else 234 | return cur_load_area_addr; 235 | #endif 236 | } 237 | 238 | /** 239 | * Get ENV data section start address. 240 | * 241 | * @return data section start address 242 | */ 243 | static uint32_t get_env_data_addr(void) { 244 | return get_env_system_addr() + ENV_PARAM_BYTE_SIZE; 245 | } 246 | 247 | /** 248 | * Get ENV end address. 249 | * It's the first word in ENV. 250 | * 251 | * @return ENV end address 252 | */ 253 | static uint32_t get_env_end_addr(void) { 254 | /* it is the first word */ 255 | return env_cache[ENV_PARAM_INDEX_END_ADDR]; 256 | } 257 | 258 | /** 259 | * Set ENV end address. 260 | * It's the first word in ENV. 261 | * 262 | * @param end_addr ENV end address 263 | */ 264 | static void set_env_end_addr(uint32_t end_addr) { 265 | env_cache[ENV_PARAM_INDEX_END_ADDR] = end_addr; 266 | } 267 | 268 | /** 269 | * Get current ENV data section size. 270 | * 271 | * @return size 272 | */ 273 | static size_t get_env_data_size(void) { 274 | if (get_env_end_addr() > get_env_data_addr()) { 275 | return get_env_end_addr() - get_env_data_addr(); 276 | } else { 277 | return 0; 278 | } 279 | } 280 | 281 | /** 282 | * Get current user used ENV size. 283 | * 284 | * @return bytes 285 | */ 286 | static size_t get_env_user_used_size(void) { 287 | if (get_env_end_addr() > get_env_system_addr()) { 288 | return get_env_end_addr() - get_env_system_addr(); 289 | } else { 290 | return 0; 291 | } 292 | } 293 | 294 | /** 295 | * Get current ENV already write bytes. 296 | * 297 | * @return write bytes 298 | */ 299 | size_t ef_get_env_write_bytes(void) { 300 | #ifndef EF_ENV_USING_PFS_MODE 301 | return get_env_user_used_size(); 302 | #else 303 | return get_env_user_used_size() * 2; 304 | #endif 305 | } 306 | 307 | /** 308 | * Write an ENV at the end of cache. 309 | * 310 | * @param key ENV name 311 | * @param value ENV value 312 | * 313 | * @return result 314 | */ 315 | static EfErrCode write_env(const char *key, const char *value) { 316 | EfErrCode result = EF_NO_ERR; 317 | size_t key_len = strlen(key), value_len = strlen(value), env_str_len; 318 | char *env_cache_bak = (char *)env_cache; 319 | 320 | /* calculate ENV storage length, contain '=' and '\0'. */ 321 | env_str_len = key_len + value_len + 2; 322 | if (env_str_len % 4 != 0) { 323 | env_str_len = (env_str_len / 4 + 1) * 4; 324 | } 325 | /* check capacity of ENV */ 326 | if (env_str_len + get_env_user_used_size() >= ENV_USER_SETTING_SIZE) { 327 | return EF_ENV_FULL; 328 | } 329 | 330 | /* calculate current ENV ram cache end address */ 331 | env_cache_bak += get_env_user_used_size(); 332 | 333 | /* copy key name */ 334 | memcpy(env_cache_bak, key, key_len); 335 | env_cache_bak += key_len; 336 | /* copy equal sign */ 337 | *env_cache_bak = '='; 338 | env_cache_bak++; 339 | /* copy value */ 340 | memcpy(env_cache_bak, value, value_len); 341 | env_cache_bak += value_len; 342 | /* fill '\0' for string end sign */ 343 | *env_cache_bak = '\0'; 344 | env_cache_bak ++; 345 | /* fill '\0' for word alignment */ 346 | memset(env_cache_bak, 0, env_str_len - (key_len + value_len + 2)); 347 | set_env_end_addr(get_env_end_addr() + env_str_len); 348 | /* ENV ram cache has changed */ 349 | env_cache_changed = true; 350 | 351 | return result; 352 | } 353 | 354 | /** 355 | * Find ENV. 356 | * 357 | * @param key ENV name 358 | * 359 | * @return found ENV in ram cache 360 | */ 361 | static char *find_env(const char *key) { 362 | char *env_start, *env_end, *env, *found_env = NULL; 363 | size_t key_len = strlen(key), env_len; 364 | 365 | if ((key == NULL) || *key == '\0') { 366 | EF_INFO("Flash ENV name must be not empty!\n"); 367 | return NULL; 368 | } 369 | 370 | /* from data section start to data section end */ 371 | env_start = (char *) ((char *) env_cache + ENV_PARAM_BYTE_SIZE); 372 | env_end = (char *) ((char *) env_cache + get_env_user_used_size()); 373 | 374 | /* ENV is null */ 375 | if (env_start == env_end) { 376 | return NULL; 377 | } 378 | 379 | env = env_start; 380 | while (env < env_end) { 381 | /* the key length must be equal */ 382 | if (!strncmp(env, key, key_len) && (env[key_len] == '=')) { 383 | found_env = env; 384 | break; 385 | } else { 386 | /* calculate ENV length, contain '\0'. */ 387 | env_len = strlen(env) + 1; 388 | /* next ENV and word alignment */ 389 | if (env_len % 4 == 0) { 390 | env += env_len; 391 | } else { 392 | env += (env_len / 4 + 1) * 4; 393 | } 394 | } 395 | } 396 | return found_env; 397 | } 398 | 399 | /** 400 | * If the ENV is not exist, create it. 401 | * @see flash_write_env 402 | * 403 | * @param key ENV name 404 | * @param value ENV value 405 | * 406 | * @return result 407 | */ 408 | static EfErrCode create_env(const char *key, const char *value) { 409 | EfErrCode result = EF_NO_ERR; 410 | 411 | EF_ASSERT(key); 412 | EF_ASSERT(value); 413 | 414 | if ((key == NULL) || *key == '\0') { 415 | EF_INFO("Flash ENV name must be not empty!\n"); 416 | return EF_ENV_NAME_ERR; 417 | } 418 | 419 | if (strchr(key, '=')) { 420 | EF_INFO("Flash ENV name can't contain '='.\n"); 421 | return EF_ENV_NAME_ERR; 422 | } 423 | 424 | /* find ENV */ 425 | if (find_env(key)) { 426 | EF_INFO("The name of \"%s\" is already exist.\n", key); 427 | return EF_ENV_NAME_EXIST; 428 | } 429 | /* write ENV at the end of cache */ 430 | result = write_env(key, value); 431 | 432 | return result; 433 | } 434 | 435 | /** 436 | * Delete an ENV in cache. 437 | * 438 | * @param key ENV name 439 | * 440 | * @return result 441 | */ 442 | static EfErrCode del_env(const char *key) { 443 | EfErrCode result = EF_NO_ERR; 444 | char *del_env = NULL; 445 | size_t del_env_length, remain_env_length; 446 | 447 | EF_ASSERT(key); 448 | 449 | if ((key == NULL) || *key == '\0') { 450 | EF_INFO("Flash ENV name must be not NULL!\n"); 451 | return EF_ENV_NAME_ERR; 452 | } 453 | 454 | if (strchr(key, '=')) { 455 | EF_INFO("Flash ENV name or value can't contain '='.\n"); 456 | return EF_ENV_NAME_ERR; 457 | } 458 | 459 | /* find ENV */ 460 | del_env = find_env(key); 461 | 462 | if (!del_env) { 463 | EF_INFO("Not find \"%s\" in ENV.\n", key); 464 | return EF_ENV_NAME_ERR; 465 | } 466 | del_env_length = strlen(del_env); 467 | /* '\0' also must be as ENV length */ 468 | del_env_length ++; 469 | /* the address must multiple of 4 */ 470 | if (del_env_length % 4 != 0) { 471 | del_env_length = (del_env_length / 4 + 1) * 4; 472 | } 473 | /* calculate remain ENV length */ 474 | remain_env_length = get_env_data_size() 475 | - (((uint32_t) del_env + del_env_length) - ((uint32_t) env_cache + ENV_PARAM_BYTE_SIZE)); 476 | /* remain ENV move forward */ 477 | memcpy(del_env, del_env + del_env_length, remain_env_length); 478 | /* reset ENV end address */ 479 | set_env_end_addr(get_env_end_addr() - del_env_length); 480 | /* ENV ram cache has changed */ 481 | env_cache_changed = true; 482 | 483 | return result; 484 | } 485 | 486 | /** 487 | * Set an ENV.If it value is NULL, delete it. 488 | * If not find it in ENV table, then create it. 489 | * 490 | * @param key ENV name 491 | * @param value ENV value 492 | * 493 | * @return result 494 | */ 495 | EfErrCode ef_set_env(const char *key, const char *value) { 496 | EfErrCode result = EF_NO_ERR; 497 | char *old_env, *old_value; 498 | 499 | if (!init_ok) { 500 | EF_INFO("ENV isn't initialize OK.\n"); 501 | return EF_ENV_INIT_FAILED; 502 | } 503 | 504 | /* lock the ENV cache */ 505 | ef_port_env_lock(); 506 | 507 | /* if ENV value is NULL, delete it */ 508 | if (value == NULL) { 509 | result = del_env(key); 510 | } else { 511 | old_env = find_env(key); 512 | /* If find this ENV, then compare the new value and old value. */ 513 | if (old_env) { 514 | /* find the old value address */ 515 | old_env = strchr(old_env, '='); 516 | old_value = old_env + 1; 517 | /* If it is changed then delete it and recreate it */ 518 | if (strcmp(old_value, value)) { 519 | result = del_env(key); 520 | if (result == EF_NO_ERR) { 521 | result = create_env(key, value); 522 | } 523 | } 524 | } else { 525 | result = create_env(key, value); 526 | } 527 | } 528 | 529 | /* unlock the ENV cache */ 530 | ef_port_env_unlock(); 531 | 532 | return result; 533 | } 534 | 535 | /** 536 | * Del an ENV. 537 | * 538 | * @param key ENV name 539 | * 540 | * @return result 541 | */ 542 | EfErrCode ef_del_env(const char *key) { 543 | EfErrCode result = EF_NO_ERR; 544 | 545 | if (!init_ok) { 546 | EF_INFO("ENV isn't initialize OK.\n"); 547 | return EF_ENV_INIT_FAILED; 548 | } 549 | 550 | /* lock the ENV cache */ 551 | ef_port_env_lock(); 552 | 553 | result = del_env(key); 554 | 555 | /* unlock the ENV cache */ 556 | ef_port_env_unlock(); 557 | 558 | return result; 559 | } 560 | 561 | /** 562 | * Get an ENV value by key name. 563 | * 564 | * @param key ENV name 565 | * 566 | * @return value 567 | */ 568 | char *ef_get_env(const char *key) { 569 | char *env = NULL, *value = NULL; 570 | 571 | if (!init_ok) { 572 | EF_INFO("ENV isn't initialize OK.\n"); 573 | return NULL; 574 | } 575 | 576 | /* find ENV */ 577 | env = find_env(key); 578 | 579 | if (env == NULL) { 580 | return NULL; 581 | } 582 | /* get value address */ 583 | value = strchr(env, '='); 584 | if (value != NULL) { 585 | /* the equal sign next character is value */ 586 | value++; 587 | } 588 | return value; 589 | } 590 | /** 591 | * Print ENV. 592 | */ 593 | void ef_print_env(void) { 594 | uint32_t *env_cache_data_addr = env_cache + ENV_PARAM_WORD_SIZE, 595 | *env_cache_end_addr = 596 | (uint32_t *) (env_cache + ENV_PARAM_WORD_SIZE + get_env_data_size() / 4); 597 | uint8_t j; 598 | char c; 599 | 600 | if (!init_ok) { 601 | EF_INFO("ENV isn't initialize OK.\n"); 602 | return; 603 | } 604 | 605 | for (; env_cache_data_addr < env_cache_end_addr; env_cache_data_addr += 1) { 606 | for (j = 0; j < 4; j++) { 607 | c = (*env_cache_data_addr) >> (8 * j); 608 | ef_print("%c", c); 609 | if (c == '\0') { 610 | ef_print("\n"); 611 | break; 612 | } 613 | } 614 | } 615 | 616 | #ifndef EF_ENV_USING_PFS_MODE 617 | ef_print("\nmode: normal\n"); 618 | ef_print("size: %ld/%ld bytes.\n", get_env_user_used_size(), ENV_USER_SETTING_SIZE); 619 | #else 620 | ef_print("\nmode: power fail safeguard\n"); 621 | ef_print("size: %ld/%ld bytes, write bytes %ld/%ld.\n", get_env_user_used_size(), 622 | ENV_USER_SETTING_SIZE, ef_get_env_write_bytes(), ENV_AREA_SIZE); 623 | ef_print("saved count: %ld\n", env_cache[ENV_PARAM_INDEX_SAVED_COUNT]); 624 | #endif 625 | 626 | #ifdef EF_ENV_AUTO_UPDATE 627 | ef_print("ver num: %d\n", env_cache[ENV_PARAM_INDEX_VER_NUM]); 628 | #endif 629 | } 630 | 631 | /** 632 | * Load flash ENV to ram. 633 | * 634 | * @return result 635 | */ 636 | #ifndef EF_ENV_USING_PFS_MODE 637 | EfErrCode ef_load_env(void) { 638 | EfErrCode result = EF_NO_ERR; 639 | uint32_t *env_cache_bak, env_end_addr; 640 | 641 | /* read ENV end address from flash */ 642 | ef_port_read(get_env_system_addr() + ENV_PARAM_INDEX_END_ADDR * 4, &env_end_addr, 4); 643 | /* if ENV is not initialize or flash has dirty data, set default for it */ 644 | if ((env_end_addr == 0xFFFFFFFF) || (env_end_addr < env_start_addr) 645 | || (env_end_addr > env_start_addr + ENV_USER_SETTING_SIZE)) { 646 | result = ef_env_set_default(); 647 | } else { 648 | /* set ENV end address */ 649 | set_env_end_addr(env_end_addr); 650 | 651 | env_cache_bak = env_cache + ENV_PARAM_WORD_SIZE; 652 | /* read all ENV from flash */ 653 | ef_port_read(get_env_data_addr(), env_cache_bak, get_env_data_size()); 654 | /* read ENV CRC code from flash */ 655 | ef_port_read(get_env_system_addr() + ENV_PARAM_INDEX_DATA_CRC * 4, 656 | &env_cache[ENV_PARAM_INDEX_DATA_CRC] , 4); 657 | /* if ENV CRC32 check is fault, set default for it */ 658 | if (!env_crc_is_ok()) { 659 | EF_INFO("Warning: ENV CRC check failed. Set it to default.\n"); 660 | result = ef_env_set_default(); 661 | } 662 | } 663 | return result; 664 | } 665 | #else 666 | EfErrCode ef_load_env(void) { 667 | EfErrCode result = EF_NO_ERR; 668 | uint32_t area0_start_address = env_start_addr, area1_start_address = env_start_addr 669 | + ENV_AREA_SIZE / 2; 670 | uint32_t area0_end_addr, area1_end_addr, area0_crc, area1_crc, area0_saved_count, area1_saved_count; 671 | bool area0_is_valid = true, area1_is_valid = true; 672 | /* read ENV area end address from flash */ 673 | ef_port_read(area0_start_address + ENV_PARAM_INDEX_END_ADDR * 4, &area0_end_addr, 4); 674 | ef_port_read(area1_start_address + ENV_PARAM_INDEX_END_ADDR * 4, &area1_end_addr, 4); 675 | if ((area0_end_addr == 0xFFFFFFFF) || (area0_end_addr < area0_start_address) 676 | || (area0_end_addr > area0_start_address + ENV_USER_SETTING_SIZE)) { 677 | area0_is_valid = false; 678 | } 679 | if ((area1_end_addr == 0xFFFFFFFF) || (area1_end_addr < area1_start_address) 680 | || (area1_end_addr > area1_start_address + ENV_USER_SETTING_SIZE)) { 681 | area1_is_valid = false; 682 | } 683 | /* check area0 CRC when it is valid */ 684 | if (area0_is_valid) { 685 | /* read ENV area0 crc32 code from flash */ 686 | ef_port_read(area0_start_address + ENV_PARAM_INDEX_DATA_CRC * 4, &area0_crc, 4); 687 | /* read ENV from ENV area0 */ 688 | ef_port_read(area0_start_address, env_cache, area0_end_addr - area0_start_address); 689 | /* current load ENV area address is area0 start address */ 690 | cur_load_area_addr = area0_start_address; 691 | if (!env_crc_is_ok()) { 692 | area0_is_valid = false; 693 | } 694 | } 695 | /* check area1 CRC when it is valid */ 696 | if (area1_is_valid) { 697 | /* read ENV area1 crc32 code from flash */ 698 | ef_port_read(area1_start_address + ENV_PARAM_INDEX_DATA_CRC * 4, &area1_crc, 4); 699 | /* read ENV from ENV area1 */ 700 | ef_port_read(area1_start_address, env_cache, area1_end_addr - area1_start_address); 701 | /* current load ENV area address is area1 start address */ 702 | cur_load_area_addr = area1_start_address; 703 | if (!env_crc_is_ok()) { 704 | area1_is_valid = false; 705 | } 706 | } 707 | /* all ENV area CRC is OK then compare saved count */ 708 | if (area0_is_valid && area1_is_valid) { 709 | /* read ENV area saved count from flash */ 710 | ef_port_read(area0_start_address + ENV_PARAM_INDEX_SAVED_COUNT * 4, 711 | &area0_saved_count, 4); 712 | ef_port_read(area1_start_address + ENV_PARAM_INDEX_SAVED_COUNT * 4, 713 | &area1_saved_count, 4); 714 | /* the bigger saved count area is valid */ 715 | if ((area0_saved_count > area1_saved_count) || ((area0_saved_count == 0) && (area1_saved_count == 0xFFFFFFFF))) { 716 | area1_is_valid = false; 717 | } else { 718 | area0_is_valid = false; 719 | } 720 | } 721 | if (area0_is_valid) { 722 | /* current load ENV area address is area0 start address */ 723 | cur_load_area_addr = area0_start_address; 724 | /* next save ENV area address is area1 start address */ 725 | next_save_area_addr = area1_start_address; 726 | /* read all ENV from area0 */ 727 | ef_port_read(area0_start_address, env_cache, area0_end_addr - area0_start_address); 728 | } else if (area1_is_valid) { 729 | /* next save ENV area address is area0 start address */ 730 | next_save_area_addr = area0_start_address; 731 | } else { 732 | /* current load ENV area address is area1 start address */ 733 | cur_load_area_addr = area1_start_address; 734 | /* next save ENV area address is area0 start address */ 735 | next_save_area_addr = area0_start_address; 736 | /* set the ENV to default */ 737 | result = ef_env_set_default(); 738 | } 739 | return result; 740 | } 741 | #endif 742 | 743 | /** 744 | * Save ENV to flash. 745 | */ 746 | EfErrCode ef_save_env(void) { 747 | EfErrCode result = EF_NO_ERR; 748 | uint32_t write_addr, write_size; 749 | 750 | /* ENV ram cache has not changed don't need to save */ 751 | if (!env_cache_changed) { 752 | return result; 753 | } 754 | 755 | #ifndef EF_ENV_USING_PFS_MODE 756 | write_addr = get_env_system_addr(); 757 | write_size = get_env_user_used_size(); 758 | /* calculate and cache CRC32 code */ 759 | env_cache[ENV_PARAM_INDEX_DATA_CRC] = calc_env_crc(); 760 | #else 761 | write_addr = next_save_area_addr; 762 | write_size = get_env_user_used_size(); 763 | /* replace next_save_area_addr with cur_load_area_addr */ 764 | next_save_area_addr = cur_load_area_addr; 765 | cur_load_area_addr = write_addr; 766 | /* change the ENV end address to next save area address */ 767 | set_env_end_addr(write_addr + write_size); 768 | /* ENV area saved count +1 */ 769 | env_cache[ENV_PARAM_INDEX_SAVED_COUNT]++; 770 | /* calculate and cache CRC32 code */ 771 | env_cache[ENV_PARAM_INDEX_DATA_CRC] = calc_env_crc(); 772 | #endif 773 | 774 | /* erase ENV */ 775 | result = ef_port_erase(write_addr, write_size); 776 | switch (result) { 777 | case EF_NO_ERR: { 778 | EF_DEBUG("Erased ENV OK.\n"); 779 | break; 780 | } 781 | case EF_ERASE_ERR: { 782 | EF_INFO("Error: Erased ENV fault! Start address is 0x%08X, size is %ld.\n", write_addr, write_size); 783 | /* will return when erase fault */ 784 | return result; 785 | } 786 | } 787 | 788 | /* write ENV to flash */ 789 | result = ef_port_write(write_addr, env_cache, write_size); 790 | switch (result) { 791 | case EF_NO_ERR: { 792 | EF_DEBUG("Saved ENV OK.\n"); 793 | break; 794 | } 795 | case EF_WRITE_ERR: { 796 | EF_INFO("Error: Saved ENV fault! Start address is 0x%08X, size is %ld.\n", write_addr, write_size); 797 | break; 798 | } 799 | } 800 | 801 | env_cache_changed = false; 802 | 803 | return result; 804 | } 805 | 806 | /** 807 | * Calculate the cached ENV CRC32 value. 808 | * 809 | * @return CRC32 value 810 | */ 811 | static uint32_t calc_env_crc(void) { 812 | uint32_t crc32 = 0; 813 | 814 | /* Calculate the ENV end address CRC32. The 4 is ENV end address bytes size. */ 815 | crc32 = ef_calc_crc32(crc32, &env_cache[ENV_PARAM_INDEX_END_ADDR], 4); 816 | 817 | #ifdef EF_ENV_USING_PFS_MODE 818 | /* Calculate the ENV area saved count CRC32. */ 819 | crc32 = ef_calc_crc32(crc32, &env_cache[ENV_PARAM_INDEX_SAVED_COUNT], 4); 820 | #endif 821 | 822 | /* Calculate the all ENV data CRC32. */ 823 | crc32 = ef_calc_crc32(crc32, &env_cache[ENV_PARAM_WORD_SIZE], get_env_data_size()); 824 | 825 | EF_DEBUG("Calculate ENV CRC32 number is 0x%08X.\n", crc32); 826 | 827 | return crc32; 828 | } 829 | 830 | /** 831 | * Check the ENV CRC32 832 | * 833 | * @return true is ok 834 | */ 835 | static bool env_crc_is_ok(void) { 836 | if (calc_env_crc() == env_cache[ENV_PARAM_INDEX_DATA_CRC]) { 837 | EF_DEBUG("Verify ENV CRC32 result is OK.\n"); 838 | return true; 839 | } else { 840 | return false; 841 | } 842 | } 843 | 844 | /** 845 | * Set and save an ENV. If set ENV is success then will save it. 846 | * 847 | * @param key ENV name 848 | * @param value ENV value 849 | * 850 | * @return result 851 | */ 852 | EfErrCode ef_set_and_save_env(const char *key, const char *value) { 853 | EfErrCode result = EF_NO_ERR; 854 | 855 | result = ef_set_env(key, value); 856 | 857 | if (result == EF_NO_ERR) { 858 | result = ef_save_env(); 859 | } 860 | 861 | return result; 862 | } 863 | 864 | /** 865 | * Del and save an ENV. If del ENV is success then will save it. 866 | * 867 | * @param key ENV name 868 | * 869 | * @return result 870 | */ 871 | EfErrCode ef_del_and_save_env(const char *key) { 872 | EfErrCode result = EF_NO_ERR; 873 | 874 | result = ef_del_env(key); 875 | 876 | if (result == EF_NO_ERR) { 877 | result = ef_save_env(); 878 | } 879 | 880 | return result; 881 | } 882 | 883 | #ifdef EF_ENV_AUTO_UPDATE 884 | /** 885 | * Auto update ENV to latest default when current EF_ENV_VER is changed. 886 | * 887 | * @return result 888 | */ 889 | static EfErrCode env_auto_update(void) 890 | { 891 | size_t i; 892 | 893 | /* lock the ENV cache */ 894 | ef_port_env_lock(); 895 | 896 | /* read ENV version number from flash*/ 897 | ef_port_read(get_env_system_addr() + ENV_PARAM_INDEX_VER_NUM * 4, 898 | &env_cache[ENV_PARAM_INDEX_VER_NUM] , 4); 899 | 900 | /* check version number */ 901 | if (env_cache[ENV_PARAM_INDEX_VER_NUM] != EF_ENV_VER_NUM) { 902 | env_cache_changed = true; 903 | /* update version number */ 904 | env_cache[ENV_PARAM_INDEX_VER_NUM] = EF_ENV_VER_NUM; 905 | /* add a new ENV when it's not found */ 906 | for (i = 0; i < default_env_set_size; i++) { 907 | if (find_env(default_env_set[i].key) == NULL) { 908 | create_env(default_env_set[i].key, default_env_set[i].value); 909 | } 910 | } 911 | } 912 | 913 | /* unlock the ENV cache */ 914 | ef_port_env_unlock(); 915 | 916 | return ef_save_env(); 917 | } 918 | #endif /* EF_ENV_AUTO_UPDATE */ 919 | 920 | #endif /* EF_ENV_USING_WL_MODE */ 921 | 922 | #endif /* defined(EF_USING_ENV) && defined(EF_ENV_USING_LEGACY_MODE) */ 923 | -------------------------------------------------------------------------------- /src/ef_env_legacy_wl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the EasyFlash Library. 3 | * 4 | * Copyright (c) 2015-2018, Armink, 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files (the 8 | * 'Software'), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | * Function: Environment variables operating interface. (wear leveling mode) 26 | * Created on: 2015-02-11 27 | */ 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | #if defined(EF_USING_ENV) && defined(EF_ENV_USING_LEGACY_MODE) 34 | 35 | #ifdef EF_ENV_USING_WL_MODE 36 | 37 | #if defined(EF_USING_ENV) && (!defined(ENV_USER_SETTING_SIZE) || !defined(ENV_AREA_SIZE)) 38 | #error "Please configure user setting ENV size or ENV area size (in ef_cfg.h)" 39 | #endif 40 | 41 | /** 42 | * ENV area has 2 sections 43 | * 1. System section 44 | * Storage ENV current using data section address. 45 | * Units: Word. Total size: @see EF_ERASE_MIN_SIZE. 46 | * 2. Data section 47 | * The data section storage ENV's parameters and detail. 48 | * When an exception has occurred on flash erase or write. The current using data section 49 | * address will move to next available position. This position depends on EF_ERASE_MIN_SIZE. 50 | * 2.1 ENV parameters part 51 | * It storage ENV's parameters. 52 | * 2.2 ENV detail part 53 | * It storage all ENV. Storage format is key=value\0. 54 | * All ENV must be 4 bytes alignment. The remaining part must fill '\0'. 55 | * 56 | * @note Word = 4 Bytes in this file 57 | * @note It will has two ENV areas(Area0, Area1) in data section when used power fail safeguard mode. 58 | */ 59 | 60 | /* flash ENV parameters part index and size */ 61 | enum { 62 | /* data section ENV detail part end address index */ 63 | ENV_PARAM_PART_INDEX_END_ADDR = 0, 64 | 65 | #ifdef EF_ENV_USING_PFS_MODE 66 | /* saved count for ENV area */ 67 | ENV_PARAM_PART_INDEX_SAVED_COUNT, 68 | #endif 69 | 70 | #ifdef EF_ENV_AUTO_UPDATE 71 | /* current version number for ENV */ 72 | ENV_PARAM_INDEX_VER_NUM, 73 | #endif 74 | 75 | /* data section CRC32 code index */ 76 | ENV_PARAM_PART_INDEX_DATA_CRC, 77 | /* ENV parameters part word size */ 78 | ENV_PARAM_PART_WORD_SIZE, 79 | /* ENV parameters part byte size */ 80 | ENV_PARAM_PART_BYTE_SIZE = ENV_PARAM_PART_WORD_SIZE * 4, 81 | }; 82 | 83 | /* default ENV set, must be initialized by user */ 84 | static ef_env const *default_env_set; 85 | /* default ENV set size, must be initialized by user */ 86 | static size_t default_env_set_size = 0; 87 | /* flash ENV data section size */ 88 | static size_t env_data_section_size = 0; 89 | /* ENV ram cache */ 90 | static uint32_t env_cache[ENV_USER_SETTING_SIZE / 4] = { 0 }; 91 | /* ENV start address in flash */ 92 | static uint32_t env_start_addr = 0; 93 | /* current using data section address */ 94 | static uint32_t cur_using_data_addr = 0; 95 | /* ENV ram cache has changed when ENV created, deleted and changed value. */ 96 | static bool env_cache_changed = false; 97 | /* initialize OK flag */ 98 | static bool init_ok = false; 99 | 100 | #ifdef EF_ENV_USING_PFS_MODE 101 | /* next save ENV area address */ 102 | static uint32_t next_save_area_addr = 0; 103 | #endif 104 | 105 | static uint32_t get_env_start_addr(void); 106 | static uint32_t get_cur_using_data_addr(void); 107 | static uint32_t get_env_detail_addr(void); 108 | static uint32_t get_env_detail_end_addr(void); 109 | static void set_cur_using_data_addr(uint32_t using_data_addr); 110 | static void set_env_detail_end_addr(uint32_t end_addr); 111 | static EfErrCode write_env(const char *key, const char *value); 112 | static char *find_env(const char *key); 113 | static size_t get_env_detail_size(void); 114 | static size_t get_env_user_used_size(void); 115 | static EfErrCode create_env(const char *key, const char *value); 116 | static EfErrCode del_env(const char *key); 117 | static EfErrCode save_cur_using_data_addr(uint32_t cur_data_addr); 118 | static uint32_t calc_env_crc(void); 119 | static bool env_crc_is_ok(void); 120 | #ifdef EF_ENV_AUTO_UPDATE 121 | static EfErrCode env_auto_update(void); 122 | #endif 123 | 124 | /** 125 | * Flash ENV initialize. 126 | * 127 | * @param default_env default ENV set for user 128 | * @param default_env_size default ENV set size 129 | * 130 | * @return result 131 | */ 132 | EfErrCode ef_env_init(ef_env const *default_env, size_t default_env_size) { 133 | EfErrCode result = EF_NO_ERR; 134 | 135 | EF_ASSERT(ENV_AREA_SIZE); 136 | EF_ASSERT(ENV_USER_SETTING_SIZE); 137 | /* must be word alignment for ENV */ 138 | EF_ASSERT(ENV_USER_SETTING_SIZE % 4 == 0); 139 | EF_ASSERT(ENV_AREA_SIZE % 4 == 0); 140 | EF_ASSERT(default_env); 141 | EF_ASSERT(default_env_size < ENV_USER_SETTING_SIZE); 142 | 143 | #ifndef EF_ENV_USING_PFS_MODE 144 | /* system section size is erase_min_size, so last part is data section */ 145 | env_data_section_size = ENV_AREA_SIZE - EF_ERASE_MIN_SIZE; 146 | #else 147 | /* system section size is erase_min_size, so last part is data section */ 148 | env_data_section_size = ENV_AREA_SIZE / 2 - EF_ERASE_MIN_SIZE; 149 | EF_ASSERT((ENV_AREA_SIZE / EF_ERASE_MIN_SIZE) % 2 == 0); 150 | #endif 151 | EF_ASSERT(env_data_section_size >= ENV_USER_SETTING_SIZE); 152 | /* the ENV data section size should be an integral multiple of erase minimum size. */ 153 | EF_ASSERT(env_data_section_size % EF_ERASE_MIN_SIZE == 0); 154 | 155 | 156 | env_start_addr = EF_START_ADDR; 157 | default_env_set = default_env; 158 | default_env_set_size = default_env_size; 159 | 160 | EF_DEBUG("ENV start address is 0x%08X, size is %d bytes.\n", EF_START_ADDR, ENV_AREA_SIZE); 161 | 162 | result = ef_load_env(); 163 | 164 | #ifdef EF_ENV_AUTO_UPDATE 165 | if (result == EF_NO_ERR) { 166 | env_auto_update(); 167 | } 168 | #endif 169 | 170 | if (result == EF_NO_ERR) { 171 | init_ok = true; 172 | } 173 | 174 | return result; 175 | } 176 | 177 | /** 178 | * ENV set default. 179 | * 180 | * @return result 181 | */ 182 | EfErrCode ef_env_set_default(void) { 183 | EfErrCode result = EF_NO_ERR; 184 | size_t i; 185 | 186 | EF_ASSERT(default_env_set); 187 | EF_ASSERT(default_env_set_size); 188 | 189 | /* lock the ENV cache */ 190 | ef_port_env_lock(); 191 | 192 | /* set ENV detail part end address is at ENV detail part start address */ 193 | set_env_detail_end_addr(get_env_detail_addr()); 194 | 195 | #ifdef EF_ENV_USING_PFS_MODE 196 | /* set saved count to default 0 */ 197 | env_cache[ENV_PARAM_PART_INDEX_SAVED_COUNT] = 0; 198 | #endif 199 | 200 | #ifdef EF_ENV_AUTO_UPDATE 201 | /* initialize version number */ 202 | env_cache[ENV_PARAM_INDEX_VER_NUM] = EF_ENV_VER_NUM; 203 | #endif 204 | 205 | /* create default ENV */ 206 | for (i = 0; i < default_env_set_size; i++) { 207 | create_env(default_env_set[i].key, default_env_set[i].value); 208 | } 209 | 210 | /* unlock the ENV cache */ 211 | ef_port_env_unlock(); 212 | 213 | result = ef_save_env(); 214 | 215 | #ifdef EF_ENV_USING_PFS_MODE 216 | /* reset other PFS area's data */ 217 | if (result == EF_NO_ERR) { 218 | env_cache_changed = true; 219 | result = ef_save_env(); 220 | } 221 | #endif 222 | 223 | return result; 224 | } 225 | 226 | /** 227 | * Get ENV start address in flash. 228 | * 229 | * @return ENV start address in flash 230 | */ 231 | static uint32_t get_env_start_addr(void) { 232 | return env_start_addr; 233 | } 234 | /** 235 | * Get current using data section address. 236 | * 237 | * @return current using data section address 238 | */ 239 | static uint32_t get_cur_using_data_addr(void) { 240 | return cur_using_data_addr; 241 | } 242 | 243 | /** 244 | * Set current using data section address. 245 | * 246 | * @param using_data_addr current using data section address 247 | */ 248 | static void set_cur_using_data_addr(uint32_t using_data_addr) { 249 | cur_using_data_addr = using_data_addr; 250 | } 251 | 252 | /** 253 | * Get ENV detail part start address. 254 | * 255 | * @return detail part start address 256 | */ 257 | static uint32_t get_env_detail_addr(void) { 258 | return get_cur_using_data_addr() + ENV_PARAM_PART_BYTE_SIZE; 259 | } 260 | 261 | /** 262 | * Get ENV detail part end address. 263 | * It's the first word in ENV. 264 | * 265 | * @return ENV end address 266 | */ 267 | static uint32_t get_env_detail_end_addr(void) { 268 | /* it is the first word */ 269 | return env_cache[ENV_PARAM_PART_INDEX_END_ADDR]; 270 | } 271 | 272 | /** 273 | * Set ENV detail part end address. 274 | * It's the first word in ENV. 275 | * 276 | * @param end_addr ENV end address 277 | */ 278 | static void set_env_detail_end_addr(uint32_t end_addr) { 279 | env_cache[ENV_PARAM_PART_INDEX_END_ADDR] = end_addr; 280 | } 281 | 282 | /** 283 | * Get current ENV detail part size. 284 | * 285 | * @return size 286 | */ 287 | static size_t get_env_detail_size(void) { 288 | if (get_env_detail_end_addr() > get_env_detail_addr()) { 289 | return get_env_detail_end_addr() - get_env_detail_addr(); 290 | } else { 291 | return 0; 292 | } 293 | } 294 | 295 | /** 296 | * Get current user used ENV size. 297 | * 298 | * @see ENV_USER_SETTING_SIZE 299 | * 300 | * @return size 301 | */ 302 | /* must be initialized */ 303 | static size_t get_env_user_used_size(void) { 304 | if (get_env_detail_end_addr() > get_cur_using_data_addr()) { 305 | return get_env_detail_end_addr() - get_cur_using_data_addr(); 306 | } else { 307 | return 0; 308 | } 309 | } 310 | 311 | /** 312 | * Get current ENV already write bytes. 313 | * 314 | * @return write bytes 315 | */ 316 | size_t ef_get_env_write_bytes(void) { 317 | #ifndef EF_ENV_USING_PFS_MODE 318 | return get_env_detail_end_addr() - get_env_start_addr(); 319 | #else 320 | return EF_ERASE_MIN_SIZE + get_env_detail_end_addr() - get_cur_using_data_addr(); 321 | #endif 322 | } 323 | 324 | /** 325 | * Write an ENV at the end of cache. 326 | * 327 | * @param key ENV name 328 | * @param value ENV value 329 | * 330 | * @return result 331 | */ 332 | static EfErrCode write_env(const char *key, const char *value) { 333 | EfErrCode result = EF_NO_ERR; 334 | size_t ker_len = strlen(key), value_len = strlen(value), env_str_len; 335 | char *env_cache_bak = (char *)env_cache; 336 | 337 | /* calculate ENV storage length, contain '=' and '\0'. */ 338 | env_str_len = ker_len + value_len + 2; 339 | if (env_str_len % 4 != 0) { 340 | env_str_len = (env_str_len / 4 + 1) * 4; 341 | } 342 | /* check capacity of ENV */ 343 | if (env_str_len + get_env_user_used_size() >= ENV_USER_SETTING_SIZE) { 344 | return EF_ENV_FULL; 345 | } 346 | /* calculate current ENV ram cache end address */ 347 | env_cache_bak += ENV_PARAM_PART_BYTE_SIZE + get_env_detail_size(); 348 | /* copy key name */ 349 | memcpy(env_cache_bak, key, ker_len); 350 | env_cache_bak += ker_len; 351 | /* copy equal sign */ 352 | *env_cache_bak = '='; 353 | env_cache_bak++; 354 | /* copy value */ 355 | memcpy(env_cache_bak, value, value_len); 356 | env_cache_bak += value_len; 357 | /* fill '\0' for string end sign */ 358 | *env_cache_bak = '\0'; 359 | env_cache_bak ++; 360 | /* fill '\0' for word alignment */ 361 | memset(env_cache_bak, 0, env_str_len - (ker_len + value_len + 2)); 362 | set_env_detail_end_addr(get_env_detail_end_addr() + env_str_len); 363 | /* ENV ram cache has changed */ 364 | env_cache_changed = true; 365 | 366 | return result; 367 | } 368 | 369 | /** 370 | * Find ENV. 371 | * 372 | * @param key ENV name 373 | * 374 | * @return found ENV in ram cache 375 | */ 376 | static char *find_env(const char *key) { 377 | char *env_start, *env_end, *env, *found_env = NULL; 378 | size_t key_len = strlen(key), env_len; 379 | 380 | if ((key == NULL) || (*key == '\0')) { 381 | EF_INFO("Flash ENV name must be not empty!\n"); 382 | return NULL; 383 | } 384 | 385 | /* from data section start to data section end */ 386 | env_start = (char *) ((char *) env_cache + ENV_PARAM_PART_BYTE_SIZE); 387 | env_end = (char *) ((char *) env_cache + ENV_PARAM_PART_BYTE_SIZE + get_env_detail_size()); 388 | 389 | /* ENV is null */ 390 | if (env_start == env_end) { 391 | return NULL; 392 | } 393 | 394 | env = env_start; 395 | while (env < env_end) { 396 | /* the key length must be equal */ 397 | if (!strncmp(env, key, key_len) && (env[key_len] == '=')) { 398 | found_env = env; 399 | break; 400 | } else { 401 | /* calculate ENV length, contain '\0'. */ 402 | env_len = strlen(env) + 1; 403 | /* next ENV and word alignment */ 404 | if (env_len % 4 == 0) { 405 | env += env_len; 406 | } else { 407 | env += (env_len / 4 + 1) * 4; 408 | } 409 | } 410 | } 411 | 412 | return found_env; 413 | } 414 | 415 | /** 416 | * If the ENV is not exist, create it. 417 | * @see flash_write_env 418 | * 419 | * @param key ENV name 420 | * @param value ENV value 421 | * 422 | * @return result 423 | */ 424 | static EfErrCode create_env(const char *key, const char *value) { 425 | EfErrCode result = EF_NO_ERR; 426 | 427 | EF_ASSERT(key); 428 | EF_ASSERT(value); 429 | 430 | if ((key == NULL) || (*key == '\0')) { 431 | EF_INFO("Flash ENV name must be not empty!\n"); 432 | return EF_ENV_NAME_ERR; 433 | } 434 | 435 | if (strchr(key, '=')) { 436 | EF_INFO("Flash ENV name can't contain '='.\n"); 437 | return EF_ENV_NAME_ERR; 438 | } 439 | 440 | /* find ENV */ 441 | if (find_env(key)) { 442 | EF_INFO("The name of \"%s\" is already exist.\n", key); 443 | return EF_ENV_NAME_EXIST; 444 | } 445 | /* write ENV at the end of cache */ 446 | result = write_env(key, value); 447 | 448 | return result; 449 | } 450 | 451 | /** 452 | * Delete an ENV in cache. 453 | * 454 | * @param key ENV name 455 | * 456 | * @return result 457 | */ 458 | static EfErrCode del_env(const char *key) { 459 | EfErrCode result = EF_NO_ERR; 460 | char *del_env = NULL; 461 | size_t del_env_length, remain_env_length; 462 | 463 | EF_ASSERT(key); 464 | 465 | if ((key == NULL) || (*key == '\0')) { 466 | EF_INFO("Flash ENV name must be not NULL!\n"); 467 | return EF_ENV_NAME_ERR; 468 | } 469 | 470 | if (strchr(key, '=')) { 471 | EF_INFO("Flash ENV name or value can't contain '='.\n"); 472 | return EF_ENV_NAME_ERR; 473 | } 474 | 475 | /* find ENV */ 476 | del_env = find_env(key); 477 | 478 | if (!del_env) { 479 | EF_INFO("Not find \"%s\" in ENV.\n", key); 480 | return EF_ENV_NAME_ERR; 481 | } 482 | del_env_length = strlen(del_env); 483 | /* '\0' also must be as ENV length */ 484 | del_env_length ++; 485 | /* the address must multiple of 4 */ 486 | if (del_env_length % 4 != 0) { 487 | del_env_length = (del_env_length / 4 + 1) * 4; 488 | } 489 | /* calculate remain ENV length */ 490 | remain_env_length = get_env_detail_size() 491 | - (((uint32_t) del_env + del_env_length) - ((uint32_t) env_cache + ENV_PARAM_PART_BYTE_SIZE)); 492 | /* remain ENV move forward */ 493 | memcpy(del_env, del_env + del_env_length, remain_env_length); 494 | /* reset ENV end address */ 495 | set_env_detail_end_addr(get_env_detail_end_addr() - del_env_length); 496 | /* ENV ram cache has changed */ 497 | env_cache_changed = true; 498 | 499 | return result; 500 | } 501 | 502 | /** 503 | * Set an ENV.If it value is NULL, delete it. 504 | * If not find it in ENV table, then create it. 505 | * 506 | * @param key ENV name 507 | * @param value ENV value 508 | * 509 | * @return result 510 | */ 511 | EfErrCode ef_set_env(const char *key, const char *value) { 512 | EfErrCode result = EF_NO_ERR; 513 | char *old_env, *old_value; 514 | 515 | if (!init_ok) { 516 | EF_INFO("ENV isn't initialize OK.\n"); 517 | return EF_ENV_INIT_FAILED; 518 | } 519 | 520 | /* lock the ENV cache */ 521 | ef_port_env_lock(); 522 | 523 | /* if ENV value is NULL, delete it */ 524 | if (value == NULL) { 525 | result = del_env(key); 526 | } else { 527 | old_env = find_env(key); 528 | /* If find this ENV, then compare the new value and old value. */ 529 | if (old_env) { 530 | /* find the old value address */ 531 | old_env = strchr(old_env, '='); 532 | old_value = old_env + 1; 533 | /* If it is changed then delete it and recreate it */ 534 | if (strcmp(old_value, value)) { 535 | result = del_env(key); 536 | if (result == EF_NO_ERR) { 537 | result = create_env(key, value); 538 | } 539 | } 540 | } else { 541 | result = create_env(key, value); 542 | } 543 | } 544 | /* unlock the ENV cache */ 545 | ef_port_env_unlock(); 546 | 547 | return result; 548 | } 549 | 550 | /** 551 | * Del an ENV. 552 | * 553 | * @param key ENV name 554 | * 555 | * @return result 556 | */ 557 | EfErrCode ef_del_env(const char *key) { 558 | EfErrCode result = EF_NO_ERR; 559 | 560 | if (!init_ok) { 561 | EF_INFO("ENV isn't initialize OK.\n"); 562 | return EF_ENV_INIT_FAILED; 563 | } 564 | 565 | /* lock the ENV cache */ 566 | ef_port_env_lock(); 567 | 568 | result = del_env(key); 569 | 570 | /* unlock the ENV cache */ 571 | ef_port_env_unlock(); 572 | 573 | return result; 574 | } 575 | 576 | /** 577 | * Get an ENV value by key name. 578 | * 579 | * @param key ENV name 580 | * 581 | * @return value 582 | */ 583 | char *ef_get_env(const char *key) { 584 | char *env = NULL, *value = NULL; 585 | 586 | if (!init_ok) { 587 | EF_INFO("ENV isn't initialize OK.\n"); 588 | return NULL; 589 | } 590 | 591 | /* find ENV */ 592 | env = find_env(key); 593 | 594 | if (env == NULL) { 595 | return NULL; 596 | } 597 | /* get value address */ 598 | value = strchr(env, '='); 599 | if (value != NULL) { 600 | /* the equal sign next character is value */ 601 | value++; 602 | } 603 | return value; 604 | } 605 | /** 606 | * Print ENV. 607 | */ 608 | void ef_print_env(void) { 609 | uint32_t *env_cache_detail_addr = env_cache + ENV_PARAM_PART_WORD_SIZE, *env_cache_end_addr = 610 | (uint32_t *) (env_cache + ENV_PARAM_PART_WORD_SIZE + get_env_detail_size() / 4); 611 | uint8_t j; 612 | char c; 613 | 614 | if (!init_ok) { 615 | EF_INFO("ENV isn't initialize OK.\n"); 616 | return; 617 | } 618 | 619 | for (; env_cache_detail_addr < env_cache_end_addr; env_cache_detail_addr += 1) { 620 | for (j = 0; j < 4; j++) { 621 | c = (*env_cache_detail_addr) >> (8 * j); 622 | ef_print("%c", c); 623 | if (c == '\0') { 624 | ef_print("\n"); 625 | break; 626 | } 627 | } 628 | } 629 | 630 | #ifndef EF_ENV_USING_PFS_MODE 631 | ef_print("\nmode: wear leveling\n"); 632 | ef_print("size: %ld/%ld bytes, write bytes %ld/%ld.\n", get_env_user_used_size(), ENV_USER_SETTING_SIZE, 633 | ef_get_env_write_bytes(), ENV_AREA_SIZE); 634 | #else 635 | ef_print("\nmode: wear leveling and power fail safeguard\n"); 636 | ef_print("size: %ld/%ld bytes, write bytes %ld/%ld.\n", get_env_user_used_size(), ENV_USER_SETTING_SIZE, 637 | ef_get_env_write_bytes(), ENV_AREA_SIZE / 2); 638 | ef_print("saved count: %ld\n", env_cache[ENV_PARAM_PART_INDEX_SAVED_COUNT]); 639 | #endif 640 | 641 | #ifdef EF_ENV_AUTO_UPDATE 642 | ef_print("ver num: %d\n", env_cache[ENV_PARAM_INDEX_VER_NUM]); 643 | #endif 644 | } 645 | 646 | /** 647 | * Load flash ENV to ram. 648 | * 649 | * @return result 650 | */ 651 | #ifndef EF_ENV_USING_PFS_MODE 652 | EfErrCode ef_load_env(void) { 653 | EfErrCode result = EF_NO_ERR; 654 | uint32_t *env_cache_bak, env_end_addr, using_data_addr; 655 | 656 | /* read current using data section address */ 657 | ef_port_read(get_env_start_addr(), &using_data_addr, 4); 658 | /* if ENV is not initialize or flash has dirty data, set default for it */ 659 | if ((using_data_addr == 0xFFFFFFFF) 660 | || (using_data_addr > get_env_start_addr() + ENV_AREA_SIZE) 661 | || (using_data_addr < get_env_start_addr() + EF_ERASE_MIN_SIZE)) { 662 | /* initialize current using data section address */ 663 | set_cur_using_data_addr(get_env_start_addr() + EF_ERASE_MIN_SIZE); 664 | /* save current using data section address to flash*/ 665 | if ((result = save_cur_using_data_addr(get_cur_using_data_addr())) == EF_NO_ERR) { 666 | /* set default ENV */ 667 | result = ef_env_set_default(); 668 | } 669 | } else { 670 | /* set current using data section address */ 671 | set_cur_using_data_addr(using_data_addr); 672 | /* read ENV detail part end address from flash */ 673 | ef_port_read(get_cur_using_data_addr() + ENV_PARAM_PART_INDEX_END_ADDR * 4, &env_end_addr, 4); 674 | /* if ENV end address has error, set default for ENV */ 675 | if (env_end_addr > get_env_start_addr() + ENV_AREA_SIZE) { 676 | /* initialize current using data section address */ 677 | set_cur_using_data_addr(get_env_start_addr() + EF_ERASE_MIN_SIZE); 678 | /* save current using data section address to flash*/ 679 | if ((result = save_cur_using_data_addr(get_cur_using_data_addr())) == EF_NO_ERR) { 680 | EF_INFO("Warning: ENV end address has error. Set it to default.\n"); 681 | result = ef_env_set_default(); 682 | } 683 | } else { 684 | /* set ENV detail part end address */ 685 | set_env_detail_end_addr(env_end_addr); 686 | 687 | env_cache_bak = env_cache + ENV_PARAM_PART_WORD_SIZE; 688 | /* read all ENV from flash */ 689 | ef_port_read(get_env_detail_addr(), env_cache_bak, get_env_detail_size()); 690 | /* read ENV CRC code from flash */ 691 | ef_port_read(get_cur_using_data_addr() + ENV_PARAM_PART_INDEX_DATA_CRC * 4, 692 | &env_cache[ENV_PARAM_PART_INDEX_DATA_CRC], 4); 693 | /* if ENV CRC32 check is fault, set default for it */ 694 | if (!env_crc_is_ok()) { 695 | EF_INFO("Warning: ENV CRC check failed. Set it to default.\n"); 696 | result = ef_env_set_default(); 697 | } 698 | } 699 | 700 | } 701 | return result; 702 | } 703 | #else 704 | EfErrCode ef_load_env(void) { 705 | EfErrCode result = EF_NO_ERR; 706 | /* ENV area0 current using address default value */ 707 | uint32_t area0_default_cur_using_addr = get_env_start_addr() + EF_ERASE_MIN_SIZE; 708 | /* ENV area1 current using address default value */ 709 | uint32_t area1_default_cur_using_addr = area0_default_cur_using_addr + ENV_AREA_SIZE / 2; 710 | uint32_t area0_cur_using_addr, area1_cur_using_addr, area0_end_addr, area1_end_addr; 711 | uint32_t area0_crc, area1_crc, area0_saved_count, area1_saved_count; 712 | bool area0_is_valid = true, area1_is_valid = true; 713 | 714 | /* read ENV area0 and area1 current using data section address */ 715 | ef_port_read(get_env_start_addr(), &area0_cur_using_addr, 4); 716 | ef_port_read(get_env_start_addr() + ENV_AREA_SIZE / 2, &area1_cur_using_addr, 4); 717 | /* if ENV is not initialize or flash has dirty data, set it isn't valid */ 718 | if ((area0_cur_using_addr == 0xFFFFFFFF) 719 | || (area0_cur_using_addr > get_env_start_addr() + ENV_AREA_SIZE / 2) 720 | || (area0_cur_using_addr < get_env_start_addr() + EF_ERASE_MIN_SIZE)) { 721 | area0_is_valid = false; 722 | } 723 | if ((area1_cur_using_addr == 0xFFFFFFFF) 724 | || (area1_cur_using_addr > get_env_start_addr() + ENV_AREA_SIZE) 725 | || (area1_cur_using_addr < get_env_start_addr() + ENV_AREA_SIZE / 2 + EF_ERASE_MIN_SIZE)) { 726 | area1_is_valid = false; 727 | } 728 | /* check area0 end address when it is valid */ 729 | if (area0_is_valid) { 730 | /* read ENV area end address from flash */ 731 | ef_port_read(area0_cur_using_addr + ENV_PARAM_PART_INDEX_END_ADDR * 4, &area0_end_addr, 4); 732 | if ((area0_end_addr == 0xFFFFFFFF) || (area0_end_addr < area0_cur_using_addr) 733 | || (area0_end_addr > area0_cur_using_addr + ENV_USER_SETTING_SIZE)) { 734 | area0_is_valid = false; 735 | } 736 | } 737 | /* check area1 end address when it is valid */ 738 | if (area1_is_valid) { 739 | /* read ENV area end address from flash */ 740 | ef_port_read(area1_cur_using_addr + ENV_PARAM_PART_INDEX_END_ADDR * 4, &area1_end_addr, 4); 741 | if ((area1_end_addr == 0xFFFFFFFF) || (area1_end_addr < area1_cur_using_addr) 742 | || (area1_end_addr > area1_cur_using_addr + ENV_USER_SETTING_SIZE)) { 743 | area1_is_valid = false; 744 | } 745 | } 746 | /* check area0 CRC when it is valid */ 747 | if (area0_is_valid) { 748 | /* read ENV area0 crc32 code from flash */ 749 | ef_port_read(area0_cur_using_addr + ENV_PARAM_PART_INDEX_DATA_CRC * 4, &area0_crc, 4); 750 | /* read ENV from ENV area0 */ 751 | ef_port_read(area0_cur_using_addr, env_cache, area0_end_addr - area0_cur_using_addr); 752 | /* current using data section address is area0 current using data section address */ 753 | set_cur_using_data_addr(area0_cur_using_addr); 754 | if (!env_crc_is_ok()) { 755 | area0_is_valid = false; 756 | } 757 | } 758 | /* check area1 CRC when it is valid */ 759 | if (area1_is_valid) { 760 | /* read ENV area1 crc32 code from flash */ 761 | ef_port_read(area1_cur_using_addr + ENV_PARAM_PART_INDEX_DATA_CRC * 4, &area1_crc, 4); 762 | /* read ENV from ENV area1 */ 763 | ef_port_read(area1_cur_using_addr, env_cache, area1_end_addr - area1_cur_using_addr); 764 | /* current using data section address is area1 current using data section address */ 765 | set_cur_using_data_addr(area1_cur_using_addr); 766 | if (!env_crc_is_ok()) { 767 | area1_is_valid = false; 768 | } 769 | } 770 | /* all ENV area CRC is OK then compare saved count */ 771 | if (area0_is_valid && area1_is_valid) { 772 | /* read ENV area saved count from flash */ 773 | ef_port_read(area0_cur_using_addr + ENV_PARAM_PART_INDEX_SAVED_COUNT * 4, 774 | &area0_saved_count, 4); 775 | ef_port_read(area1_cur_using_addr + ENV_PARAM_PART_INDEX_SAVED_COUNT * 4, 776 | &area1_saved_count, 4); 777 | /* the bigger saved count area is valid */ 778 | if ((area0_saved_count > area1_saved_count) || ((area0_saved_count == 0) && (area1_saved_count == 0xFFFFFFFF))) { 779 | area1_is_valid = false; 780 | } else { 781 | area0_is_valid = false; 782 | } 783 | } 784 | if (area0_is_valid) { 785 | /* current using data section address is area0 current using data section address */ 786 | set_cur_using_data_addr(area0_cur_using_addr); 787 | /* next save ENV area address is area1 current using address value */ 788 | next_save_area_addr = area1_cur_using_addr; 789 | /* read all ENV from area0 */ 790 | ef_port_read(area0_cur_using_addr, env_cache, area0_end_addr - area0_cur_using_addr); 791 | } else if (area1_is_valid) { 792 | /* already read data section and set_cur_using_data_addr above current code, 793 | * so just set next save ENV area address is area0 current using address value */ 794 | next_save_area_addr = area0_cur_using_addr; 795 | } else { 796 | /* current using data section address is area1 current using address default value */ 797 | set_cur_using_data_addr(area1_default_cur_using_addr); 798 | /* next save ENV area address default is area0 current using address default value */ 799 | next_save_area_addr = area0_default_cur_using_addr; 800 | /* save current using data section address to flash*/ 801 | if (((result = save_cur_using_data_addr(area0_default_cur_using_addr)) == EF_NO_ERR) 802 | && ((result = save_cur_using_data_addr(area1_default_cur_using_addr)) == EF_NO_ERR)) { 803 | /* set the ENV to default */ 804 | result = ef_env_set_default(); 805 | } 806 | } 807 | return result; 808 | } 809 | #endif 810 | 811 | /** 812 | * Save ENV to flash. 813 | */ 814 | EfErrCode ef_save_env(void) { 815 | EfErrCode result = EF_NO_ERR; 816 | uint32_t cur_using_addr_bak, move_offset_addr; 817 | size_t env_used_size = get_env_user_used_size(); 818 | uint32_t data_sec_end_addr; 819 | 820 | /* ENV ram cache has not changed don't need to save */ 821 | if (!env_cache_changed) { 822 | return result; 823 | } 824 | 825 | #ifndef EF_ENV_USING_PFS_MODE 826 | data_sec_end_addr = get_env_start_addr() + ENV_AREA_SIZE - 4; 827 | cur_using_addr_bak = get_cur_using_data_addr(); 828 | #else 829 | cur_using_addr_bak = next_save_area_addr; 830 | /* replace next_save_area_addr with cur_using_data_addr */ 831 | next_save_area_addr = get_cur_using_data_addr(); 832 | set_cur_using_data_addr(cur_using_addr_bak); 833 | /* change the ENV detail end address to next save area address */ 834 | set_env_detail_end_addr(get_cur_using_data_addr() + env_used_size); 835 | /* area0 or area1 */ 836 | if (get_cur_using_data_addr() < get_env_start_addr() + ENV_AREA_SIZE / 2) { 837 | data_sec_end_addr = get_env_start_addr() + ENV_AREA_SIZE / 2 - 4; 838 | } else { 839 | data_sec_end_addr = get_env_start_addr() + ENV_AREA_SIZE - 4; 840 | } 841 | /* ENV area saved count +1 */ 842 | env_cache[ENV_PARAM_PART_INDEX_SAVED_COUNT]++; 843 | #endif 844 | 845 | /* wear leveling process, automatic move ENV to next available position */ 846 | while (get_cur_using_data_addr() + env_used_size < data_sec_end_addr) { 847 | /* calculate and cache CRC32 code */ 848 | env_cache[ENV_PARAM_PART_INDEX_DATA_CRC] = calc_env_crc(); 849 | /* erase ENV */ 850 | result = ef_port_erase(get_cur_using_data_addr(), env_used_size); 851 | switch (result) { 852 | case EF_NO_ERR: { 853 | EF_DEBUG("Erased ENV OK.\n"); 854 | break; 855 | } 856 | case EF_ERASE_ERR: { 857 | EF_INFO("Warning: Erased ENV fault! Start address is 0x%08X, size is %ld.\n", 858 | get_cur_using_data_addr(), env_used_size); 859 | EF_INFO("Moving ENV to next available position.\n"); 860 | /* Calculate move offset address. 861 | * Current strategy is optimistic. It will offset the flash erasure minimum size. 862 | */ 863 | move_offset_addr = EF_ERASE_MIN_SIZE; 864 | /* calculate and set next available data section address */ 865 | set_cur_using_data_addr(get_cur_using_data_addr() + move_offset_addr); 866 | /* calculate and set next available ENV detail part end address */ 867 | set_env_detail_end_addr(get_env_detail_end_addr() + move_offset_addr); 868 | continue; 869 | } 870 | } 871 | /* write ENV to flash */ 872 | result = ef_port_write(get_cur_using_data_addr(), env_cache, env_used_size); 873 | switch (result) { 874 | case EF_NO_ERR: { 875 | EF_DEBUG("Saved ENV OK.\n"); 876 | break; 877 | } 878 | case EF_WRITE_ERR: { 879 | EF_INFO("Warning: Saved ENV fault! Start address is 0x%08X, size is %ld.\n", 880 | get_cur_using_data_addr(), env_used_size); 881 | EF_INFO("Moving ENV to next available position.\n"); 882 | /* Calculate move offset address. 883 | * Current strategy is optimistic. It will offset the flash erasure minimum size. 884 | */ 885 | move_offset_addr = EF_ERASE_MIN_SIZE; 886 | /* calculate and set next available data section address */ 887 | set_cur_using_data_addr(get_cur_using_data_addr() + move_offset_addr); 888 | /* calculate and set next available ENV detail part end address */ 889 | set_env_detail_end_addr(get_env_detail_end_addr() + move_offset_addr); 890 | continue; 891 | } 892 | } 893 | /* save ENV success */ 894 | if (result == EF_NO_ERR) { 895 | break; 896 | } 897 | } 898 | 899 | if (get_cur_using_data_addr() + env_used_size < data_sec_end_addr) { 900 | /* current using data section address has changed, save it */ 901 | if (get_cur_using_data_addr() != cur_using_addr_bak) { 902 | result = save_cur_using_data_addr(get_cur_using_data_addr()); 903 | } 904 | } else { 905 | result = EF_ENV_FULL; 906 | EF_INFO("Error: The flash has no available space to save ENV.\n"); 907 | } 908 | 909 | env_cache_changed = false; 910 | 911 | return result; 912 | } 913 | 914 | /** 915 | * Calculate the cached ENV CRC32 value. 916 | * 917 | * @return CRC32 value 918 | */ 919 | static uint32_t calc_env_crc(void) { 920 | uint32_t crc32 = 0; 921 | 922 | /* Calculate the ENV end address and all ENV data CRC32. 923 | * The 4 is ENV end address bytes size. */ 924 | crc32 = ef_calc_crc32(crc32, &env_cache[ENV_PARAM_PART_INDEX_END_ADDR], 4); 925 | crc32 = ef_calc_crc32(crc32, &env_cache[ENV_PARAM_PART_WORD_SIZE], get_env_detail_size()); 926 | EF_DEBUG("Calculate ENV CRC32 number is 0x%08X.\n", crc32); 927 | 928 | return crc32; 929 | } 930 | 931 | /** 932 | * Check the ENV CRC32 933 | * 934 | * @return true is ok 935 | */ 936 | static bool env_crc_is_ok(void) { 937 | if (calc_env_crc() == env_cache[ENV_PARAM_PART_INDEX_DATA_CRC]) { 938 | EF_DEBUG("Verify ENV CRC32 result is OK.\n"); 939 | return true; 940 | } else { 941 | return false; 942 | } 943 | } 944 | 945 | /** 946 | * Save current using data section address to flash. 947 | * 948 | * @param cur_data_addr current using data section address 949 | * 950 | * @return result 951 | */ 952 | #ifndef EF_ENV_USING_PFS_MODE 953 | static EfErrCode save_cur_using_data_addr(uint32_t cur_data_addr) { 954 | EfErrCode result = EF_NO_ERR; 955 | 956 | /* erase ENV system section */ 957 | result = ef_port_erase(get_env_start_addr(), 4); 958 | if (result == EF_NO_ERR) { 959 | /* write current using data section address to flash */ 960 | result = ef_port_write(get_env_start_addr(), &cur_data_addr, 4); 961 | if (result == EF_WRITE_ERR) { 962 | EF_INFO("Error: Write system section fault! Start address is 0x%08X, size is %ld.\n", 963 | get_env_start_addr(), 4); 964 | EF_INFO("Note: The ENV can not be used.\n"); 965 | } 966 | } else { 967 | EF_INFO("Error: Erased system section fault! Start address is 0x%08X, size is %ld.\n", 968 | get_env_start_addr(), 4); 969 | EF_INFO("Note: The ENV can not be used\n"); 970 | } 971 | return result; 972 | } 973 | #else 974 | static EfErrCode save_cur_using_data_addr(uint32_t cur_data_addr) { 975 | EfErrCode result = EF_NO_ERR; 976 | uint32_t cur_system_sec_addr; 977 | 978 | if (cur_data_addr < get_env_start_addr() + ENV_AREA_SIZE / 2) { 979 | /* current using system section is in ENV area0 */ 980 | cur_system_sec_addr = get_env_start_addr(); 981 | } else { 982 | /* current using system section is in ENV area1 */ 983 | cur_system_sec_addr = get_env_start_addr() + ENV_AREA_SIZE / 2; 984 | } 985 | /* erase ENV system section */ 986 | result = ef_port_erase(cur_system_sec_addr, 4); 987 | if (result == EF_NO_ERR) { 988 | /* write area0 and area1 current using data section address to flash */ 989 | result = ef_port_write(cur_system_sec_addr, &cur_data_addr, 4); 990 | if (result == EF_WRITE_ERR) { 991 | EF_INFO("Error: Write system section fault! Start address is 0x%08X, size is %ld.\n", 992 | cur_system_sec_addr, 4); 993 | EF_INFO("Note: The ENV can not be used.\n"); 994 | } 995 | } else { 996 | EF_INFO("Error: Erased system section fault! Start address is 0x%08X, size is %ld.\n", 997 | cur_system_sec_addr, 4); 998 | EF_INFO("Note: The ENV can not be used\n"); 999 | } 1000 | return result; 1001 | } 1002 | #endif 1003 | 1004 | /** 1005 | * Set and save an ENV. If set ENV is success then will save it. 1006 | * 1007 | * @param key ENV name 1008 | * @param value ENV value 1009 | * 1010 | * @return result 1011 | */ 1012 | EfErrCode ef_set_and_save_env(const char *key, const char *value) { 1013 | EfErrCode result = EF_NO_ERR; 1014 | 1015 | result = ef_set_env(key, value); 1016 | 1017 | if (result == EF_NO_ERR) { 1018 | result = ef_save_env(); 1019 | } 1020 | 1021 | return result; 1022 | } 1023 | 1024 | /** 1025 | * Del and save an ENV. If del ENV is success then will save it. 1026 | * 1027 | * @param key ENV name 1028 | * 1029 | * @return result 1030 | */ 1031 | EfErrCode ef_del_and_save_env(const char *key) { 1032 | EfErrCode result = EF_NO_ERR; 1033 | 1034 | result = ef_del_env(key); 1035 | 1036 | if (result == EF_NO_ERR) { 1037 | result = ef_save_env(); 1038 | } 1039 | 1040 | return result; 1041 | } 1042 | 1043 | #ifdef EF_ENV_AUTO_UPDATE 1044 | /** 1045 | * Auto update ENV to latest default when current EF_ENV_VER is changed. 1046 | * 1047 | * @return result 1048 | */ 1049 | static EfErrCode env_auto_update(void) 1050 | { 1051 | size_t i; 1052 | 1053 | /* lock the ENV cache */ 1054 | ef_port_env_lock(); 1055 | 1056 | /* read ENV version number from flash*/ 1057 | ef_port_read(get_cur_using_data_addr() + ENV_PARAM_INDEX_VER_NUM * 4, 1058 | &env_cache[ENV_PARAM_INDEX_VER_NUM] , 4); 1059 | 1060 | /* check version number */ 1061 | if (env_cache[ENV_PARAM_INDEX_VER_NUM] != EF_ENV_VER_NUM) { 1062 | env_cache_changed = true; 1063 | /* update version number */ 1064 | env_cache[ENV_PARAM_INDEX_VER_NUM] = EF_ENV_VER_NUM; 1065 | /* add a new ENV when it's not found */ 1066 | for (i = 0; i < default_env_set_size; i++) { 1067 | if (find_env(default_env_set[i].key) == NULL) { 1068 | create_env(default_env_set[i].key, default_env_set[i].value); 1069 | } 1070 | } 1071 | } 1072 | 1073 | /* unlock the ENV cache */ 1074 | ef_port_env_unlock(); 1075 | 1076 | return ef_save_env(); 1077 | } 1078 | #endif /* EF_ENV_AUTO_UPDATE */ 1079 | 1080 | #endif /* EF_ENV_USING_WL_MODE */ 1081 | 1082 | #endif /* defined(EF_USING_ENV) && defined(EF_ENV_USING_LEGACY_MODE) */ 1083 | -------------------------------------------------------------------------------- /src/ef_iap.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the EasyFlash Library. 3 | * 4 | * Copyright (c) 2015-2017, Armink, 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files (the 8 | * 'Software'), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | * Function: IAP(In-Application Programming) operating interface. 26 | * Created on: 2015-01-05 27 | */ 28 | 29 | #include 30 | 31 | #ifdef EF_USING_IAP 32 | 33 | /* IAP section backup application section start address in flash */ 34 | static uint32_t bak_app_start_addr = 0; 35 | 36 | /** 37 | * Flash IAP function initialize. 38 | * 39 | * @return result 40 | */ 41 | EfErrCode ef_iap_init(void) { 42 | EfErrCode result = EF_NO_ERR; 43 | 44 | bak_app_start_addr = EF_START_ADDR ; 45 | 46 | #if defined(EF_USING_ENV) 47 | bak_app_start_addr += ENV_AREA_SIZE; 48 | #endif 49 | 50 | #if defined(EF_USING_LOG) 51 | bak_app_start_addr += LOG_AREA_SIZE; 52 | #endif 53 | 54 | return result; 55 | } 56 | 57 | /** 58 | * Erase backup area application data. 59 | * 60 | * @param app_size application size 61 | * 62 | * @return result 63 | */ 64 | EfErrCode ef_erase_bak_app(size_t app_size) { 65 | EfErrCode result = EF_NO_ERR; 66 | 67 | result = ef_port_erase(ef_get_bak_app_start_addr(), app_size); 68 | switch (result) { 69 | case EF_NO_ERR: { 70 | EF_INFO("Erased backup area application OK.\n"); 71 | break; 72 | } 73 | case EF_ERASE_ERR: { 74 | EF_INFO("Warning: Erase backup area application fault!\n"); 75 | /* will return when erase fault */ 76 | return result; 77 | } 78 | } 79 | 80 | return result; 81 | } 82 | 83 | /** 84 | * Erase user old application by using specified erase function. 85 | * 86 | * @param user_app_addr application entry address 87 | * @param app_size application size 88 | * @param app_erase user specified application erase function 89 | * 90 | * @return result 91 | */ 92 | EfErrCode ef_erase_spec_user_app(uint32_t user_app_addr, size_t app_size, 93 | EfErrCode (*app_erase)(uint32_t addr, size_t size)) { 94 | EfErrCode result = EF_NO_ERR; 95 | 96 | result = app_erase(user_app_addr, app_size); 97 | switch (result) { 98 | case EF_NO_ERR: { 99 | EF_INFO("Erased user application OK.\n"); 100 | break; 101 | } 102 | case EF_ERASE_ERR: { 103 | EF_INFO("Warning: Erase user application fault!\n"); 104 | /* will return when erase fault */ 105 | return result; 106 | } 107 | } 108 | 109 | return result; 110 | } 111 | 112 | /** 113 | * Erase user old application by using default `ef_port_erase` function. 114 | * 115 | * @param user_app_addr application entry address 116 | * @param app_size application size 117 | * 118 | * @return result 119 | */ 120 | EfErrCode ef_erase_user_app(uint32_t user_app_addr, size_t app_size) { 121 | return ef_erase_spec_user_app(user_app_addr, app_size, ef_port_erase); 122 | } 123 | 124 | /** 125 | * Erase old bootloader 126 | * 127 | * @param bl_addr bootloader entry address 128 | * @param bl_size bootloader size 129 | * 130 | * @return result 131 | */ 132 | EfErrCode ef_erase_bl(uint32_t bl_addr, size_t bl_size) { 133 | EfErrCode result = EF_NO_ERR; 134 | 135 | result = ef_port_erase(bl_addr, bl_size); 136 | switch (result) { 137 | case EF_NO_ERR: { 138 | EF_INFO("Erased bootloader OK.\n"); 139 | break; 140 | } 141 | case EF_ERASE_ERR: { 142 | EF_INFO("Warning: Erase bootloader fault!\n"); 143 | /* will return when erase fault */ 144 | return result; 145 | } 146 | } 147 | 148 | return result; 149 | } 150 | 151 | /** 152 | * Write data of application to backup area. 153 | * 154 | * @param data a part of application 155 | * @param size data size 156 | * @param cur_size current write application size 157 | * @param total_size application total size 158 | * 159 | * @return result 160 | */ 161 | EfErrCode ef_write_data_to_bak(uint8_t *data, size_t size, size_t *cur_size, 162 | size_t total_size) { 163 | EfErrCode result = EF_NO_ERR; 164 | 165 | /* make sure don't write excess data */ 166 | if (*cur_size + size > total_size) { 167 | size = total_size - *cur_size; 168 | } 169 | 170 | result = ef_port_write(ef_get_bak_app_start_addr() + *cur_size, (uint32_t *) data, size); 171 | switch (result) { 172 | case EF_NO_ERR: { 173 | *cur_size += size; 174 | EF_DEBUG("Write data to backup area OK.\n"); 175 | break; 176 | } 177 | case EF_WRITE_ERR: { 178 | EF_INFO("Warning: Write data to backup area fault!\n"); 179 | break; 180 | } 181 | } 182 | 183 | return result; 184 | } 185 | 186 | /** 187 | * Copy backup area application to application entry by using specified write function. 188 | * 189 | * @param user_app_addr application entry address 190 | * @param app_size application size 191 | * @param app_write user specified application write function 192 | * 193 | * @return result 194 | */ 195 | EfErrCode ef_copy_spec_app_from_bak(uint32_t user_app_addr, size_t app_size, 196 | EfErrCode (*app_write)(uint32_t addr, const uint32_t *buf, size_t size)) { 197 | size_t cur_size; 198 | uint32_t app_cur_addr, bak_cur_addr; 199 | EfErrCode result = EF_NO_ERR; 200 | /* 32 words size buffer */ 201 | uint32_t buff[32]; 202 | 203 | /* cycle copy data */ 204 | for (cur_size = 0; cur_size < app_size; cur_size += sizeof(buff)) { 205 | app_cur_addr = user_app_addr + cur_size; 206 | bak_cur_addr = ef_get_bak_app_start_addr() + cur_size; 207 | ef_port_read(bak_cur_addr, buff, sizeof(buff)); 208 | result = app_write(app_cur_addr, buff, sizeof(buff)); 209 | if (result != EF_NO_ERR) { 210 | break; 211 | } 212 | } 213 | 214 | switch (result) { 215 | case EF_NO_ERR: { 216 | EF_INFO("Write data to application entry OK.\n"); 217 | break; 218 | } 219 | case EF_WRITE_ERR: { 220 | EF_INFO("Warning: Write data to application entry fault!\n"); 221 | break; 222 | } 223 | } 224 | 225 | return result; 226 | } 227 | 228 | /** 229 | * Copy backup area application to application entry by using default `ef_port_write` function. 230 | * 231 | * @param user_app_addr application entry address 232 | * @param app_size application size 233 | * 234 | * @return result 235 | */ 236 | EfErrCode ef_copy_app_from_bak(uint32_t user_app_addr, size_t app_size) { 237 | return ef_copy_spec_app_from_bak(user_app_addr, app_size, ef_port_write); 238 | } 239 | 240 | /** 241 | * Copy backup area bootloader to bootloader entry. 242 | * 243 | * @param bl_addr bootloader entry address 244 | * @param bl_size bootloader size 245 | * 246 | * @return result 247 | */ 248 | EfErrCode ef_copy_bl_from_bak(uint32_t bl_addr, size_t bl_size) { 249 | size_t cur_size; 250 | uint32_t bl_cur_addr, bak_cur_addr; 251 | EfErrCode result = EF_NO_ERR; 252 | /* 32 words buffer */ 253 | uint32_t buff[32]; 254 | 255 | /* cycle copy data by 32bytes buffer */ 256 | for (cur_size = 0; cur_size < bl_size; cur_size += sizeof(buff)) { 257 | bl_cur_addr = bl_addr + cur_size; 258 | bak_cur_addr = ef_get_bak_app_start_addr() + cur_size; 259 | ef_port_read(bak_cur_addr, buff, sizeof(buff)); 260 | result = ef_port_write(bl_cur_addr, buff, sizeof(buff)); 261 | if (result != EF_NO_ERR) { 262 | break; 263 | } 264 | } 265 | 266 | switch (result) { 267 | case EF_NO_ERR: { 268 | EF_INFO("Write data to bootloader entry OK.\n"); 269 | break; 270 | } 271 | case EF_WRITE_ERR: { 272 | EF_INFO("Warning: Write data to bootloader entry fault!\n"); 273 | break; 274 | } 275 | } 276 | 277 | return result; 278 | } 279 | 280 | /** 281 | * Get IAP section start address in flash. 282 | * 283 | * @return size 284 | */ 285 | uint32_t ef_get_bak_app_start_addr(void) { 286 | return bak_app_start_addr; 287 | } 288 | 289 | #endif /* EF_USING_IAP */ 290 | -------------------------------------------------------------------------------- /src/ef_log.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the EasyFlash Library. 3 | * 4 | * Copyright (c) 2015-2019, Armink, 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files (the 8 | * 'Software'), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | * Function: Save logs to flash. 26 | * Created on: 2015-06-04 27 | */ 28 | 29 | #include 30 | 31 | #ifdef EF_USING_LOG 32 | 33 | #if defined(EF_USING_LOG) && !defined(LOG_AREA_SIZE) 34 | #error "Please configure log area size (in ef_cfg.h)" 35 | #endif 36 | 37 | /* magic code on every sector header. 'EF' is 0xEF30EF30 */ 38 | #define LOG_SECTOR_MAGIC 0xEF30EF30 39 | /* sector header size, includes the sector magic code and status magic code */ 40 | #define LOG_SECTOR_HEADER_SIZE 12 41 | /* sector header word size,what is equivalent to the total number of sectors header index */ 42 | #define LOG_SECTOR_HEADER_WORD_SIZE 3 43 | 44 | /** 45 | * Sector status magic code 46 | * The sector status is 8B after LOG_SECTOR_MAGIC at every sector header. 47 | * ============================================== 48 | * | header(12B) | status | 49 | * ---------------------------------------------- 50 | * | 0xEF30EF30 0xFFFFFFFF 0xFFFFFFFF | empty | 51 | * | 0xEF30EF30 0xFEFEFEFE 0xFFFFFFFF | using | 52 | * | 0xEF30EF30 0xFEFEFEFE 0xFCFCFCFC | full | 53 | * ============================================== 54 | * 55 | * State transition relationship: empty->using->full 56 | * The FULL status will change to EMPTY after sector clean. 57 | */ 58 | #define SECTOR_STATUS_MAGIC_EMPUT 0xFFFFFFFF 59 | #define SECTOR_STATUS_MAGIC_USING 0xFEFEFEFE 60 | #define SECTOR_STATUS_MAGIC_FULL 0xFCFCFCFC 61 | 62 | typedef enum { 63 | SECTOR_STATUS_EMPUT, 64 | SECTOR_STATUS_USING, 65 | SECTOR_STATUS_FULL, 66 | SECTOR_STATUS_HEADER_ERROR, 67 | } SectorStatus; 68 | 69 | typedef enum { 70 | SECTOR_HEADER_MAGIC_INDEX, 71 | SECTOR_HEADER_USING_INDEX, 72 | SECTOR_HEADER_FULL_INDEX, 73 | } SectorHeaderIndex; 74 | 75 | /* the stored logs start address and end address. It's like a ring buffer implemented on flash. */ 76 | static uint32_t log_start_addr = 0, log_end_addr = 0; 77 | /* saved log area address for flash */ 78 | static uint32_t log_area_start_addr = 0; 79 | /* initialize OK flag */ 80 | static bool init_ok = false; 81 | 82 | static void find_start_and_end_addr(void); 83 | static uint32_t get_next_flash_sec_addr(uint32_t cur_addr); 84 | 85 | /** 86 | * The flash save log function initialize. 87 | * 88 | * @return result 89 | */ 90 | EfErrCode ef_log_init(void) { 91 | EfErrCode result = EF_NO_ERR; 92 | 93 | EF_ASSERT(LOG_AREA_SIZE); 94 | EF_ASSERT(EF_ERASE_MIN_SIZE); 95 | /* the log area size must be an integral multiple of erase minimum size. */ 96 | EF_ASSERT(LOG_AREA_SIZE % EF_ERASE_MIN_SIZE == 0); 97 | /* the log area size must be more than twice of EF_ERASE_MIN_SIZE */ 98 | EF_ASSERT(LOG_AREA_SIZE / EF_ERASE_MIN_SIZE >= 2); 99 | 100 | #ifdef EF_USING_ENV 101 | log_area_start_addr = EF_START_ADDR + ENV_AREA_SIZE; 102 | #else 103 | log_area_start_addr = EF_START_ADDR; 104 | #endif 105 | 106 | /* find the log store start address and end address */ 107 | find_start_and_end_addr(); 108 | /* initialize OK */ 109 | init_ok = true; 110 | 111 | return result; 112 | } 113 | 114 | /** 115 | * Get flash sector current status. 116 | * 117 | * @param addr sector address, this function will auto calculate the sector header address by this address. 118 | * 119 | * @return the flash sector current status 120 | */ 121 | static SectorStatus get_sector_status(uint32_t addr) { 122 | uint32_t header_buf[LOG_SECTOR_HEADER_WORD_SIZE] = {0}, header_addr = 0; 123 | uint32_t sector_header_magic = 0; 124 | uint32_t status_full_magic = 0, status_use_magic = 0; 125 | 126 | /* calculate the sector header address */ 127 | header_addr = addr & (~(EF_ERASE_MIN_SIZE - 1)); 128 | 129 | if (ef_port_read(header_addr, header_buf, sizeof(header_buf)) == EF_NO_ERR) { 130 | sector_header_magic = header_buf[SECTOR_HEADER_MAGIC_INDEX]; 131 | status_use_magic = header_buf[SECTOR_HEADER_USING_INDEX]; 132 | status_full_magic = header_buf[SECTOR_HEADER_FULL_INDEX]; 133 | } else { 134 | EF_DEBUG("Error: Read sector header data error.\n"); 135 | return SECTOR_STATUS_HEADER_ERROR; 136 | } 137 | 138 | /* compare header magic code */ 139 | if(sector_header_magic == LOG_SECTOR_MAGIC){ 140 | if((status_use_magic == SECTOR_STATUS_MAGIC_EMPUT) && (status_full_magic == SECTOR_STATUS_MAGIC_EMPUT)) { 141 | return SECTOR_STATUS_EMPUT; 142 | } else if((status_use_magic == SECTOR_STATUS_MAGIC_USING) && (status_full_magic == SECTOR_STATUS_MAGIC_EMPUT)) { 143 | return SECTOR_STATUS_USING; 144 | } else if((status_use_magic == SECTOR_STATUS_MAGIC_USING) && (status_full_magic == SECTOR_STATUS_MAGIC_FULL)) { 145 | return SECTOR_STATUS_FULL; 146 | } else { 147 | return SECTOR_STATUS_HEADER_ERROR; 148 | } 149 | } else { 150 | return SECTOR_STATUS_HEADER_ERROR; 151 | } 152 | 153 | } 154 | 155 | /** 156 | * Write flash sector current status. 157 | * 158 | * @param addr sector address, this function will auto calculate the sector header address by this address. 159 | * @param status sector cur status 160 | * 161 | * @return result 162 | */ 163 | static EfErrCode write_sector_status(uint32_t addr, SectorStatus status) { 164 | uint32_t header, header_addr = 0; 165 | 166 | /* calculate the sector header address */ 167 | header_addr = addr & (~(EF_ERASE_MIN_SIZE - 1)); 168 | 169 | /* calculate the sector staus magic */ 170 | switch (status) { 171 | case SECTOR_STATUS_EMPUT: { 172 | header = LOG_SECTOR_MAGIC; 173 | return ef_port_write(header_addr, &header, sizeof(header)); 174 | } 175 | case SECTOR_STATUS_USING: { 176 | header = SECTOR_STATUS_MAGIC_USING; 177 | return ef_port_write(header_addr + sizeof(header), &header, sizeof(header)); 178 | } 179 | case SECTOR_STATUS_FULL: { 180 | header = SECTOR_STATUS_MAGIC_FULL; 181 | return ef_port_write(header_addr + sizeof(header) * 2, &header, sizeof(header)); 182 | } 183 | default: 184 | return EF_WRITE_ERR; 185 | } 186 | } 187 | 188 | /** 189 | * Find the current flash sector using end address by continuous 0xFF. 190 | * 191 | * @param addr sector address 192 | * 193 | * @return current flash sector using end address 194 | */ 195 | static uint32_t find_sec_using_end_addr(uint32_t addr) { 196 | /* read section data buffer size */ 197 | #define READ_BUF_SIZE 32 198 | 199 | uint32_t sector_start = addr, data_start = addr, continue_ff = 0, read_buf_size = 0, i; 200 | uint8_t buf[READ_BUF_SIZE]; 201 | 202 | EF_ASSERT(READ_BUF_SIZE % 4 == 0); 203 | /* calculate the sector start and data start address */ 204 | sector_start = addr & (~(EF_ERASE_MIN_SIZE - 1)); 205 | data_start = sector_start + LOG_SECTOR_HEADER_SIZE; 206 | 207 | /* counts continuous 0xFF which is end of sector */ 208 | while (data_start < sector_start + EF_ERASE_MIN_SIZE) { 209 | if (data_start + READ_BUF_SIZE < sector_start + EF_ERASE_MIN_SIZE) { 210 | read_buf_size = READ_BUF_SIZE; 211 | } else { 212 | read_buf_size = sector_start + EF_ERASE_MIN_SIZE - data_start; 213 | } 214 | ef_port_read(data_start, (uint32_t *)buf, read_buf_size); 215 | for (i = 0; i < read_buf_size; i++) { 216 | if (buf[i] == 0xFF) { 217 | continue_ff++; 218 | } else { 219 | continue_ff = 0; 220 | } 221 | } 222 | data_start += read_buf_size; 223 | } 224 | /* calculate current flash sector using end address */ 225 | if (continue_ff >= EF_ERASE_MIN_SIZE - LOG_SECTOR_HEADER_SIZE) { 226 | /* from 0 to sec_size all sector is 0xFF, so the sector is empty */ 227 | return sector_start + LOG_SECTOR_HEADER_SIZE; 228 | } else if (continue_ff >= 4) { 229 | /* form end_addr - 4 to sec_size length all area is 0xFF, so it's used part of the sector. 230 | * the address must be word alignment. */ 231 | if (continue_ff % 4 != 0) { 232 | continue_ff = (continue_ff / 4 + 1) * 4; 233 | } 234 | return sector_start + EF_ERASE_MIN_SIZE - continue_ff; 235 | } else { 236 | /* all sector not has continuous 0xFF, so the sector is full */ 237 | return sector_start + EF_ERASE_MIN_SIZE; 238 | } 239 | } 240 | 241 | /** 242 | * Find the log store start address and end address. 243 | * It's like a ring buffer implemented on flash. 244 | * The flash log area can be in two states depending on start address and end address: 245 | * state 1 state 2 246 | * |============| |============| 247 | * log area start--> |############| <-- start address |############| <-- end address 248 | * |############| | empty | 249 | * |------------| |------------| 250 | * |############| |############| <-- start address 251 | * |############| |############| 252 | * |------------| |------------| 253 | * | . | | . | 254 | * | . | | . | 255 | * | . | | . | 256 | * |------------| |------------| 257 | * |############| <-- end address |############| 258 | * | empty | |############| 259 | * log area end --> |============| |============| 260 | * 261 | * LOG_AREA_SIZE = log area end - log area star 262 | * 263 | */ 264 | static void find_start_and_end_addr(void) { 265 | size_t cur_size = 0; 266 | SectorStatus cur_sec_status, last_sec_status; 267 | uint32_t cur_using_sec_addr = 0; 268 | /* all status sector counts */ 269 | size_t empty_sec_counts = 0, using_sec_counts = 0, full_sector_counts = 0; 270 | /* total sector number */ 271 | size_t total_sec_num = LOG_AREA_SIZE / EF_ERASE_MIN_SIZE; 272 | /* see comment of find_start_and_end_addr function */ 273 | uint8_t cur_log_sec_state = 0; 274 | 275 | /* get the first sector status */ 276 | cur_sec_status = get_sector_status(log_area_start_addr); 277 | last_sec_status = cur_sec_status; 278 | 279 | for (cur_size = EF_ERASE_MIN_SIZE; cur_size < LOG_AREA_SIZE; cur_size += EF_ERASE_MIN_SIZE) { 280 | /* get current sector status */ 281 | cur_sec_status = get_sector_status(log_area_start_addr + cur_size); 282 | /* compare last and current status */ 283 | switch (last_sec_status) { 284 | case SECTOR_STATUS_EMPUT: { 285 | switch (cur_sec_status) { 286 | case SECTOR_STATUS_EMPUT: 287 | break; 288 | case SECTOR_STATUS_USING: 289 | EF_DEBUG("Error: Log area error! Now will clean all log area.\n"); 290 | ef_log_clean(); 291 | return; 292 | case SECTOR_STATUS_FULL: 293 | EF_DEBUG("Error: Log area error! Now will clean all log area.\n"); 294 | ef_log_clean(); 295 | return; 296 | default: 297 | break; 298 | } 299 | empty_sec_counts++; 300 | break; 301 | } 302 | case SECTOR_STATUS_USING: { 303 | switch (cur_sec_status) { 304 | case SECTOR_STATUS_EMPUT: 305 | /* like state 1 */ 306 | cur_log_sec_state = 1; 307 | log_start_addr = log_area_start_addr; 308 | cur_using_sec_addr = log_area_start_addr + cur_size - EF_ERASE_MIN_SIZE; 309 | break; 310 | case SECTOR_STATUS_USING: 311 | EF_DEBUG("Error: Log area error! Now will clean all log area.\n"); 312 | ef_log_clean(); 313 | return; 314 | case SECTOR_STATUS_FULL: 315 | /* like state 2 */ 316 | cur_log_sec_state = 2; 317 | log_start_addr = log_area_start_addr + cur_size; 318 | cur_using_sec_addr = log_area_start_addr + cur_size - EF_ERASE_MIN_SIZE; 319 | break; 320 | default: 321 | break; 322 | } 323 | using_sec_counts++; 324 | break; 325 | } 326 | case SECTOR_STATUS_FULL: { 327 | switch (cur_sec_status) { 328 | case SECTOR_STATUS_EMPUT: 329 | /* like state 1 */ 330 | if (cur_log_sec_state == 2) { 331 | EF_DEBUG("Error: Log area error! Now will clean all log area.\n"); 332 | ef_log_clean(); 333 | return; 334 | } else { 335 | cur_log_sec_state = 1; 336 | log_start_addr = log_area_start_addr; 337 | log_end_addr = log_area_start_addr + cur_size; 338 | cur_using_sec_addr = log_area_start_addr + cur_size - EF_ERASE_MIN_SIZE; 339 | } 340 | break; 341 | case SECTOR_STATUS_USING: 342 | if(total_sec_num <= 2) { 343 | /* like state 1 */ 344 | cur_log_sec_state = 1; 345 | log_start_addr = log_area_start_addr; 346 | cur_using_sec_addr = log_area_start_addr + cur_size; 347 | } else { 348 | /* like state 2 when the sector is the last one */ 349 | if (cur_size + EF_ERASE_MIN_SIZE >= LOG_AREA_SIZE) { 350 | cur_log_sec_state = 2; 351 | log_start_addr = get_next_flash_sec_addr(log_area_start_addr + cur_size); 352 | cur_using_sec_addr = log_area_start_addr + cur_size; 353 | } 354 | } 355 | break; 356 | case SECTOR_STATUS_FULL: 357 | break; 358 | default: 359 | break; 360 | } 361 | full_sector_counts++; 362 | break; 363 | } 364 | case SECTOR_STATUS_HEADER_ERROR: 365 | EF_DEBUG("Error: Log sector header error! Now will clean all log area.\n"); 366 | ef_log_clean(); 367 | return; 368 | } 369 | last_sec_status = cur_sec_status; 370 | } 371 | 372 | /* the last sector status counts */ 373 | if (cur_sec_status == SECTOR_STATUS_EMPUT) { 374 | empty_sec_counts++; 375 | } else if (cur_sec_status == SECTOR_STATUS_USING) { 376 | using_sec_counts++; 377 | } else if (cur_sec_status == SECTOR_STATUS_FULL) { 378 | full_sector_counts++; 379 | } else if (cur_sec_status == SECTOR_STATUS_HEADER_ERROR) { 380 | EF_DEBUG("Error: Log sector header error! Now will clean all log area.\n"); 381 | ef_log_clean(); 382 | return; 383 | } 384 | 385 | if (using_sec_counts != 1) { 386 | /* this state is almost impossible */ 387 | EF_DEBUG("Error: There must be only one sector status is USING! Now will clean all log area.\n"); 388 | ef_log_clean(); 389 | } else { 390 | /* find the end address */ 391 | log_end_addr = find_sec_using_end_addr(cur_using_sec_addr); 392 | } 393 | 394 | } 395 | 396 | /** 397 | * Get log used flash total size. 398 | * 399 | * @return log used flash total size. @note NOT contain sector headers 400 | */ 401 | size_t ef_log_get_used_size(void) { 402 | size_t header_total_num = 0, physical_size = 0; 403 | /* must be call this function after initialize OK */ 404 | if (!init_ok) { 405 | return 0; 406 | } 407 | 408 | if (log_start_addr < log_end_addr) { 409 | physical_size = log_end_addr - log_start_addr; 410 | } else { 411 | physical_size = LOG_AREA_SIZE - (log_start_addr - log_end_addr); 412 | } 413 | 414 | header_total_num = physical_size / EF_ERASE_MIN_SIZE + 1; 415 | 416 | return physical_size - header_total_num * LOG_SECTOR_HEADER_SIZE; 417 | } 418 | 419 | /** 420 | * Sequential reading log data. It will ignore sector headers. 421 | * 422 | * @param addr address 423 | * @param log log buffer 424 | * @param size log size, not contain sector headers. 425 | * 426 | * @return result 427 | */ 428 | static EfErrCode log_seq_read(uint32_t addr, uint32_t *log, size_t size) { 429 | EfErrCode result = EF_NO_ERR; 430 | size_t read_size = 0, read_size_temp = 0; 431 | 432 | while (size) { 433 | /* move to sector data address */ 434 | if ((addr + read_size) % EF_ERASE_MIN_SIZE == 0) { 435 | addr += LOG_SECTOR_HEADER_SIZE; 436 | } 437 | /* calculate current sector last data size */ 438 | read_size_temp = EF_ERASE_MIN_SIZE - (addr % EF_ERASE_MIN_SIZE); 439 | if (size < read_size_temp) { 440 | read_size_temp = size; 441 | } 442 | result = ef_port_read(addr + read_size, log + read_size / 4, read_size_temp); 443 | if (result != EF_NO_ERR) { 444 | return result; 445 | } 446 | read_size += read_size_temp; 447 | size -= read_size_temp; 448 | } 449 | 450 | return result; 451 | } 452 | 453 | /** 454 | * Calculate flash physical address by log index. 455 | * 456 | * @param index log index 457 | * 458 | * @return flash physical address 459 | */ 460 | static uint32_t log_index2addr(size_t index) { 461 | size_t header_total_offset = 0; 462 | /* total include sector number */ 463 | size_t sector_num = index / (EF_ERASE_MIN_SIZE - LOG_SECTOR_HEADER_SIZE) + 1; 464 | 465 | header_total_offset = sector_num * LOG_SECTOR_HEADER_SIZE; 466 | if (log_start_addr < log_end_addr) { 467 | return log_start_addr + index + header_total_offset; 468 | } else { 469 | if (log_start_addr + index + header_total_offset < log_area_start_addr + LOG_AREA_SIZE) { 470 | return log_start_addr + index + header_total_offset; 471 | } else { 472 | return log_start_addr + index + header_total_offset - LOG_AREA_SIZE; 473 | 474 | } 475 | } 476 | } 477 | 478 | /** 479 | * Read log from flash. 480 | * 481 | * @param index index for saved log. 482 | * Minimum index is 0. 483 | * Maximum index is ef_log_get_used_size() - 1. 484 | * @param log the log which will read from flash 485 | * @param size read bytes size 486 | * 487 | * @return result 488 | */ 489 | EfErrCode ef_log_read(size_t index, uint32_t *log, size_t size) { 490 | EfErrCode result = EF_NO_ERR; 491 | size_t cur_using_size = ef_log_get_used_size(); 492 | size_t read_size_temp = 0; 493 | size_t header_total_num = 0; 494 | 495 | if (!size) { 496 | return result; 497 | } 498 | 499 | EF_ASSERT(size % 4 == 0); 500 | EF_ASSERT(index < cur_using_size); 501 | 502 | if (index + size > cur_using_size) { 503 | EF_DEBUG("Warning: Log read size out of bound. Cut read size.\n"); 504 | size = cur_using_size - index; 505 | } 506 | /* must be call this function after initialize OK */ 507 | if (!init_ok) { 508 | return EF_ENV_INIT_FAILED; 509 | } 510 | 511 | if (log_start_addr < log_end_addr) { 512 | log_seq_read(log_index2addr(index), log, size); 513 | } else { 514 | if (log_index2addr(index) + size <= log_area_start_addr + LOG_AREA_SIZE) { 515 | /* Flash log area 516 | * |--------------| 517 | * log_area_start_addr --> |##############| 518 | * |##############| 519 | * |##############| 520 | * |--------------| 521 | * |##############| 522 | * |##############| 523 | * |##############| <-- log_end_addr 524 | * |--------------| 525 | * log_start_addr --> |##############| 526 | * read start --> |**************| <-- read end 527 | * |##############| 528 | * |--------------| 529 | * 530 | * read from (log_start_addr + log_index2addr(index)) to (log_start_addr + index + log_index2addr(index)) 531 | */ 532 | result = log_seq_read(log_index2addr(index), log, size); 533 | } else if (log_index2addr(index) < log_area_start_addr + LOG_AREA_SIZE) { 534 | /* Flash log area 535 | * |--------------| 536 | * log_area_start_addr --> |**************| <-- read end 537 | * |##############| 538 | * |##############| 539 | * |--------------| 540 | * |##############| 541 | * |##############| 542 | * |##############| <-- log_end_addr 543 | * |--------------| 544 | * log_start_addr --> |##############| 545 | * read start --> |**************| 546 | * |**************| 547 | * |--------------| 548 | * read will by 2 steps 549 | * step1: read from (log_start_addr + log_index2addr(index)) to flash log area end address 550 | * step2: read from flash log area start address to read size's end address 551 | */ 552 | read_size_temp = (log_area_start_addr + LOG_AREA_SIZE) - log_index2addr(index); 553 | header_total_num = read_size_temp / EF_ERASE_MIN_SIZE; 554 | /* Minus some ignored bytes */ 555 | read_size_temp -= header_total_num * LOG_SECTOR_HEADER_SIZE; 556 | result = log_seq_read(log_index2addr(index), log, read_size_temp); 557 | if (result == EF_NO_ERR) { 558 | result = log_seq_read(log_area_start_addr, log + read_size_temp / 4, size - read_size_temp); 559 | } 560 | } else { 561 | /* Flash log area 562 | * |--------------| 563 | * log_area_start_addr --> |##############| 564 | * read start --> |**************| 565 | * |**************| <-- read end 566 | * |--------------| 567 | * |##############| 568 | * |##############| 569 | * |##############| <-- log_end_addr 570 | * |--------------| 571 | * log_start_addr --> |##############| 572 | * |##############| 573 | * |##############| 574 | * |--------------| 575 | * read from (log_start_addr + log_index2addr(index) - LOG_AREA_SIZE) to read size's end address 576 | */ 577 | result = log_seq_read(log_index2addr(index) - LOG_AREA_SIZE, log, size); 578 | } 579 | } 580 | 581 | return result; 582 | } 583 | 584 | /** 585 | * Write log to flash. 586 | * 587 | * @param log the log which will be write to flash 588 | * @param size write bytes size 589 | * 590 | * @return result 591 | */ 592 | EfErrCode ef_log_write(const uint32_t *log, size_t size) { 593 | EfErrCode result = EF_NO_ERR; 594 | size_t write_size = 0, writable_size = 0; 595 | uint32_t write_addr = log_end_addr, erase_addr; 596 | SectorStatus sector_status; 597 | 598 | EF_ASSERT(size % 4 == 0); 599 | /* must be call this function after initialize OK */ 600 | if (!init_ok) { 601 | return EF_ENV_INIT_FAILED; 602 | } 603 | 604 | if ((sector_status = get_sector_status(write_addr)) == SECTOR_STATUS_HEADER_ERROR) { 605 | return EF_WRITE_ERR; 606 | } 607 | /* write some log when current sector status is USING and EMPTY */ 608 | if ((sector_status == SECTOR_STATUS_USING) || (sector_status == SECTOR_STATUS_EMPUT)) { 609 | /* write the already erased but not used area */ 610 | writable_size = EF_ERASE_MIN_SIZE - ((write_addr - log_area_start_addr) % EF_ERASE_MIN_SIZE); 611 | if (size >= writable_size) { 612 | result = ef_port_write(write_addr, log, writable_size); 613 | if (result != EF_NO_ERR) { 614 | goto exit; 615 | } 616 | /* change the current sector status to FULL */ 617 | result = write_sector_status(write_addr, SECTOR_STATUS_FULL); 618 | if (result != EF_NO_ERR) { 619 | goto exit; 620 | } 621 | write_size += writable_size; 622 | } else { 623 | result = ef_port_write(write_addr, log, size); 624 | log_end_addr = write_addr + size; 625 | goto exit; 626 | } 627 | } 628 | /* erase and write remain log */ 629 | while (true) { 630 | /* calculate next available sector address */ 631 | erase_addr = write_addr = get_next_flash_sec_addr(write_addr - 4); 632 | /* move the flash log start address to next available sector address */ 633 | if (log_start_addr == erase_addr) { 634 | log_start_addr = get_next_flash_sec_addr(log_start_addr); 635 | } 636 | /* erase sector */ 637 | result = ef_port_erase(erase_addr, EF_ERASE_MIN_SIZE); 638 | if (result != EF_NO_ERR) { 639 | goto exit; 640 | } 641 | /* change the sector status to EMPTY and USING when write begin sector start address */ 642 | result = write_sector_status(write_addr, SECTOR_STATUS_EMPUT); 643 | result = write_sector_status(write_addr, SECTOR_STATUS_USING); 644 | if (result == EF_NO_ERR) { 645 | write_addr += LOG_SECTOR_HEADER_SIZE; 646 | } else { 647 | goto exit; 648 | } 649 | /* calculate current sector writable data size */ 650 | writable_size = EF_ERASE_MIN_SIZE - LOG_SECTOR_HEADER_SIZE; 651 | if (size - write_size >= writable_size) { 652 | result = ef_port_write(write_addr, log + write_size / 4, writable_size); 653 | if (result != EF_NO_ERR) { 654 | goto exit; 655 | } 656 | /* change the current sector status to FULL */ 657 | result = write_sector_status(write_addr, SECTOR_STATUS_FULL); 658 | if (result != EF_NO_ERR) { 659 | goto exit; 660 | } 661 | log_end_addr = write_addr + writable_size; 662 | write_size += writable_size; 663 | write_addr += writable_size; 664 | } else { 665 | result = ef_port_write(write_addr, log + write_size / 4, size - write_size); 666 | if (result != EF_NO_ERR) { 667 | goto exit; 668 | } 669 | log_end_addr = write_addr + (size - write_size); 670 | break; 671 | } 672 | } 673 | 674 | exit: 675 | return result; 676 | } 677 | 678 | /** 679 | * Get next flash sector address.The log total sector like ring buffer which implement by flash. 680 | * 681 | * @param cur_addr cur flash address 682 | * 683 | * @return next flash sector address 684 | */ 685 | static uint32_t get_next_flash_sec_addr(uint32_t cur_addr) { 686 | size_t cur_sec_id = (cur_addr - log_area_start_addr) / EF_ERASE_MIN_SIZE; 687 | size_t sec_total_num = LOG_AREA_SIZE / EF_ERASE_MIN_SIZE; 688 | 689 | if (cur_sec_id + 1 >= sec_total_num) { 690 | /* return to ring head */ 691 | return log_area_start_addr; 692 | } else { 693 | return log_area_start_addr + (cur_sec_id + 1) * EF_ERASE_MIN_SIZE; 694 | } 695 | } 696 | 697 | /** 698 | * Clean all log which in flash. 699 | * 700 | * @return result 701 | */ 702 | EfErrCode ef_log_clean(void) { 703 | EfErrCode result = EF_NO_ERR; 704 | uint32_t write_addr = log_area_start_addr; 705 | 706 | /* clean address */ 707 | log_start_addr = log_area_start_addr; 708 | log_end_addr = log_start_addr + LOG_SECTOR_HEADER_SIZE; 709 | /* erase log flash area */ 710 | result = ef_port_erase(log_area_start_addr, LOG_AREA_SIZE); 711 | if (result != EF_NO_ERR) { 712 | goto exit; 713 | } 714 | /* setting first sector is EMPTY to USING */ 715 | write_sector_status(write_addr, SECTOR_STATUS_EMPUT); 716 | write_sector_status(write_addr, SECTOR_STATUS_USING); 717 | if (result != EF_NO_ERR) { 718 | goto exit; 719 | } 720 | write_addr += EF_ERASE_MIN_SIZE; 721 | /* add sector header */ 722 | while (true) { 723 | write_sector_status(write_addr, SECTOR_STATUS_EMPUT); 724 | if (result != EF_NO_ERR) { 725 | goto exit; 726 | } 727 | write_addr += EF_ERASE_MIN_SIZE; 728 | if (write_addr >= log_area_start_addr + LOG_AREA_SIZE) { 729 | break; 730 | } 731 | } 732 | 733 | exit: 734 | return result; 735 | } 736 | 737 | #endif /* EF_USING_LOG */ 738 | -------------------------------------------------------------------------------- /src/ef_utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the EasyFlash Library. 3 | * 4 | * Copyright (c) 2015-2017, Armink, 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files (the 8 | * 'Software'), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | * Function: Some utils for this library. 26 | * Created on: 2015-01-14 27 | */ 28 | 29 | #include 30 | 31 | static const uint32_t crc32_table[] = 32 | { 33 | 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 34 | 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 35 | 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 36 | 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 37 | 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 38 | 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 39 | 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 40 | 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 41 | 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 42 | 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 43 | 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 44 | 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 45 | 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 46 | 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 47 | 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 48 | 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 49 | 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 50 | 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 51 | 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 52 | 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 53 | 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 54 | 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 55 | 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 56 | 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 57 | 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 58 | 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 59 | 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 60 | 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 61 | 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 62 | 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 63 | 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 64 | 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 65 | 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 66 | 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 67 | 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 68 | 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 69 | 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 70 | 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 71 | 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 72 | 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 73 | 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 74 | 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 75 | 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d 76 | }; 77 | 78 | /** 79 | * Calculate the CRC32 value of a memory buffer. 80 | * 81 | * @param crc accumulated CRC32 value, must be 0 on first call 82 | * @param buf buffer to calculate CRC32 value for 83 | * @param size bytes in buffer 84 | * 85 | * @return calculated CRC32 value 86 | */ 87 | uint32_t ef_calc_crc32(uint32_t crc, const void *buf, size_t size) 88 | { 89 | const uint8_t *p; 90 | 91 | p = (const uint8_t *)buf; 92 | crc = crc ^ ~0U; 93 | 94 | while (size--) { 95 | crc = crc32_table[(crc ^ *p++) & 0xFF] ^ (crc >> 8); 96 | } 97 | 98 | return crc ^ ~0U; 99 | } 100 | --------------------------------------------------------------------------------