1、【增加】Flash Log功能Demo,位于demo\os\rt-thread\stm32f10x。

Signed-off-by: armink <armink.ztl@gmail.com>
pull/3/head
armink 11 years ago
parent f4af95b656
commit 80b877d8ed

@ -4,7 +4,7 @@
# 1. 介绍
EasyLogger是一款超轻量级(ROM<1.6K, RAM<0.3K)CIoT穿log4czlogCEasyLogger
[EasyLogger](https://github.com/armink/EasyLogger)是一款超轻量级(ROM<1.6K, RAM<0.3K)、高性能的C志库,非常适合对资源敏感的软件项目,例如:IoT品、可穿戴设备、智能家居等等。相比log4czlog些知名的C志库,EasyLogger功能更加简单,提供给用户的接口更少,但上手会很快,更多实用功能支持以插件形式进行动态扩展。
## 1.1 主要特性
@ -71,7 +71,7 @@ EasyLogger拥有过滤方式、输出格式、输出开关这些属性。
### 2.4 输出格式
输出格式支持:级别、时间、标签、进程信息、线程信息、文件路径、行号、方法名
输出格式支持:级别、时间、标签、进程信息、线程信息、文件路径、行号、方法名。每种优先级别可以独立设置输出格式。
> 注:默认为 **RAW格式**RAW格式日志不支持标签过滤
@ -88,14 +88,20 @@ EasyLogger拥有过滤方式、输出格式、输出开关这些属性。
下图为在终端中输入命令来控制日志的输出及过滤器的设置更加直观的展示了EasyLogger核心功能。
![easylogger](http://git.oschina.net/Armink/EasyLogger/raw/master/docs/zh/images/EasyLoggerDemo.gif)
- Demo路径`demo`
- API文档`docs\zh\api.md`
- 移植文档:即将发布……
> 注以上内容对应的API可以打开[思维导图](http://naotu.baidu.com/viewshare.html?shareId=ausqm3j44f4k)看到更清晰的逻辑。
![easylogger](http://git.oschina.net/Armink/EasyLogger/raw/master/docs/zh/images/EasyLoggerDemo.gif)
### 2.6.2 Flash Log
### 2.6.2 Flash Log将日志保存到Flash中
下图过程为通过控制台输出日志并将输出的日志存储到Flash中。重启再读取上次保存的日志最后清空Flash日志。
- Demo路径`demo\os\rt-thread\stm32f10x`
- API文档`docs\zh\api.md`
- 移植文档:即将发布……
![FlashLog](http://git.oschina.net/Armink/EasyLogger/raw/master/docs/zh/images/LogDemo.gif)
# 3. 后期

File diff suppressed because it is too large Load Diff

@ -297,7 +297,9 @@
<name>CCIncludePath2</name>
<state>$PROJ_DIR$\..\app\inc</state>
<state>$PROJ_DIR$\..\components\rtt_uart</state>
<state>$PROJ_DIR$\..\components\easyflash\inc</state>
<state>$PROJ_DIR$\..\components\easylogger\inc</state>
<state>$PROJ_DIR$\..\components\easylogger\plugins\flash</state>
<state>$PROJ_DIR$\..\components\others</state>
<state>$PROJ_DIR$\..\Libraries\STM32F10x_StdPeriph_Driver\inc</state>
<state>$PROJ_DIR$\..\RT-Thread-1.2.2\include</state>
@ -307,6 +309,7 @@
<state>$PROJ_DIR$\..\Libraries\CMSIS_EWARM\Include</state>
<state>$PROJ_DIR$\..\Libraries\CMSIS_EWARM\CM3\DeviceSupport\ST\STM32F10x</state>
<state>$PROJ_DIR$\..\..\..\..\..\easylogger\inc</state>
<state>$PROJ_DIR$\..\..\..\..\..\easylogger\plugins\flash</state>
</option>
<option>
<name>CCStdIncCheck</name>
@ -951,8 +954,50 @@
</group>
<group>
<name>components</name>
<group>
<name>easyflash</name>
<group>
<name>port</name>
<file>
<name>$PROJ_DIR$\..\components\easyflash\port\ef_port.c</name>
</file>
</group>
<group>
<name>src</name>
<file>
<name>$PROJ_DIR$\..\components\easyflash\src\easyflash.c</name>
</file>
<file>
<name>$PROJ_DIR$\..\components\easyflash\src\ef_env.c</name>
</file>
<file>
<name>$PROJ_DIR$\..\components\easyflash\src\ef_env_wl.c</name>
</file>
<file>
<name>$PROJ_DIR$\..\components\easyflash\src\ef_iap.c</name>
</file>
<file>
<name>$PROJ_DIR$\..\components\easyflash\src\ef_log.c</name>
</file>
<file>
<name>$PROJ_DIR$\..\components\easyflash\src\ef_utils.c</name>
</file>
</group>
</group>
<group>
<name>easylogger</name>
<group>
<name>plugins</name>
<group>
<name>flash</name>
<file>
<name>$PROJ_DIR$\..\..\..\..\..\easylogger\plugins\flash\elog_flash.c</name>
</file>
<file>
<name>$PROJ_DIR$\..\components\easylogger\plugins\flash\elog_flash_port.c</name>
</file>
</group>
</group>
<group>
<name>port</name>
<file>

@ -72,7 +72,7 @@
<Factory>Workspace</Factory>
<Session>
<NodeDict><ExpandedNode>EasyLogger</ExpandedNode><ExpandedNode>EasyLogger/app</ExpandedNode><ExpandedNode>EasyLogger/components</ExpandedNode><ExpandedNode>EasyLogger/components/others</ExpandedNode><ExpandedNode>EasyLogger/libs</ExpandedNode><ExpandedNode>EasyLogger/libs/cmsis</ExpandedNode><ExpandedNode>EasyLogger/rt_thread_1.2.2</ExpandedNode></NodeDict></Session>
<NodeDict><ExpandedNode>EasyLogger</ExpandedNode><ExpandedNode>EasyLogger/app</ExpandedNode></NodeDict></Session>
</Tab>
</Tabs>
@ -82,14 +82,14 @@
<Pane><Tab><Factory>TextEditor</Factory><Filename>$WS_DIR$\..\app\src\app_task.c</Filename><XPos>0</XPos><YPos>0</YPos><SelStart>0</SelStart><SelEnd>0</SelEnd><XPos2>0</XPos2><YPos2>0</YPos2><SelStart2>2976</SelStart2><SelEnd2>2976</SelEnd2></Tab><ActiveTab>0</ActiveTab><Tab><Factory>TextEditor</Factory><Filename>$WS_DIR$\..\app\src\app.c</Filename><XPos>0</XPos><YPos>0</YPos><SelStart>0</SelStart><SelEnd>0</SelEnd><XPos2>0</XPos2><YPos2>0</YPos2><SelStart2>25</SelStart2><SelEnd2>25</SelEnd2></Tab><Tab><Factory>TextEditor</Factory><Filename>$WS_DIR$\..\..\..\..\..\easylogger\src\elog.c</Filename><XPos>0</XPos><YPos>0</YPos><SelStart>0</SelStart><SelEnd>0</SelEnd><XPos2>0</XPos2><YPos2>88</YPos2><SelStart2>3351</SelStart2><SelEnd2>3351</SelEnd2></Tab><Tab><Factory>TextEditor</Factory><Filename>$WS_DIR$\..\..\..\..\..\easylogger\inc\elog.h</Filename><XPos>0</XPos><YPos>0</YPos><SelStart>0</SelStart><SelEnd>0</SelEnd><XPos2>0</XPos2><YPos2>72</YPos2><SelStart2>3784</SelStart2><SelEnd2>3784</SelEnd2></Tab></Pane><ActivePane>0</ActivePane><Sizes><Pane><X>1000000</X><Y>1000000</Y></Pane></Sizes><SplitMode>1</SplitMode></Editor>
<Pane><Tab><Factory>TextEditor</Factory><Filename>$WS_DIR$\..\app\src\app_task.c</Filename><XPos>0</XPos><YPos>0</YPos><SelStart>0</SelStart><SelEnd>0</SelEnd><XPos2>0</XPos2><YPos2>0</YPos2><SelStart2>98</SelStart2><SelEnd2>98</SelEnd2></Tab><Tab><Factory>TextEditor</Factory><Filename>$WS_DIR$\..\app\src\app.c</Filename><XPos>0</XPos><YPos>0</YPos><SelStart>0</SelStart><SelEnd>0</SelEnd><XPos2>0</XPos2><YPos2>0</YPos2><SelStart2>25</SelStart2><SelEnd2>25</SelEnd2></Tab><ActiveTab>1</ActiveTab></Pane><ActivePane>0</ActivePane><Sizes><Pane><X>1000000</X><Y>1000000</Y></Pane></Sizes><SplitMode>1</SplitMode></Editor>
<Positions>
<Top><Row0><Sizes><Toolbar-035fa940><key>iaridepm.enu1</key></Toolbar-035fa940></Sizes></Row0><Row1><Sizes><Toolbar-1363cbe8><key>debuggergui.enu1</key></Toolbar-1363cbe8><Toolbar-1363c350><key>armjlink.enu1</key></Toolbar-1363c350></Sizes></Row1></Top><Left><Row0><Sizes><Wnd4><Rect><Top>-2</Top><Left>-2</Left><Bottom>700</Bottom><Right>262</Right><x>-2</x><y>-2</y><xscreen>200</xscreen><yscreen>200</yscreen><sizeHorzCX>119048</sizeHorzCX><sizeHorzCY>206825</sizeHorzCY><sizeVertCX>157143</sizeVertCX><sizeVertCY>725957</sizeVertCY></Rect></Wnd4></Sizes></Row0></Left><Right><Row0><Sizes><Wnd5><Rect><Top>-2</Top><Left>-2</Left><Bottom>700</Bottom><Right>470</Right><x>-2</x><y>-2</y><xscreen>200</xscreen><yscreen>200</yscreen><sizeHorzCX>119048</sizeHorzCX><sizeHorzCY>206825</sizeHorzCY><sizeVertCX>280952</sizeVertCX><sizeVertCY>725957</sizeVertCY></Rect></Wnd5></Sizes></Row0></Right><Bottom><Row0><Sizes><Wnd3><Rect><Top>-2</Top><Left>-2</Left><Bottom>198</Bottom><Right>1682</Right><x>-2</x><y>-2</y><xscreen>1684</xscreen><yscreen>200</yscreen><sizeHorzCX>1002381</sizeHorzCX><sizeHorzCY>206825</sizeHorzCY><sizeVertCX>119048</sizeVertCX><sizeVertCY>206825</sizeVertCY></Rect></Wnd3></Sizes></Row0></Bottom><Float><Sizes/></Float></Positions>
<Top><Row0><Sizes><Toolbar-033285a8><key>iaridepm.enu1</key></Toolbar-033285a8></Sizes></Row0><Row1><Sizes><Toolbar-140c8720><key>debuggergui.enu1</key></Toolbar-140c8720><Toolbar-220059c0><key>armjlink.enu1</key></Toolbar-220059c0></Sizes></Row1></Top><Left><Row0><Sizes><Wnd4><Rect><Top>-2</Top><Left>-2</Left><Bottom>700</Bottom><Right>262</Right><x>-2</x><y>-2</y><xscreen>200</xscreen><yscreen>200</yscreen><sizeHorzCX>119048</sizeHorzCX><sizeHorzCY>206825</sizeHorzCY><sizeVertCX>157143</sizeVertCX><sizeVertCY>725957</sizeVertCY></Rect></Wnd4></Sizes></Row0></Left><Right><Row0><Sizes><Wnd5><Rect><Top>-2</Top><Left>-2</Left><Bottom>700</Bottom><Right>470</Right><x>-2</x><y>-2</y><xscreen>200</xscreen><yscreen>200</yscreen><sizeHorzCX>119048</sizeHorzCX><sizeHorzCY>206825</sizeHorzCY><sizeVertCX>280952</sizeVertCX><sizeVertCY>725957</sizeVertCY></Rect></Wnd5></Sizes></Row0></Right><Bottom><Row0><Sizes><Wnd3><Rect><Top>-2</Top><Left>-2</Left><Bottom>198</Bottom><Right>1682</Right><x>-2</x><y>-2</y><xscreen>1684</xscreen><yscreen>200</yscreen><sizeHorzCX>1002381</sizeHorzCX><sizeHorzCY>206825</sizeHorzCY><sizeVertCX>119048</sizeVertCX><sizeVertCY>206825</sizeVertCY></Rect></Wnd3></Sizes></Row0></Bottom><Float><Sizes/></Float></Positions>
</Desktop>
</Project>

@ -15,8 +15,8 @@ OverrideDefaultClocks=0
CpuClock=72000000
ClockAutoDetect=0
ClockWanted=2000000
JtagSpeed=2250000
Prescaler=32
JtagSpeed=2000000
Prescaler=36
TimeStampPrescIndex=0
TimeStampPrescData=0
PcSampCYCTAP=1
@ -28,7 +28,7 @@ ITMportsTermIO=0
ITMportsLogFile=0
ITMlogFile=$PROJ_DIR$\ITM.log
[DebugChecksum]
Checksum=-1118035218
Checksum=-483865419
[Exceptions]
StopOnUncaught=_ 0
StopOnThrow=_ 0

@ -17,7 +17,7 @@
<Build><ColumnWidth0>20</ColumnWidth0><ColumnWidth1>1215</ColumnWidth1><ColumnWidth2>324</ColumnWidth2><ColumnWidth3>81</ColumnWidth3></Build><Debug-Log><ColumnWidth0>20</ColumnWidth0><ColumnWidth1>1621</ColumnWidth1></Debug-Log><TerminalIO/></Static>
<Windows>
<Wnd0>
<Wnd2>
<Tabs>
<Tab>
<Identity>TabID-24465-26237</Identity>
@ -29,20 +29,20 @@
</Tab>
</Tabs>
<SelectedTab>0</SelectedTab></Wnd0><Wnd1><Tabs><Tab><Identity>TabID-28731-28409</Identity><TabName>Build</TabName><Factory>Build</Factory><Session/></Tab><Tab><Identity>TabID-15495-1620</Identity><TabName>Debug Log</TabName><Factory>Debug-Log</Factory><Session/></Tab></Tabs><SelectedTab>0</SelectedTab></Wnd1></Windows>
<SelectedTab>0</SelectedTab></Wnd2><Wnd3><Tabs><Tab><Identity>TabID-28731-28409</Identity><TabName>Build</TabName><Factory>Build</Factory><Session/></Tab><Tab><Identity>TabID-15495-1620</Identity><TabName>Debug Log</TabName><Factory>Debug-Log</Factory><Session/></Tab></Tabs><SelectedTab>0</SelectedTab></Wnd3></Windows>
<Editor>
<Pane><Tab><Factory>TextEditor</Factory><Filename>$WS_DIR$\..\app\src\app_task.c</Filename><XPos>0</XPos><YPos>0</YPos><SelStart>0</SelStart><SelEnd>0</SelEnd><XPos2>0</XPos2><YPos2>0</YPos2><SelStart2>96</SelStart2><SelEnd2>96</SelEnd2></Tab><ActiveTab>0</ActiveTab></Pane><ActivePane>0</ActivePane><Sizes><Pane><X>1000000</X><Y>1000000</Y></Pane></Sizes><SplitMode>1</SplitMode></Editor>
<Pane><Tab><Factory>TextEditor</Factory><Filename>$WS_DIR$\..\app\src\app_task.c</Filename><XPos>0</XPos><YPos>0</YPos><SelStart>0</SelStart><SelEnd>0</SelEnd><XPos2>0</XPos2><YPos2>0</YPos2><SelStart2>98</SelStart2><SelEnd2>98</SelEnd2></Tab><ActiveTab>0</ActiveTab></Pane><ActivePane>0</ActivePane><Sizes><Pane><X>1000000</X><Y>1000000</Y></Pane></Sizes><SplitMode>1</SplitMode></Editor>
<Positions>
<Top><Row0><Sizes><Toolbar-02e89540><key>iaridepm.enu1</key></Toolbar-02e89540></Sizes></Row0></Top><Left><Row0><Sizes><Wnd0><Rect><Top>-2</Top><Left>-2</Left><Bottom>724</Bottom><Right>306</Right><x>-2</x><y>-2</y><xscreen>200</xscreen><yscreen>200</yscreen><sizeHorzCX>119048</sizeHorzCX><sizeHorzCY>206825</sizeHorzCY><sizeVertCX>183333</sizeVertCX><sizeVertCY>750776</sizeVertCY></Rect></Wnd0></Sizes></Row0></Left><Right><Row0><Sizes/></Row0></Right><Bottom><Row0><Sizes><Wnd1><Rect><Top>-2</Top><Left>-2</Left><Bottom>198</Bottom><Right>1682</Right><x>-2</x><y>-2</y><xscreen>1684</xscreen><yscreen>200</yscreen><sizeHorzCX>1002381</sizeHorzCX><sizeHorzCY>206825</sizeHorzCY><sizeVertCX>119048</sizeVertCX><sizeVertCY>206825</sizeVertCY></Rect></Wnd1></Sizes></Row0></Bottom><Float><Sizes/></Float></Positions>
<Top><Row0><Sizes><Toolbar-033285a8><key>iaridepm.enu1</key></Toolbar-033285a8></Sizes></Row0><Row1><Sizes/></Row1></Top><Left><Row0><Sizes><Wnd2><Rect><Top>-2</Top><Left>-2</Left><Bottom>724</Bottom><Right>306</Right><x>-2</x><y>-2</y><xscreen>200</xscreen><yscreen>200</yscreen><sizeHorzCX>119048</sizeHorzCX><sizeHorzCY>206825</sizeHorzCY><sizeVertCX>183333</sizeVertCX><sizeVertCY>750776</sizeVertCY></Rect></Wnd2></Sizes></Row0></Left><Right><Row0><Sizes/></Row0></Right><Bottom><Row0><Sizes><Wnd3><Rect><Top>-2</Top><Left>-2</Left><Bottom>198</Bottom><Right>1682</Right><x>-2</x><y>-2</y><xscreen>1684</xscreen><yscreen>200</yscreen><sizeHorzCX>1002381</sizeHorzCX><sizeHorzCY>206825</sizeHorzCY><sizeVertCX>119048</sizeVertCX><sizeVertCY>206825</sizeVertCY></Rect></Wnd3></Sizes></Row0></Bottom><Float><Sizes/></Float></Positions>
</Desktop>
</Workspace>

@ -6,24 +6,34 @@
通过 `app\src\app_task.c``test_elog()` 方法来测试日志的输出,用户可以接入终端来控制日志的输出与过滤器的设置。
### 1.1、使用方法
## 2、使用方法
打开电脑的终端与Demo的串口1进行连接串口配置 115200 8 1 N此时在终端中就可以输入 "2.6 Demo" Gif动画中提到的常用命令如下
- 1、elog使能与失能输出日志。elog on使能elog off失能
- 2、elog_lvl设置过滤级别(0-5)。
- 3、elog_tag设置过滤标签设置后则只有当日志的标签包含过滤标签时才会被输出。不带任何参数则清空过滤标签。
### 2.1 核心功能
- 1、elog使能与失能输出日志。elog on使能elog off失能提示在日志输出过多不方便输入命令测试的时候可以将日志输出先失能
- 2、elog_lvl设置过滤级别(0-5)
- 3、elog_tag设置过滤标签设置后则只有当日志的标签包含过滤标签时才会被输出。不带任何参数则清空过滤标签
- 4、elog_kw设置过滤关键词设置后则只有当日志的 **所有内容** 包含过滤关键词时,才会被输出。不带任何参数则清空过滤关键词。
## 2、文件说明
### 2.2 Flash Log将日志保存到Flash中
这里我在`components\easylogger\plugins\flash\elog_flash_cfg.h`配置开启缓冲模式此时只有缓冲区满了才会向Flash中写入。
`components\easylogger\port\elog_port.c` 移植参考文件
-1、elog_flash read读取存储在Flash中的所有日志
- 1.1、elog_flash read xxxx读取最近保存的xxxx字节大小的日志
-2、elog_flash flush立刻将缓冲区中的所有日志保存至Flash中注意只有开启的了缓冲功能才会有效
-3、elog_flash clean清空Flash中的所有已保存日志此时缓冲区的日志也将被清空。
`RVMDK` 下为Keil工程文件
## 3、文件说明
`EWARM` 下为IAR工程文件
- `components\easylogger\port\elog_port.c` 核心功能移植参考文件
- `components\easylogger\plugins\flash\elog_flash_port.c` Flash Log功能移植参考文件
- `RVMDK` 下为Keil工程文件
- `EWARM` 下为IAR工程文件
## 3、其他功能
## 4、其他功能
- 1、新增 RTT断言及硬件异常的钩子方法使得系统在出现异常时错误日志依然可以被输出或保存。参考 `app\src\app_task.c` 中的 `rtt_user_assert_hook``exception_hook` 方法。
- 2、新增 EasyLogger断言的钩子方法使得系统在出现异常时错误日志依然可以被输出或保存。参考 `app\src\app_task.c` 中的 `elog_user_assert_hook` 方法。
- 1、新增 RTT断言及硬件异常的钩子方法使得系统或MCU在出现异常时,错误日志依然可以被输出同时保存至Flash。参考 `app\src\app_task.c` 中的 `rtt_user_assert_hook``exception_hook` 方法。
- 2、新增 EasyLogger断言的钩子方法使得系统在出现异常时错误日志依然可以被输出同时保存至Flash。参考 `app\src\app_task.c` 中的 `elog_user_assert_hook` 方法。

@ -2,7 +2,8 @@
#include <stdlib.h>
#include <stdio.h>
#include "bsp.h"
#include "elog.h"
#include "elog_flash.h"
#include "easyflash.h"
#include "finsh.h"
#include "shell.h"
#include "cpuusage.h"
@ -88,8 +89,8 @@ static void test_elog(void) {
void sys_init_thread(void* parameter){
set_system_status(SYSTEM_STATUS_INIT);
/* EasyLogger initialization */
if (elog_init() == ELOG_NO_ERR) {
/* initialize EasyFlash and EasyLogger */
if ((easyflash_init() == EF_NO_ERR)&&(elog_init() == ELOG_NO_ERR)) {
/* set enabled format */
elog_set_fmt(ELOG_LVL_ASSERT, ELOG_FMT_ALL & ~ELOG_FMT_P_INFO);
elog_set_fmt(ELOG_LVL_ERROR, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME);
@ -99,6 +100,8 @@ void sys_init_thread(void* parameter){
elog_set_fmt(ELOG_LVL_VERBOSE, ELOG_FMT_ALL & ~(ELOG_FMT_FUNC | ELOG_FMT_P_INFO));
/* set EasyLogger assert hook */
elog_assert_set_hook(elog_user_assert_hook);
/* initialize EasyLogger Flash plugin */
elog_flash_init();
/* start EasyLogger */
elog_start();
/* set hardware exception hook */
@ -116,18 +119,26 @@ void sys_init_thread(void* parameter){
}
static void elog_user_assert_hook(const char* ex, const char* func, size_t line) {
/* disable logger output lock */
elog_output_lock_enabled(false);
// elog_flash_lock_enabled(false);
elog_a("elog", "(%s) has assert failed at %s:%ld.", ex, func, line);
// elog_flash_flush();
/* disable flash plugin lock */
elog_flash_lock_enabled(false);
/* output logger assert information */
elog_a("elog", "(%s) has assert failed at %s:%ld.\n", ex, func, line);
/* write all buffered log to flash */
elog_flash_flush();
while(1);
}
static void rtt_user_assert_hook(const char* ex, const char* func, rt_size_t line) {
/* disable logger output lock */
elog_output_lock_enabled(false);
// elog_flash_lock_enabled(false);
/* disable flash plugin lock */
elog_flash_lock_enabled(false);
/* output rtt assert information */
elog_a("rtt", "(%s) has assert failed at %s:%ld.\n", ex, func, line);
// elog_flash_flush();
/* write all buffered log to flash */
elog_flash_flush();
while(1);
}
@ -144,8 +155,10 @@ static rt_err_t exception_hook(void *context) {
};
struct exception_stack_frame *exception_stack = (struct exception_stack_frame *) context;
/* disable logger output lock */
elog_output_lock_enabled(false);
//elog_flash_lock_enabled(false);
/* disable flash plugin lock */
elog_flash_lock_enabled(false);
elog_e("hw_fault", "psr: 0x%08x", exception_stack->psr);
elog_e("hw_fault", " pc: 0x%08x", exception_stack->pc);
@ -157,7 +170,8 @@ static rt_err_t exception_hook(void *context) {
elog_e("hw_fault", "r00: 0x%08x", exception_stack->r0);
elog_e("hw_fault", "hard fault on thread: %s", rt_thread_self()->name);
//elog_flash_flush();
/* write all buffered log to flash */
elog_flash_flush();
return RT_EOK;
}

@ -9,7 +9,7 @@
#include <stm32f10x_conf.h>
#include <finsh.h>
#include "cpuusage.h"
#include "elog.h"
#include "elog_flash.h"
static void reboot(uint8_t argc, char **argv) {
NVIC_SystemReset();
@ -77,3 +77,30 @@ static void elog_kw(uint8_t argc, char **argv) {
}
}
MSH_CMD_EXPORT(elog_kw, Set EasyLogger filter keyword);
static void elog_flash(uint8_t argc, char **argv) {
if (argc >= 2) {
if (!strcmp(argv[1], "read")) {
if (argc >= 3) {
elog_flash_outout_recent(atol(argv[2]));
}else {
elog_flash_outout_all();
}
} else if (!strcmp(argv[1], "clean")) {
elog_flash_clean();
} else if (!strcmp(argv[1], "flush")) {
#ifdef ELOG_FLASH_USING_BUF_MODE
elog_flash_flush();
#else
rt_kprintf("EasyLogger flash log buffer mode is not open.\n");
#endif
} else {
rt_kprintf("Please input elog_flash {<read>, <clean>, <flush>}.\n");
}
} else {
rt_kprintf("Please input elog_flash {<read>, <clean>, <flush>}.\n");
}
}
MSH_CMD_EXPORT(elog_flash, EasyLogger <read> <clean> <flush> flash log);

@ -0,0 +1,147 @@
/*
* This file is part of the EasyFlash Library.
*
* Copyright (c) 2014, Armink, <armink.ztl@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Function: It is an head file for this library. You can see all be called functions.
* Created on: 2014-09-10
*/
#ifndef EASYFLASH_H_
#define EASYFLASH_H_
#include <ef_cfg.h>
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
#if defined(EF_USING_ENV) && (!defined(ENV_USER_SETTING_SIZE) || !defined(ENV_AREA_SIZE))
#error "Please configure user setting ENV size or ENV area size (in ef_cfg.h)"
#endif
#if defined(EF_USING_LOG) && !defined(LOG_AREA_SIZE)
#error "Please configure log area size (in ef_cfg.h)"
#endif
#if !defined(EF_START_ADDR)
#error "Please configure backup area start address (in ef_cfg.h)"
#endif
#if !defined(EF_USING_ENV)
#error "Please configure minimum size of flash erasure (in ef_cfg.h)"
#endif
/* EasyFlash debug print function. Must be implement by user. */
#define EF_DEBUG(...) ef_log_debug(__FILE__, __LINE__, __VA_ARGS__)
/* EasyFlash routine print function. Must be implement by user. */
#define EF_INFO(...) ef_log_info(__VA_ARGS__)
/* EasyFlash assert for developer. */
#define EF_ASSERT(EXPR) \
if (!(EXPR)) \
{ \
EF_DEBUG("(%s) has assert failed at %s.\n", #EXPR, __FUNCTION__); \
while (1); \
}
/* EasyFlash software version number */
#define EF_SW_VERSION "1.07.29"
typedef struct _eflash_env{
char *key;
char *value;
}ef_env, *ef_env_t;
/* EasyFlash error code */
typedef enum {
EF_NO_ERR,
EF_ERASE_ERR,
EF_WRITE_ERR,
EF_ENV_NAME_ERR,
EF_ENV_NAME_EXIST,
EF_ENV_FULL,
} EfErrCode;
/* the flash sector current status */
typedef enum {
FLASH_SECTOR_EMPTY,
FLASH_SECTOR_USING,
FLASH_SECTOR_FULL,
}FlashSecrorStatus;
/* easyflash.c */
EfErrCode easyflash_init(void);
#ifdef EF_USING_ENV
/* ef_env.c ef_env_wl.c */
void ef_load_env(void);
void ef_print_env(void);
char *ef_get_env(const char *key);
EfErrCode ef_set_env(const char *key, const char *value);
EfErrCode ef_save_env(void);
EfErrCode ef_env_set_default(void);
size_t ef_get_env_write_bytes(void);
EfErrCode ef_set_and_save_env(const char *key, const char *value);
#endif
#ifdef EF_USING_IAP
/* ef_iap.c */
EfErrCode ef_erase_bak_app(size_t app_size);
EfErrCode ef_erase_user_app(uint32_t user_app_addr, size_t user_app_size);
EfErrCode ef_erase_bl(uint32_t bl_addr, size_t bl_size);
EfErrCode ef_write_data_to_bak(uint8_t *data, size_t size, size_t *cur_size,
size_t total_size);
EfErrCode ef_copy_app_from_bak(uint32_t user_app_addr, size_t app_size);
EfErrCode ef_copy_bl_from_bak(uint32_t bl_addr, size_t bl_size);
#endif
#ifdef EF_USING_LOG
/* ef_log.c */
EfErrCode ef_log_read(size_t index, uint32_t *log, size_t size);
EfErrCode ef_log_write(const uint32_t *log, size_t size);
EfErrCode ef_log_clean(void);
size_t ef_log_get_used_size(void);
#endif
/* ef_utils.c */
uint32_t ef_calc_crc32(uint32_t crc, const void *buf, size_t size);
FlashSecrorStatus ef_get_sector_status(uint32_t addr, size_t sec_size);
uint32_t ef_find_sec_using_end_addr(uint32_t addr, size_t sec_size);
/* ef_port.c */
EfErrCode ef_port_read(uint32_t addr, uint32_t *buf, size_t size);
EfErrCode ef_port_erase(uint32_t addr, size_t size);
EfErrCode ef_port_write(uint32_t addr, const uint32_t *buf, size_t size);
void ef_port_env_lock(void);
void ef_port_env_unlock(void);
void ef_log_debug(const char *file, const long line, const char *format, ...);
void ef_log_info(const char *format, ...);
void ef_print(const char *format, ...);
#ifdef __cplusplus
}
#endif
#endif /* EASYFLASH_H_ */

@ -0,0 +1,115 @@
/*
* This file is part of the EasyFlash Library.
*
* Copyright (c) 2015, Armink, <armink.ztl@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Function: It is the configure head file for this library.
* Created on: 2015-07-14
*/
#ifndef EF_CFG_H_
#define EF_CFG_H_
#include <stm32f10x_conf.h>
/* using ENV function */
#define EF_USING_ENV
/* using wear leveling mode for ENV */
/* #define EF_ENV_USING_WL_MODE */
/* using power fail safeguard mode for ENV */
/* #define EF_ENV_USING_PFS_MODE */
/* using IAP function */
#define EF_USING_IAP
/* using save log function */
#define EF_USING_LOG
/* page size for stm32 flash */
#if defined(STM32F10X_LD) || defined(STM32F10X_LD_VL) || defined (STM32F10X_MD) || defined (STM32F10X_MD_VL)
#define PAGE_SIZE 1024
#else
#define PAGE_SIZE 2048
#endif
/* the minimum size of flash erasure */
#define EF_ERASE_MIN_SIZE PAGE_SIZE /* it is one page for STM3210x */
/**
*
* This all Backup Area Flash storage index. All used flash area configure is under here.
* |----------------------------| Storage Size
* | Environment variables area | ENV area size @see ENV_AREA_SIZE
* | 1.system section | ENV_SYSTEM_SIZE
* | 2:data section | ENV_AREA_SIZE - ENV_SYSTEM_SIZE
* |----------------------------|
* | Saved log area | Log area size @see LOG_AREA_SIZE
* |----------------------------|
* |(IAP)Downloaded application | IAP already downloaded application, unfixed size
* |----------------------------|
*
* @note all area size must be aligned with EF_ERASE_MIN_SIZE
* @note EasyFlash will use ram to buffered the ENV.At some time some flash's EF_ERASE_MIN_SIZE is so big,
* and you want use ENV size is less than it. So you must defined ENV_USER_SETTING_SIZE for ENV.
* @note ENV area size has some limitations in different modes.
* 1.Normal mode: no more limitations
* 2.Wear leveling mode: system section will used an flash section and the data section will used at least 2 flash sections
* 3.Power fail safeguard mode: ENV area will has an backup. It is twice as normal mode.
* 4.wear leveling and power fail safeguard mode: The system section will used an flash section.
* The data section is twice as wear leveling mode data section size.
* For example:
* The EF_ERASE_MIN_SIZE is 128K and the ENV_USER_SETTING_SIZE: 2K. The ENV_AREA_SIZE in different mode you can define
* 1.Normal mode: 1*EF_ERASE_MIN_SIZE
* 2.Wear leveling mode: 2*EF_ERASE_MIN_SIZE (It has 2 flash section to store ENV. So ENV can erase at least 200,000 times)
* 3.Power fail safeguard mode: 2*EF_ERASE_MIN_SIZE
* 4.Wear leveling and power fail safeguard mode: 5*EF_ERASE_MIN_SIZE
* @note the log area size must be more than twice of EF_ERASE_MIN_SIZE
*/
/* backup area start address */
#define EF_START_ADDR (FLASH_BASE + 80 * 1024) /* from the chip position: 80KB */
/* the user setting size of ENV, must be word alignment */
#define ENV_USER_SETTING_SIZE (2 * 1024)
#ifndef EF_ENV_USING_PFS_MODE
#ifndef EF_ENV_USING_WL_MODE
/* ENV area total bytes size in normal mode. */
#define ENV_AREA_SIZE (1 * EF_ERASE_MIN_SIZE) /* 2K */
#else
/* ENV area total bytes size in wear leveling mode. */
#define ENV_AREA_SIZE (4 * EF_ERASE_MIN_SIZE) /* 8K */
#endif
#else
#ifndef EF_ENV_USING_WL_MODE
/* ENV area total bytes size in power fail safeguard mode. */
#define ENV_AREA_SIZE (2 * EF_ERASE_MIN_SIZE) /* 4K */
#else
/* ENV area total bytes size in wear leveling and power fail safeguard mode. */
#define ENV_AREA_SIZE (5 * EF_ERASE_MIN_SIZE) /* 10K */
#endif
#endif
/* saved log area size */
#define LOG_AREA_SIZE (10 * EF_ERASE_MIN_SIZE) /* 20K */
/* print debug information of flash */
#define PRINT_DEBUG
#endif /* EF_CFG_H_ */

@ -0,0 +1,235 @@
/*
* This file is part of the EasyFlash Library.
*
* Copyright (c) 2015, Armink, <armink.ztl@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Function: Portable interface for stm32f10x platform.
* Created on: 2015-01-16
*/
#include <easyflash.h>
#include <rthw.h>
#include <rtthread.h>
#include <stm32f10x_conf.h>
/* default environment variables set for user */
static const ef_env default_env_set[] = {
{"iap_need_copy_app","0"},
{"iap_copy_app_size","0"},
{"stop_in_bootloader","0"},
{"device_id","1"},
{"boot_times","0"},
};
static char log_buf[128];
static struct rt_semaphore env_cache_lock;
/**
* Flash port for hardware initialize.
*
* @param default_env default ENV set for user
* @param default_env_size default ENV size
*
* @return result
*/
EfErrCode ef_port_init(ef_env const **default_env, size_t *default_env_size) {
EfErrCode result = EF_NO_ERR;
*default_env = default_env_set;
*default_env_size = sizeof(default_env_set) / sizeof(default_env_set[0]);
rt_sem_init(&env_cache_lock, "env lock", 1, RT_IPC_FLAG_PRIO);
return result;
}
/**
* Read data from flash.
* @note This operation's units is word.
*
* @param addr flash address
* @param buf buffer to store read data
* @param size read bytes size
*
* @return result
*/
EfErrCode ef_port_read(uint32_t addr, uint32_t *buf, size_t size) {
EfErrCode result = EF_NO_ERR;
EF_ASSERT(size % 4 == 0);
/*copy from flash to ram */
for (; size > 0; size -= 4, addr += 4, buf++) {
*buf = *(uint32_t *) addr;
}
return result;
}
/**
* Erase data on flash.
* @note This operation is irreversible.
* @note This operation's units is different which on many chips.
*
* @param addr flash address
* @param size erase bytes size
*
* @return result
*/
EfErrCode ef_port_erase(uint32_t addr, size_t size) {
EfErrCode result = EF_NO_ERR;
FLASH_Status flash_status;
size_t erase_pages, i;
/* make sure the start address is a multiple of FLASH_ERASE_MIN_SIZE */
EF_ASSERT(addr % EF_ERASE_MIN_SIZE == 0);
/* calculate pages */
erase_pages = size / PAGE_SIZE;
if (size % PAGE_SIZE != 0) {
erase_pages++;
}
/* start erase */
FLASH_Unlock();
FLASH_ClearFlag(FLASH_FLAG_BSY | FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
for (i = 0; i < erase_pages; i++) {
flash_status = FLASH_ErasePage(addr + (PAGE_SIZE * i));
if (flash_status != FLASH_COMPLETE) {
result = EF_ERASE_ERR;
break;
}
}
FLASH_Lock();
return result;
}
/**
* Write data to flash.
* @note This operation's units is word.
* @note This operation must after erase. @see flash_erase.
*
* @param addr flash address
* @param buf the write data buffer
* @param size write bytes size
*
* @return result
*/
EfErrCode ef_port_write(uint32_t addr, const uint32_t *buf, size_t size) {
EfErrCode result = EF_NO_ERR;
size_t i;
uint32_t read_data;
EF_ASSERT(size % 4 == 0);
FLASH_Unlock();
FLASH_ClearFlag(FLASH_FLAG_BSY | FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
for (i = 0; i < size; i += 4, buf++, addr += 4) {
/* write data */
FLASH_ProgramWord(addr, *buf);
read_data = *(uint32_t *)addr;
/* check data */
if (read_data != *buf) {
result = EF_WRITE_ERR;
break;
}
}
FLASH_Lock();
return result;
}
/**
* lock the ENV ram cache
*/
void ef_port_env_lock(void) {
rt_sem_take(&env_cache_lock, RT_WAITING_FOREVER);
}
/**
* unlock the ENV ram cache
*/
void ef_port_env_unlock(void) {
rt_sem_release(&env_cache_lock);
}
/**
* This function is print flash debug info.
*
* @param file the file which has call this function
* @param line the line number which has call this function
* @param format output format
* @param ... args
*
*/
void ef_log_debug(const char *file, const long line, const char *format, ...) {
#ifdef PRINT_DEBUG
va_list args;
/* args point to the first variable parameter */
va_start(args, format);
ef_print("[Flash](%s:%ld) ", file, line);
/* must use vprintf to print */
rt_vsprintf(log_buf, format, args);
ef_print("%s", log_buf);
va_end(args);
#endif
}
/**
* This function is print flash routine info.
*
* @param format output format
* @param ... args
*/
void ef_log_info(const char *format, ...) {
va_list args;
/* args point to the first variable parameter */
va_start(args, format);
ef_print("[Flash]");
/* must use vprintf to print */
rt_vsprintf(log_buf, format, args);
ef_print("%s", log_buf);
va_end(args);
}
/**
* This function is print flash non-package info.
*
* @param format output format
* @param ... args
*/
void ef_print(const char *format, ...) {
va_list args;
/* args point to the first variable parameter */
va_start(args, format);
/* must use vprintf to print */
rt_vsprintf(log_buf, format, args);
rt_kprintf("%s", log_buf);
va_end(args);
}

@ -0,0 +1,103 @@
/*
* This file is part of the EasyFlash Library.
*
* Copyright (c) 2014, Armink, <armink.ztl@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Function: Initialize interface for this library.
* Created on: 2014-09-09
*/
/**
*
* This all Backup Area Flash storage index. All used flash area configure is under here.
* |----------------------------| Storage Size
* | Environment variables area | ENV area size @see ENV_AREA_SIZE
* | 1.system section | ENV_SYSTEM_SIZE
* | 2:data section | ENV_AREA_SIZE - ENV_SYSTEM_SIZE
* |----------------------------|
* | Saved log area | Log area size @see LOG_AREA_SIZE
* |----------------------------|
* |(IAP)Downloaded application | IAP already downloaded application, unfixed size
* |----------------------------|
*
* @note all area size must be aligned with EF_ERASE_MIN_SIZE
* @note EasyFlash will use ram to buffered the ENV. At some time flash's EF_ERASE_MIN_SIZE is so big,
* and you want use ENV size is less than it. So you must defined ENV_USER_SETTING_SIZE for ENV.
* @note ENV area size has some limitations in different modes.
* 1.Normal mode: no more limitations
* 2.Wear leveling mode: system section will used an flash section and the data section will used at least 2 flash sections
* 3.Power fail safeguard mode: ENV area will has an backup. It is twice as normal mode.
* 4.wear leveling and power fail safeguard mode: The system section will used an flash section.
* The data section is twice as wear leveling mode data section size.
* For example:
* The EF_ERASE_MIN_SIZE is 128K and the ENV_USER_SETTING_SIZE: 2K. The ENV_AREA_SIZE in different mode you can define
* 1.Normal mode: 1*EF_ERASE_MIN_SIZE
* 2.Wear leveling mode: 3*EF_ERASE_MIN_SIZE (It has 2 flash section to store ENV. So ENV can erase at least 200,000 times)
* 3.Power fail safeguard mode: 2*EF_ERASE_MIN_SIZE
* 4.Wear leveling and power fail safeguard mode: 5*EF_ERASE_MIN_SIZE
* @note the log area size must be more than twice of EF_ERASE_MIN_SIZE
*/
#include <easyflash.h>
/**
* EasyFlash system initialize.
*
* @return result
*/
EfErrCode easyflash_init(void) {
extern EfErrCode ef_port_init(ef_env const **default_env, size_t *default_env_size);
extern EfErrCode ef_env_init(ef_env const *default_env, size_t default_env_size);
extern EfErrCode ef_iap_init(void);
extern EfErrCode ef_log_init(void);
size_t default_env_set_size = 0;
const ef_env *default_env_set;
EfErrCode result = EF_NO_ERR;
result = ef_port_init(&default_env_set, &default_env_set_size);
#ifdef EF_USING_ENV
if (result == EF_NO_ERR) {
result = ef_env_init(default_env_set, default_env_set_size);
}
#endif
#ifdef EF_USING_IAP
if (result == EF_NO_ERR) {
result = ef_iap_init();
}
#endif
#ifdef EF_USING_LOG
if (result == EF_NO_ERR) {
result = ef_log_init();
}
#endif
if (result == EF_NO_ERR) {
EF_DEBUG("EasyFlash V%s is initialize success.\n", EF_SW_VERSION);
} else {
EF_DEBUG("EasyFlash V%s is initialize fail.\n", EF_SW_VERSION);
}
return result;
}

@ -0,0 +1,759 @@
/*
* This file is part of the EasyFlash Library.
*
* Copyright (c) 2014, Armink, <armink.ztl@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Function: Environment variables operating interface. (normal mode)
* Created on: 2014-10-06
*/
#include <easyflash.h>
#include <string.h>
#include <stdlib.h>
#ifdef EF_USING_ENV
#ifndef EF_ENV_USING_WL_MODE
/**
* ENV area has 2 sections
* 1. System section
* It storage ENV parameters. (Units: Word)
* 2. Data section
* It storage all ENV. Storage format is key=value\0.
* All ENV must be 4 bytes alignment. The remaining part must fill '\0'.
*
* @note Word = 4 Bytes in this file
* @note It will has two ENV areas(Area0, Area1) when used power fail safeguard mode.
*/
/* flash ENV parameters index and size in system section */
enum {
/* data section ENV end address index in system section */
ENV_PARAM_INDEX_END_ADDR = 0,
#ifdef EF_ENV_USING_PFS_MODE
/* saved count for ENV area */
ENV_PARAM_INDEX_SAVED_COUNT,
#endif
/* data section CRC32 code index in system section */
ENV_PARAM_INDEX_DATA_CRC,
/* flash ENV parameters word size */
ENV_PARAM_WORD_SIZE,
/* flash ENV parameters byte size */
ENV_PARAM_BYTE_SIZE = ENV_PARAM_WORD_SIZE * 4,
};
/* default ENV set, must be initialized by user */
static ef_env const *default_env_set = NULL;
/* default ENV set size, must be initialized by user */
static size_t default_env_set_size = NULL;
/* ENV ram cache */
static uint32_t env_cache[ENV_USER_SETTING_SIZE / 4] = { 0 };
/* ENV start address in flash */
static uint32_t env_start_addr = NULL;
/* ENV ram cache has changed when ENV created, deleted and changed value. */
static bool env_cache_changed = false;
#ifdef EF_ENV_USING_PFS_MODE
/* current load ENV area address */
static uint32_t cur_load_area_addr = NULL;
/* next save ENV area address */
static uint32_t next_save_area_addr = NULL;
#endif
static uint32_t get_env_system_addr(void);
static uint32_t get_env_data_addr(void);
static uint32_t get_env_end_addr(void);
static void set_env_end_addr(uint32_t end_addr);
static EfErrCode write_env(const char *key, const char *value);
static char *find_env(const char *key);
static EfErrCode del_env(const char *key);
static size_t get_env_data_size(void);
static EfErrCode create_env(const char *key, const char *value);
static uint32_t calc_env_crc(void);
static bool env_crc_is_ok(void);
/**
* Flash ENV initialize.
*
* @param default_env default ENV set for user
* @param default_env_size default ENV set size
*
* @note user_size must equal with total_size in normal mode
*
* @return result
*/
EfErrCode ef_env_init(ef_env const *default_env, size_t default_env_size) {
EfErrCode result = EF_NO_ERR;
EF_ASSERT(ENV_AREA_SIZE);
EF_ASSERT(ENV_USER_SETTING_SIZE);
EF_ASSERT(EF_ERASE_MIN_SIZE);
/* must be word alignment for ENV */
EF_ASSERT(ENV_USER_SETTING_SIZE % 4 == 0);
EF_ASSERT(ENV_AREA_SIZE % 4 == 0);
EF_ASSERT(default_env);
EF_ASSERT(default_env_size < ENV_USER_SETTING_SIZE);
#ifndef EF_ENV_USING_PFS_MODE
/* total_size must be aligned with erase_min_size */
if (ENV_USER_SETTING_SIZE % EF_ERASE_MIN_SIZE == 0) {
EF_ASSERT(ENV_USER_SETTING_SIZE == ENV_AREA_SIZE);
} else {
EF_ASSERT((ENV_USER_SETTING_SIZE/EF_ERASE_MIN_SIZE + 1)*EF_ERASE_MIN_SIZE == ENV_AREA_SIZE);
}
#else
/* total_size must be aligned with erase_min_size */
if (ENV_USER_SETTING_SIZE % EF_ERASE_MIN_SIZE == 0) {
/* it has double area when used power fail safeguard mode */
EF_ASSERT(2*ENV_USER_SETTING_SIZE == ENV_AREA_SIZE);
} else {
/* it has double area when used power fail safeguard mode */
EF_ASSERT(2*(ENV_USER_SETTING_SIZE/EF_ERASE_MIN_SIZE + 1)*EF_ERASE_MIN_SIZE == ENV_AREA_SIZE);
}
#endif
env_start_addr = EF_START_ADDR;
default_env_set = default_env;
default_env_set_size = default_env_size;
EF_DEBUG("ENV start address is 0x%08X, size is %d bytes.\n", EF_START_ADDR, ENV_AREA_SIZE);
ef_load_env();
return result;
}
/**
* ENV set default.
*
* @return result
*/
EfErrCode ef_env_set_default(void){
EfErrCode result = EF_NO_ERR;
size_t i;
EF_ASSERT(default_env_set);
EF_ASSERT(default_env_set_size);
/* lock the ENV cache */
ef_port_env_lock();
/* set environment end address is at data section start address */
set_env_end_addr(get_env_data_addr());
#ifdef EF_ENV_USING_PFS_MODE
/* set saved count to default 0 */
env_cache[ENV_PARAM_INDEX_SAVED_COUNT] = 0;
#endif
/* create default ENV */
for (i = 0; i < default_env_set_size; i++) {
create_env(default_env_set[i].key, default_env_set[i].value);
}
/* unlock the ENV cache */
ef_port_env_unlock();
ef_save_env();
return result;
}
/**
* Get ENV system section start address.
*
* @return system section start address
*/
static uint32_t get_env_system_addr(void) {
#ifndef EF_ENV_USING_PFS_MODE
EF_ASSERT(env_start_addr);
return env_start_addr;
#else
EF_ASSERT(cur_load_area_addr);
return cur_load_area_addr;
#endif
}
/**
* Get ENV data section start address.
*
* @return data section start address
*/
static uint32_t get_env_data_addr(void) {
return get_env_system_addr() + ENV_PARAM_BYTE_SIZE;
}
/**
* Get ENV end address.
* It's the first word in ENV.
*
* @return ENV end address
*/
static uint32_t get_env_end_addr(void) {
/* it is the first word */
return env_cache[ENV_PARAM_INDEX_END_ADDR];
}
/**
* Set ENV end address.
* It's the first word in ENV.
*
* @param end_addr ENV end address
*/
static void set_env_end_addr(uint32_t end_addr) {
env_cache[ENV_PARAM_INDEX_END_ADDR] = end_addr;
}
/**
* Get current ENV data section size.
*
* @return size
*/
static size_t get_env_data_size(void) {
return get_env_end_addr() - get_env_data_addr();
}
/**
* Get current ENV already write bytes.
*
* @return write bytes
*/
size_t ef_get_env_write_bytes(void) {
#ifndef EF_ENV_USING_PFS_MODE
return get_env_end_addr() - env_start_addr;
#else
/* It has two ENV areas(Area0, Area1) on used power fail safeguard mode */
return 2 * (get_env_end_addr() - get_env_system_addr());
#endif
}
/**
* Write an ENV at the end of cache.
*
* @param key ENV name
* @param value ENV value
*
* @return result
*/
static EfErrCode write_env(const char *key, const char *value) {
EfErrCode result = EF_NO_ERR;
size_t ker_len = strlen(key), value_len = strlen(value), env_str_len;
char *env_cache_bak = (char *)env_cache;
/* calculate ENV storage length, contain '=' and '\0'. */
env_str_len = ker_len + value_len + 2;
if (env_str_len % 4 != 0) {
env_str_len = (env_str_len / 4 + 1) * 4;
}
/* check capacity of ENV */
if (env_str_len + get_env_data_size() >= ENV_USER_SETTING_SIZE) {
return EF_ENV_FULL;
}
/* calculate current ENV ram cache end address */
env_cache_bak += get_env_end_addr() - get_env_system_addr();
/* copy key name */
memcpy(env_cache_bak, key, ker_len);
env_cache_bak += ker_len;
/* copy equal sign */
*env_cache_bak = '=';
env_cache_bak++;
/* copy value */
memcpy(env_cache_bak, value, value_len);
env_cache_bak += value_len;
/* fill '\0' for string end sign */
*env_cache_bak = '\0';
env_cache_bak ++;
/* fill '\0' for word alignment */
memset(env_cache_bak, 0, env_str_len - (ker_len + value_len + 2));
set_env_end_addr(get_env_end_addr() + env_str_len);
/* ENV ram cache has changed */
env_cache_changed = true;
return result;
}
/**
* Find ENV.
*
* @param key ENV name
*
* @return found ENV in ram cache
*/
static char *find_env(const char *key) {
char *env_start, *env_end, *env, *found_env = NULL;
size_t key_len = strlen(key), env_len;
EF_ASSERT(env_start_addr);
if (*key == NULL) {
EF_INFO("Flash ENV name must be not empty!\n");
return NULL;
}
/* from data section start to data section end */
env_start = (char *) ((char *) env_cache + ENV_PARAM_BYTE_SIZE);
env_end = (char *) ((char *) env_cache + (get_env_end_addr() - get_env_system_addr()));
/* ENV is null */
if (env_start == env_end) {
return NULL;
}
env = env_start;
while (env < env_end) {
/* the key length must be equal */
if (!strncmp(env, key, key_len) && (env[key_len] == '=')) {
found_env = env;
break;
} else {
/* calculate ENV length, contain '\0'. */
env_len = strlen(env) + 1;
/* next ENV and word alignment */
if (env_len % 4 == 0) {
env += env_len;
} else {
env += (env_len / 4 + 1) * 4;
}
}
}
return found_env;
}
/**
* If the ENV is not exist, create it.
* @see flash_write_env
*
* @param key ENV name
* @param value ENV value
*
* @return result
*/
static EfErrCode create_env(const char *key, const char *value) {
EfErrCode result = EF_NO_ERR;
EF_ASSERT(key);
EF_ASSERT(value);
if (*key == NULL) {
EF_INFO("Flash ENV name must be not empty!\n");
return EF_ENV_NAME_ERR;
}
if (strchr(key, '=')) {
EF_INFO("Flash ENV name can't contain '='.\n");
return EF_ENV_NAME_ERR;
}
/* find ENV */
if (find_env(key)) {
EF_INFO("The name of \"%s\" is already exist.\n", key);
return EF_ENV_NAME_EXIST;
}
/* write ENV at the end of cache */
result = write_env(key, value);
return result;
}
/**
* Delete an ENV in cache.
*
* @param key ENV name
*
* @return result
*/
static EfErrCode del_env(const char *key){
EfErrCode result = EF_NO_ERR;
char *del_env = NULL;
size_t del_env_length, remain_env_length;
EF_ASSERT(key);
if (*key == NULL) {
EF_INFO("Flash ENV name must be not NULL!\n");
return EF_ENV_NAME_ERR;
}
if (strchr(key, '=')) {
EF_INFO("Flash ENV name or value can't contain '='.\n");
return EF_ENV_NAME_ERR;
}
/* find ENV */
del_env = find_env(key);
if (!del_env) {
EF_INFO("Not find \"%s\" in ENV.\n", key);
return EF_ENV_NAME_ERR;
}
del_env_length = strlen(del_env);
/* '\0' also must be as ENV length */
del_env_length ++;
/* the address must multiple of 4 */
if (del_env_length % 4 != 0) {
del_env_length = (del_env_length / 4 + 1) * 4;
}
/* calculate remain ENV length */
remain_env_length = get_env_data_size()
- (((uint32_t) del_env + del_env_length) - ((uint32_t) env_cache + ENV_PARAM_BYTE_SIZE));
/* remain ENV move forward */
memcpy(del_env, del_env + del_env_length, remain_env_length);
/* reset ENV end address */
set_env_end_addr(get_env_end_addr() - del_env_length);
/* ENV ram cache has changed */
env_cache_changed = true;
return result;
}
/**
* Set an ENV. If it value is empty, delete it.
* If not find it in ENV table, then create it.
*
* @param key ENV name
* @param value ENV value
*
* @return result
*/
EfErrCode ef_set_env(const char *key, const char *value) {
EfErrCode result = EF_NO_ERR;
char *old_env, *old_value;
/* lock the ENV cache */
ef_port_env_lock();
/* if ENV value is empty, delete it */
if (*value == NULL) {
result = del_env(key);
} else {
old_env = find_env(key);
/* If find this ENV, then compare the new value and old value. */
if (old_env) {
/* find the old value address */
old_env = strchr(old_env, '=');
old_value = old_env + 1;
/* If it is changed then delete it and recreate it */
if (strcmp(old_value, value)) {
result = del_env(key);
if (result == EF_NO_ERR) {
result = create_env(key, value);
}
}
} else {
result = create_env(key, value);
}
}
/* unlock the ENV cache */
ef_port_env_unlock();
return result;
}
/**
* Get an ENV value by key name.
*
* @param key ENV name
*
* @return value
*/
char *ef_get_env(const char *key) {
char *env = NULL, *value = NULL;
/* find ENV */
env = find_env(key);
if (env == NULL) {
return NULL;
}
/* get value address */
value = strchr(env, '=');
if (value != NULL) {
/* the equal sign next character is value */
value++;
}
return value;
}
/**
* Print ENV.
*/
void ef_print_env(void) {
uint32_t *env_cache_data_addr = env_cache + ENV_PARAM_WORD_SIZE,
*env_cache_end_addr =
(uint32_t *) (env_cache + ENV_PARAM_WORD_SIZE + get_env_data_size() / 4);
uint8_t j;
char c;
for (; env_cache_data_addr < env_cache_end_addr; env_cache_data_addr += 1) {
for (j = 0; j < 4; j++) {
c = (*env_cache_data_addr) >> (8 * j);
ef_print("%c", c);
if (c == NULL) {
ef_print("\n");
break;
}
}
}
#ifndef EF_ENV_USING_PFS_MODE
ef_print("\nENV size: %ld/%ld bytes.\n", ef_get_env_write_bytes(), ENV_AREA_SIZE);
#else
ef_print("\nENV size: %ld/%ld bytes, saved count: %ld, mode: power fail safeguard.\n",
ef_get_env_write_bytes(), ENV_AREA_SIZE, env_cache[ENV_PARAM_INDEX_SAVED_COUNT]);
#endif
}
/**
* Load flash ENV to ram.
*/
#ifndef EF_ENV_USING_PFS_MODE
void ef_load_env(void) {
uint32_t *env_cache_bak, env_end_addr;
/* read ENV end address from flash */
ef_port_read(get_env_system_addr() + ENV_PARAM_INDEX_END_ADDR * 4, &env_end_addr, 4);
/* if ENV is not initialize or flash has dirty data, set default for it */
if ((env_end_addr == 0xFFFFFFFF) || (env_end_addr < env_start_addr)
|| (env_end_addr > env_start_addr + ENV_USER_SETTING_SIZE)) {
ef_env_set_default();
} else {
/* set ENV end address */
set_env_end_addr(env_end_addr);
env_cache_bak = env_cache + ENV_PARAM_WORD_SIZE;
/* read all ENV from flash */
ef_port_read(get_env_data_addr(), env_cache_bak, get_env_data_size());
/* read ENV CRC code from flash */
ef_port_read(get_env_system_addr() + ENV_PARAM_INDEX_DATA_CRC * 4,
&env_cache[ENV_PARAM_INDEX_DATA_CRC] , 4);
/* if ENV CRC32 check is fault, set default for it */
if (!env_crc_is_ok()) {
EF_INFO("Warning: ENV CRC check failed. Set it to default.\n");
ef_env_set_default();
}
}
}
#else
void ef_load_env(void) {
uint32_t area0_start_address = env_start_addr, area1_start_address = env_start_addr
+ ENV_AREA_SIZE / 2;
uint32_t area0_end_addr, area1_end_addr, area0_crc, area1_crc, area0_saved_count, area1_saved_count;
bool area0_is_valid = true, area1_is_valid = true;
/* read ENV area end address from flash */
ef_port_read(area0_start_address + ENV_PARAM_INDEX_END_ADDR * 4, &area0_end_addr, 4);
ef_port_read(area1_start_address + ENV_PARAM_INDEX_END_ADDR * 4, &area1_end_addr, 4);
if ((area0_end_addr == 0xFFFFFFFF) || (area0_end_addr < area0_start_address)
|| (area0_end_addr > area0_start_address + ENV_USER_SETTING_SIZE)) {
area0_is_valid = false;
}
if ((area1_end_addr == 0xFFFFFFFF) || (area1_end_addr < area1_start_address)
|| (area1_end_addr > area1_start_address + ENV_USER_SETTING_SIZE)) {
area1_is_valid = false;
}
/* check area0 CRC when it is valid */
if (area0_is_valid) {
/* read ENV area0 crc32 code from flash */
ef_port_read(area0_start_address + ENV_PARAM_INDEX_DATA_CRC * 4, &area0_crc, 4);
/* read ENV from ENV area0 */
ef_port_read(area0_start_address, env_cache, area0_end_addr - area0_start_address);
/* current load ENV area address is area0 start address */
cur_load_area_addr = area0_start_address;
if (!env_crc_is_ok()) {
area0_is_valid = false;
}
}
/* check area1 CRC when it is valid */
if (area1_is_valid) {
/* read ENV area1 crc32 code from flash */
ef_port_read(area1_start_address + ENV_PARAM_INDEX_DATA_CRC * 4, &area1_crc, 4);
/* read ENV from ENV area1 */
ef_port_read(area1_start_address, env_cache, area1_end_addr - area1_start_address);
/* current load ENV area address is area1 start address */
cur_load_area_addr = area1_start_address;
if (!env_crc_is_ok()) {
area1_is_valid = false;
}
}
/* all ENV area CRC is OK then compare saved count */
if (area0_is_valid && area1_is_valid) {
/* read ENV area saved count from flash */
ef_port_read(area0_start_address + ENV_PARAM_INDEX_SAVED_COUNT * 4,
&area0_saved_count, 4);
ef_port_read(area1_start_address + ENV_PARAM_INDEX_SAVED_COUNT * 4,
&area1_saved_count, 4);
/* the bigger saved count area is valid */
if ((area0_saved_count > area1_saved_count)||((area0_saved_count == 0)&&(area1_saved_count == 0xFFFFFFFF))) {
area1_is_valid = false;
} else {
area0_is_valid = false;
}
}
if (area0_is_valid) {
/* current load ENV area address is area0 start address */
cur_load_area_addr = area0_start_address;
/* next save ENV area address is area1 start address */
next_save_area_addr = area1_start_address;
/* read all ENV from area0 */
ef_port_read(area0_start_address, env_cache, area0_end_addr - area0_start_address);
} else if (area1_is_valid) {
/* next save ENV area address is area0 start address */
next_save_area_addr = area0_start_address;
} else {
/* current load ENV area address is area1 start address */
cur_load_area_addr = area1_start_address;
/* next save ENV area address is area0 start address */
next_save_area_addr = area0_start_address;
/* set the ENV to default */
ef_env_set_default();
}
}
#endif
/**
* Save ENV to flash.
*/
EfErrCode ef_save_env(void) {
EfErrCode result = EF_NO_ERR;
uint32_t write_addr, write_size;
/* ENV ram cache has not changed don't need to save */
if (!env_cache_changed) {
return result;
}
#ifndef EF_ENV_USING_PFS_MODE
write_addr = get_env_system_addr();
write_size = get_env_end_addr() - get_env_system_addr();
/* calculate and cache CRC32 code */
env_cache[ENV_PARAM_INDEX_DATA_CRC] = calc_env_crc();
#else
write_addr = next_save_area_addr;
write_size = get_env_end_addr() - get_env_system_addr();
/* replace next_save_area_addr with cur_load_area_addr */
next_save_area_addr = cur_load_area_addr;
cur_load_area_addr = write_addr;
/* change the ENV end address to next save area address */
set_env_end_addr(write_addr + write_size);
/* ENV area saved count +1 */
env_cache[ENV_PARAM_INDEX_SAVED_COUNT]++;
/* calculate and cache CRC32 code */
env_cache[ENV_PARAM_INDEX_DATA_CRC] = calc_env_crc();
#endif
/* erase ENV */
result = ef_port_erase(write_addr, write_size);
switch (result) {
case EF_NO_ERR: {
EF_INFO("Erased ENV OK.\n");
break;
}
case EF_ERASE_ERR: {
EF_INFO("Warning: Erased ENV fault!\n");
/* will return when erase fault */
return result;
}
}
/* write ENV to flash */
result = ef_port_write(write_addr, env_cache, write_size);
switch (result) {
case EF_NO_ERR: {
EF_INFO("Saved ENV OK.\n");
break;
}
case EF_WRITE_ERR: {
EF_INFO("Warning: Saved ENV fault!\n");
break;
}
}
env_cache_changed = false;
return result;
}
/**
* Calculate the cached ENV CRC32 value.
*
* @return CRC32 value
*/
static uint32_t calc_env_crc(void) {
uint32_t crc32 = 0;
/* Calculate the ENV end address CRC32. The 4 is ENV end address bytes size. */
crc32 = ef_calc_crc32(crc32, &env_cache[ENV_PARAM_INDEX_END_ADDR], 4);
#ifdef EF_ENV_USING_PFS_MODE
/* Calculate the ENV area saved count CRC32. */
crc32 = ef_calc_crc32(crc32, &env_cache[ENV_PARAM_INDEX_SAVED_COUNT], 4);
#endif
/* Calculate the all ENV data CRC32. */
crc32 = ef_calc_crc32(crc32, &env_cache[ENV_PARAM_WORD_SIZE], get_env_data_size());
EF_DEBUG("Calculate ENV CRC32 number is 0x%08X.\n", crc32);
return crc32;
}
/**
* Check the ENV CRC32
*
* @return true is ok
*/
static bool env_crc_is_ok(void) {
if (calc_env_crc() == env_cache[ENV_PARAM_INDEX_DATA_CRC]) {
EF_DEBUG("Verify ENV CRC32 result is OK.\n");
return true;
} else {
return false;
}
}
/**
* Set and save an ENV. If set ENV is success then will save it.
*
* @param key ENV name
* @param value ENV value
*
* @return result
*/
EfErrCode ef_set_and_save_env(const char *key, const char *value) {
EfErrCode result = EF_NO_ERR;
result = ef_set_env(key, value);
if (result == EF_NO_ERR) {
result = ef_save_env();
}
return result;
}
#endif /* EF_ENV_USING_WL_MODE */
#endif /* EF_USING_ENV */

@ -0,0 +1,926 @@
/*
* This file is part of the EasyFlash Library.
*
* Copyright (c) 2015, Armink, <armink.ztl@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Function: Environment variables operating interface. (wear leveling mode)
* Created on: 2015-02-11
*/
#include <easyflash.h>
#include <string.h>
#include <stdlib.h>
#ifdef EF_USING_ENV
#ifdef EF_ENV_USING_WL_MODE
/**
* ENV area has 2 sections
* 1. System section
* Storage ENV current using data section address.
* Units: Word. Total size: @see EF_ERASE_MIN_SIZE.
* 2. Data section
* The data section storage ENV's parameters and detail.
* When an exception has occurred on flash erase or write. The current using data section
* address will move to next available position. This position depends on EF_ERASE_MIN_SIZE.
* 2.1 ENV parameters part
* It storage ENV's parameters.
* 2.2 ENV detail part
* It storage all ENV. Storage format is key=value\0.
* All ENV must be 4 bytes alignment. The remaining part must fill '\0'.
*
* @note Word = 4 Bytes in this file
* @note It will has two ENV areas(Area0, Area1) in data section when used power fail safeguard mode.
*/
/* flash ENV parameters part index and size */
enum {
/* data section ENV detail part end address index */
ENV_PARAM_PART_INDEX_END_ADDR = 0,
#ifdef EF_ENV_USING_PFS_MODE
/* saved count for ENV area */
ENV_PARAM_PART_INDEX_SAVED_COUNT,
#endif
/* data section CRC32 code index */
ENV_PARAM_PART_INDEX_DATA_CRC,
/* ENV parameters part word size */
ENV_PARAM_PART_WORD_SIZE,
/* ENV parameters part byte size */
ENV_PARAM_PART_BYTE_SIZE = ENV_PARAM_PART_WORD_SIZE * 4,
};
/* default ENV set, must be initialized by user */
static ef_env const *default_env_set = NULL;
/* default ENV set size, must be initialized by user */
static size_t default_env_set_size = NULL;
/* flash ENV data section size */
static size_t env_data_section_size = NULL;
/* ENV ram cache */
static uint32_t env_cache[ENV_USER_SETTING_SIZE / 4] = { 0 };
/* ENV start address in flash */
static uint32_t env_start_addr = NULL;
/* current using data section address */
static uint32_t cur_using_data_addr = NULL;
/* ENV ram cache has changed when ENV created, deleted and changed value. */
static bool env_cache_changed = false;
#ifdef EF_ENV_USING_PFS_MODE
/* next save ENV area address */
static uint32_t next_save_area_addr = NULL;
#endif
static uint32_t get_env_start_addr(void);
static uint32_t get_cur_using_data_addr(void);
static uint32_t get_env_detail_addr(void);
static uint32_t get_env_detail_end_addr(void);
static void set_cur_using_data_addr(uint32_t using_data_addr);
static void set_env_detail_end_addr(uint32_t end_addr);
static EfErrCode write_env(const char *key, const char *value);
static char *find_env(const char *key);
static size_t get_env_detail_size(void);
static size_t get_env_user_used_size(void);
static EfErrCode create_env(const char *key, const char *value);
static EfErrCode del_env(const char *key);
static EfErrCode save_cur_using_data_addr(uint32_t cur_data_addr);
static uint32_t calc_env_crc(void);
static bool env_crc_is_ok(void);
/**
* Flash ENV initialize.
*
* @param default_env default ENV set for user
* @param default_env_size default ENV set size
*
* @return result
*/
EfErrCode ef_env_init(ef_env const *default_env, size_t default_env_size) {
EfErrCode result = EF_NO_ERR;
EF_ASSERT(ENV_AREA_SIZE);
EF_ASSERT(ENV_USER_SETTING_SIZE);
/* must be word alignment for ENV */
EF_ASSERT(ENV_USER_SETTING_SIZE % 4 == 0);
EF_ASSERT(ENV_AREA_SIZE % 4 == 0);
EF_ASSERT(default_env);
EF_ASSERT(default_env_size < ENV_USER_SETTING_SIZE);
/* system section size is erase_min_size, so last part is data section */
env_data_section_size = ENV_AREA_SIZE - EF_ERASE_MIN_SIZE;
/* the ENV data section size should be an integral multiple of erase minimum size. */
EF_ASSERT(env_data_section_size % EF_ERASE_MIN_SIZE == 0);
#ifndef EF_ENV_USING_PFS_MODE
EF_ASSERT(env_data_section_size >= ENV_USER_SETTING_SIZE);
#else
/* it has double area when used power fail safeguard mode */
EF_ASSERT(env_data_section_size >= 2*ENV_USER_SETTING_SIZE);
EF_ASSERT((env_data_section_size / EF_ERASE_MIN_SIZE) % 2 == 0);
#endif
env_start_addr = EF_START_ADDR;
default_env_set = default_env;
default_env_set_size = default_env_size;
EF_DEBUG("ENV start address is 0x%08X, size is %d bytes.\n", EF_START_ADDR, ENV_AREA_SIZE);
ef_load_env();
return result;
}
/**
* ENV set default.
*
* @return result
*/
EfErrCode ef_env_set_default(void){
EfErrCode result = EF_NO_ERR;
size_t i;
EF_ASSERT(default_env_set);
EF_ASSERT(default_env_set_size);
/* lock the ENV cache */
ef_port_env_lock();
/* set ENV detail part end address is at ENV detail part start address */
set_env_detail_end_addr(get_env_detail_addr());
#ifdef EF_ENV_USING_PFS_MODE
/* set saved count to default 0 */
env_cache[ENV_PARAM_PART_INDEX_SAVED_COUNT] = 0;
#endif
/* create default ENV */
for (i = 0; i < default_env_set_size; i++) {
create_env(default_env_set[i].key, default_env_set[i].value);
}
/* unlock the ENV cache */
ef_port_env_unlock();
ef_save_env();
return result;
}
/**
* Get ENV start address in flash.
*
* @return ENV start address in flash
*/
static uint32_t get_env_start_addr(void) {
EF_ASSERT(env_start_addr);
return env_start_addr;
}
/**
* Get current using data section address.
*
* @return current using data section address
*/
static uint32_t get_cur_using_data_addr(void) {
EF_ASSERT(cur_using_data_addr);
return cur_using_data_addr;
}
/**
* Set current using data section address.
*
* @param using_data_addr current using data section address
*/
static void set_cur_using_data_addr(uint32_t using_data_addr) {
cur_using_data_addr = using_data_addr;
}
/**
* Get ENV detail part start address.
*
* @return detail part start address
*/
static uint32_t get_env_detail_addr(void) {
EF_ASSERT(cur_using_data_addr);
return get_cur_using_data_addr() + ENV_PARAM_PART_BYTE_SIZE;
}
/**
* Get ENV detail part end address.
* It's the first word in ENV.
*
* @return ENV end address
*/
static uint32_t get_env_detail_end_addr(void) {
/* it is the first word */
return env_cache[ENV_PARAM_PART_INDEX_END_ADDR];
}
/**
* Set ENV detail part end address.
* It's the first word in ENV.
*
* @param end_addr ENV end address
*/
static void set_env_detail_end_addr(uint32_t end_addr) {
env_cache[ENV_PARAM_PART_INDEX_END_ADDR] = end_addr;
}
/**
* Get current ENV detail part size.
*
* @return size
*/
static size_t get_env_detail_size(void) {
return get_env_detail_end_addr() - get_env_detail_addr();
}
/**
* Get current user used ENV size.
*
* @see ENV_USER_SETTING_SIZE
*
* @return size
*/
/* must be initialized */
static size_t get_env_user_used_size(void) {
return get_env_detail_end_addr() - get_cur_using_data_addr();
}
/**
* Get current ENV already write bytes.
*
* @return write bytes
*/
size_t ef_get_env_write_bytes(void) {
#ifndef EF_ENV_USING_PFS_MODE
return get_env_detail_end_addr() - get_env_start_addr();
#else
if (get_cur_using_data_addr()
< get_env_start_addr() + EF_ERASE_MIN_SIZE + env_data_section_size / 2) {
/* current using is ENV area0 */
return EF_ERASE_MIN_SIZE + 2 * (get_env_detail_end_addr() - (get_env_start_addr()
+EF_ERASE_MIN_SIZE));
} else {
/* current using is ENV area1 */
return EF_ERASE_MIN_SIZE + 2 * (get_env_detail_end_addr() - (get_env_start_addr()
+ EF_ERASE_MIN_SIZE + env_data_section_size / 2));
}
#endif
}
/**
* Write an ENV at the end of cache.
*
* @param key ENV name
* @param value ENV value
*
* @return result
*/
static EfErrCode write_env(const char *key, const char *value) {
EfErrCode result = EF_NO_ERR;
size_t ker_len = strlen(key), value_len = strlen(value), env_str_len;
char *env_cache_bak = (char *)env_cache;
/* calculate ENV storage length, contain '=' and '\0'. */
env_str_len = ker_len + value_len + 2;
if (env_str_len % 4 != 0) {
env_str_len = (env_str_len / 4 + 1) * 4;
}
/* check capacity of ENV */
if (env_str_len + get_env_detail_size() >= ENV_USER_SETTING_SIZE) {
return EF_ENV_FULL;
}
/* calculate current ENV ram cache end address */
env_cache_bak += ENV_PARAM_PART_BYTE_SIZE + get_env_detail_size();
/* copy key name */
memcpy(env_cache_bak, key, ker_len);
env_cache_bak += ker_len;
/* copy equal sign */
*env_cache_bak = '=';
env_cache_bak++;
/* copy value */
memcpy(env_cache_bak, value, value_len);
env_cache_bak += value_len;
/* fill '\0' for string end sign */
*env_cache_bak = '\0';
env_cache_bak ++;
/* fill '\0' for word alignment */
memset(env_cache_bak, 0, env_str_len - (ker_len + value_len + 2));
set_env_detail_end_addr(get_env_detail_end_addr() + env_str_len);
/* ENV ram cache has changed */
env_cache_changed = true;
return result;
}
/**
* Find ENV.
*
* @param key ENV name
*
* @return found ENV in ram cache
*/
static char *find_env(const char *key) {
char *env_start, *env_end, *env, *found_env = NULL;
size_t key_len = strlen(key), env_len;
EF_ASSERT(cur_using_data_addr);
if (*key == NULL) {
EF_INFO("Flash ENV name must be not empty!\n");
return NULL;
}
/* from data section start to data section end */
env_start = (char *) ((char *) env_cache + ENV_PARAM_PART_BYTE_SIZE);
env_end = (char *) ((char *) env_cache + ENV_PARAM_PART_BYTE_SIZE + get_env_detail_size());
/* ENV is null */
if (env_start == env_end) {
return NULL;
}
env = env_start;
while (env < env_end) {
/* the key length must be equal */
if (!strncmp(env, key, key_len) && (env[key_len] == '=')) {
found_env = env;
break;
} else {
/* calculate ENV length, contain '\0'. */
env_len = strlen(env) + 1;
/* next ENV and word alignment */
if (env_len % 4 == 0) {
env += env_len;
} else {
env += (env_len / 4 + 1) * 4;
}
}
}
return found_env;
}
/**
* If the ENV is not exist, create it.
* @see flash_write_env
*
* @param key ENV name
* @param value ENV value
*
* @return result
*/
static EfErrCode create_env(const char *key, const char *value) {
EfErrCode result = EF_NO_ERR;
EF_ASSERT(key);
EF_ASSERT(value);
if (*key == NULL) {
EF_INFO("Flash ENV name must be not empty!\n");
return EF_ENV_NAME_ERR;
}
if (strchr(key, '=')) {
EF_INFO("Flash ENV name can't contain '='.\n");
return EF_ENV_NAME_ERR;
}
/* find ENV */
if (find_env(key)) {
EF_INFO("The name of \"%s\" is already exist.\n", key);
return EF_ENV_NAME_EXIST;
}
/* write ENV at the end of cache */
result = write_env(key, value);
return result;
}
/**
* Delete an ENV in cache.
*
* @param key ENV name
*
* @return result
*/
static EfErrCode del_env(const char *key) {
EfErrCode result = EF_NO_ERR;
char *del_env = NULL;
size_t del_env_length, remain_env_length;
EF_ASSERT(key);
if (*key == NULL) {
EF_INFO("Flash ENV name must be not NULL!\n");
return EF_ENV_NAME_ERR;
}
if (strchr(key, '=')) {
EF_INFO("Flash ENV name or value can't contain '='.\n");
return EF_ENV_NAME_ERR;
}
/* find ENV */
del_env = find_env(key);
if (!del_env) {
EF_INFO("Not find \"%s\" in ENV.\n", key);
return EF_ENV_NAME_ERR;
}
del_env_length = strlen(del_env);
/* '\0' also must be as ENV length */
del_env_length ++;
/* the address must multiple of 4 */
if (del_env_length % 4 != 0) {
del_env_length = (del_env_length / 4 + 1) * 4;
}
/* calculate remain ENV length */
remain_env_length = get_env_detail_size()
- (((uint32_t) del_env + del_env_length) - ((uint32_t) env_cache + ENV_PARAM_PART_BYTE_SIZE));
/* remain ENV move forward */
memcpy(del_env, del_env + del_env_length, remain_env_length);
/* reset ENV end address */
set_env_detail_end_addr(get_env_detail_end_addr() - del_env_length);
/* ENV ram cache has changed */
env_cache_changed = true;
return result;
}
/**
* Set an ENV. If it value is empty, delete it.
* If not find it in ENV table, then create it.
*
* @param key ENV name
* @param value ENV value
*
* @return result
*/
EfErrCode ef_set_env(const char *key, const char *value) {
EfErrCode result = EF_NO_ERR;
char *old_env, *old_value;
/* lock the ENV cache */
ef_port_env_lock();
/* if ENV value is empty, delete it */
if (*value == NULL) {
result = del_env(key);
} else {
old_env = find_env(key);
/* If find this ENV, then compare the new value and old value. */
if (old_env) {
/* find the old value address */
old_env = strchr(old_env, '=');
old_value = old_env + 1;
/* If it is changed then delete it and recreate it */
if (strcmp(old_value, value)) {
result = del_env(key);
if (result == EF_NO_ERR) {
result = create_env(key, value);
}
}
} else {
result = create_env(key, value);
}
}
/* unlock the ENV cache */
ef_port_env_unlock();
return result;
}
/**
* Get an ENV value by key name.
*
* @param key ENV name
*
* @return value
*/
char *ef_get_env(const char *key) {
char *env = NULL, *value = NULL;
/* find ENV */
env = find_env(key);
if (env == NULL) {
return NULL;
}
/* get value address */
value = strchr(env, '=');
if (value != NULL) {
/* the equal sign next character is value */
value++;
}
return value;
}
/**
* Print ENV.
*/
void ef_print_env(void) {
uint32_t *env_cache_detail_addr = env_cache + ENV_PARAM_PART_WORD_SIZE, *env_cache_end_addr =
(uint32_t *) (env_cache + ENV_PARAM_PART_WORD_SIZE + get_env_detail_size() / 4);
uint8_t j;
char c;
for (; env_cache_detail_addr < env_cache_end_addr; env_cache_detail_addr += 1) {
for (j = 0; j < 4; j++) {
c = (*env_cache_detail_addr) >> (8 * j);
ef_print("%c", c);
if (c == NULL) {
ef_print("\n");
break;
}
}
}
#ifndef EF_ENV_USING_PFS_MODE
ef_print("\nENV size: %ld/%ld bytes, write bytes %ld/%ld, mode: wear leveling.\n",
get_env_user_used_size(), ENV_USER_SETTING_SIZE, ef_get_env_write_bytes(),
ENV_AREA_SIZE);
#else
ef_print("\nENV size: %ld/%ld bytes, write bytes %ld/%ld, saved count: %ld, mode: wear leveling and power fail safeguard.\n",
get_env_user_used_size(), ENV_USER_SETTING_SIZE, ef_get_env_write_bytes(),
ENV_AREA_SIZE, env_cache[ENV_PARAM_PART_INDEX_SAVED_COUNT]);
#endif
}
/**
* Load flash ENV to ram.
*/
#ifndef EF_ENV_USING_PFS_MODE
void ef_load_env(void) {
uint32_t *env_cache_bak, env_end_addr, using_data_addr;
/* read current using data section address */
ef_port_read(get_env_start_addr(), &using_data_addr, 4);
/* if ENV is not initialize or flash has dirty data, set default for it */
if ((using_data_addr == 0xFFFFFFFF)
|| (using_data_addr > get_env_start_addr() + ENV_AREA_SIZE)
|| (using_data_addr < get_env_start_addr() + EF_ERASE_MIN_SIZE)) {
/* initialize current using data section address */
set_cur_using_data_addr(get_env_start_addr() + EF_ERASE_MIN_SIZE);
/* save current using data section address to flash*/
save_cur_using_data_addr(get_cur_using_data_addr());
/* set default ENV */
ef_env_set_default();
} else {
/* set current using data section address */
set_cur_using_data_addr(using_data_addr);
/* read ENV detail part end address from flash */
ef_port_read(get_cur_using_data_addr() + ENV_PARAM_PART_INDEX_END_ADDR * 4, &env_end_addr, 4);
/* if ENV end address has error, set default for ENV */
if (env_end_addr > get_env_start_addr() + ENV_AREA_SIZE) {
ef_env_set_default();
} else {
/* set ENV detail part end address */
set_env_detail_end_addr(env_end_addr);
env_cache_bak = env_cache + ENV_PARAM_PART_WORD_SIZE;
/* read all ENV from flash */
ef_port_read(get_env_detail_addr(), env_cache_bak, get_env_detail_size());
/* read ENV CRC code from flash */
ef_port_read(get_cur_using_data_addr() + ENV_PARAM_PART_INDEX_DATA_CRC * 4,
&env_cache[ENV_PARAM_PART_INDEX_DATA_CRC], 4);
/* if ENV CRC32 check is fault, set default for it */
if (!env_crc_is_ok()) {
EF_INFO("Warning: ENV CRC check failed. Set it to default.\n");
ef_env_set_default();
}
}
}
}
#else
void ef_load_env(void) {
/* ENV area0 current using address default value */
uint32_t area0_default_cur_using_addr = get_env_start_addr() + EF_ERASE_MIN_SIZE;
/* ENV area1 current using address default value */
uint32_t area1_default_cur_using_addr = area0_default_cur_using_addr + env_data_section_size / 2;
uint32_t area0_cur_using_addr, area1_cur_using_addr, area0_end_addr, area1_end_addr;
uint32_t area0_crc, area1_crc, area0_saved_count, area1_saved_count;
bool area0_is_valid = true, area1_is_valid = true;
/* read ENV area0 and area1 current using address */
ef_port_read(get_env_start_addr(), &area0_cur_using_addr, 4);
ef_port_read(get_env_start_addr() + 4, &area1_cur_using_addr, 4);
/* if ENV is not initialize or flash has dirty data, set it isn't valid */
if ((area0_cur_using_addr == 0xFFFFFFFF)
|| (area0_cur_using_addr > get_env_start_addr() + ENV_AREA_SIZE)
|| (area0_cur_using_addr < get_env_start_addr() + EF_ERASE_MIN_SIZE)) {
area0_is_valid = false;
}
if ((area1_cur_using_addr == 0xFFFFFFFF)
|| (area1_cur_using_addr > get_env_start_addr() + ENV_AREA_SIZE)
|| (area1_cur_using_addr < get_env_start_addr() + EF_ERASE_MIN_SIZE)) {
area1_is_valid = false;
}
/* check area0 end address when it is valid */
if (area0_is_valid) {
/* read ENV area end address from flash */
ef_port_read(area0_cur_using_addr + ENV_PARAM_PART_INDEX_END_ADDR * 4, &area0_end_addr, 4);
if ((area0_end_addr == 0xFFFFFFFF) || (area0_end_addr < area0_cur_using_addr)
|| (area0_end_addr > area0_cur_using_addr + ENV_USER_SETTING_SIZE)) {
area0_is_valid = false;
}
}
/* check area1 end address when it is valid */
if (area1_is_valid) {
/* read ENV area end address from flash */
ef_port_read(area1_cur_using_addr + ENV_PARAM_PART_INDEX_END_ADDR * 4, &area1_end_addr, 4);
if ((area1_end_addr == 0xFFFFFFFF) || (area1_end_addr < area1_cur_using_addr)
|| (area1_end_addr > area1_cur_using_addr + ENV_USER_SETTING_SIZE)) {
area1_is_valid = false;
}
}
/* check area0 CRC when it is valid */
if (area0_is_valid) {
/* read ENV area0 crc32 code from flash */
ef_port_read(area0_cur_using_addr + ENV_PARAM_PART_INDEX_DATA_CRC * 4, &area0_crc, 4);
/* read ENV from ENV area0 */
ef_port_read(area0_cur_using_addr, env_cache, area0_end_addr - area0_cur_using_addr);
/* current using data section address is area0 current using data section address */
set_cur_using_data_addr(area0_cur_using_addr);
if (!env_crc_is_ok()) {
area0_is_valid = false;
}
}
/* check area1 CRC when it is valid */
if (area1_is_valid) {
/* read ENV area1 crc32 code from flash */
ef_port_read(area1_cur_using_addr + ENV_PARAM_PART_INDEX_DATA_CRC * 4, &area1_crc, 4);
/* read ENV from ENV area1 */
ef_port_read(area1_cur_using_addr, env_cache, area1_end_addr - area1_cur_using_addr);
/* current using data section address is area1 current using data section address */
set_cur_using_data_addr(area1_cur_using_addr);
if (!env_crc_is_ok()) {
area1_is_valid = false;
}
}
/* all ENV area CRC is OK then compare saved count */
if (area0_is_valid && area1_is_valid) {
/* read ENV area saved count from flash */
ef_port_read(area0_cur_using_addr + ENV_PARAM_PART_INDEX_SAVED_COUNT * 4,
&area0_saved_count, 4);
ef_port_read(area1_cur_using_addr + ENV_PARAM_PART_INDEX_SAVED_COUNT * 4,
&area1_saved_count, 4);
/* the bigger saved count area is valid */
if ((area0_saved_count > area1_saved_count)||((area0_saved_count == 0)&&(area1_saved_count == 0xFFFFFFFF))) {
area1_is_valid = false;
} else {
area0_is_valid = false;
}
}
if (area0_is_valid) {
/* current using data section address is area0 current using data section address */
set_cur_using_data_addr(area0_cur_using_addr);
/* next save ENV area address is area1 current using address default value */
next_save_area_addr = area1_default_cur_using_addr;
/* read all ENV from area0 */
ef_port_read(area0_cur_using_addr, env_cache, area0_end_addr - area0_cur_using_addr);
} else if (area1_is_valid) {
/* next save ENV area address is area0 current using address default value */
next_save_area_addr = area0_default_cur_using_addr;
} else {
/* current using data section address is area1 current using address default value */
set_cur_using_data_addr(area1_default_cur_using_addr);
/* next save ENV area address default is area0 current using address default value */
next_save_area_addr = area0_default_cur_using_addr;
/* save current using data section address to flash*/
save_cur_using_data_addr(area0_default_cur_using_addr);
save_cur_using_data_addr(area1_default_cur_using_addr);
/* set the ENV to default */
ef_env_set_default();
}
}
#endif
/**
* Save ENV to flash.
*/
EfErrCode ef_save_env(void) {
EfErrCode result = EF_NO_ERR;
uint32_t cur_using_addr_bak, move_offset_addr;
size_t env_used_size = get_env_user_used_size();
/* ENV ram cache has not changed don't need to save */
if (!env_cache_changed) {
return result;
}
#ifndef EF_ENV_USING_PFS_MODE
cur_using_addr_bak = get_cur_using_data_addr();
#else
cur_using_addr_bak = next_save_area_addr;
/* replace next_save_area_addr with cur_using_data_addr */
next_save_area_addr = get_cur_using_data_addr();
set_cur_using_data_addr(cur_using_addr_bak);
/* change the ENV detail end address to next save area address */
set_env_detail_end_addr(get_cur_using_data_addr() + env_used_size);
/* ENV area saved count +1 */
env_cache[ENV_PARAM_PART_INDEX_SAVED_COUNT]++;
#endif
/* wear leveling process, automatic move ENV to next available position */
while (get_cur_using_data_addr() + env_used_size < get_env_start_addr() + ENV_AREA_SIZE) {
/* calculate and cache CRC32 code */
env_cache[ENV_PARAM_PART_INDEX_DATA_CRC] = calc_env_crc();
/* erase ENV */
result = ef_port_erase(get_cur_using_data_addr(), env_used_size);
switch (result) {
case EF_NO_ERR: {
EF_INFO("Erased ENV OK.\n");
break;
}
case EF_ERASE_ERR: {
EF_INFO("Warning: Erased ENV fault!\n");
EF_INFO("Moving ENV to next available position.\n");
/* Calculate move offset address.
* Current strategy is optimistic. It will offset the flash erasure minimum size.
*/
move_offset_addr = EF_ERASE_MIN_SIZE;
/* calculate and set next available data section address */
set_cur_using_data_addr(get_cur_using_data_addr() + move_offset_addr);
/* calculate and set next available ENV detail part end address */
set_env_detail_end_addr(get_env_detail_end_addr() + move_offset_addr);
continue;
}
}
/* write ENV to flash */
result = ef_port_write(get_cur_using_data_addr(), env_cache, env_used_size);
switch (result) {
case EF_NO_ERR: {
EF_INFO("Saved ENV OK.\n");
break;
}
case EF_WRITE_ERR: {
EF_INFO("Warning: Saved ENV fault!\n");
EF_INFO("Moving ENV to next available position.\n");
/* Calculate move offset address.
* Current strategy is optimistic. It will offset the flash erasure minimum size.
*/
move_offset_addr = EF_ERASE_MIN_SIZE;
/* calculate and set next available data section address */
set_cur_using_data_addr(get_cur_using_data_addr() + move_offset_addr);
/* calculate and set next available ENV detail part end address */
set_env_detail_end_addr(get_env_detail_end_addr() + move_offset_addr);
continue;
}
}
/* save ENV success */
if (result == EF_NO_ERR) {
break;
}
}
if (get_cur_using_data_addr() + env_used_size < get_env_start_addr() + ENV_AREA_SIZE) {
/* current using data section address has changed, save it */
if (get_cur_using_data_addr() != cur_using_addr_bak) {
save_cur_using_data_addr(get_cur_using_data_addr());
}
} else {
result = EF_ENV_FULL;
EF_INFO("Error: The flash has no available space to save ENV.\n");
/* clear current using data section address on flash */
save_cur_using_data_addr(0xFFFFFFFF);
}
env_cache_changed = false;
return result;
}
/**
* Calculate the cached ENV CRC32 value.
*
* @return CRC32 value
*/
static uint32_t calc_env_crc(void) {
uint32_t crc32 = 0;
/* Calculate the ENV end address and all ENV data CRC32.
* The 4 is ENV end address bytes size. */
crc32 = ef_calc_crc32(crc32, &env_cache[ENV_PARAM_PART_INDEX_END_ADDR], 4);
crc32 = ef_calc_crc32(crc32, &env_cache[ENV_PARAM_PART_WORD_SIZE], get_env_detail_size());
EF_DEBUG("Calculate ENV CRC32 number is 0x%08X.\n", crc32);
return crc32;
}
/**
* Check the ENV CRC32
*
* @return true is ok
*/
static bool env_crc_is_ok(void) {
if (calc_env_crc() == env_cache[ENV_PARAM_PART_INDEX_DATA_CRC]) {
EF_DEBUG("Verify ENV CRC32 result is OK.\n");
return true;
} else {
return false;
}
}
/**
* Save current using data section address to flash.
*
* @param cur_data_addr current using data section address
*
* @return result
*/
#ifndef EF_ENV_USING_PFS_MODE
static EfErrCode save_cur_using_data_addr(uint32_t cur_data_addr) {
EfErrCode result = EF_NO_ERR;
/* erase ENV system section */
result = ef_port_erase(get_env_start_addr(), 4);
if (result == EF_NO_ERR) {
/* write current using data section address to flash */
result = ef_port_write(get_env_start_addr(), &cur_data_addr, 4);
if (result == EF_WRITE_ERR) {
EF_INFO("Error: Write system section fault!\n");
EF_INFO("Note: The ENV can not be used.\n");
}
} else {
EF_INFO("Error: Erased system section fault!\n");
EF_INFO("Note: The ENV can not be used\n");
}
return result;
}
#else
static EfErrCode save_cur_using_data_addr(uint32_t cur_data_addr) {
EfErrCode result = EF_NO_ERR;
uint32_t cur_using_addr[2];
/* read area0 and area1 current using data section address for backup */
ef_port_read(get_env_start_addr(), &cur_using_addr[0], 4);
ef_port_read(get_env_start_addr() + 4, &cur_using_addr[1], 4);
if (cur_data_addr < get_env_start_addr() + EF_ERASE_MIN_SIZE + env_data_section_size / 2){
/* current using data section is in ENV area0 */
cur_using_addr[0] = cur_data_addr;
} else {
/* current using data section is in ENV area1 */
cur_using_addr[1] = cur_data_addr;
}
/* erase ENV system section */
result = ef_port_erase(get_env_start_addr(), 8);
if (result == EF_NO_ERR) {
/* write area0 and area1 current using data section address to flash */
result = ef_port_write(get_env_start_addr(), cur_using_addr, 8);
if (result == EF_WRITE_ERR) {
EF_INFO("Error: Write system section fault!\n");
EF_INFO("Note: The ENV can not be used.\n");
}
} else {
EF_INFO("Error: Erased system section fault!\n");
EF_INFO("Note: The ENV can not be used\n");
}
return result;
}
#endif
/**
* Set and save an ENV. If set ENV is success then will save it.
*
* @param key ENV name
* @param value ENV value
*
* @return result
*/
EfErrCode ef_set_and_save_env(const char *key, const char *value) {
EfErrCode result = EF_NO_ERR;
result = ef_set_env(key, value);
if (result == EF_NO_ERR) {
result = ef_save_env();
}
return result;
}
#endif /* EF_ENV_USING_WL_MODE */
#endif /* EF_USING_ENV */

@ -0,0 +1,265 @@
/*
* This file is part of the EasyFlash Library.
*
* Copyright (c) 2015, Armink, <armink.ztl@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Function: IAP(In-Application Programming) operating interface.
* Created on: 2015-01-05
*/
#include <easyflash.h>
#ifdef EF_USING_IAP
/* IAP section backup application section start address in flash */
static uint32_t bak_app_start_addr = NULL;
static uint32_t get_bak_app_start_addr(void);
/**
* Flash IAP function initialize.
*
* @return result
*/
EfErrCode ef_iap_init(void) {
EfErrCode result = EF_NO_ERR;
bak_app_start_addr = EF_START_ADDR ;
#if defined(EF_USING_ENV)
bak_app_start_addr += ENV_AREA_SIZE;
#endif
#if defined(EF_USING_LOG)
bak_app_start_addr += LOG_AREA_SIZE;
#endif
return result;
}
/**
* Erase backup area application data.
*
* @param app_size application size
*
* @return result
*/
EfErrCode ef_erase_bak_app(size_t app_size) {
EfErrCode result = EF_NO_ERR;
result = ef_port_erase(get_bak_app_start_addr(), app_size);
switch (result) {
case EF_NO_ERR: {
EF_INFO("Erased backup area application OK.\n");
break;
}
case EF_ERASE_ERR: {
EF_INFO("Warning: Erase backup area application fault!\n");
/* will return when erase fault */
return result;
}
}
return result;
}
/**
* Erase user old application
*
* @param user_app_addr application entry address
* @param app_size application size
*
* @return result
*/
EfErrCode ef_erase_user_app(uint32_t user_app_addr, size_t app_size) {
EfErrCode result = EF_NO_ERR;
result = ef_port_erase(user_app_addr, app_size);
switch (result) {
case EF_NO_ERR: {
EF_INFO("Erased user application OK.\n");
break;
}
case EF_ERASE_ERR: {
EF_INFO("Warning: Erase user application fault!\n");
/* will return when erase fault */
return result;
}
}
return result;
}
/**
* Erase old bootloader
*
* @param bl_addr bootloader entry address
* @param bl_size bootloader size
*
* @return result
*/
EfErrCode ef_erase_bl(uint32_t bl_addr, size_t bl_size) {
EfErrCode result = EF_NO_ERR;
result = ef_port_erase(bl_addr, bl_size);
switch (result) {
case EF_NO_ERR: {
EF_INFO("Erased bootloader OK.\n");
break;
}
case EF_ERASE_ERR: {
EF_INFO("Warning: Erase bootloader fault!\n");
/* will return when erase fault */
return result;
}
}
return result;
}
/**
* Write data of application to backup area.
*
* @param data a part of application
* @param size data size
* @param cur_size current write application size
* @param total_size application total size
*
* @return result
*/
EfErrCode ef_write_data_to_bak(uint8_t *data, size_t size, size_t *cur_size,
size_t total_size) {
EfErrCode result = EF_NO_ERR;
/* make sure don't write excess data */
if (*cur_size + size > total_size) {
size = total_size - *cur_size;
}
result = ef_port_write(get_bak_app_start_addr() + *cur_size, (uint32_t *) data, size);
switch (result) {
case EF_NO_ERR: {
*cur_size += size;
EF_INFO("Write data to backup area OK.\n");
break;
}
case EF_WRITE_ERR: {
EF_INFO("Warning: Write data to backup area fault!\n");
break;
}
}
return result;
}
/**
* Copy backup area application to application entry.
*
* @param user_app_addr application entry address
* @param app_size application size
*
* @return result
*/
EfErrCode ef_copy_app_from_bak(uint32_t user_app_addr, size_t app_size) {
size_t cur_size;
uint32_t app_cur_addr, bak_cur_addr;
EfErrCode result = EF_NO_ERR;
/* 32 words size buffer */
uint32_t buff[32];
/* cycle copy data */
for (cur_size = 0; cur_size < app_size; cur_size += sizeof(buff)) {
app_cur_addr = user_app_addr + cur_size;
bak_cur_addr = get_bak_app_start_addr() + cur_size;
ef_port_read(bak_cur_addr, buff, sizeof(buff));
result = ef_port_write(app_cur_addr, buff, sizeof(buff));
if (result != EF_NO_ERR) {
break;
}
}
switch (result) {
case EF_NO_ERR: {
EF_INFO("Write data to application entry OK.\n");
break;
}
case EF_WRITE_ERR: {
EF_INFO("Warning: Write data to application entry fault!\n");
break;
}
}
return result;
}
/**
* Copy backup area bootloader to bootloader entry.
*
* @param bl_addr bootloader entry address
* @param bl_size bootloader size
*
* @return result
*/
EfErrCode ef_copy_bl_from_bak(uint32_t bl_addr, size_t bl_size) {
size_t cur_size;
uint32_t bl_cur_addr, bak_cur_addr;
EfErrCode result = EF_NO_ERR;
/* 32 words buffer */
uint32_t buff[32];
/* cycle copy data by 32bytes buffer */
for (cur_size = 0; cur_size < bl_size; cur_size += sizeof(buff)) {
bl_cur_addr = bl_addr + cur_size;
bak_cur_addr = get_bak_app_start_addr() + cur_size;
ef_port_read(bak_cur_addr, buff, sizeof(buff));
result = ef_port_write(bl_cur_addr, buff, sizeof(buff));
if (result != EF_NO_ERR) {
break;
}
}
switch (result) {
case EF_NO_ERR: {
EF_INFO("Write data to bootloader entry OK.\n");
break;
}
case EF_WRITE_ERR: {
EF_INFO("Warning: Write data to bootloader entry fault!\n");
break;
}
}
return result;
}
/**
* Get IAP section start address in flash.
*
* @return size
*/
static uint32_t get_bak_app_start_addr(void) {
EF_ASSERT(bak_app_start_addr);
return bak_app_start_addr;
}
#endif /* EF_USING_IAP */

@ -0,0 +1,439 @@
/*
* This file is part of the EasyFlash Library.
*
* Copyright (c) 2015, Armink, <armink.ztl@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Function: Save logs to flash.
* Created on: 2015-06-04
*/
#include <easyflash.h>
#ifdef EF_USING_LOG
/* the stored logs start address and end address. It's like a ring buffer which implement by flash. */
static uint32_t log_start_addr = 0, log_end_addr = 0;
/* saved log area address for flash */
static uint32_t log_area_start_addr = 0;
/* initialize OK flag */
static bool init_ok = false;
static void find_start_and_end_addr(void);
static uint32_t get_next_flash_sec_addr(uint32_t cur_addr);
/**
* The flash save log function initialize.
*
* @return result
*/
EfErrCode ef_log_init(void) {
EfErrCode result = EF_NO_ERR;
EF_ASSERT(LOG_AREA_SIZE);
EF_ASSERT(EF_ERASE_MIN_SIZE);
/* the log area size must be an integral multiple of erase minimum size. */
EF_ASSERT(LOG_AREA_SIZE % EF_ERASE_MIN_SIZE == 0);
/* the log area size must be more than twice of EF_ERASE_MIN_SIZE */
EF_ASSERT(LOG_AREA_SIZE / EF_ERASE_MIN_SIZE >= 2);
#ifdef EF_USING_ENV
log_area_start_addr = EF_START_ADDR + ENV_AREA_SIZE;
#else
log_area_start_addr = EF_START_ADDR;
#endif
/* find the log store start address and end address */
find_start_and_end_addr();
/* initialize OK */
init_ok = true;
return result;
}
/**
* Find the log store start address and end address.
* It's like a ring buffer which implement by flash.
* The flash log area has two state when find start address and end address.
* state 1 state 2
* |============| |============|
* log area start--> |############| <-- start address |############| <-- end address
* |############| | empty |
* |------------| |------------|
* |############| |############| <-- start address
* |############| |############|
* |------------| |------------|
* | . | | . |
* | . | | . |
* | . | | . |
* |------------| |------------|
* |############| <-- end address |############|
* | empty | |############|
* log area end --> |============| |============|
*
* LOG_AREA_SIZE = log area end - log area star
*
*/
static void find_start_and_end_addr(void) {
size_t cur_size = 0;
FlashSecrorStatus cur_sec_status, last_sec_status;
uint32_t cur_using_sec_addr = 0;
/* all status sector counts */
size_t empty_sec_counts = 0, using_sec_counts = 0, full_sector_counts = 0;
/* total sector number */
size_t total_sec_num = LOG_AREA_SIZE / EF_ERASE_MIN_SIZE;
/* see comment of find_start_and_end_addr function */
uint8_t cur_log_sec_state = 0;
/* get the first sector status */
cur_sec_status = ef_get_sector_status(log_area_start_addr, EF_ERASE_MIN_SIZE);
last_sec_status = cur_sec_status;
for (cur_size = EF_ERASE_MIN_SIZE; cur_size < LOG_AREA_SIZE; cur_size += EF_ERASE_MIN_SIZE) {
/* get current sector status */
cur_sec_status = ef_get_sector_status(log_area_start_addr + cur_size, EF_ERASE_MIN_SIZE);
/* compare last and current status */
switch (last_sec_status) {
case FLASH_SECTOR_EMPTY: {
switch (cur_sec_status) {
case FLASH_SECTOR_EMPTY:
break;
case FLASH_SECTOR_USING:
EF_DEBUG("Error: Log area error! Now will clean all log area.\n");
ef_log_clean();
return;
case FLASH_SECTOR_FULL:
EF_DEBUG("Error: Log area error! Now will clean all log area.\n");
ef_log_clean();
return;
}
empty_sec_counts++;
break;
}
case FLASH_SECTOR_USING: {
switch (cur_sec_status) {
case FLASH_SECTOR_EMPTY:
/* like state 1 */
cur_log_sec_state = 1;
log_start_addr = log_area_start_addr;
cur_using_sec_addr = log_area_start_addr + cur_size - EF_ERASE_MIN_SIZE;
break;
case FLASH_SECTOR_USING:
EF_DEBUG("Error: Log area error! Now will clean all log area.\n");
ef_log_clean();
return;
case FLASH_SECTOR_FULL:
/* like state 2 */
cur_log_sec_state = 2;
log_start_addr = log_area_start_addr + cur_size;
cur_using_sec_addr = log_area_start_addr + cur_size - EF_ERASE_MIN_SIZE;
break;
}
using_sec_counts++;
break;
}
case FLASH_SECTOR_FULL: {
switch (cur_sec_status) {
case FLASH_SECTOR_EMPTY:
/* like state 1 */
if (cur_log_sec_state == 2) {
EF_DEBUG("Error: Log area error! Now will clean all log area.\n");
ef_log_clean();
return;
} else {
cur_log_sec_state = 1;
log_start_addr = log_area_start_addr;
/* word alignment */
log_end_addr = log_area_start_addr + cur_size - 4;
cur_using_sec_addr = log_area_start_addr + cur_size - EF_ERASE_MIN_SIZE;
}
break;
case FLASH_SECTOR_USING:
if(total_sec_num <= 2) {
/* like state 1 */
cur_log_sec_state = 1;
log_start_addr = log_area_start_addr;
cur_using_sec_addr = log_area_start_addr + cur_size;
} else {
/* state 1 or 2*/
}
break;
case FLASH_SECTOR_FULL:
break;
}
full_sector_counts++;
break;
}
}
last_sec_status = cur_sec_status;
}
/* the last sector status counts */
if (cur_sec_status == FLASH_SECTOR_EMPTY) {
empty_sec_counts++;
} else if (cur_sec_status == FLASH_SECTOR_USING) {
using_sec_counts++;
} else if (cur_sec_status == FLASH_SECTOR_FULL) {
full_sector_counts++;
}
if (using_sec_counts > 1) {
EF_DEBUG("Error: Log area error! Now will clean all log area.\n");
ef_log_clean();
return;
} else if (empty_sec_counts == total_sec_num) {
log_start_addr = log_end_addr = log_area_start_addr;
} else if (full_sector_counts == total_sec_num) {
/* this state is almost impossible */
EF_DEBUG("Error: Log area error! Now will clean all log area.\n");
ef_log_clean();
return;
} else if (((cur_log_sec_state == 1) && (cur_using_sec_addr != 0))
|| (cur_log_sec_state == 2)) {
/* find the end address */
log_end_addr = ef_find_sec_using_end_addr(cur_using_sec_addr, EF_ERASE_MIN_SIZE);
}
}
/**
* Get log used flash total size.
*
* @return log used flash total size
*/
size_t ef_log_get_used_size(void) {
EF_ASSERT(log_start_addr);
EF_ASSERT(log_end_addr);
/* must be call this function after initialize OK */
EF_ASSERT(init_ok);
if (log_start_addr < log_end_addr) {
return log_end_addr - log_start_addr + 4;
} else if (log_start_addr > log_end_addr) {
return LOG_AREA_SIZE - (log_start_addr - log_end_addr) + 4;
} else {
return 0;
}
}
/**
* Read log from flash.
*
* @param index index for saved log.
* Minimum index is 0.
* Maximum index is log used flash total size - 1.
* @param log the log which will read from flash
* @param size read bytes size
*
* @return result
*/
EfErrCode ef_log_read(size_t index, uint32_t *log, size_t size) {
EfErrCode result = EF_NO_ERR;
size_t cur_using_size = ef_log_get_used_size();
size_t read_size_temp = 0;
EF_ASSERT(size % 4 == 0);
EF_ASSERT(index + size <= cur_using_size);
/* must be call this function after initialize OK */
EF_ASSERT(init_ok);
if (!size) {
return result;
}
if (log_start_addr < log_end_addr) {
result = ef_port_read(log_area_start_addr + index, log, size);
} else if (log_start_addr > log_end_addr) {
if (log_start_addr + index + size <= log_area_start_addr + LOG_AREA_SIZE) {
/* Flash log area
* |--------------|
* log_area_start_addr --> |##############|
* |##############|
* |##############|
* |--------------|
* |##############|
* |##############|
* |##############| <-- log_end_addr
* |--------------|
* log_start_addr --> |##############|
* read start --> |**************| <-- read end
* |##############|
* |--------------|
*
* read from (log_start_addr + index) to (log_start_addr + index + size)
*/
result = ef_port_read(log_start_addr + index, log, size);
} else if (log_start_addr + index < log_area_start_addr + LOG_AREA_SIZE) {
/* Flash log area
* |--------------|
* log_area_start_addr --> |**************| <-- read end
* |##############|
* |##############|
* |--------------|
* |##############|
* |##############|
* |##############| <-- log_end_addr
* |--------------|
* log_start_addr --> |##############|
* read start --> |**************|
* |**************|
* |--------------|
* read will by 2 steps
* step1: read from (log_start_addr + index) to flash log area end address
* step2: read from flash log area start address to read size's end address
*/
read_size_temp = (log_area_start_addr + LOG_AREA_SIZE) - (log_start_addr + index);
result = ef_port_read(log_start_addr + index, log, read_size_temp);
if (result == EF_NO_ERR) {
result = ef_port_read(log_area_start_addr, log + read_size_temp,
size - read_size_temp);
}
} else {
/* Flash log area
* |--------------|
* log_area_start_addr --> |##############|
* read start --> |**************|
* |**************| <-- read end
* |--------------|
* |##############|
* |##############|
* |##############| <-- log_end_addr
* |--------------|
* log_start_addr --> |##############|
* |##############|
* |##############|
* |--------------|
* read from (log_start_addr + index - LOG_AREA_SIZE) to read size's end address
*/
result = ef_port_read(log_start_addr + index - LOG_AREA_SIZE, log, size);
}
}
return result;
}
/**
* Write log to flash.
*
* @param log the log which will be write to flash
* @param size write bytes size
*
* @return result
*/
EfErrCode ef_log_write(const uint32_t *log, size_t size) {
EfErrCode result = EF_NO_ERR;
size_t cur_using_size = ef_log_get_used_size(), write_size = 0, writable_size = 0;
uint32_t write_addr, erase_addr;
EF_ASSERT(size % 4 == 0);
/* must be call this function after initialize OK */
EF_ASSERT(init_ok);
/* write address is after log end address */
write_addr = log_end_addr + 4;
/* write the already erased but not used area */
writable_size = EF_ERASE_MIN_SIZE - ((write_addr - log_area_start_addr) % EF_ERASE_MIN_SIZE);
if (writable_size != EF_ERASE_MIN_SIZE) {
if (size > writable_size) {
result = ef_port_write(write_addr, log, writable_size);
if (result != EF_NO_ERR) {
goto exit;
}
write_size += writable_size;
} else {
result = ef_port_write(write_addr, log, size);
log_end_addr = write_addr + size - 4;
goto exit;
}
}
/* erase and write remain log */
while (true) {
/* calculate next available sector address */
erase_addr = write_addr = get_next_flash_sec_addr(write_addr - 4);
/* move the flash log start address to next available sector address */
if (log_start_addr == erase_addr) {
log_start_addr = get_next_flash_sec_addr(log_start_addr);
}
/* erase sector */
result = ef_port_erase(erase_addr, EF_ERASE_MIN_SIZE);
if (result == EF_NO_ERR) {
if (size - write_size > EF_ERASE_MIN_SIZE) {
result = ef_port_write(write_addr, log + write_size / 4, EF_ERASE_MIN_SIZE);
if (result != EF_NO_ERR) {
goto exit;
}
log_end_addr = write_addr + EF_ERASE_MIN_SIZE - 4;
write_size += EF_ERASE_MIN_SIZE;
write_addr += EF_ERASE_MIN_SIZE;
} else {
result = ef_port_write(write_addr, log + write_size / 4, size - write_size);
if (result != EF_NO_ERR) {
goto exit;
}
log_end_addr = write_addr + (size - write_size) - 4;
break;
}
} else {
goto exit;
}
}
exit:
return result;
}
/**
* Get next flash sector address.The log total sector like ring buffer which implement by flash.
*
* @param cur_addr cur flash address
*
* @return next flash sector address
*/
static uint32_t get_next_flash_sec_addr(uint32_t cur_addr) {
size_t cur_sec_id = (cur_addr - log_area_start_addr) / EF_ERASE_MIN_SIZE;
size_t sec_total_num = LOG_AREA_SIZE / EF_ERASE_MIN_SIZE;
if (cur_sec_id + 1 >= sec_total_num) {
/* return to ring head */
return log_area_start_addr;
} else {
return log_area_start_addr + (cur_sec_id + 1) * EF_ERASE_MIN_SIZE;
}
}
/**
* Clean all log which in flash.
*
* @return result
*/
EfErrCode ef_log_clean(void) {
EfErrCode result = EF_NO_ERR;
EF_ASSERT(log_area_start_addr);
/* clean address */
log_start_addr = log_end_addr = log_area_start_addr;
/* erase log flash area */
result = ef_port_erase(log_area_start_addr, LOG_AREA_SIZE);
return result;
}
#endif /* EF_USING_LOG */

@ -0,0 +1,160 @@
/*
* This file is part of the EasyFlash Library.
*
* Copyright (c) 2015, Armink, <armink.ztl@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Function: Some utils for this library.
* Created on: 2015-01-14
*/
#include <easyflash.h>
static const uint32_t crc32_table[] =
{
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
/**
* Calculate the CRC32 value of a memory buffer.
*
* @param crc accumulated CRC32 value, must be 0 on first call
* @param buf buffer to calculate CRC32 value for
* @param size bytes in buffer
*
* @return calculated CRC32 value
*/
uint32_t ef_calc_crc32(uint32_t crc, const void *buf, size_t size)
{
const uint8_t *p;
p = buf;
crc = crc ^ ~0U;
while (size--) {
crc = crc32_table[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
}
return crc ^ ~0U;
}
/**
* Get this flash sector current status.
*
* @param addr sector address
* @param sec_size sector address
*
* @return the flash sector current status
*/
FlashSecrorStatus ef_get_sector_status(uint32_t addr, size_t sec_size) {
uint32_t cur_using_addr = ef_find_sec_using_end_addr(addr, sec_size);
/* get current status by current using address */
if (cur_using_addr == addr) {
return FLASH_SECTOR_EMPTY;
} else if (cur_using_addr < addr + sec_size - 4) {
return FLASH_SECTOR_USING;
} else {
return FLASH_SECTOR_FULL;
}
}
/**
* Find the current flash sector using end address by continuous 0xFF.
*
* @param addr sector address
* @param sec_size sector address
*
* @return current flash sector using end address
*/
uint32_t ef_find_sec_using_end_addr(uint32_t addr, size_t sec_size) {
size_t start, continue_ff;
/* counts continuous 0xFF */
for (start = 0; start < sec_size; start++) {
if (*(uint8_t *) (addr + start) == 0xFF) {
/* make sure it is not the last byte */
if (start < sec_size - 1) {
/* start counts */
for (continue_ff = 1; continue_ff < sec_size - start; continue_ff++) {
if (*(uint8_t *) (addr + start + continue_ff) != 0xFF) {
start += continue_ff;
break;
}
}
/* all sector counts finish */
if (continue_ff == sec_size - start) {
break;
}
}
}
}
if ((start == 0) && (continue_ff == sec_size)) {
/* from 0 to sec_size all sector is 0xFF, so the sector is empty */
return addr;
} else if (start < sec_size) {
/* form start to sec_size all area is 0xFF, so it's used part of the sector */
return addr + start;
} else {
/* all sector not has continuous 0xFF, alignment by word */
return addr + sec_size - 4;
}
}

@ -0,0 +1,37 @@
/*
* This file is part of the EasyLogger Library.
*
* Copyright (c) 2015, Armink, <armink.ztl@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Function: It is the configure head file for this flash log plugin.
* Created on: 2015-07-30
*/
#ifndef _ELOG_FLASH_CFG_H_
#define _ELOG_FLASH_CFG_H_
/* EasyLogger flash log plugin's using buffer mode */
#define ELOG_FLASH_USING_BUF_MODE
/* EasyLogger flash log plugin's RAM buffer size */
#define ELOG_FLASH_BUF_SIZE 1024
#endif /* _ELOG_FLASH_CFG_H_ */

@ -0,0 +1,71 @@
/*
* This file is part of the EasyLogger Library.
*
* Copyright (c) 2015, Armink, <armink.ztl@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Function: Portable interface for EasyLogger's flash log pulgin.
* Created on: 2015-07-28
*/
#include "elog_flash.h"
#include <rthw.h>
#include <rtthread.h>
static struct rt_semaphore flash_log_lock;
/**
* EasyLogger flash log pulgin port initialize
*
* @return result
*/
ElogErrCode elog_flash_port_init(void) {
ElogErrCode result = ELOG_NO_ERR;
rt_sem_init(&flash_log_lock, "elog flash lock", 1, RT_IPC_FLAG_PRIO);
return result;
}
/**
* output flash saved log port interface
*
* @param log flash saved log
* @param size log size
*/
void elog_flash_port_output(const char *log, size_t size) {
/* output to terminal */
rt_kprintf("%.*s", size, log);
}
/**
* flash log lock
*/
void elog_flash_port_lock(void) {
rt_sem_take(&flash_log_lock, RT_WAITING_FOREVER);
}
/**
* flash log unlock
*/
void elog_flash_port_unlock(void) {
rt_sem_release(&flash_log_lock);
}

@ -26,7 +26,8 @@
* Created on: 2015-04-28
*/
#include "elog.h"
#include <elog.h>
#include <elog_flash.h>
#include <rthw.h>
#include <rtthread.h>
@ -54,7 +55,8 @@ ElogErrCode elog_port_init(void) {
void elog_port_output(const char *log, size_t size) {
/* output to terminal */
rt_kprintf("%.*s", size, log);
//TODO output to flash
/* output to flash */
elog_flash_write(log, size);
}
/**

@ -30,7 +30,7 @@
#define __ELOG_FLASH_H__
#include <elog.h>
#include "elog_flash_cfg.h"
#include <elog_flash_cfg.h>
#ifdef __cplusplus
extern "C" {

Loading…
Cancel
Save