Add first version.
This commit is contained in:
commit
cc40a2187b
|
@ -0,0 +1,40 @@
|
||||||
|
* text=auto
|
||||||
|
|
||||||
|
*.S text
|
||||||
|
*.asm text
|
||||||
|
*.c text
|
||||||
|
*.cc text
|
||||||
|
*.cpp text
|
||||||
|
*.cxx text
|
||||||
|
*.h text
|
||||||
|
*.htm text
|
||||||
|
*.html text
|
||||||
|
*.in text
|
||||||
|
*.ld text
|
||||||
|
*.m4 text
|
||||||
|
*.mak text
|
||||||
|
*.mk text
|
||||||
|
*.py text
|
||||||
|
*.rb text
|
||||||
|
*.s text
|
||||||
|
*.sct text
|
||||||
|
*.sh text
|
||||||
|
*.txt text
|
||||||
|
*.xml text
|
||||||
|
Makefile text
|
||||||
|
AUTHORS text
|
||||||
|
COPYING text
|
||||||
|
|
||||||
|
*.LZO -text
|
||||||
|
*.Opt -text
|
||||||
|
*.Uv2 -text
|
||||||
|
*.ewp -text
|
||||||
|
*.eww -text
|
||||||
|
*.vcproj -text
|
||||||
|
*.bat -text
|
||||||
|
*.dos -text
|
||||||
|
*.icf -text
|
||||||
|
*.inf -text
|
||||||
|
*.ini -text
|
||||||
|
*.sct -text
|
||||||
|
*.xsd -text
|
|
@ -0,0 +1,201 @@
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
|
@ -0,0 +1,99 @@
|
||||||
|
# FlashDB:超轻量级嵌入式数据库
|
||||||
|
|
||||||
|
## 简介
|
||||||
|
|
||||||
|
FlashDB 是一款超轻量级的嵌入式数据库,专注于提供嵌入式产品的数据存储方案。与传统的基于文件系统的数据库不同,FlashDB 结合了 Flash 的特性,具有较强的性能及可靠性。并在保证极低的资源占用前提下,尽可能延长 Flash 使用寿命。
|
||||||
|
|
||||||
|
FlashDB 提供两种数据库模式:
|
||||||
|
|
||||||
|
- **键值数据库** :是一种非关系数据库,它将数据存储为键值(Key-Value)对集合,其中键作为唯一标识符。KVDB 操作简洁,可扩展性强。
|
||||||
|
- **时序数据库** :时间序列数据库 (Time Series Database , 简称 TSDB),它将数据按照 **时间顺序存储** 。TSDB 数据具有时间戳,数据存储量大,插入及查询性能高。
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
|
||||||
|
如今,物联网产品种类越来越多,运行时产生的数据种类及总量及也在不断变大。FlashDB 提供了多样化的数据存储方案,不仅资源占用小,并且存储容量大,非常适合用于物联网产品。下面是主要应用场景:
|
||||||
|
|
||||||
|
- **键值数据库** :
|
||||||
|
- 产品参数存储
|
||||||
|
- 用户配置信息存储
|
||||||
|
- 小文件管理
|
||||||
|
- **时序数据库** :
|
||||||
|
- 存储动态产生的结构化数据:如 温湿度传感器采集的环境监测信息,智能手环实时记录的人体健康信息等
|
||||||
|
- 记录运行日志:存储产品历史的运行日志,异常告警的记录等
|
||||||
|
|
||||||
|
## 主要特性
|
||||||
|
|
||||||
|
- 资源占用极低,内存占用几乎为 **0** ;
|
||||||
|
- 支持 多分区,**多实例** 。数据量大时,可细化分区,降低检索时间;
|
||||||
|
- 支持 **磨损平衡** ,延长 Flash 寿命;
|
||||||
|
- 支持 **掉电保护** 功能,可靠性高;
|
||||||
|
- 支持 字符串及 blob 两种 KV 类型,方便用户操作;
|
||||||
|
- 支持 KV **增量升级** ,产品固件升级后, KVDB 内容也支持自动升级;
|
||||||
|
- 支持 修改每条 TSDB 记录的状态,方便用户进行管理;
|
||||||
|
|
||||||
|
## 性能及资源占用
|
||||||
|
|
||||||
|
### TSDB 性能测试1 (nor flash W25Q64)
|
||||||
|
|
||||||
|
```shell
|
||||||
|
msh />tsl bench
|
||||||
|
Append 1250 TSL in 5 seconds, average: 250.00 tsl/S, 4.00 ms/per
|
||||||
|
Query total spent 2218 (ms) for 1251 TSL, min 1, max 2, average: 1.77 ms/per
|
||||||
|
```
|
||||||
|
|
||||||
|
插入平均:4 ms,查询平均:1.8 ms
|
||||||
|
|
||||||
|
### TSDB 性能测试2 (stm32f2 onchip flash)
|
||||||
|
|
||||||
|
```shell
|
||||||
|
msh />tsl bench
|
||||||
|
Append 13421 TSL in 5 seconds, average: 2684.20 tsl/S, 0.37 ms/per
|
||||||
|
Query total spent 1475 (ms) for 13422 TSL, min 0, max 1, average: 0.11 ms/per
|
||||||
|
```
|
||||||
|
|
||||||
|
插入平均:0.37 ms,查询平均:0.12 ms
|
||||||
|
|
||||||
|
### 资源占用 (stm32f4 IAR8.20)
|
||||||
|
|
||||||
|
```shell
|
||||||
|
Module ro code ro data rw data
|
||||||
|
------ ------- ------- -------
|
||||||
|
fdb.o 276 232 1
|
||||||
|
fdb_kvdb.o 4 584 356 1
|
||||||
|
fdb_tsdb.o 1 160 236
|
||||||
|
fdb_utils.o 418 1 024
|
||||||
|
```
|
||||||
|
|
||||||
|
上面是 IAR 的 map 文件信息,可见 FlashDB 的资源占用非常低
|
||||||
|
|
||||||
|
## 如何使用
|
||||||
|
|
||||||
|
### 移植
|
||||||
|
|
||||||
|
FlashDB 底层的 Flash 管理及操作依赖于 RT-Thread 的 FAL (Flash Abstraction Layer) Flash 抽象层开源软件包 ,该开源库也支持运行在 **裸机平台** [(点击查看介绍)](http://packages.rt-thread.org/detail.html?package=fal)。所以只需要将所用到的 Flash 对接到 FAL ,即可完成整个移植工作。
|
||||||
|
|
||||||
|
FAL 移植主要流程:
|
||||||
|
|
||||||
|
- 定义 flash 设备,详见 ([GitHub](https://github.com/RT-Thread-packages/fal#21%E5%AE%9A%E4%B9%89-flash-%E8%AE%BE%E5%A4%87)|[Gitee](https://gitee.com/RT-Thread-Mirror/fal#21%E5%AE%9A%E4%B9%89-flash-%E8%AE%BE%E5%A4%87))
|
||||||
|
- 定义 flash 设备表,详见 ([GitHub](https://github.com/RT-Thread-packages/fal#22%E5%AE%9A%E4%B9%89-flash-%E8%AE%BE%E5%A4%87%E8%A1%A8)|[Gitee](https://gitee.com/RT-Thread-Mirror/fal#22%E5%AE%9A%E4%B9%89-flash-%E8%AE%BE%E5%A4%87%E8%A1%A8))
|
||||||
|
- 定义 flash 分区表,详见 ([GitHub](https://github.com/RT-Thread-packages/fal#23%E5%AE%9A%E4%B9%89-flash-%E5%88%86%E5%8C%BA%E8%A1%A8)|[Gitee](https://gitee.com/RT-Thread-Mirror/fal#23%E5%AE%9A%E4%B9%89-flash-%E5%88%86%E5%8C%BA%E8%A1%A8))
|
||||||
|
|
||||||
|
### 示例
|
||||||
|
|
||||||
|
FlashDB 提供了主要功能的示例,直接加入工程即可运行,并具有一定的参考性
|
||||||
|
|
||||||
|
| 文件路径 | 介绍 | 备注 |
|
||||||
|
| ------------------------------------------------------------ | --------------------------------------- | ---- |
|
||||||
|
| [`samples/kvdb_type_string_sample.c`](samples/kvdb_type_string_sample.c) | KVDB 使用字符型键值的示例 | |
|
||||||
|
| [`samples/kvdb_type_blob_sample.c`](samples/kvdb_type_blob_sample.c) | KVDB 使用 blob 型(任意类型)键值的示例 | |
|
||||||
|
| [`samples/tsdb_sample.c`](samples/tsdb_sample.c) | TSDB 示例 | |
|
||||||
|
|
||||||
|
## 支持
|
||||||
|
|
||||||
|
![support](docs/zh/images/wechat_support.png)
|
||||||
|
|
||||||
|
如果 FlashDB 解决了你的问题,不妨扫描上面二维码请我 **喝杯咖啡**~
|
||||||
|
|
||||||
|
## 许可
|
||||||
|
|
||||||
|
采用 Apache-2.0 开源协议,细节请阅读项目中的 LICENSE 文件内容。
|
|
@ -0,0 +1 @@
|
||||||
|
# Coming soon...
|
|
@ -0,0 +1,3 @@
|
||||||
|
|File name |Description|
|
||||||
|
|:----- |:----|
|
||||||
|
|api.md |API description|
|
|
@ -0,0 +1,4 @@
|
||||||
|
|File or folder name |Description|
|
||||||
|
|:----- |:----|
|
||||||
|
|en |English documents|
|
||||||
|
|zh |中文文档(简体)|
|
|
@ -0,0 +1,5 @@
|
||||||
|
# FlashDB API 说明
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
马上就来……
|
|
@ -0,0 +1,3 @@
|
||||||
|
# FlashDB 功能设计与实现
|
||||||
|
|
||||||
|
马上就来……
|
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
|
@ -0,0 +1,5 @@
|
||||||
|
# FlashDB 移植说明
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
马上就来……
|
|
@ -0,0 +1,5 @@
|
||||||
|
|文件名 |描述|
|
||||||
|
|:----- |:----|
|
||||||
|
|api.md |API 说明|
|
||||||
|
|port.md |移植说明|
|
||||||
|
|design.md |设计文档|
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief configuration file
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _FDB_CFG_H_
|
||||||
|
#define _FDB_CFG_H_
|
||||||
|
|
||||||
|
/* using KVDB function */
|
||||||
|
#define FDB_USING_KVDB
|
||||||
|
|
||||||
|
#ifdef FDB_USING_KVDB
|
||||||
|
/* Auto update KV to latest default when current KVDB version number is changed. @see fdb_kvdb.ver_num */
|
||||||
|
/* #define FDB_KV_AUTO_UPDATE */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* using TSDB (Time series database) feature */
|
||||||
|
#define FDB_USING_TSDB
|
||||||
|
|
||||||
|
/* the flash write granularity, unit: bit
|
||||||
|
* only support 1(nor flash)/ 8(stm32f2/f4)/ 32(stm32f1) */
|
||||||
|
#define FDB_WRITE_GRAN /* @note you must define it for a value */
|
||||||
|
|
||||||
|
/* MCU Endian Configuration, default is Little Endian Order. */
|
||||||
|
/* #define FDB_BIG_ENDIAN */
|
||||||
|
|
||||||
|
/* log print macro. default EF_PRINT macro is printf() */
|
||||||
|
/* #define FDB_PRINT(...) my_printf(__VA_ARGS__) */
|
||||||
|
|
||||||
|
/* print debug information */
|
||||||
|
#define FDB_DEBUG_ENABLE
|
||||||
|
|
||||||
|
#endif /* _FDB_CFG_H_ */
|
|
@ -0,0 +1,279 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Public definition.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _FDB_DEF_H_
|
||||||
|
#define _FDB_DEF_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* software version number */
|
||||||
|
#define FDB_SW_VERSION "1.0.0 beta"
|
||||||
|
#define FDB_SW_VERSION_NUM 0x10000
|
||||||
|
|
||||||
|
/* the KV max name length must less then it */
|
||||||
|
#ifndef FDB_KV_NAME_MAX
|
||||||
|
#define FDB_KV_NAME_MAX 32
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* the KV cache table size, it will improve KV search speed when using cache */
|
||||||
|
#ifndef FDB_KV_CACHE_TABLE_SIZE
|
||||||
|
#define FDB_KV_CACHE_TABLE_SIZE 16
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* the sector cache table size, it will improve KV save speed when using cache */
|
||||||
|
#ifndef FDB_SECTOR_CACHE_TABLE_SIZE
|
||||||
|
#define FDB_SECTOR_CACHE_TABLE_SIZE 4
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (FDB_KV_CACHE_TABLE_SIZE > 0) && (FDB_SECTOR_CACHE_TABLE_SIZE > 0)
|
||||||
|
#define FDB_KV_USING_CACHE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* log function. default FDB_PRINT macro is printf() */
|
||||||
|
#ifndef FDB_PRINT
|
||||||
|
#define FDB_PRINT(...) printf(__VA_ARGS__)
|
||||||
|
#endif
|
||||||
|
#define FDB_LOG_PREFIX1() FDB_PRINT("[FlashDB]"FDB_LOG_TAG)
|
||||||
|
#define FDB_LOG_PREFIX2() FDB_PRINT(" ")
|
||||||
|
#define FDB_LOG_PREFIX() FDB_LOG_PREFIX1();FDB_LOG_PREFIX2()
|
||||||
|
#ifdef FDB_DEBUG_ENABLE
|
||||||
|
#define FDB_DEBUG(...) FDB_LOG_PREFIX();FDB_PRINT("(%s:%d) ", __FILE__, __LINE__);FDB_PRINT(__VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define FDB_DEBUG(...)
|
||||||
|
#endif
|
||||||
|
/* routine print function. Must be implement by user. */
|
||||||
|
#define FDB_INFO(...) FDB_LOG_PREFIX();FDB_PRINT(__VA_ARGS__)
|
||||||
|
/* assert for developer. */
|
||||||
|
#define FDB_ASSERT(EXPR) \
|
||||||
|
if (!(EXPR)) \
|
||||||
|
{ \
|
||||||
|
FDB_DEBUG("(%s) has assert failed at %s.\n", #EXPR, __FUNCTION__); \
|
||||||
|
while (1); \
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef time_t fdb_time_t;
|
||||||
|
#ifdef FDB_USING_TIMESTAMP_64BIT
|
||||||
|
typedef int64_t fdb_time_t;
|
||||||
|
#endif
|
||||||
|
typedef fdb_time_t (*fdb_get_time)(void);
|
||||||
|
|
||||||
|
struct fdb_default_kv_node {
|
||||||
|
char *key;
|
||||||
|
void *value;
|
||||||
|
size_t value_len;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fdb_default_kv {
|
||||||
|
struct fdb_default_kv_node *kvs;
|
||||||
|
size_t num;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* error code */
|
||||||
|
typedef enum {
|
||||||
|
FDB_NO_ERR,
|
||||||
|
FDB_ERASE_ERR,
|
||||||
|
FDB_READ_ERR,
|
||||||
|
FDB_WRITE_ERR,
|
||||||
|
FDB_PART_NOT_FOUND,
|
||||||
|
FDB_KV_NAME_ERR,
|
||||||
|
FDB_KV_NAME_EXIST,
|
||||||
|
FDB_KV_FULL,
|
||||||
|
FDB_INIT_FAILED,
|
||||||
|
} fdb_err_t;
|
||||||
|
|
||||||
|
enum fdb_kv_status {
|
||||||
|
FDB_KV_UNUSED,
|
||||||
|
FDB_KV_PRE_WRITE,
|
||||||
|
FDB_KV_WRITE,
|
||||||
|
FDB_KV_PRE_DELETE,
|
||||||
|
FDB_KV_DELETED,
|
||||||
|
FDB_KV_ERR_HDR,
|
||||||
|
FDB_KV_STATUS_NUM,
|
||||||
|
};
|
||||||
|
typedef enum fdb_kv_status fdb_kv_status_t;
|
||||||
|
|
||||||
|
enum fdb_tsl_status {
|
||||||
|
FDB_TSL_UNUSED,
|
||||||
|
FDB_TSL_PRE_WRITE,
|
||||||
|
FDB_TSL_WRITE,
|
||||||
|
FDB_TSL_USER_STATUS1,
|
||||||
|
FDB_TSL_DELETED,
|
||||||
|
FDB_TSL_USER_STATUS2,
|
||||||
|
FDB_TSL_STATUS_NUM,
|
||||||
|
};
|
||||||
|
typedef enum fdb_tsl_status fdb_tsl_status_t;
|
||||||
|
|
||||||
|
/* key-value node object */
|
||||||
|
struct fdb_kv {
|
||||||
|
fdb_kv_status_t status; /**< node status, @see fdb_kv_status_t */
|
||||||
|
bool crc_is_ok; /**< node CRC32 check is OK */
|
||||||
|
uint8_t name_len; /**< name length */
|
||||||
|
uint32_t magic; /**< magic word(`K`, `V`, `4`, `0`) */
|
||||||
|
uint32_t len; /**< node total length (header + name + value), must align by FDB_WRITE_GRAN */
|
||||||
|
uint32_t value_len; /**< value length */
|
||||||
|
char name[FDB_KV_NAME_MAX]; /**< name */
|
||||||
|
struct {
|
||||||
|
uint32_t start; /**< node start address */
|
||||||
|
uint32_t value; /**< value start address */
|
||||||
|
} addr;
|
||||||
|
};
|
||||||
|
typedef struct fdb_kv *fdb_kv_t;
|
||||||
|
|
||||||
|
/* time series log node object */
|
||||||
|
struct fdb_tsl {
|
||||||
|
fdb_tsl_status_t status; /**< node status, @see fdb_log_status_t */
|
||||||
|
fdb_time_t time; /**< node timestamp */
|
||||||
|
uint32_t log_len; /**< log length, must align by FDB_WRITE_GRAN */
|
||||||
|
struct {
|
||||||
|
uint32_t index; /**< node index address */
|
||||||
|
uint32_t log; /**< log data address */
|
||||||
|
} addr;
|
||||||
|
};
|
||||||
|
typedef struct fdb_tsl *fdb_tsl_t;
|
||||||
|
typedef bool (*fdb_tsl_cb)(fdb_tsl_t tsl, void *arg);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
FDB_DB_TYPE_KV,
|
||||||
|
FDB_DB_TYPE_TS,
|
||||||
|
} fdb_db_type;
|
||||||
|
|
||||||
|
/* the flash sector store status */
|
||||||
|
enum fdb_sector_store_status {
|
||||||
|
FDB_SECTOR_STORE_UNUSED,
|
||||||
|
FDB_SECTOR_STORE_EMPTY,
|
||||||
|
FDB_SECTOR_STORE_USING,
|
||||||
|
FDB_SECTOR_STORE_FULL,
|
||||||
|
FDB_SECTOR_STORE_STATUS_NUM,
|
||||||
|
};
|
||||||
|
typedef enum fdb_sector_store_status fdb_sector_store_status_t;
|
||||||
|
|
||||||
|
/* the flash sector dirty status */
|
||||||
|
enum fdb_sector_dirty_status {
|
||||||
|
FDB_SECTOR_DIRTY_UNUSED,
|
||||||
|
FDB_SECTOR_DIRTY_FALSE,
|
||||||
|
FDB_SECTOR_DIRTY_TRUE,
|
||||||
|
FDB_SECTOR_DIRTY_GC,
|
||||||
|
FDB_SECTOR_DIRTY_STATUS_NUM,
|
||||||
|
};
|
||||||
|
typedef enum fdb_sector_dirty_status fdb_sector_dirty_status_t;
|
||||||
|
|
||||||
|
/* KVDB section information */
|
||||||
|
struct kvdb_sec_info {
|
||||||
|
bool check_ok; /**< sector header check is OK */
|
||||||
|
struct {
|
||||||
|
fdb_sector_store_status_t store; /**< sector store status @see fdb_sector_store_status_t */
|
||||||
|
fdb_sector_dirty_status_t dirty; /**< sector dirty status @see sector_dirty_status_t */
|
||||||
|
} status;
|
||||||
|
uint32_t addr; /**< sector start address */
|
||||||
|
uint32_t magic; /**< magic word(`E`, `F`, `4`, `0`) */
|
||||||
|
uint32_t combined; /**< the combined next sector number, 0xFFFFFFFF: not combined */
|
||||||
|
size_t remain; /**< remain size */
|
||||||
|
uint32_t empty_kv; /**< the next empty KV node start address */
|
||||||
|
};
|
||||||
|
typedef struct kvdb_sec_info *kv_sec_info_t;
|
||||||
|
|
||||||
|
/* TSDB section information */
|
||||||
|
struct tsdb_sec_info {
|
||||||
|
bool check_ok; /**< sector header check is OK */
|
||||||
|
fdb_sector_store_status_t status; /**< sector store status @see fdb_sector_store_status_t */
|
||||||
|
uint32_t addr; /**< sector start address */
|
||||||
|
uint32_t magic; /**< magic word(`T`, `S`, `L`, `0`) */
|
||||||
|
fdb_time_t start_time; /**< the first start node's timestamp, 0xFFFFFFFF: unused */
|
||||||
|
fdb_time_t end_time; /**< the last end node's timestamp, 0xFFFFFFFF: unused */
|
||||||
|
uint32_t end_idx; /**< the last end node's index, 0xFFFFFFFF: unused */
|
||||||
|
fdb_tsl_status_t end_info_stat[2]; /**< the last end node's info status */
|
||||||
|
size_t remain; /**< remain size */
|
||||||
|
uint32_t empty_idx; /**< the next empty node index address */
|
||||||
|
uint32_t empty_data; /**< the next empty node's data end address */
|
||||||
|
};
|
||||||
|
typedef struct tsdb_sec_info *tsdb_sec_info_t;
|
||||||
|
|
||||||
|
struct kv_cache_node {
|
||||||
|
uint16_t name_crc; /**< KV name's CRC32 low 16bit value */
|
||||||
|
uint16_t active; /**< KV node access active degree */
|
||||||
|
uint32_t addr; /**< KV node address */
|
||||||
|
};
|
||||||
|
typedef struct kv_cache_node *kv_cache_node_t;
|
||||||
|
|
||||||
|
struct sector_cache_node {
|
||||||
|
uint32_t addr; /**< sector start address */
|
||||||
|
uint32_t empty_addr; /**< sector empty address */
|
||||||
|
};
|
||||||
|
typedef struct sector_cache_node *sector_cache_node_t;
|
||||||
|
|
||||||
|
/* database structure */
|
||||||
|
typedef struct fdb_db *fdb_db_t;
|
||||||
|
struct fdb_db {
|
||||||
|
const char *name; /**< database name */
|
||||||
|
fdb_db_type type; /**< database type */
|
||||||
|
const struct fal_partition *part; /**< flash partition */
|
||||||
|
uint32_t sec_size; /**< flash section size. It's a multiple of block size */
|
||||||
|
bool init_ok; /**< initialized successfully */
|
||||||
|
void (*lock)(fdb_db_t db); /**< lock the database operate */
|
||||||
|
void (*unlock)(fdb_db_t db); /**< unlock the database operate */
|
||||||
|
|
||||||
|
void *user_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* KVDB structure */
|
||||||
|
struct fdb_kvdb {
|
||||||
|
struct fdb_db parent; /**< inherit from fdb_db */
|
||||||
|
struct fdb_default_kv default_kvs; /**< default KV */
|
||||||
|
bool gc_request; /**< request a GC check */
|
||||||
|
bool in_recovery_check; /**< is in recovery check status when first reboot */
|
||||||
|
|
||||||
|
#ifdef FDB_KV_USING_CACHE
|
||||||
|
/* KV cache table */
|
||||||
|
struct kv_cache_node kv_cache_table[FDB_KV_CACHE_TABLE_SIZE];
|
||||||
|
/* sector cache table, it caching the sector info which status is current using */
|
||||||
|
struct sector_cache_node sector_cache_table[FDB_SECTOR_CACHE_TABLE_SIZE];
|
||||||
|
#endif /* FDB_KV_USING_CACHE */
|
||||||
|
|
||||||
|
#ifdef FDB_KV_AUTO_UPDATE
|
||||||
|
uint32_t ver_num; /**< setting version number for update */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void *user_data;
|
||||||
|
};
|
||||||
|
typedef struct fdb_kvdb *fdb_kvdb_t;
|
||||||
|
|
||||||
|
/* TSDB structure */
|
||||||
|
struct fdb_tsdb {
|
||||||
|
struct fdb_db parent; /**< inherit from fdb_db */
|
||||||
|
struct tsdb_sec_info cur_sec; /**< current using sector */
|
||||||
|
fdb_time_t last_time; /**< last TSL timestamp */
|
||||||
|
fdb_get_time get_time; /**< the current timestamp get function */
|
||||||
|
size_t max_len; /**< the max log length */
|
||||||
|
uint32_t oldest_addr; /**< the oldest sector start address */
|
||||||
|
|
||||||
|
void *user_data;
|
||||||
|
};
|
||||||
|
typedef struct fdb_tsdb *fdb_tsdb_t;
|
||||||
|
|
||||||
|
/* blob structure */
|
||||||
|
struct fdb_blob {
|
||||||
|
void *buf; /**< blob data buffer */
|
||||||
|
size_t size; /**< blob data buffer size */
|
||||||
|
struct {
|
||||||
|
uint32_t meta_addr; /**< saved KV or TSL index address */
|
||||||
|
uint32_t addr; /**< blob data saved address */
|
||||||
|
size_t len; /**< blob data saved length */
|
||||||
|
} saved;
|
||||||
|
};
|
||||||
|
typedef struct fdb_blob *fdb_blob_t;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _FDB_DEF_H_ */
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief low level API and definition
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _FDB_LOW_LVL_H_
|
||||||
|
#define _FDB_LOW_LVL_H_
|
||||||
|
|
||||||
|
#include <fdb_cfg.h>
|
||||||
|
#include <fdb_def.h>
|
||||||
|
|
||||||
|
#if (FDB_WRITE_GRAN == 1)
|
||||||
|
#define FDB_STATUS_TABLE_SIZE(status_number) ((status_number * FDB_WRITE_GRAN + 7)/8)
|
||||||
|
#else
|
||||||
|
#define FDB_STATUS_TABLE_SIZE(status_number) (((status_number - 1) * FDB_WRITE_GRAN + 7)/8)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Return the most contiguous size aligned at specified width. RT_ALIGN(13, 4)
|
||||||
|
* would return 16.
|
||||||
|
*/
|
||||||
|
#define FDB_ALIGN(size, align) (((size) + (align) - 1) & ~((align) - 1))
|
||||||
|
/* align by write granularity */
|
||||||
|
#define FDB_WG_ALIGN(size) (FDB_ALIGN(size, (FDB_WRITE_GRAN + 7)/8))
|
||||||
|
/**
|
||||||
|
* Return the down number of aligned at specified width. RT_ALIGN_DOWN(13, 4)
|
||||||
|
* would return 12.
|
||||||
|
*/
|
||||||
|
#define FDB_ALIGN_DOWN(size, align) ((size) & ~((align) - 1))
|
||||||
|
/* align down by write granularity */
|
||||||
|
#define FDB_WG_ALIGN_DOWN(size) (FDB_ALIGN_DOWN(size, (FDB_WRITE_GRAN + 7)/8))
|
||||||
|
|
||||||
|
#define FDB_STORE_STATUS_TABLE_SIZE FDB_STATUS_TABLE_SIZE(FDB_SECTOR_STORE_STATUS_NUM)
|
||||||
|
#define FDB_DIRTY_STATUS_TABLE_SIZE FDB_STATUS_TABLE_SIZE(FDB_SECTOR_DIRTY_STATUS_NUM)
|
||||||
|
|
||||||
|
/* the data is unused */
|
||||||
|
#define FDB_DATA_UNUSED 0xFFFFFFFF
|
||||||
|
|
||||||
|
fdb_err_t _fdb_kv_load(fdb_kvdb_t db);
|
||||||
|
size_t _fdb_set_status(uint8_t status_table[], size_t status_num, size_t status_index);
|
||||||
|
size_t _fdb_get_status(uint8_t status_table[], size_t status_num);
|
||||||
|
uint32_t _fdb_continue_ff_addr(fdb_db_t db, uint32_t start, uint32_t end);
|
||||||
|
fdb_err_t _fdb_init_ex(fdb_db_t db, const char *name, const char *part_name, fdb_db_type type, void *user_data);
|
||||||
|
void _fdb_init_finish(fdb_db_t db, fdb_err_t result);
|
||||||
|
fdb_err_t _fdb_write_status(fdb_db_t db, uint32_t addr, uint8_t status_table[], size_t status_num, size_t status_index);
|
||||||
|
size_t _fdb_read_status(fdb_db_t db, uint32_t addr, uint8_t status_table[], size_t total_num);
|
||||||
|
fdb_err_t _fdb_flash_read(fdb_db_t db, uint32_t addr, void *buf, size_t size);
|
||||||
|
fdb_err_t _fdb_flash_erase(fdb_db_t db, uint32_t addr, size_t size);
|
||||||
|
fdb_err_t _fdb_flash_write(fdb_db_t db, uint32_t addr, const void *buf, size_t size);
|
||||||
|
|
||||||
|
#endif /* _FDB_LOW_LVL_H_ */
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Public APIs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _FLASHDB_H_
|
||||||
|
#define _FLASHDB_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <fal.h>
|
||||||
|
#include <fdb_cfg.h>
|
||||||
|
#include <fdb_def.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* fdb.c */
|
||||||
|
void fdb_lock_set(fdb_db_t db, void (*lock)(fdb_db_t db), void (*unlock)(fdb_db_t db));
|
||||||
|
void fdb_sec_size_set(fdb_db_t db, uint32_t sec_size);
|
||||||
|
fdb_err_t fdb_kvdb_init(fdb_kvdb_t db, const char *name, const char *part_name, struct fdb_default_kv *default_kv,
|
||||||
|
void *user_data);
|
||||||
|
fdb_err_t fdb_tsdb_init(fdb_tsdb_t db, const char *name, const char *part_name, fdb_get_time get_time, size_t max_len,
|
||||||
|
void *user_data);
|
||||||
|
|
||||||
|
/* blob API */
|
||||||
|
fdb_blob_t fdb_blob_make (fdb_blob_t blob, const void *value_buf, size_t buf_len);
|
||||||
|
size_t fdb_blob_read (fdb_db_t db, fdb_blob_t blob);
|
||||||
|
|
||||||
|
/* Key-Value API like a KV DB */
|
||||||
|
fdb_err_t fdb_kv_set (fdb_kvdb_t db, const char *key, const char *value);
|
||||||
|
char *fdb_kv_get (fdb_kvdb_t db, const char *key);
|
||||||
|
fdb_err_t fdb_kv_set_blob (fdb_kvdb_t db, const char *key, fdb_blob_t blob);
|
||||||
|
size_t fdb_kv_get_blob (fdb_kvdb_t db, const char *key, fdb_blob_t blob);
|
||||||
|
fdb_err_t fdb_kv_del (fdb_kvdb_t db, const char *key);
|
||||||
|
fdb_kv_t fdb_kv_get_obj (fdb_kvdb_t db, const char *key, fdb_kv_t kv);
|
||||||
|
fdb_blob_t fdb_kv_to_blob (fdb_kv_t kv, fdb_blob_t blob);
|
||||||
|
fdb_err_t fdb_kv_set_default(fdb_kvdb_t db);
|
||||||
|
void fdb_kv_print (fdb_kvdb_t db);
|
||||||
|
|
||||||
|
/* Time series log API like a TSDB */
|
||||||
|
fdb_err_t fdb_tsl_append (fdb_tsdb_t db, fdb_blob_t blob);
|
||||||
|
void fdb_tsl_iter (fdb_tsdb_t db, fdb_tsl_cb cb, void *cb_arg);
|
||||||
|
void fdb_tsl_iter_by_time(fdb_tsdb_t db, fdb_time_t from, fdb_time_t to, fdb_tsl_cb cb, void *cb_arg);
|
||||||
|
size_t fdb_tsl_query_count (fdb_tsdb_t db, fdb_time_t from, fdb_time_t to, fdb_tsl_status_t status);
|
||||||
|
fdb_err_t fdb_tsl_set_status (fdb_tsdb_t db, fdb_tsl_t tsl, fdb_tsl_status_t status);
|
||||||
|
void fdb_tsl_clean (fdb_tsdb_t db);
|
||||||
|
fdb_blob_t fdb_tsl_to_blob (fdb_tsl_t tsl, fdb_blob_t blob);
|
||||||
|
|
||||||
|
/* fdb_utils.c */
|
||||||
|
uint32_t fdb_calc_crc32(uint32_t crc, const void *buf, size_t size);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _FLASHDB_H_ */
|
|
@ -0,0 +1,504 @@
|
||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
Version 2.1, February 1999
|
||||||
|
|
||||||
|
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
(This is the first released version of the Lesser GPL. It also counts
|
||||||
|
as the successor of the GNU Library Public License, version 2, hence
|
||||||
|
the version number 2.1.)
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
Licenses are intended to guarantee your freedom to share and change
|
||||||
|
free software--to make sure the software is free for all its users.
|
||||||
|
|
||||||
|
This license, the Lesser General Public License, applies to some
|
||||||
|
specially designated software packages--typically libraries--of the
|
||||||
|
Free Software Foundation and other authors who decide to use it. You
|
||||||
|
can use it too, but we suggest you first think carefully about whether
|
||||||
|
this license or the ordinary General Public License is the better
|
||||||
|
strategy to use in any particular case, based on the explanations below.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom of use,
|
||||||
|
not price. Our General Public Licenses are designed to make sure that
|
||||||
|
you have the freedom to distribute copies of free software (and charge
|
||||||
|
for this service if you wish); that you receive source code or can get
|
||||||
|
it if you want it; that you can change the software and use pieces of
|
||||||
|
it in new free programs; and that you are informed that you can do
|
||||||
|
these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
distributors to deny you these rights or to ask you to surrender these
|
||||||
|
rights. These restrictions translate to certain responsibilities for
|
||||||
|
you if you distribute copies of the library or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of the library, whether gratis
|
||||||
|
or for a fee, you must give the recipients all the rights that we gave
|
||||||
|
you. You must make sure that they, too, receive or can get the source
|
||||||
|
code. If you link other code with the library, you must provide
|
||||||
|
complete object files to the recipients, so that they can relink them
|
||||||
|
with the library after making changes to the library and recompiling
|
||||||
|
it. And you must show them these terms so they know their rights.
|
||||||
|
|
||||||
|
We protect your rights with a two-step method: (1) we copyright the
|
||||||
|
library, and (2) we offer you this license, which gives you legal
|
||||||
|
permission to copy, distribute and/or modify the library.
|
||||||
|
|
||||||
|
To protect each distributor, we want to make it very clear that
|
||||||
|
there is no warranty for the free library. Also, if the library is
|
||||||
|
modified by someone else and passed on, the recipients should know
|
||||||
|
that what they have is not the original version, so that the original
|
||||||
|
author's reputation will not be affected by problems that might be
|
||||||
|
introduced by others.
|
||||||
|
|
||||||
|
Finally, software patents pose a constant threat to the existence of
|
||||||
|
any free program. We wish to make sure that a company cannot
|
||||||
|
effectively restrict the users of a free program by obtaining a
|
||||||
|
restrictive license from a patent holder. Therefore, we insist that
|
||||||
|
any patent license obtained for a version of the library must be
|
||||||
|
consistent with the full freedom of use specified in this license.
|
||||||
|
|
||||||
|
Most GNU software, including some libraries, is covered by the
|
||||||
|
ordinary GNU General Public License. This license, the GNU Lesser
|
||||||
|
General Public License, applies to certain designated libraries, and
|
||||||
|
is quite different from the ordinary General Public License. We use
|
||||||
|
this license for certain libraries in order to permit linking those
|
||||||
|
libraries into non-free programs.
|
||||||
|
|
||||||
|
When a program is linked with a library, whether statically or using
|
||||||
|
a shared library, the combination of the two is legally speaking a
|
||||||
|
combined work, a derivative of the original library. The ordinary
|
||||||
|
General Public License therefore permits such linking only if the
|
||||||
|
entire combination fits its criteria of freedom. The Lesser General
|
||||||
|
Public License permits more lax criteria for linking other code with
|
||||||
|
the library.
|
||||||
|
|
||||||
|
We call this license the "Lesser" General Public License because it
|
||||||
|
does Less to protect the user's freedom than the ordinary General
|
||||||
|
Public License. It also provides other free software developers Less
|
||||||
|
of an advantage over competing non-free programs. These disadvantages
|
||||||
|
are the reason we use the ordinary General Public License for many
|
||||||
|
libraries. However, the Lesser license provides advantages in certain
|
||||||
|
special circumstances.
|
||||||
|
|
||||||
|
For example, on rare occasions, there may be a special need to
|
||||||
|
encourage the widest possible use of a certain library, so that it becomes
|
||||||
|
a de-facto standard. To achieve this, non-free programs must be
|
||||||
|
allowed to use the library. A more frequent case is that a free
|
||||||
|
library does the same job as widely used non-free libraries. In this
|
||||||
|
case, there is little to gain by limiting the free library to free
|
||||||
|
software only, so we use the Lesser General Public License.
|
||||||
|
|
||||||
|
In other cases, permission to use a particular library in non-free
|
||||||
|
programs enables a greater number of people to use a large body of
|
||||||
|
free software. For example, permission to use the GNU C Library in
|
||||||
|
non-free programs enables many more people to use the whole GNU
|
||||||
|
operating system, as well as its variant, the GNU/Linux operating
|
||||||
|
system.
|
||||||
|
|
||||||
|
Although the Lesser General Public License is Less protective of the
|
||||||
|
users' freedom, it does ensure that the user of a program that is
|
||||||
|
linked with the Library has the freedom and the wherewithal to run
|
||||||
|
that program using a modified version of the Library.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow. Pay close attention to the difference between a
|
||||||
|
"work based on the library" and a "work that uses the library". The
|
||||||
|
former contains code derived from the library, whereas the latter must
|
||||||
|
be combined with the library in order to run.
|
||||||
|
|
||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License Agreement applies to any software library or other
|
||||||
|
program which contains a notice placed by the copyright holder or
|
||||||
|
other authorized party saying it may be distributed under the terms of
|
||||||
|
this Lesser General Public License (also called "this License").
|
||||||
|
Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
A "library" means a collection of software functions and/or data
|
||||||
|
prepared so as to be conveniently linked with application programs
|
||||||
|
(which use some of those functions and data) to form executables.
|
||||||
|
|
||||||
|
The "Library", below, refers to any such software library or work
|
||||||
|
which has been distributed under these terms. A "work based on the
|
||||||
|
Library" means either the Library or any derivative work under
|
||||||
|
copyright law: that is to say, a work containing the Library or a
|
||||||
|
portion of it, either verbatim or with modifications and/or translated
|
||||||
|
straightforwardly into another language. (Hereinafter, translation is
|
||||||
|
included without limitation in the term "modification".)
|
||||||
|
|
||||||
|
"Source code" for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For a library, complete source code means
|
||||||
|
all the source code for all modules it contains, plus any associated
|
||||||
|
interface definition files, plus the scripts used to control compilation
|
||||||
|
and installation of the library.
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running a program using the Library is not restricted, and output from
|
||||||
|
such a program is covered only if its contents constitute a work based
|
||||||
|
on the Library (independent of the use of the Library in a tool for
|
||||||
|
writing it). Whether that is true depends on what the Library does
|
||||||
|
and what the program that uses the Library does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Library's
|
||||||
|
complete source code as you receive it, in any medium, provided that
|
||||||
|
you conspicuously and appropriately publish on each copy an
|
||||||
|
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||||
|
all the notices that refer to this License and to the absence of any
|
||||||
|
warranty; and distribute a copy of this License along with the
|
||||||
|
Library.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy,
|
||||||
|
and you may at your option offer warranty protection in exchange for a
|
||||||
|
fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Library or any portion
|
||||||
|
of it, thus forming a work based on the Library, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The modified work must itself be a software library.
|
||||||
|
|
||||||
|
b) You must cause the files modified to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
c) You must cause the whole of the work to be licensed at no
|
||||||
|
charge to all third parties under the terms of this License.
|
||||||
|
|
||||||
|
d) If a facility in the modified Library refers to a function or a
|
||||||
|
table of data to be supplied by an application program that uses
|
||||||
|
the facility, other than as an argument passed when the facility
|
||||||
|
is invoked, then you must make a good faith effort to ensure that,
|
||||||
|
in the event an application does not supply such function or
|
||||||
|
table, the facility still operates, and performs whatever part of
|
||||||
|
its purpose remains meaningful.
|
||||||
|
|
||||||
|
(For example, a function in a library to compute square roots has
|
||||||
|
a purpose that is entirely well-defined independent of the
|
||||||
|
application. Therefore, Subsection 2d requires that any
|
||||||
|
application-supplied function or table used by this function must
|
||||||
|
be optional: if the application does not supply it, the square
|
||||||
|
root function must still compute square roots.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Library,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Library, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote
|
||||||
|
it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Library.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Library
|
||||||
|
with the Library (or with a work based on the Library) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||||
|
License instead of this License to a given copy of the Library. To do
|
||||||
|
this, you must alter all the notices that refer to this License, so
|
||||||
|
that they refer to the ordinary GNU General Public License, version 2,
|
||||||
|
instead of to this License. (If a newer version than version 2 of the
|
||||||
|
ordinary GNU General Public License has appeared, then you can specify
|
||||||
|
that version instead if you wish.) Do not make any other change in
|
||||||
|
these notices.
|
||||||
|
|
||||||
|
Once this change is made in a given copy, it is irreversible for
|
||||||
|
that copy, so the ordinary GNU General Public License applies to all
|
||||||
|
subsequent copies and derivative works made from that copy.
|
||||||
|
|
||||||
|
This option is useful when you wish to copy part of the code of
|
||||||
|
the Library into a program that is not a library.
|
||||||
|
|
||||||
|
4. You may copy and distribute the Library (or a portion or
|
||||||
|
derivative of it, under Section 2) in object code or executable form
|
||||||
|
under the terms of Sections 1 and 2 above provided that you accompany
|
||||||
|
it with the complete corresponding machine-readable source code, which
|
||||||
|
must be distributed under the terms of Sections 1 and 2 above on a
|
||||||
|
medium customarily used for software interchange.
|
||||||
|
|
||||||
|
If distribution of object code is made by offering access to copy
|
||||||
|
from a designated place, then offering equivalent access to copy the
|
||||||
|
source code from the same place satisfies the requirement to
|
||||||
|
distribute the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
5. A program that contains no derivative of any portion of the
|
||||||
|
Library, but is designed to work with the Library by being compiled or
|
||||||
|
linked with it, is called a "work that uses the Library". Such a
|
||||||
|
work, in isolation, is not a derivative work of the Library, and
|
||||||
|
therefore falls outside the scope of this License.
|
||||||
|
|
||||||
|
However, linking a "work that uses the Library" with the Library
|
||||||
|
creates an executable that is a derivative of the Library (because it
|
||||||
|
contains portions of the Library), rather than a "work that uses the
|
||||||
|
library". The executable is therefore covered by this License.
|
||||||
|
Section 6 states terms for distribution of such executables.
|
||||||
|
|
||||||
|
When a "work that uses the Library" uses material from a header file
|
||||||
|
that is part of the Library, the object code for the work may be a
|
||||||
|
derivative work of the Library even though the source code is not.
|
||||||
|
Whether this is true is especially significant if the work can be
|
||||||
|
linked without the Library, or if the work is itself a library. The
|
||||||
|
threshold for this to be true is not precisely defined by law.
|
||||||
|
|
||||||
|
If such an object file uses only numerical parameters, data
|
||||||
|
structure layouts and accessors, and small macros and small inline
|
||||||
|
functions (ten lines or less in length), then the use of the object
|
||||||
|
file is unrestricted, regardless of whether it is legally a derivative
|
||||||
|
work. (Executables containing this object code plus portions of the
|
||||||
|
Library will still fall under Section 6.)
|
||||||
|
|
||||||
|
Otherwise, if the work is a derivative of the Library, you may
|
||||||
|
distribute the object code for the work under the terms of Section 6.
|
||||||
|
Any executables containing that work also fall under Section 6,
|
||||||
|
whether or not they are linked directly with the Library itself.
|
||||||
|
|
||||||
|
6. As an exception to the Sections above, you may also combine or
|
||||||
|
link a "work that uses the Library" with the Library to produce a
|
||||||
|
work containing portions of the Library, and distribute that work
|
||||||
|
under terms of your choice, provided that the terms permit
|
||||||
|
modification of the work for the customer's own use and reverse
|
||||||
|
engineering for debugging such modifications.
|
||||||
|
|
||||||
|
You must give prominent notice with each copy of the work that the
|
||||||
|
Library is used in it and that the Library and its use are covered by
|
||||||
|
this License. You must supply a copy of this License. If the work
|
||||||
|
during execution displays copyright notices, you must include the
|
||||||
|
copyright notice for the Library among them, as well as a reference
|
||||||
|
directing the user to the copy of this License. Also, you must do one
|
||||||
|
of these things:
|
||||||
|
|
||||||
|
a) Accompany the work with the complete corresponding
|
||||||
|
machine-readable source code for the Library including whatever
|
||||||
|
changes were used in the work (which must be distributed under
|
||||||
|
Sections 1 and 2 above); and, if the work is an executable linked
|
||||||
|
with the Library, with the complete machine-readable "work that
|
||||||
|
uses the Library", as object code and/or source code, so that the
|
||||||
|
user can modify the Library and then relink to produce a modified
|
||||||
|
executable containing the modified Library. (It is understood
|
||||||
|
that the user who changes the contents of definitions files in the
|
||||||
|
Library will not necessarily be able to recompile the application
|
||||||
|
to use the modified definitions.)
|
||||||
|
|
||||||
|
b) Use a suitable shared library mechanism for linking with the
|
||||||
|
Library. A suitable mechanism is one that (1) uses at run time a
|
||||||
|
copy of the library already present on the user's computer system,
|
||||||
|
rather than copying library functions into the executable, and (2)
|
||||||
|
will operate properly with a modified version of the library, if
|
||||||
|
the user installs one, as long as the modified version is
|
||||||
|
interface-compatible with the version that the work was made with.
|
||||||
|
|
||||||
|
c) Accompany the work with a written offer, valid for at
|
||||||
|
least three years, to give the same user the materials
|
||||||
|
specified in Subsection 6a, above, for a charge no more
|
||||||
|
than the cost of performing this distribution.
|
||||||
|
|
||||||
|
d) If distribution of the work is made by offering access to copy
|
||||||
|
from a designated place, offer equivalent access to copy the above
|
||||||
|
specified materials from the same place.
|
||||||
|
|
||||||
|
e) Verify that the user has already received a copy of these
|
||||||
|
materials or that you have already sent this user a copy.
|
||||||
|
|
||||||
|
For an executable, the required form of the "work that uses the
|
||||||
|
Library" must include any data and utility programs needed for
|
||||||
|
reproducing the executable from it. However, as a special exception,
|
||||||
|
the materials to be distributed need not include anything that is
|
||||||
|
normally distributed (in either source or binary form) with the major
|
||||||
|
components (compiler, kernel, and so on) of the operating system on
|
||||||
|
which the executable runs, unless that component itself accompanies
|
||||||
|
the executable.
|
||||||
|
|
||||||
|
It may happen that this requirement contradicts the license
|
||||||
|
restrictions of other proprietary libraries that do not normally
|
||||||
|
accompany the operating system. Such a contradiction means you cannot
|
||||||
|
use both them and the Library together in an executable that you
|
||||||
|
distribute.
|
||||||
|
|
||||||
|
7. You may place library facilities that are a work based on the
|
||||||
|
Library side-by-side in a single library together with other library
|
||||||
|
facilities not covered by this License, and distribute such a combined
|
||||||
|
library, provided that the separate distribution of the work based on
|
||||||
|
the Library and of the other library facilities is otherwise
|
||||||
|
permitted, and provided that you do these two things:
|
||||||
|
|
||||||
|
a) Accompany the combined library with a copy of the same work
|
||||||
|
based on the Library, uncombined with any other library
|
||||||
|
facilities. This must be distributed under the terms of the
|
||||||
|
Sections above.
|
||||||
|
|
||||||
|
b) Give prominent notice with the combined library of the fact
|
||||||
|
that part of it is a work based on the Library, and explaining
|
||||||
|
where to find the accompanying uncombined form of the same work.
|
||||||
|
|
||||||
|
8. You may not copy, modify, sublicense, link with, or distribute
|
||||||
|
the Library except as expressly provided under this License. Any
|
||||||
|
attempt otherwise to copy, modify, sublicense, link with, or
|
||||||
|
distribute the Library is void, and will automatically terminate your
|
||||||
|
rights under this License. However, parties who have received copies,
|
||||||
|
or rights, from you under this License will not have their licenses
|
||||||
|
terminated so long as such parties remain in full compliance.
|
||||||
|
|
||||||
|
9. You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Library or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Library (or any work based on the
|
||||||
|
Library), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Library or works based on it.
|
||||||
|
|
||||||
|
10. Each time you redistribute the Library (or any work based on the
|
||||||
|
Library), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute, link with or modify the Library
|
||||||
|
subject to these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties with
|
||||||
|
this License.
|
||||||
|
|
||||||
|
11. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot
|
||||||
|
distribute so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you
|
||||||
|
may not distribute the Library at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Library by
|
||||||
|
all those who receive copies directly or indirectly through you, then
|
||||||
|
the only way you could satisfy both it and this License would be to
|
||||||
|
refrain entirely from distribution of the Library.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under any
|
||||||
|
particular circumstance, the balance of the section is intended to apply,
|
||||||
|
and the section as a whole is intended to apply in other circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
12. If the distribution and/or use of the Library is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Library under this License may add
|
||||||
|
an explicit geographical distribution limitation excluding those countries,
|
||||||
|
so that distribution is permitted only in or among countries not thus
|
||||||
|
excluded. In such case, this License incorporates the limitation as if
|
||||||
|
written in the body of this License.
|
||||||
|
|
||||||
|
13. The Free Software Foundation may publish revised and/or new
|
||||||
|
versions of the Lesser General Public License from time to time.
|
||||||
|
Such new versions will be similar in spirit to the present version,
|
||||||
|
but may differ in detail to address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Library
|
||||||
|
specifies a version number of this License which applies to it and
|
||||||
|
"any later version", you have the option of following the terms and
|
||||||
|
conditions either of that version or of any later version published by
|
||||||
|
the Free Software Foundation. If the Library does not specify a
|
||||||
|
license version number, you may choose any version ever published by
|
||||||
|
the Free Software Foundation.
|
||||||
|
|
||||||
|
14. If you wish to incorporate parts of the Library into other free
|
||||||
|
programs whose distribution conditions are incompatible with these,
|
||||||
|
write to the author to ask for permission. For software which is
|
||||||
|
copyrighted by the Free Software Foundation, write to the Free
|
||||||
|
Software Foundation; we sometimes make exceptions for this. Our
|
||||||
|
decision will be guided by the two goals of preserving the free status
|
||||||
|
of all derivatives of our free software and of promoting the sharing
|
||||||
|
and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||||
|
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||||
|
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||||
|
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||||
|
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||||
|
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||||
|
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||||
|
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||||
|
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||||
|
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||||
|
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||||
|
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||||
|
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||||
|
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||||
|
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||||
|
DAMAGES.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Libraries
|
||||||
|
|
||||||
|
If you develop a new library, and you want it to be of the greatest
|
||||||
|
possible use to the public, we recommend making it free software that
|
||||||
|
everyone can redistribute and change. You can do so by permitting
|
||||||
|
redistribution under these terms (or, alternatively, under the terms of the
|
||||||
|
ordinary General Public License).
|
||||||
|
|
||||||
|
To apply these terms, attach the following notices to the library. It is
|
||||||
|
safest to attach them to the start of each source file to most effectively
|
||||||
|
convey the exclusion of warranty; and each file should have at least the
|
||||||
|
"copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
{description}
|
||||||
|
Copyright (C) {year} {fullname}
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||||
|
USA
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your
|
||||||
|
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||||
|
necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||||
|
library `Frob' (a library for tweaking knobs) written by James Random
|
||||||
|
Hacker.
|
||||||
|
|
||||||
|
{signature of Ty Coon}, 1 April 1990
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
That's all there is to it!
|
|
@ -0,0 +1,310 @@
|
||||||
|
# FAL:Flash 抽象层
|
||||||
|
|
||||||
|
## 1、FAL介绍
|
||||||
|
|
||||||
|
FAL (Flash Abstraction Layer) Flash 抽象层,是对 Flash 及基于 Flash 的分区进行管理、操作的抽象层,对上层统一了 Flash 及 分区操作的 API (框架图如下所示),并具有以下特性:
|
||||||
|
|
||||||
|
- 支持静态可配置的分区表,并可关联多个 Flash 设备;
|
||||||
|
- 分区表支持 **自动装载** 。避免在多固件项目,分区表被多次定义的问题;
|
||||||
|
- 代码精简,对操作系统 **无依赖** ,可运行于裸机平台,比如对资源有一定要求的 Bootloader;
|
||||||
|
- 统一的操作接口。保证了文件系统、OTA、NVM(例如:[EasyFlash](https://github.com/armink-rtt-pkgs/EasyFlash)) 等对 Flash 有一定依赖的组件,底层 Flash 驱动的可重用性;
|
||||||
|
- 自带基于 Finsh/MSH 的测试命令,可以通过 Shell 按字节寻址的方式操作(读写擦) Flash 或分区,方便开发者进行调试、测试;
|
||||||
|
|
||||||
|
![FAL framework](docs/figures/fal_framework.png)
|
||||||
|
|
||||||
|
### 1.1、打开 FAL
|
||||||
|
|
||||||
|
使用 fal package 需要在 RT-Thread 的包管理器中选择它,具体路径如下:
|
||||||
|
|
||||||
|
```
|
||||||
|
RT-Thread online packages
|
||||||
|
system packages --->
|
||||||
|
--- fal: Flash Abstraction Layer implement. Manage flash device and partition.
|
||||||
|
[*] Enable debug log output
|
||||||
|
[*] FAL partition table config has defined on 'fal_cfg.h'
|
||||||
|
(onchip) The flash device which saving partition table
|
||||||
|
(65536) The patition table end address relative to flash device offset.
|
||||||
|
[ ] FAL uses SFUD drivers
|
||||||
|
(norflash0) The name of the device used by FAL (NEW)
|
||||||
|
version (latest) --->
|
||||||
|
```
|
||||||
|
|
||||||
|
每个功能的配置说明如下:
|
||||||
|
|
||||||
|
- 开启调试日志输出(默认开启);
|
||||||
|
- 分区表是否在 `fal_cfg.h` 中定义(默认开启)。如果关闭此选项,fal 将会自动去指定 Flash 的指定位置去检索并装载分区表,具体配置详见下面两个选项;
|
||||||
|
- 存放分区表的 Flash 设备;
|
||||||
|
- 分区表的 **结束地址** 位于 Flash 设备上的偏移。fal 将从此地址开始往回进行检索分区表,直接读取到 Flash 顶部。如果不确定分区表具体位置,这里也可以配置为 Flash 的结束地址,fal 将会检索整个 Flash,检索时间可能会增加。
|
||||||
|
- 启用 FAL 针对 SFUD 的移植文件(默认关闭);
|
||||||
|
- 应输入调用 `rt_sfud_flash_probe` 函数时传入的 FLASH 设备名称(也可以通过 list_device 命令查看 Block Device 的名字获取)。该名称与分区表中的 Flash 名称对应,只有正确设置设备名字,才能完成对 FLASH 的读写操作。
|
||||||
|
|
||||||
|
然后让 RT-Thread 的包管理器自动更新,或者使用 `pkgs --update` 命令更新包到 BSP 中。
|
||||||
|
|
||||||
|
### 1.2、FAL 目录
|
||||||
|
|
||||||
|
| 名称 | 说明 |
|
||||||
|
| ------- | ---------- |
|
||||||
|
| inc | 头文件目录 |
|
||||||
|
| src | 源代码目录 |
|
||||||
|
| samples | 例程目录 |
|
||||||
|
|
||||||
|
### 1.3、FAL API
|
||||||
|
|
||||||
|
FAL 相关的 API 如图所示,[点击此处查看 API 参数详解](docs/fal_api.md)。
|
||||||
|
|
||||||
|
![FAL API](docs/figures/fal-api.png)
|
||||||
|
|
||||||
|
### 1.4、许可证
|
||||||
|
|
||||||
|
fal package 遵循 LGPLv2.1 许可,详见 `LICENSE` 文件。
|
||||||
|
|
||||||
|
### 1.5、依赖
|
||||||
|
|
||||||
|
对 RT-Thread 无依赖,也可用于裸机。
|
||||||
|
|
||||||
|
> 测试命令功能需要依赖 RT-Thread Finsh/MSH
|
||||||
|
|
||||||
|
## 2、使用 FAL
|
||||||
|
|
||||||
|
使用 FAL 的基本步骤如下所示:
|
||||||
|
|
||||||
|
1. 打开 FAL:从 Env 中打开 fal 软件包并下载到工程。
|
||||||
|
2. FAL 移植:定义 flash 设备、定义 flash 设备表、定义 flash 分区表。以下主要对步骤 2 展开讲解。
|
||||||
|
3. 调用 fal_init() 初始化该库:移植完成后,可在应用层调用,如在 main 函数中调用。
|
||||||
|
|
||||||
|
![fal 移植](docs/figures/fal-port.png)
|
||||||
|
|
||||||
|
### 2.1、定义 flash 设备
|
||||||
|
|
||||||
|
在定义 Flash 设备表前,需要先定义 Flash 设备。可以是片内 flash, 也可以是片外基于 SFUD 的 spi flash:
|
||||||
|
|
||||||
|
- 定义片内 flash 设备可以参考 [`fal_flash_sfud_port.c`](https://github.com/RT-Thread-packages/fal/blob/master/samples/porting/fal_flash_sfud_port.c) 。
|
||||||
|
- 定义片外 spi flash 设备可以参考 [`fal_flash_stm32f2_port.c`](https://github.com/RT-Thread-packages/fal/blob/master/samples/porting/fal_flash_stm32f2_port.c) 。
|
||||||
|
|
||||||
|
定义具体的 Flash 设备对象,用户需要根据自己的 Flash 情况分别实现 `init`、 `read`、 `write`、 `erase` 这些操作函数:
|
||||||
|
|
||||||
|
- `static int init(void)`:**可选** 的初始化操作。
|
||||||
|
- `static int read(long offset, uint8_t *buf, size_t size)`:读取操作。
|
||||||
|
|
||||||
|
| 参数 | 描述 |
|
||||||
|
| ------ | ------------------------- |
|
||||||
|
| offset | 读取数据的 Flash 偏移地址 |
|
||||||
|
| buf | 存放待读取数据的缓冲区 |
|
||||||
|
| size | 待读取数据的大小 |
|
||||||
|
| return | 返回实际读取的数据大小 |
|
||||||
|
|
||||||
|
- `static int write(long offset, const uint8_t *buf, size_t size)` :写入操作。
|
||||||
|
|
||||||
|
| 参数 | 描述 |
|
||||||
|
| ------ | ------------------------- |
|
||||||
|
| offset | 写入数据的 Flash 偏移地址 |
|
||||||
|
| buf | 存放待写入数据的缓冲区 |
|
||||||
|
| size | 待写入数据的大小 |
|
||||||
|
| return | 返回实际写入的数据大小 |
|
||||||
|
|
||||||
|
- `static int erase(long offset, size_t size)` :擦除操作。
|
||||||
|
|
||||||
|
| 参数 | 描述 |
|
||||||
|
| ------ | ------------------------- |
|
||||||
|
| offset | 擦除区域的 Flash 偏移地址 |
|
||||||
|
| size | 擦除区域的大小 |
|
||||||
|
| return | 返回实际擦除的区域大小 |
|
||||||
|
|
||||||
|
用户需要根据自己的 Flash 情况分别实现这些操作函数。在文件最底部定义了具体的 Flash 设备对象 ,如下示例定义了 stm32f2 片上 flash:stm32f2_onchip_flash
|
||||||
|
|
||||||
|
```c
|
||||||
|
const struct fal_flash_dev stm32f2_onchip_flash =
|
||||||
|
{
|
||||||
|
.name = "stm32_onchip",
|
||||||
|
.addr = 0x08000000,
|
||||||
|
.len = 1024*1024,
|
||||||
|
.blk_size = 128*1024,
|
||||||
|
.ops = {init, read, write, erase},
|
||||||
|
.write_gran = 8
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
- `"stm32_onchip"` : Flash 设备的名字。
|
||||||
|
- `0x08000000`: 对 Flash 操作的起始地址。
|
||||||
|
- `1024*1024`:Flash 的总大小(1MB)。
|
||||||
|
- `128*1024`:Flash 块/扇区大小(因为 STM32F2 各块大小不均匀,所以擦除粒度为最大块的大小:128K)。
|
||||||
|
- `{init, read, write, erase}` :Flash 的操作函数。 如果没有 init 初始化过程,第一个操作函数位置可以置空。
|
||||||
|
- `8` : 设置写粒度,单位 bit, 0 表示未生效(默认值为 0 ),该成员是 fal 版本大于 0.4.0 的新增成员。各个 flash 写入粒度不尽相同,可通过该成员进行设置,以下列举几种常见 Flash 写粒度:
|
||||||
|
- nor flash: 1 bit
|
||||||
|
- stm32f4: 8 bit
|
||||||
|
- stm32f1: 32 bit
|
||||||
|
- stm32l4: 64 bit
|
||||||
|
|
||||||
|
### 2.2、定义 flash 设备表
|
||||||
|
|
||||||
|
Flash 设备表定义在 `fal_cfg.h` 头文件中,定义分区表前需 **新建 `fal_cfg.h` 文件** ,请将该文件统一放在对应 BSP 或工程目录的 port 文件夹下,并将该头文件路径加入到工程。fal_cfg.h 可以参考 [示例文件 fal/samples/porting/fal_cfg.h](https://github.com/RT-Thread-packages/fal/blob/master/samples/porting/samples/porting/fal_cfg.h) 完成。
|
||||||
|
|
||||||
|
设备表示例:
|
||||||
|
|
||||||
|
```c
|
||||||
|
/* ===================== Flash device Configuration ========================= */
|
||||||
|
extern const struct fal_flash_dev stm32f2_onchip_flash;
|
||||||
|
extern struct fal_flash_dev nor_flash0;
|
||||||
|
|
||||||
|
/* flash device table */
|
||||||
|
#define FAL_FLASH_DEV_TABLE \
|
||||||
|
{ \
|
||||||
|
&stm32f2_onchip_flash, \
|
||||||
|
&nor_flash0, \
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Flash 设备表中,有两个 Flash 对象,一个为 STM32F2 的片内 Flash ,一个为片外的 Nor Flash。
|
||||||
|
|
||||||
|
### 2.3、定义 flash 分区表
|
||||||
|
|
||||||
|
分区表也定义在 `fal_cfg.h` 头文件中。Flash 分区基于 Flash 设备,每个 Flash 设备又可以有 N 个分区,这些分区的集合就是分区表。在配置分区表前,务必保证已定义好 **Flash 设备** 及 **设备表**。fal_cfg.h 可以参考 [示例文件 fal/samples/porting/fal_cfg.h](https://github.com/RT-Thread-packages/fal/blob/master/samples/porting/samples/porting/fal_cfg.h) 完成。
|
||||||
|
|
||||||
|
分区表示例:
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define NOR_FLASH_DEV_NAME "norflash0"
|
||||||
|
/* ====================== Partition Configuration ========================== */
|
||||||
|
#ifdef FAL_PART_HAS_TABLE_CFG
|
||||||
|
/* partition table */
|
||||||
|
#define FAL_PART_TABLE \
|
||||||
|
{ \
|
||||||
|
{FAL_PART_MAGIC_WORD, "bl", "stm32_onchip", 0, 64*1024, 0}, \
|
||||||
|
{FAL_PART_MAGIC_WORD, "app", "stm32_onchip", 64*1024, 704*1024, 0}, \
|
||||||
|
{FAL_PART_MAGIC_WORD, "easyflash", NOR_FLASH_DEV_NAME, 0, 1024*1024, 0}, \
|
||||||
|
{FAL_PART_MAGIC_WORD, "download", NOR_FLASH_DEV_NAME, 1024*1024, 1024*1024, 0}, \
|
||||||
|
}
|
||||||
|
#endif /* FAL_PART_HAS_TABLE_CFG */
|
||||||
|
```
|
||||||
|
|
||||||
|
上面这个分区表详细描述信息如下:
|
||||||
|
|
||||||
|
| 分区名 | Flash 设备名 | 偏移地址 | 大小 | 说明 |
|
||||||
|
| ----------- | -------------- | --------- | ----- | ------------------ |
|
||||||
|
| "bl" | "stm32_onchip" | 0 | 64KB | 引导程序 |
|
||||||
|
| "app" | "stm32_onchip" | 64*1024 | 704KB | 应用程序 |
|
||||||
|
| "easyflash" | "norflash0" | 0 | 1MB | EasyFlash 参数存储 |
|
||||||
|
| "download" | "norflash0" | 1024*1024 | 1MB | OTA 下载区 |
|
||||||
|
|
||||||
|
用户需要修改的分区参数包括:分区名称、关联的 Flash 设备名、偏移地址(相对 Flash 设备内部)、大小,需要注意以下几点:
|
||||||
|
|
||||||
|
- 分区名保证 **不能重复**;
|
||||||
|
- 关联的 Flash 设备 **务必已经在 Flash 设备表中定义好** ,并且 **名称一致** ,否则会出现无法找到 Flash 设备的错误;
|
||||||
|
- 分区的起始地址和大小 **不能超过 Flash 设备的地址范围** ,否则会导致包初始化错误;
|
||||||
|
|
||||||
|
> 注意:每个分区定义时,除了填写上面介绍的参数属性外,需在前面增加 `FAL_PART_MAGIC_WORD` 属性,末尾增加 `0` (目前用于保留功能)
|
||||||
|
|
||||||
|
## 3、Finsh/MSH 测试命令
|
||||||
|
|
||||||
|
fal 提供了丰富的测试命令,项目只要在 RT-Thread 上开启 Finsh/MSH 功能即可。在做一些基于 Flash 的应用开发、调试时,这些命令会非常实用。它可以准确的写入或者读取指定位置的原始 Flash 数据,快速的验证 Flash 驱动的完整性,甚至可以对 Flash 进行性能测试。
|
||||||
|
|
||||||
|
具体功能如下:输入 fal 可以看到完整的命令列表
|
||||||
|
|
||||||
|
```
|
||||||
|
msh />fal
|
||||||
|
Usage:
|
||||||
|
fal probe [dev_name|part_name] - probe flash device or partition by given name
|
||||||
|
fal read addr size - read 'size' bytes starting at 'addr'
|
||||||
|
fal write addr data1 ... dataN - write some bytes 'data' starting at 'addr'
|
||||||
|
fal erase addr size - erase 'size' bytes starting at 'addr'
|
||||||
|
fal bench <blk_size> - benchmark test with per block size
|
||||||
|
|
||||||
|
msh />
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.1、指定待操作的 Flash 设备或 Flash 分区
|
||||||
|
|
||||||
|
当第一次使用 fal 命令时,直接输入 `fal probe` 将会显示分区表信息。可以指定待操作的对象为分区表里的某个分区,或者某个 Flash 设备。
|
||||||
|
|
||||||
|
分区或者 Flash 被成功选中后,还将会显示它的一些属性情况。大致效果如下:
|
||||||
|
|
||||||
|
```
|
||||||
|
msh />fal probe
|
||||||
|
No flash device or partition was probed.
|
||||||
|
Usage: fal probe [dev_name|part_name] - probe flash device or partition by given name.
|
||||||
|
[I/FAL] ==================== FAL partition table ====================
|
||||||
|
[I/FAL] | name | flash_dev | offset | length |
|
||||||
|
[I/FAL] -------------------------------------------------------------
|
||||||
|
[I/FAL] | bl | stm32_onchip | 0x00000000 | 0x00010000 |
|
||||||
|
[I/FAL] | app | stm32_onchip | 0x00010000 | 0x000b0000 |
|
||||||
|
[I/FAL] | ef | norflash0 | 0x00000000 | 0x00100000 |
|
||||||
|
[I/FAL] | download | norflash0 | 0x00100000 | 0x00100000 |
|
||||||
|
[I/FAL] =============================================================
|
||||||
|
msh />
|
||||||
|
msh />fal probe download
|
||||||
|
Probed a flash partition | download | flash_dev: norflash0 | offset: 1048576 | len: 1048576 |.
|
||||||
|
msh />
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2、擦除数据
|
||||||
|
|
||||||
|
先输入 `fal erase` ,后面跟着待擦除数据的起始地址以及长度。以下命令为:从 0 地址(相对 Flash 或分区)开始擦除 4096 字节数据
|
||||||
|
|
||||||
|
> 注意:根据 Flash 特性,擦除动作将按扇区对齐进行处理。所以,如果擦除操作地址或长度未按照 Flash 的扇区对齐,将会擦除掉与其关联的整个扇区数据。
|
||||||
|
|
||||||
|
```
|
||||||
|
msh />fal erase 0 4096
|
||||||
|
Erase data success. Start from 0x00000000, size is 4096.
|
||||||
|
msh />
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.3、写入数据
|
||||||
|
|
||||||
|
先输入 `fal write` ,后面跟着 N 个待写入的数据,并以空格隔开。以下命令为:从地址 8 的位置依次开始写入 1、2、3、4 、 5 这 5 个字节数据
|
||||||
|
|
||||||
|
```
|
||||||
|
msh />fal write 8 1 2 3 4 5
|
||||||
|
Write data success. Start from 0x00000008, size is 5.
|
||||||
|
Write data: 1 2 3 4 5 .
|
||||||
|
msh />
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.4、读取数据
|
||||||
|
|
||||||
|
先输入 `fal read` ,后面跟着待读取数据的起始地址以及长度。以下命令为:从 0 地址开始读取 64 字节数据
|
||||||
|
|
||||||
|
```
|
||||||
|
msh />fal read 0 64
|
||||||
|
Read data success. Start from 0x00000000, size is 64. The data is:
|
||||||
|
Offset (h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
|
||||||
|
[00000000] FF FF FF FF FF FF FF FF 01 02 03 04 05 FF FF FF
|
||||||
|
[00000010] FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
|
||||||
|
[00000020] FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
|
||||||
|
[00000030] FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
|
||||||
|
|
||||||
|
msh />
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.5、性能测试
|
||||||
|
|
||||||
|
性能测试将会测试 Flash 的擦除、写入及读取速度,同时将会测试写入及读取数据的准确性,保证整个 Flash 或整个分区的 写入与读取 数据的一致性。
|
||||||
|
|
||||||
|
先输入 `fal bench` ,后面跟着待测试 Flash 的扇区大小(请查看对应的 Flash 手册,SPI Nor Flash 一般为 4096)。由于性能测试将会让整个 Flash 或者整个分区的数据丢失,所以命令最后必须跟 `yes` 。
|
||||||
|
|
||||||
|
```
|
||||||
|
msh />fal bench 4096 yes
|
||||||
|
Erasing 1048576 bytes data, waiting...
|
||||||
|
Erase benchmark success, total time: 2.674S.
|
||||||
|
Writing 1048576 bytes data, waiting...
|
||||||
|
Write benchmark success, total time: 7.107S.
|
||||||
|
Reading 1048576 bytes data, waiting...
|
||||||
|
Read benchmark success, total time: 2.716S.
|
||||||
|
msh />
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4、常见应用
|
||||||
|
|
||||||
|
- [基于 FAL 分区的 fatfs 文件系统例程](https://github.com/RT-Thread/IoT_Board/tree/master/examples/15_component_fs_flash)
|
||||||
|
- [基于 FAL 分区的 littlefs 文件系统应用笔记](https://www.rt-thread.org/document/site/application-note/components/dfs/an0027-littlefs/)
|
||||||
|
- [基于 FAL 分区的 EasyFlash 移植说明](https://github.com/armink-rtt-pkgs/EasyFlash/tree/master/ports)
|
||||||
|
|
||||||
|
## 5、常见问题
|
||||||
|
|
||||||
|
**1、使用 FAL 时,无法找到 `fal_cfg.h` 头文件**
|
||||||
|
|
||||||
|
`fal_cfg.h` 为 fal 软件包的配置文件,需要用户手动新建,并定义相关的分区表信息。请将该文件统一放在 BSP 的 port 文件夹下或工程目录的 port 文件夹下(若没有则新建 port 文件夹),并将该头文件路径加入到工程,详见 "`2.2、定义 flash 设备表`" 小节。
|
||||||
|
|
||||||
|
## 6、联系方式
|
||||||
|
|
||||||
|
* 维护:[armink](https://github.com/armink)
|
||||||
|
* 主页:https://github.com/RT-Thread-packages/fal
|
|
@ -0,0 +1,145 @@
|
||||||
|
# FAL API
|
||||||
|
|
||||||
|
## 查找 Flash 设备
|
||||||
|
|
||||||
|
```C
|
||||||
|
const struct fal_flash_dev *fal_flash_device_find(const char *name)
|
||||||
|
```
|
||||||
|
|
||||||
|
| 参数 | 描述 |
|
||||||
|
| :----- | :----------------------- |
|
||||||
|
| name | Flash 设备名称 |
|
||||||
|
| return | 如果查找成功,将返回 Flash 设备对象,查找失败返回 NULL |
|
||||||
|
|
||||||
|
## 查找 Flash 分区
|
||||||
|
|
||||||
|
```C
|
||||||
|
const struct fal_partition *fal_partition_find(const char *name)
|
||||||
|
```
|
||||||
|
|
||||||
|
| 参数 | 描述 |
|
||||||
|
| :----- | :----------------------- |
|
||||||
|
| name | Flash 分区名称 |
|
||||||
|
| return | 如果查找成功,将返回 Flash 分区对象,查找失败返回 NULL |
|
||||||
|
|
||||||
|
## 获取分区表
|
||||||
|
|
||||||
|
```C
|
||||||
|
const struct fal_partition *fal_get_partition_table(size_t *len)
|
||||||
|
```
|
||||||
|
|
||||||
|
| 参数 | 描述 |
|
||||||
|
| :----- | :----------------------- |
|
||||||
|
| len | 分区表的长度 |
|
||||||
|
| return | 分区表 |
|
||||||
|
|
||||||
|
## 临时设置分区表
|
||||||
|
|
||||||
|
FAL 初始化时会自动装载默认分区表。使用该设置将临时修改分区表,重启后会 **丢失** 该设置
|
||||||
|
|
||||||
|
```C
|
||||||
|
void fal_set_partition_table_temp(struct fal_partition *table, size_t len)
|
||||||
|
```
|
||||||
|
|
||||||
|
| 参数 | 描述 |
|
||||||
|
| :----- | :----------------------- |
|
||||||
|
| table | 分区表 |
|
||||||
|
| len | 分区表的长度 |
|
||||||
|
|
||||||
|
## 从分区读取数据
|
||||||
|
|
||||||
|
```C
|
||||||
|
int fal_partition_read(const struct fal_partition *part, uint32_t addr, uint8_t *buf, size_t size)
|
||||||
|
```
|
||||||
|
|
||||||
|
| 参数 | 描述 |
|
||||||
|
| :----- | :----------------------- |
|
||||||
|
| part | 分区对象 |
|
||||||
|
| addr | 相对分区的偏移地址 |
|
||||||
|
| buf | 存放待读取数据的缓冲区 |
|
||||||
|
| size | 待读取数据的大小 |
|
||||||
|
| return | 返回实际读取的数据大小 |
|
||||||
|
|
||||||
|
## 往分区写入数据
|
||||||
|
|
||||||
|
```C
|
||||||
|
int fal_partition_write(const struct fal_partition *part, uint32_t addr, const uint8_t *buf, size_t size)
|
||||||
|
```
|
||||||
|
|
||||||
|
| 参数 | 描述 |
|
||||||
|
| :----- | :----------------------- |
|
||||||
|
| part | 分区对象 |
|
||||||
|
| addr | 相对分区的偏移地址 |
|
||||||
|
| buf | 存放待写入数据的缓冲区 |
|
||||||
|
| size | 待写入数据的大小 |
|
||||||
|
| return | 返回实际写入的数据大小 |
|
||||||
|
|
||||||
|
## 擦除分区数据
|
||||||
|
|
||||||
|
```C
|
||||||
|
int fal_partition_erase(const struct fal_partition *part, uint32_t addr, size_t size)
|
||||||
|
```
|
||||||
|
|
||||||
|
| 参数 | 描述 |
|
||||||
|
| :----- | :----------------------- |
|
||||||
|
| part | 分区对象 |
|
||||||
|
| addr | 相对分区的偏移地址 |
|
||||||
|
| size | 擦除区域的大小 |
|
||||||
|
| return | 返回实际擦除的区域大小 |
|
||||||
|
|
||||||
|
## 擦除整个分区数据
|
||||||
|
|
||||||
|
```C
|
||||||
|
int fal_partition_erase_all(const struct fal_partition *part)
|
||||||
|
```
|
||||||
|
|
||||||
|
| 参数 | 描述 |
|
||||||
|
| :----- | :----------------------- |
|
||||||
|
| part | 分区对象 |
|
||||||
|
| return | 返回实际擦除的区域大小 |
|
||||||
|
|
||||||
|
## 打印分区表
|
||||||
|
|
||||||
|
```c
|
||||||
|
void fal_show_part_table(void)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 创建块设备
|
||||||
|
|
||||||
|
该函数可以根据指定的分区名称,创建对应的块设备,以便于在指定的分区上挂载文件系统
|
||||||
|
|
||||||
|
```C
|
||||||
|
struct rt_device *fal_blk_device_create(const char *parition_name)
|
||||||
|
```
|
||||||
|
|
||||||
|
| 参数 | 描述 |
|
||||||
|
| :----- | :----------------------- |
|
||||||
|
| parition_name | 分区名称 |
|
||||||
|
| return | 创建成功,则返回对应的块设备,失败返回空 |
|
||||||
|
|
||||||
|
## 创建 MTD Nor Flash 设备
|
||||||
|
|
||||||
|
该函数可以根据指定的分区名称,创建对应的 MTD Nor Flash 设备,以便于在指定的分区上挂载文件系统
|
||||||
|
|
||||||
|
```C
|
||||||
|
struct rt_device *fal_mtd_nor_device_create(const char *parition_name)
|
||||||
|
```
|
||||||
|
|
||||||
|
| 参数 | 描述 |
|
||||||
|
| :------------ | :---------------------------------------------------- |
|
||||||
|
| parition_name | 分区名称 |
|
||||||
|
| return | 创建成功,则返回对应的 MTD Nor Flash 设备,失败返回空 |
|
||||||
|
|
||||||
|
## 创建字符设备
|
||||||
|
|
||||||
|
该函数可以根据指定的分区名称,创建对应的字符设备,以便于通过 deivice 接口或 devfs 接口操作分区,开启了 POSIX 后,还可以通过 oepn/read/write 函数操作分区。
|
||||||
|
|
||||||
|
```C
|
||||||
|
struct rt_device *fal_char_device_create(const char *parition_name)
|
||||||
|
```
|
||||||
|
|
||||||
|
| 参数 | 描述 |
|
||||||
|
| :------------ | :----------------------------------------- |
|
||||||
|
| parition_name | 分区名称 |
|
||||||
|
| return | 创建成功,则返回对应的字符设备,失败返回空 |
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 46 KiB |
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
Binary file not shown.
After Width: | Height: | Size: 33 KiB |
|
@ -0,0 +1,166 @@
|
||||||
|
/*
|
||||||
|
* File : fal.h
|
||||||
|
* This file is part of FAL (Flash Abstraction Layer) package
|
||||||
|
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Change Logs:
|
||||||
|
* Date Author Notes
|
||||||
|
* 2018-05-17 armink the first version
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _FAL_H_
|
||||||
|
#define _FAL_H_
|
||||||
|
|
||||||
|
#include <rtconfig.h>
|
||||||
|
#include <fal_cfg.h>
|
||||||
|
#include "fal_def.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FAL (Flash Abstraction Layer) initialization.
|
||||||
|
* It will initialize all flash device and all flash partition.
|
||||||
|
*
|
||||||
|
* @return >= 0: partitions total number
|
||||||
|
*/
|
||||||
|
int fal_init(void);
|
||||||
|
|
||||||
|
/* =============== flash device operator API =============== */
|
||||||
|
/**
|
||||||
|
* find flash device by name
|
||||||
|
*
|
||||||
|
* @param name flash device name
|
||||||
|
*
|
||||||
|
* @return != NULL: flash device
|
||||||
|
* NULL: not found
|
||||||
|
*/
|
||||||
|
const struct fal_flash_dev *fal_flash_device_find(const char *name);
|
||||||
|
|
||||||
|
/* =============== partition operator API =============== */
|
||||||
|
/**
|
||||||
|
* find the partition by name
|
||||||
|
*
|
||||||
|
* @param name partition name
|
||||||
|
*
|
||||||
|
* @return != NULL: partition
|
||||||
|
* NULL: not found
|
||||||
|
*/
|
||||||
|
const struct fal_partition *fal_partition_find(const char *name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the partition table
|
||||||
|
*
|
||||||
|
* @param len return the partition table length
|
||||||
|
*
|
||||||
|
* @return partition table
|
||||||
|
*/
|
||||||
|
const struct fal_partition *fal_get_partition_table(size_t *len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set partition table temporarily
|
||||||
|
* This setting will modify the partition table temporarily, the setting will be lost after restart.
|
||||||
|
*
|
||||||
|
* @param table partition table
|
||||||
|
* @param len partition table length
|
||||||
|
*/
|
||||||
|
void fal_set_partition_table_temp(struct fal_partition *table, size_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* read data from partition
|
||||||
|
*
|
||||||
|
* @param part partition
|
||||||
|
* @param addr relative address for partition
|
||||||
|
* @param buf read buffer
|
||||||
|
* @param size read size
|
||||||
|
*
|
||||||
|
* @return >= 0: successful read data size
|
||||||
|
* -1: error
|
||||||
|
*/
|
||||||
|
int fal_partition_read(const struct fal_partition *part, uint32_t addr, uint8_t *buf, size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* write data to partition
|
||||||
|
*
|
||||||
|
* @param part partition
|
||||||
|
* @param addr relative address for partition
|
||||||
|
* @param buf write buffer
|
||||||
|
* @param size write size
|
||||||
|
*
|
||||||
|
* @return >= 0: successful write data size
|
||||||
|
* -1: error
|
||||||
|
*/
|
||||||
|
int fal_partition_write(const struct fal_partition *part, uint32_t addr, const uint8_t *buf, size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* erase partition data
|
||||||
|
*
|
||||||
|
* @param part partition
|
||||||
|
* @param addr relative address for partition
|
||||||
|
* @param size erase size
|
||||||
|
*
|
||||||
|
* @return >= 0: successful erased data size
|
||||||
|
* -1: error
|
||||||
|
*/
|
||||||
|
int fal_partition_erase(const struct fal_partition *part, uint32_t addr, size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* erase partition all data
|
||||||
|
*
|
||||||
|
* @param part partition
|
||||||
|
*
|
||||||
|
* @return >= 0: successful erased data size
|
||||||
|
* -1: error
|
||||||
|
*/
|
||||||
|
int fal_partition_erase_all(const struct fal_partition *part);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* print the partition table
|
||||||
|
*/
|
||||||
|
void fal_show_part_table(void);
|
||||||
|
|
||||||
|
/* =============== API provided to RT-Thread =============== */
|
||||||
|
/**
|
||||||
|
* create RT-Thread block device by specified partition
|
||||||
|
*
|
||||||
|
* @param parition_name partition name
|
||||||
|
*
|
||||||
|
* @return != NULL: created block device
|
||||||
|
* NULL: created failed
|
||||||
|
*/
|
||||||
|
struct rt_device *fal_blk_device_create(const char *parition_name);
|
||||||
|
|
||||||
|
#if defined(RT_USING_MTD_NOR)
|
||||||
|
/**
|
||||||
|
* create RT-Thread MTD NOR device by specified partition
|
||||||
|
*
|
||||||
|
* @param parition_name partition name
|
||||||
|
*
|
||||||
|
* @return != NULL: created MTD NOR device
|
||||||
|
* NULL: created failed
|
||||||
|
*/
|
||||||
|
struct rt_device *fal_mtd_nor_device_create(const char *parition_name);
|
||||||
|
#endif /* defined(RT_USING_MTD_NOR) */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create RT-Thread char device by specified partition
|
||||||
|
*
|
||||||
|
* @param parition_name partition name
|
||||||
|
*
|
||||||
|
* @return != NULL: created char device
|
||||||
|
* NULL: created failed
|
||||||
|
*/
|
||||||
|
struct rt_device *fal_char_device_create(const char *parition_name);
|
||||||
|
|
||||||
|
#endif /* _FAL_H_ */
|
|
@ -0,0 +1,156 @@
|
||||||
|
/*
|
||||||
|
* File : fal_def.h
|
||||||
|
* This file is part of FAL (Flash Abstraction Layer) package
|
||||||
|
* COPYRIGHT (C) 2006 - 2019, RT-Thread Development Team
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Change Logs:
|
||||||
|
* Date Author Notes
|
||||||
|
* 2018-05-17 armink the first version
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _FAL_DEF_H_
|
||||||
|
#define _FAL_DEF_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define FAL_SW_VERSION "0.5.0"
|
||||||
|
|
||||||
|
#ifndef FAL_MALLOC
|
||||||
|
#define FAL_MALLOC malloc
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef FAL_CALLOC
|
||||||
|
#define FAL_CALLOC calloc
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef FAL_REALLOC
|
||||||
|
#define FAL_REALLOC realloc
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef FAL_FREE
|
||||||
|
#define FAL_FREE free
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef FAL_DEBUG
|
||||||
|
#define FAL_DEBUG 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef FAL_PRINTF
|
||||||
|
#ifdef RT_VER_NUM
|
||||||
|
/* for RT-Thread platform */
|
||||||
|
extern void rt_kprintf(const char *fmt, ...);
|
||||||
|
#define FAL_PRINTF rt_kprintf
|
||||||
|
#else
|
||||||
|
#define FAL_PRINTF printf
|
||||||
|
#endif /* RT_VER_NUM */
|
||||||
|
#endif /* FAL_PRINTF */
|
||||||
|
|
||||||
|
#if FAL_DEBUG
|
||||||
|
#ifdef assert
|
||||||
|
#undef assert
|
||||||
|
#endif
|
||||||
|
#define assert(EXPR) \
|
||||||
|
if (!(EXPR)) \
|
||||||
|
{ \
|
||||||
|
FAL_PRINTF("(%s) has assert failed at %s.\n", #EXPR, __FUNCTION__); \
|
||||||
|
while (1); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/* debug level log */
|
||||||
|
#ifdef log_d
|
||||||
|
#undef log_d
|
||||||
|
#endif
|
||||||
|
#define log_d(...) FAL_PRINTF("[D/FAL] (%s:%d) ", __FUNCTION__, __LINE__); FAL_PRINTF(__VA_ARGS__);FAL_PRINTF("\n")
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#ifdef assert
|
||||||
|
#undef assert
|
||||||
|
#endif
|
||||||
|
#define assert(EXPR) ((void)0);
|
||||||
|
|
||||||
|
/* debug level log */
|
||||||
|
#ifdef log_d
|
||||||
|
#undef log_d
|
||||||
|
#endif
|
||||||
|
#define log_d(...)
|
||||||
|
#endif /* FAL_DEBUG */
|
||||||
|
|
||||||
|
/* error level log */
|
||||||
|
#ifdef log_e
|
||||||
|
#undef log_e
|
||||||
|
#endif
|
||||||
|
#define log_e(...) FAL_PRINTF("\033[31;22m[E/FAL] (%s:%d) ", __FUNCTION__, __LINE__);FAL_PRINTF(__VA_ARGS__);FAL_PRINTF("\033[0m\n")
|
||||||
|
|
||||||
|
/* info level log */
|
||||||
|
#ifdef log_i
|
||||||
|
#undef log_i
|
||||||
|
#endif
|
||||||
|
#define log_i(...) FAL_PRINTF("\033[32;22m[I/FAL] "); FAL_PRINTF(__VA_ARGS__);FAL_PRINTF("\033[0m\n")
|
||||||
|
|
||||||
|
/* FAL flash and partition device name max length */
|
||||||
|
#ifndef FAL_DEV_NAME_MAX
|
||||||
|
#define FAL_DEV_NAME_MAX 24
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct fal_flash_dev
|
||||||
|
{
|
||||||
|
char name[FAL_DEV_NAME_MAX];
|
||||||
|
|
||||||
|
/* flash device start address and len */
|
||||||
|
uint32_t addr;
|
||||||
|
size_t len;
|
||||||
|
/* the block size in the flash for erase minimum granularity */
|
||||||
|
size_t blk_size;
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
int (*init)(void);
|
||||||
|
int (*read)(long offset, uint8_t *buf, size_t size);
|
||||||
|
int (*write)(long offset, const uint8_t *buf, size_t size);
|
||||||
|
int (*erase)(long offset, size_t size);
|
||||||
|
} ops;
|
||||||
|
|
||||||
|
/* write minimum granularity, unit: bit.
|
||||||
|
1(nor flash)/ 8(stm32f4)/ 32(stm32f1)/ 64(stm32l4)
|
||||||
|
0 will not take effect. */
|
||||||
|
size_t write_gran;
|
||||||
|
};
|
||||||
|
typedef struct fal_flash_dev *fal_flash_dev_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FAL partition
|
||||||
|
*/
|
||||||
|
struct fal_partition
|
||||||
|
{
|
||||||
|
uint32_t magic_word;
|
||||||
|
|
||||||
|
/* partition name */
|
||||||
|
char name[FAL_DEV_NAME_MAX];
|
||||||
|
/* flash device name for partition */
|
||||||
|
char flash_name[FAL_DEV_NAME_MAX];
|
||||||
|
|
||||||
|
/* partition offset address on flash device */
|
||||||
|
long offset;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
uint32_t reserved;
|
||||||
|
};
|
||||||
|
typedef struct fal_partition *fal_partition_t;
|
||||||
|
|
||||||
|
#endif /* _FAL_DEF_H_ */
|
|
@ -0,0 +1,4 @@
|
||||||
|
| 文件夹 | 说明 |
|
||||||
|
| :------ | :----------------------- |
|
||||||
|
| porting | 移植相关的示例代码及文档 |
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
# Flash 设备及分区移植示例
|
||||||
|
|
||||||
|
本示例主要演示 Flash 设备及分区相关的移植。
|
||||||
|
|
||||||
|
## 1、Flash 设备
|
||||||
|
|
||||||
|
在定义 Flash 设备表前,需要先定义 Flash 设备,参考 [`fal_flash_sfud_port.c`](fal_flash_sfud_port.c) (基于 [SFUD](https://github.com/armink/SFUD) 万能 SPI Flash 驱动的 Flash 设备)与 [`fal_flash_stm32f2_port.c`](fal_flash_stm32f2_port.c) (STM32F2 片内 Flash)这两个文件。这里简介下 `fal_flash_stm32f2_port.c` 里的代码实现。
|
||||||
|
|
||||||
|
### 1.1 定义 Flash 设备
|
||||||
|
|
||||||
|
针对 Flash 的不同操作,这里定义了如下几个操作函数:
|
||||||
|
|
||||||
|
- `static int init(void)`:**可选** 的初始化操作
|
||||||
|
|
||||||
|
- `static int read(long offset, uint8_t *buf, size_t size)`:读取操作
|
||||||
|
|
||||||
|
|参数 |描述|
|
||||||
|
|:----- |:----|
|
||||||
|
|offset |读取数据的 Flash 偏移地址|
|
||||||
|
|buf |存放待读取数据的缓冲区|
|
||||||
|
|size |待读取数据的大小|
|
||||||
|
|return |返回实际读取的数据大小|
|
||||||
|
|
||||||
|
- `static int write(long offset, const uint8_t *buf, size_t size)` :写入操作
|
||||||
|
|
||||||
|
| 参数 | 描述 |
|
||||||
|
| :----- | :------------------------ |
|
||||||
|
| offset | 写入数据的 Flash 偏移地址 |
|
||||||
|
| buf | 存放待写入数据的缓冲区 |
|
||||||
|
| size | 待写入数据的大小 |
|
||||||
|
| return | 返回实际写入的数据大小 |
|
||||||
|
|
||||||
|
- `static int erase(long offset, size_t size)` :擦除操作
|
||||||
|
|
||||||
|
| 参数 | 描述 |
|
||||||
|
| :----- | :------------------------ |
|
||||||
|
| offset | 擦除区域的 Flash 偏移地址 |
|
||||||
|
| size | 擦除区域的大小 |
|
||||||
|
| return | 返回实际擦除的区域大小 |
|
||||||
|
|
||||||
|
用户需要根据自己的 Flash 情况分别实现这些操作函数。在文件最底部定义了具体的 Flash 设备对象(stm32f2_onchip_flash):
|
||||||
|
|
||||||
|
`const struct fal_flash_dev stm32f2_onchip_flash = { "stm32_onchip", 0x08000000, 1024*1024, 128*1024, {init, read, write, erase} };`
|
||||||
|
|
||||||
|
- `"stm32_onchip"` : Flash 设备的名字
|
||||||
|
- 0x08000000: 对 Flash 操作的起始地址
|
||||||
|
- 1024*1024:Flash 的总大小(1MB)
|
||||||
|
- 128*1024:Flash 块/扇区大小(因为 STM32F2 各块大小不均匀,所以擦除粒度为最大块的大小:128K)
|
||||||
|
- {init, read, write, erase} }:Flash 的操作函数。 如果没有 init 初始化过程,第一个操作函数位置可以置空。
|
||||||
|
|
||||||
|
### 1.2 定义 Flash 设备表
|
||||||
|
|
||||||
|
Flash 设备表定义在 `fal_cfg.h` 头文件中,定义分区表前需 **新建 `fal_cfg.h` 文件** 。
|
||||||
|
|
||||||
|
参考 [示例文件 samples/porting/fal_cfg.h](samples/porting/fal_cfg.h) 或如下代码:
|
||||||
|
|
||||||
|
```c
|
||||||
|
/* ===================== Flash device Configuration ========================= */
|
||||||
|
extern const struct fal_flash_dev stm32f2_onchip_flash;
|
||||||
|
extern struct fal_flash_dev nor_flash0;
|
||||||
|
|
||||||
|
/* flash device table */
|
||||||
|
#define FAL_FLASH_DEV_TABLE \
|
||||||
|
{ \
|
||||||
|
&stm32f2_onchip_flash, \
|
||||||
|
&nor_flash0, \
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Flash 设备表中,有两个 Flash 对象,一个为 STM32F2 的片内 Flash ,一个为片外的 Nor Flash。
|
||||||
|
|
||||||
|
## 2、Flash 分区
|
||||||
|
|
||||||
|
Flash 分区基于 Flash 设备,每个 Flash 设备又可以有 N 个分区,这些分区的集合就是分区表。在配置分区表前,务必保证已定义好 Flash 设备及设备表。
|
||||||
|
|
||||||
|
分区表也定义在 `fal_cfg.h` 头文件中。参考 [示例文件 samples/porting/fal_cfg.h](samples/porting/fal_cfg.h) 或如下代码:
|
||||||
|
|
||||||
|
```C
|
||||||
|
#define NOR_FLASH_DEV_NAME "norflash0"
|
||||||
|
/* ====================== Partition Configuration ========================== */
|
||||||
|
#ifdef FAL_PART_HAS_TABLE_CFG
|
||||||
|
/* partition table */
|
||||||
|
#define FAL_PART_TABLE \
|
||||||
|
{ \
|
||||||
|
{FAL_PART_MAGIC_WORD, "bl", "stm32_onchip", 0, 64*1024, 0}, \
|
||||||
|
{FAL_PART_MAGIC_WORD, "app", "stm32_onchip", 64*1024, 704*1024, 0}, \
|
||||||
|
{FAL_PART_MAGIC_WORD, "easyflash", NOR_FLASH_DEV_NAME, 0, 1024*1024, 0}, \
|
||||||
|
{FAL_PART_MAGIC_WORD, "download", NOR_FLASH_DEV_NAME, 1024*1024, 1024*1024, 0}, \
|
||||||
|
}
|
||||||
|
#endif /* FAL_PART_HAS_TABLE_CFG */
|
||||||
|
```
|
||||||
|
|
||||||
|
上面这个分区表详细描述信息如下:
|
||||||
|
|
||||||
|
| 分区名 | Flash 设备名 | 偏移地址 | 大小 | 说明 |
|
||||||
|
| :---------- | :------------- | :-------- | :---- | :----------------- |
|
||||||
|
| "bl" | "stm32_onchip" | 0 | 64KB | 引导程序 |
|
||||||
|
| "app" | "stm32_onchip" | 64*1024 | 704KB | 应用程序 |
|
||||||
|
| "easyflash" | "norflash0" | 0 | 1MB | EasyFlash 参数存储 |
|
||||||
|
| "download" | "norflash0" | 1024*1024 | 1MB | OTA 下载区 |
|
||||||
|
|
||||||
|
用户需要修改的分区参数包括:分区名称、关联的 Flash 设备名、偏移地址(相对 Flash 设备内部)、大小,需要注意以下几点:
|
||||||
|
|
||||||
|
- 分区名保证 **不能重复**
|
||||||
|
- 关联的 Flash 设备 **务必已经在 Flash 设备表中定义好** ,并且 **名称一致** ,否则会出现无法找到 Flash 设备的错误
|
||||||
|
- 分区的起始地址和大小 **不能超过 Flash 设备的地址范围** ,否则会导致包初始化错误
|
||||||
|
|
||||||
|
> 注意:每个分区定义时,除了填写上面介绍的参数属性外,需在前面增加 `FAL_PART_MAGIC_WORD` 属性,末尾增加 `0` (目前用于保留功能)
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* File : fal_cfg.h
|
||||||
|
* This file is part of FAL (Flash Abstraction Layer) package
|
||||||
|
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Change Logs:
|
||||||
|
* Date Author Notes
|
||||||
|
* 2018-05-17 armink the first version
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _FAL_CFG_H_
|
||||||
|
#define _FAL_CFG_H_
|
||||||
|
|
||||||
|
#include <rtconfig.h>
|
||||||
|
#include <board.h>
|
||||||
|
|
||||||
|
#define NOR_FLASH_DEV_NAME "norflash0"
|
||||||
|
|
||||||
|
/* ===================== Flash device Configuration ========================= */
|
||||||
|
extern const struct fal_flash_dev stm32f2_onchip_flash;
|
||||||
|
extern struct fal_flash_dev nor_flash0;
|
||||||
|
|
||||||
|
/* flash device table */
|
||||||
|
#define FAL_FLASH_DEV_TABLE \
|
||||||
|
{ \
|
||||||
|
&stm32f2_onchip_flash, \
|
||||||
|
&nor_flash0, \
|
||||||
|
}
|
||||||
|
/* ====================== Partition Configuration ========================== */
|
||||||
|
#ifdef FAL_PART_HAS_TABLE_CFG
|
||||||
|
/* partition table */
|
||||||
|
#define FAL_PART_TABLE \
|
||||||
|
{ \
|
||||||
|
{FAL_PART_MAGIC_WORD, "bl", "stm32_onchip", 0, 64*1024, 0}, \
|
||||||
|
{FAL_PART_MAGIC_WORD, "app", "stm32_onchip", 64*1024, 704*1024, 0}, \
|
||||||
|
{FAL_PART_MAGIC_WORD, "easyflash", NOR_FLASH_DEV_NAME, 0, 1024*1024, 0}, \
|
||||||
|
{FAL_PART_MAGIC_WORD, "download", NOR_FLASH_DEV_NAME, 1024*1024, 1024*1024, 0}, \
|
||||||
|
}
|
||||||
|
#endif /* FAL_PART_HAS_TABLE_CFG */
|
||||||
|
|
||||||
|
#endif /* _FAL_CFG_H_ */
|
|
@ -0,0 +1,110 @@
|
||||||
|
/*
|
||||||
|
* File : fal_flash_sfud_port.c
|
||||||
|
* This file is part of FAL (Flash Abstraction Layer) package
|
||||||
|
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Change Logs:
|
||||||
|
* Date Author Notes
|
||||||
|
* 2018-01-26 armink the first version
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <fal.h>
|
||||||
|
#include <sfud.h>
|
||||||
|
|
||||||
|
#ifdef FAL_USING_SFUD_PORT
|
||||||
|
#ifdef RT_USING_SFUD
|
||||||
|
#include <spi_flash_sfud.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef FAL_USING_NOR_FLASH_DEV_NAME
|
||||||
|
#define FAL_USING_NOR_FLASH_DEV_NAME "norflash0"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int init(void);
|
||||||
|
static int read(long offset, uint8_t *buf, size_t size);
|
||||||
|
static int write(long offset, const uint8_t *buf, size_t size);
|
||||||
|
static int erase(long offset, size_t size);
|
||||||
|
|
||||||
|
static sfud_flash_t sfud_dev = NULL;
|
||||||
|
struct fal_flash_dev nor_flash0 =
|
||||||
|
{
|
||||||
|
.name = FAL_USING_NOR_FLASH_DEV_NAME,
|
||||||
|
.addr = 0,
|
||||||
|
.len = 8 * 1024 * 1024,
|
||||||
|
.blk_size = 4096,
|
||||||
|
.ops = {init, read, write, erase},
|
||||||
|
.write_gran = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
static int init(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifdef RT_USING_SFUD
|
||||||
|
/* RT-Thread RTOS platform */
|
||||||
|
sfud_dev = rt_sfud_flash_find_by_dev_name(FAL_USING_NOR_FLASH_DEV_NAME);
|
||||||
|
#else
|
||||||
|
/* bare metal platform */
|
||||||
|
extern sfud_flash sfud_norflash0;
|
||||||
|
sfud_dev = &sfud_norflash0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (NULL == sfud_dev)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* update the flash chip information */
|
||||||
|
nor_flash0.blk_size = sfud_dev->chip.erase_gran;
|
||||||
|
nor_flash0.len = sfud_dev->chip.capacity;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int read(long offset, uint8_t *buf, size_t size)
|
||||||
|
{
|
||||||
|
assert(sfud_dev);
|
||||||
|
assert(sfud_dev->init_ok);
|
||||||
|
sfud_read(sfud_dev, nor_flash0.addr + offset, size, buf);
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int write(long offset, const uint8_t *buf, size_t size)
|
||||||
|
{
|
||||||
|
assert(sfud_dev);
|
||||||
|
assert(sfud_dev->init_ok);
|
||||||
|
if (sfud_write(sfud_dev, nor_flash0.addr + offset, size, buf) != SFUD_SUCCESS)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int erase(long offset, size_t size)
|
||||||
|
{
|
||||||
|
assert(sfud_dev);
|
||||||
|
assert(sfud_dev->init_ok);
|
||||||
|
if (sfud_erase(sfud_dev, nor_flash0.addr + offset, size) != SFUD_SUCCESS)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
#endif /* FAL_USING_SFUD_PORT */
|
||||||
|
|
|
@ -0,0 +1,212 @@
|
||||||
|
/*
|
||||||
|
* File : fal_flash_stm32f2_port.c
|
||||||
|
* This file is part of FAL (Flash Abstraction Layer) package
|
||||||
|
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Change Logs:
|
||||||
|
* Date Author Notes
|
||||||
|
* 2018-01-26 armink the first version
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <fal.h>
|
||||||
|
|
||||||
|
#include <stm32f2xx.h>
|
||||||
|
|
||||||
|
/* base address of the flash sectors */
|
||||||
|
#define ADDR_FLASH_SECTOR_0 ((uint32_t)0x08000000) /* Base address of Sector 0, 16 K bytes */
|
||||||
|
#define ADDR_FLASH_SECTOR_1 ((uint32_t)0x08004000) /* Base address of Sector 1, 16 K bytes */
|
||||||
|
#define ADDR_FLASH_SECTOR_2 ((uint32_t)0x08008000) /* Base address of Sector 2, 16 K bytes */
|
||||||
|
#define ADDR_FLASH_SECTOR_3 ((uint32_t)0x0800C000) /* Base address of Sector 3, 16 K bytes */
|
||||||
|
#define ADDR_FLASH_SECTOR_4 ((uint32_t)0x08010000) /* Base address of Sector 4, 64 K bytes */
|
||||||
|
#define ADDR_FLASH_SECTOR_5 ((uint32_t)0x08020000) /* Base address of Sector 5, 128 K bytes */
|
||||||
|
#define ADDR_FLASH_SECTOR_6 ((uint32_t)0x08040000) /* Base address of Sector 6, 128 K bytes */
|
||||||
|
#define ADDR_FLASH_SECTOR_7 ((uint32_t)0x08060000) /* Base address of Sector 7, 128 K bytes */
|
||||||
|
#define ADDR_FLASH_SECTOR_8 ((uint32_t)0x08080000) /* Base address of Sector 8, 128 K bytes */
|
||||||
|
#define ADDR_FLASH_SECTOR_9 ((uint32_t)0x080A0000) /* Base address of Sector 9, 128 K bytes */
|
||||||
|
#define ADDR_FLASH_SECTOR_10 ((uint32_t)0x080C0000) /* Base address of Sector 10, 128 K bytes */
|
||||||
|
#define ADDR_FLASH_SECTOR_11 ((uint32_t)0x080E0000) /* Base address of Sector 11, 128 K bytes */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the sector of a given address
|
||||||
|
*
|
||||||
|
* @param address flash address
|
||||||
|
*
|
||||||
|
* @return The sector of a given address
|
||||||
|
*/
|
||||||
|
static uint32_t stm32_get_sector(uint32_t address)
|
||||||
|
{
|
||||||
|
uint32_t sector = 0;
|
||||||
|
|
||||||
|
if ((address < ADDR_FLASH_SECTOR_1) && (address >= ADDR_FLASH_SECTOR_0))
|
||||||
|
{
|
||||||
|
sector = FLASH_Sector_0;
|
||||||
|
}
|
||||||
|
else if ((address < ADDR_FLASH_SECTOR_2) && (address >= ADDR_FLASH_SECTOR_1))
|
||||||
|
{
|
||||||
|
sector = FLASH_Sector_1;
|
||||||
|
}
|
||||||
|
else if ((address < ADDR_FLASH_SECTOR_3) && (address >= ADDR_FLASH_SECTOR_2))
|
||||||
|
{
|
||||||
|
sector = FLASH_Sector_2;
|
||||||
|
}
|
||||||
|
else if ((address < ADDR_FLASH_SECTOR_4) && (address >= ADDR_FLASH_SECTOR_3))
|
||||||
|
{
|
||||||
|
sector = FLASH_Sector_3;
|
||||||
|
}
|
||||||
|
else if ((address < ADDR_FLASH_SECTOR_5) && (address >= ADDR_FLASH_SECTOR_4))
|
||||||
|
{
|
||||||
|
sector = FLASH_Sector_4;
|
||||||
|
}
|
||||||
|
else if ((address < ADDR_FLASH_SECTOR_6) && (address >= ADDR_FLASH_SECTOR_5))
|
||||||
|
{
|
||||||
|
sector = FLASH_Sector_5;
|
||||||
|
}
|
||||||
|
else if ((address < ADDR_FLASH_SECTOR_7) && (address >= ADDR_FLASH_SECTOR_6))
|
||||||
|
{
|
||||||
|
sector = FLASH_Sector_6;
|
||||||
|
}
|
||||||
|
else if ((address < ADDR_FLASH_SECTOR_8) && (address >= ADDR_FLASH_SECTOR_7))
|
||||||
|
{
|
||||||
|
sector = FLASH_Sector_7;
|
||||||
|
}
|
||||||
|
else if ((address < ADDR_FLASH_SECTOR_9) && (address >= ADDR_FLASH_SECTOR_8))
|
||||||
|
{
|
||||||
|
sector = FLASH_Sector_8;
|
||||||
|
}
|
||||||
|
else if ((address < ADDR_FLASH_SECTOR_10) && (address >= ADDR_FLASH_SECTOR_9))
|
||||||
|
{
|
||||||
|
sector = FLASH_Sector_9;
|
||||||
|
}
|
||||||
|
else if ((address < ADDR_FLASH_SECTOR_11) && (address >= ADDR_FLASH_SECTOR_10))
|
||||||
|
{
|
||||||
|
sector = FLASH_Sector_10;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sector = FLASH_Sector_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sector;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the sector size
|
||||||
|
*
|
||||||
|
* @param sector sector
|
||||||
|
*
|
||||||
|
* @return sector size
|
||||||
|
*/
|
||||||
|
static uint32_t stm32_get_sector_size(uint32_t sector) {
|
||||||
|
assert(IS_FLASH_SECTOR(sector));
|
||||||
|
|
||||||
|
switch (sector) {
|
||||||
|
case FLASH_Sector_0: return 16 * 1024;
|
||||||
|
case FLASH_Sector_1: return 16 * 1024;
|
||||||
|
case FLASH_Sector_2: return 16 * 1024;
|
||||||
|
case FLASH_Sector_3: return 16 * 1024;
|
||||||
|
case FLASH_Sector_4: return 64 * 1024;
|
||||||
|
case FLASH_Sector_5: return 128 * 1024;
|
||||||
|
case FLASH_Sector_6: return 128 * 1024;
|
||||||
|
case FLASH_Sector_7: return 128 * 1024;
|
||||||
|
case FLASH_Sector_8: return 128 * 1024;
|
||||||
|
case FLASH_Sector_9: return 128 * 1024;
|
||||||
|
case FLASH_Sector_10: return 128 * 1024;
|
||||||
|
case FLASH_Sector_11: return 128 * 1024;
|
||||||
|
default : return 128 * 1024;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static int init(void)
|
||||||
|
{
|
||||||
|
/* do nothing now */
|
||||||
|
}
|
||||||
|
|
||||||
|
static int read(long offset, uint8_t *buf, size_t size)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
uint32_t addr = stm32f2_onchip_flash.addr + offset;
|
||||||
|
for (i = 0; i < size; i++, addr++, buf++)
|
||||||
|
{
|
||||||
|
*buf = *(uint8_t *) addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int write(long offset, const uint8_t *buf, size_t size)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
uint32_t read_data;
|
||||||
|
uint32_t addr = stm32f2_onchip_flash.addr + offset;
|
||||||
|
|
||||||
|
FLASH_Unlock();
|
||||||
|
FLASH_ClearFlag(
|
||||||
|
FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR
|
||||||
|
| FLASH_FLAG_PGSERR);
|
||||||
|
for (i = 0; i < size; i++, buf++, addr++)
|
||||||
|
{
|
||||||
|
/* write data */
|
||||||
|
FLASH_ProgramByte(addr, *buf);
|
||||||
|
read_data = *(uint8_t *) addr;
|
||||||
|
/* check data */
|
||||||
|
if (read_data != *buf)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FLASH_Lock();
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int erase(long offset, size_t size)
|
||||||
|
{
|
||||||
|
FLASH_Status flash_status;
|
||||||
|
size_t erased_size = 0;
|
||||||
|
uint32_t cur_erase_sector;
|
||||||
|
uint32_t addr = stm32f2_onchip_flash.addr + offset;
|
||||||
|
|
||||||
|
/* start erase */
|
||||||
|
FLASH_Unlock();
|
||||||
|
FLASH_ClearFlag(
|
||||||
|
FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR
|
||||||
|
| FLASH_FLAG_PGSERR);
|
||||||
|
/* it will stop when erased size is greater than setting size */
|
||||||
|
while (erased_size < size)
|
||||||
|
{
|
||||||
|
cur_erase_sector = stm32_get_sector(addr + erased_size);
|
||||||
|
flash_status = FLASH_EraseSector(cur_erase_sector, VoltageRange_3);
|
||||||
|
if (flash_status != FLASH_COMPLETE)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
erased_size += stm32_get_sector_size(cur_erase_sector);
|
||||||
|
}
|
||||||
|
FLASH_Lock();
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct fal_flash_dev stm32f2_onchip_flash =
|
||||||
|
{
|
||||||
|
.name = "stm32_onchip",
|
||||||
|
.addr = 0x08000000,
|
||||||
|
.len = 1024*1024,
|
||||||
|
.blk_size = 128*1024,
|
||||||
|
.ops = {init, read, write, erase},
|
||||||
|
.write_gran = 8
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* File : fal.c
|
||||||
|
* This file is part of FAL (Flash Abstraction Layer) package
|
||||||
|
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Change Logs:
|
||||||
|
* Date Author Notes
|
||||||
|
* 2018-05-17 armink the first version
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <fal.h>
|
||||||
|
|
||||||
|
static uint8_t init_ok = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FAL (Flash Abstraction Layer) initialization.
|
||||||
|
* It will initialize all flash device and all flash partition.
|
||||||
|
*
|
||||||
|
* @return >= 0: partitions total number
|
||||||
|
*/
|
||||||
|
int fal_init(void)
|
||||||
|
{
|
||||||
|
extern int fal_flash_init(void);
|
||||||
|
extern int fal_partition_init(void);
|
||||||
|
|
||||||
|
int result;
|
||||||
|
|
||||||
|
/* initialize all flash device on FAL flash table */
|
||||||
|
result = fal_flash_init();
|
||||||
|
|
||||||
|
if (result < 0) {
|
||||||
|
goto __exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* initialize all flash partition on FAL partition table */
|
||||||
|
result = fal_partition_init();
|
||||||
|
|
||||||
|
__exit:
|
||||||
|
|
||||||
|
if ((result > 0) && (!init_ok))
|
||||||
|
{
|
||||||
|
init_ok = 1;
|
||||||
|
log_i("RT-Thread Flash Abstraction Layer (V%s) initialize success.", FAL_SW_VERSION);
|
||||||
|
}
|
||||||
|
else if(result <= 0)
|
||||||
|
{
|
||||||
|
init_ok = 0;
|
||||||
|
log_e("RT-Thread Flash Abstraction Layer (V%s) initialize failed.", FAL_SW_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the FAL is initialized successfully
|
||||||
|
*
|
||||||
|
* @return 0: not init or init failed; 1: init success
|
||||||
|
*/
|
||||||
|
int fal_init_check(void)
|
||||||
|
{
|
||||||
|
return init_ok;
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
* File : fal_flash.c
|
||||||
|
* This file is part of FAL (Flash Abstraction Layer) package
|
||||||
|
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Change Logs:
|
||||||
|
* Date Author Notes
|
||||||
|
* 2018-05-17 armink the first version
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <fal.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* flash device table, must defined by user */
|
||||||
|
#if !defined(FAL_FLASH_DEV_TABLE)
|
||||||
|
#error "You must defined flash device table (FAL_FLASH_DEV_TABLE) on 'fal_cfg.h'"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const struct fal_flash_dev * const device_table[] = FAL_FLASH_DEV_TABLE;
|
||||||
|
static const size_t device_table_len = sizeof(device_table) / sizeof(device_table[0]);
|
||||||
|
static uint8_t init_ok = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize all flash device on FAL flash table
|
||||||
|
*
|
||||||
|
* @return result
|
||||||
|
*/
|
||||||
|
int fal_flash_init(void)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (init_ok)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < device_table_len; i++)
|
||||||
|
{
|
||||||
|
assert(device_table[i]->ops.read);
|
||||||
|
assert(device_table[i]->ops.write);
|
||||||
|
assert(device_table[i]->ops.erase);
|
||||||
|
/* init flash device on flash table */
|
||||||
|
if (device_table[i]->ops.init)
|
||||||
|
{
|
||||||
|
device_table[i]->ops.init();
|
||||||
|
}
|
||||||
|
log_d("Flash device | %*.*s | addr: 0x%08lx | len: 0x%08x | blk_size: 0x%08x |initialized finish.",
|
||||||
|
FAL_DEV_NAME_MAX, FAL_DEV_NAME_MAX, device_table[i]->name, device_table[i]->addr, device_table[i]->len,
|
||||||
|
device_table[i]->blk_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
init_ok = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* find flash device by name
|
||||||
|
*
|
||||||
|
* @param name flash device name
|
||||||
|
*
|
||||||
|
* @return != NULL: flash device
|
||||||
|
* NULL: not found
|
||||||
|
*/
|
||||||
|
const struct fal_flash_dev *fal_flash_device_find(const char *name)
|
||||||
|
{
|
||||||
|
assert(init_ok);
|
||||||
|
assert(name);
|
||||||
|
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < device_table_len; i++)
|
||||||
|
{
|
||||||
|
if (!strncmp(name, device_table[i]->name, FAL_DEV_NAME_MAX)) {
|
||||||
|
return device_table[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
|
@ -0,0 +1,493 @@
|
||||||
|
/*
|
||||||
|
* File : fal_partition.c
|
||||||
|
* This file is part of FAL (Flash Abstraction Layer) package
|
||||||
|
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Change Logs:
|
||||||
|
* Date Author Notes
|
||||||
|
* 2018-05-17 armink the first version
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <fal.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/* partition magic word */
|
||||||
|
#define FAL_PART_MAGIC_WORD 0x45503130
|
||||||
|
#define FAL_PART_MAGIC_WORD_H 0x4550L
|
||||||
|
#define FAL_PART_MAGIC_WORD_L 0x3130L
|
||||||
|
#define FAL_PART_MAGIC_WROD 0x45503130
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FAL partition table config has defined on 'fal_cfg.h'.
|
||||||
|
* When this option is disable, it will auto find the partition table on a specified location in flash partition.
|
||||||
|
*/
|
||||||
|
#ifdef FAL_PART_HAS_TABLE_CFG
|
||||||
|
|
||||||
|
/* check partition table definition */
|
||||||
|
#if !defined(FAL_PART_TABLE)
|
||||||
|
#error "You must defined FAL_PART_TABLE on 'fal_cfg.h'"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __CC_ARM /* ARM Compiler */
|
||||||
|
#define SECTION(x) __attribute__((section(x)))
|
||||||
|
#define USED __attribute__((used))
|
||||||
|
#elif defined (__IAR_SYSTEMS_ICC__) /* for IAR Compiler */
|
||||||
|
#define SECTION(x) @ x
|
||||||
|
#define USED __root
|
||||||
|
#elif defined (__GNUC__) /* GNU GCC Compiler */
|
||||||
|
#define SECTION(x) __attribute__((section(x)))
|
||||||
|
#define USED __attribute__((used))
|
||||||
|
#else
|
||||||
|
#error not supported tool chain
|
||||||
|
#endif /* __CC_ARM */
|
||||||
|
USED static const struct fal_partition partition_table_def[] SECTION("FalPartTable") = FAL_PART_TABLE;
|
||||||
|
static const struct fal_partition *partition_table = NULL;
|
||||||
|
|
||||||
|
#else /* FAL_PART_HAS_TABLE_CFG */
|
||||||
|
|
||||||
|
#if !defined(FAL_PART_TABLE_FLASH_DEV_NAME)
|
||||||
|
#error "You must defined FAL_PART_TABLE_FLASH_DEV_NAME on 'fal_cfg.h'"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* check partition table end offset address definition */
|
||||||
|
#if !defined(FAL_PART_TABLE_END_OFFSET)
|
||||||
|
#error "You must defined FAL_PART_TABLE_END_OFFSET on 'fal_cfg.h'"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static struct fal_partition *partition_table = NULL;
|
||||||
|
#endif /* FAL_PART_HAS_TABLE_CFG */
|
||||||
|
|
||||||
|
static uint8_t init_ok = 0;
|
||||||
|
static size_t partition_table_len = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* print the partition table
|
||||||
|
*/
|
||||||
|
void fal_show_part_table(void)
|
||||||
|
{
|
||||||
|
char *item1 = "name", *item2 = "flash_dev";
|
||||||
|
size_t i, part_name_max = strlen(item1), flash_dev_name_max = strlen(item2);
|
||||||
|
const struct fal_partition *part;
|
||||||
|
|
||||||
|
if (partition_table_len)
|
||||||
|
{
|
||||||
|
for (i = 0; i < partition_table_len; i++)
|
||||||
|
{
|
||||||
|
part = &partition_table[i];
|
||||||
|
if (strlen(part->name) > part_name_max)
|
||||||
|
{
|
||||||
|
part_name_max = strlen(part->name);
|
||||||
|
}
|
||||||
|
if (strlen(part->flash_name) > flash_dev_name_max)
|
||||||
|
{
|
||||||
|
flash_dev_name_max = strlen(part->flash_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log_i("==================== FAL partition table ====================");
|
||||||
|
log_i("| %-*.*s | %-*.*s | offset | length |", part_name_max, FAL_DEV_NAME_MAX, item1, flash_dev_name_max,
|
||||||
|
FAL_DEV_NAME_MAX, item2);
|
||||||
|
log_i("-------------------------------------------------------------");
|
||||||
|
for (i = 0; i < partition_table_len; i++)
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifdef FAL_PART_HAS_TABLE_CFG
|
||||||
|
part = &partition_table[i];
|
||||||
|
#else
|
||||||
|
part = &partition_table[partition_table_len - i - 1];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
log_i("| %-*.*s | %-*.*s | 0x%08lx | 0x%08x |", part_name_max, FAL_DEV_NAME_MAX, part->name, flash_dev_name_max,
|
||||||
|
FAL_DEV_NAME_MAX, part->flash_name, part->offset, part->len);
|
||||||
|
}
|
||||||
|
log_i("=============================================================");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize all flash partition on FAL partition table
|
||||||
|
*
|
||||||
|
* @return partitions total number
|
||||||
|
*/
|
||||||
|
int fal_partition_init(void)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
const struct fal_flash_dev *flash_dev = NULL;
|
||||||
|
|
||||||
|
if (init_ok)
|
||||||
|
{
|
||||||
|
return partition_table_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef FAL_PART_HAS_TABLE_CFG
|
||||||
|
partition_table = &partition_table_def[0];
|
||||||
|
partition_table_len = sizeof(partition_table_def) / sizeof(partition_table_def[0]);
|
||||||
|
#else
|
||||||
|
/* load partition table from the end address FAL_PART_TABLE_END_OFFSET, error return 0 */
|
||||||
|
long part_table_offset = FAL_PART_TABLE_END_OFFSET;
|
||||||
|
size_t table_num = 0, table_item_size = 0;
|
||||||
|
uint8_t part_table_find_ok = 0;
|
||||||
|
uint32_t read_magic_word;
|
||||||
|
fal_partition_t new_part = NULL;
|
||||||
|
|
||||||
|
flash_dev = fal_flash_device_find(FAL_PART_TABLE_FLASH_DEV_NAME);
|
||||||
|
if (flash_dev == NULL)
|
||||||
|
{
|
||||||
|
log_e("Initialize failed! Flash device (%s) NOT found.", FAL_PART_TABLE_FLASH_DEV_NAME);
|
||||||
|
goto _exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check partition table offset address */
|
||||||
|
if (part_table_offset < 0 || part_table_offset >= (long) flash_dev->len)
|
||||||
|
{
|
||||||
|
log_e("Setting partition table end offset address(%ld) out of flash bound(<%d).", part_table_offset, flash_dev->len);
|
||||||
|
goto _exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
table_item_size = sizeof(struct fal_partition);
|
||||||
|
new_part = (fal_partition_t)FAL_MALLOC(table_item_size);
|
||||||
|
if (new_part == NULL)
|
||||||
|
{
|
||||||
|
log_e("Initialize failed! No memory for table buffer.");
|
||||||
|
goto _exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* find partition table location */
|
||||||
|
{
|
||||||
|
uint8_t read_buf[64];
|
||||||
|
|
||||||
|
part_table_offset -= sizeof(read_buf);
|
||||||
|
while (part_table_offset >= 0)
|
||||||
|
{
|
||||||
|
if (flash_dev->ops.read(part_table_offset, read_buf, sizeof(read_buf)) > 0)
|
||||||
|
{
|
||||||
|
/* find magic word in read buf */
|
||||||
|
for (i = 0; i < sizeof(read_buf) - sizeof(read_magic_word) + 1; i++)
|
||||||
|
{
|
||||||
|
read_magic_word = read_buf[0 + i] + (read_buf[1 + i] << 8) + (read_buf[2 + i] << 16) + (read_buf[3 + i] << 24);
|
||||||
|
if (read_magic_word == ((FAL_PART_MAGIC_WORD_H << 16) + FAL_PART_MAGIC_WORD_L))
|
||||||
|
{
|
||||||
|
part_table_find_ok = 1;
|
||||||
|
part_table_offset += i;
|
||||||
|
log_d("Find the partition table on '%s' offset @0x%08lx.", FAL_PART_TABLE_FLASH_DEV_NAME,
|
||||||
|
part_table_offset);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* read failed */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (part_table_find_ok)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* calculate next read buf position */
|
||||||
|
if (part_table_offset >= (long)sizeof(read_buf))
|
||||||
|
{
|
||||||
|
part_table_offset -= sizeof(read_buf);
|
||||||
|
part_table_offset += (sizeof(read_magic_word) - 1);
|
||||||
|
}
|
||||||
|
else if (part_table_offset != 0)
|
||||||
|
{
|
||||||
|
part_table_offset = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* find failed */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* load partition table */
|
||||||
|
while (part_table_find_ok)
|
||||||
|
{
|
||||||
|
memset(new_part, 0x00, table_num);
|
||||||
|
if (flash_dev->ops.read(part_table_offset - table_item_size * (table_num), (uint8_t *) new_part,
|
||||||
|
table_item_size) < 0)
|
||||||
|
{
|
||||||
|
log_e("Initialize failed! Flash device (%s) read error!", flash_dev->name);
|
||||||
|
table_num = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_part->magic_word != ((FAL_PART_MAGIC_WORD_H << 16) + FAL_PART_MAGIC_WORD_L))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
partition_table = (fal_partition_t) FAL_REALLOC(partition_table, table_item_size * (table_num + 1));
|
||||||
|
if (partition_table == NULL)
|
||||||
|
{
|
||||||
|
log_e("Initialize failed! No memory for partition table");
|
||||||
|
table_num = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(partition_table + table_num, new_part, table_item_size);
|
||||||
|
|
||||||
|
table_num++;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (table_num == 0)
|
||||||
|
{
|
||||||
|
log_e("Partition table NOT found on flash: %s (len: %d) from offset: 0x%08x.", FAL_PART_TABLE_FLASH_DEV_NAME,
|
||||||
|
FAL_DEV_NAME_MAX, FAL_PART_TABLE_END_OFFSET);
|
||||||
|
goto _exit;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
partition_table_len = table_num;
|
||||||
|
}
|
||||||
|
#endif /* FAL_PART_HAS_TABLE_CFG */
|
||||||
|
|
||||||
|
/* check the partition table device exists */
|
||||||
|
|
||||||
|
for (i = 0; i < partition_table_len; i++)
|
||||||
|
{
|
||||||
|
flash_dev = fal_flash_device_find(partition_table[i].flash_name);
|
||||||
|
if (flash_dev == NULL)
|
||||||
|
{
|
||||||
|
log_d("Warning: Do NOT found the flash device(%s).", partition_table[i].flash_name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (partition_table[i].offset >= (long)flash_dev->len)
|
||||||
|
{
|
||||||
|
log_e("Initialize failed! Partition(%s) offset address(%ld) out of flash bound(<%d).",
|
||||||
|
partition_table[i].name, partition_table[i].offset, flash_dev->len);
|
||||||
|
partition_table_len = 0;
|
||||||
|
goto _exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init_ok = 1;
|
||||||
|
|
||||||
|
_exit:
|
||||||
|
|
||||||
|
#if FAL_DEBUG
|
||||||
|
fal_show_part_table();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef FAL_PART_HAS_TABLE_CFG
|
||||||
|
if (new_part)
|
||||||
|
{
|
||||||
|
FAL_FREE(new_part);
|
||||||
|
}
|
||||||
|
#endif /* !FAL_PART_HAS_TABLE_CFG */
|
||||||
|
|
||||||
|
return partition_table_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* find the partition by name
|
||||||
|
*
|
||||||
|
* @param name partition name
|
||||||
|
*
|
||||||
|
* @return != NULL: partition
|
||||||
|
* NULL: not found
|
||||||
|
*/
|
||||||
|
const struct fal_partition *fal_partition_find(const char *name)
|
||||||
|
{
|
||||||
|
assert(init_ok);
|
||||||
|
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < partition_table_len; i++)
|
||||||
|
{
|
||||||
|
if (!strcmp(name, partition_table[i].name))
|
||||||
|
{
|
||||||
|
return &partition_table[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the partition table
|
||||||
|
*
|
||||||
|
* @param len return the partition table length
|
||||||
|
*
|
||||||
|
* @return partition table
|
||||||
|
*/
|
||||||
|
const struct fal_partition *fal_get_partition_table(size_t *len)
|
||||||
|
{
|
||||||
|
assert(init_ok);
|
||||||
|
assert(len);
|
||||||
|
|
||||||
|
*len = partition_table_len;
|
||||||
|
|
||||||
|
return partition_table;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set partition table temporarily
|
||||||
|
* This setting will modify the partition table temporarily, the setting will be lost after restart.
|
||||||
|
*
|
||||||
|
* @param table partition table
|
||||||
|
* @param len partition table length
|
||||||
|
*/
|
||||||
|
void fal_set_partition_table_temp(struct fal_partition *table, size_t len)
|
||||||
|
{
|
||||||
|
assert(init_ok);
|
||||||
|
assert(table);
|
||||||
|
|
||||||
|
partition_table_len = len;
|
||||||
|
partition_table = table;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* read data from partition
|
||||||
|
*
|
||||||
|
* @param part partition
|
||||||
|
* @param addr relative address for partition
|
||||||
|
* @param buf read buffer
|
||||||
|
* @param size read size
|
||||||
|
*
|
||||||
|
* @return >= 0: successful read data size
|
||||||
|
* -1: error
|
||||||
|
*/
|
||||||
|
int fal_partition_read(const struct fal_partition *part, uint32_t addr, uint8_t *buf, size_t size)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
const struct fal_flash_dev *flash_dev = NULL;
|
||||||
|
|
||||||
|
assert(part);
|
||||||
|
assert(buf);
|
||||||
|
|
||||||
|
if (addr + size > part->len)
|
||||||
|
{
|
||||||
|
log_e("Partition read error! Partition address out of bound.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
flash_dev = fal_flash_device_find(part->flash_name);
|
||||||
|
if (flash_dev == NULL)
|
||||||
|
{
|
||||||
|
log_e("Partition read error! Don't found flash device(%s) of the partition(%s).", part->flash_name, part->name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = flash_dev->ops.read(part->offset + addr, buf, size);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
log_e("Partition read error! Flash device(%s) read error!", part->flash_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* write data to partition
|
||||||
|
*
|
||||||
|
* @param part partition
|
||||||
|
* @param addr relative address for partition
|
||||||
|
* @param buf write buffer
|
||||||
|
* @param size write size
|
||||||
|
*
|
||||||
|
* @return >= 0: successful write data size
|
||||||
|
* -1: error
|
||||||
|
*/
|
||||||
|
int fal_partition_write(const struct fal_partition *part, uint32_t addr, const uint8_t *buf, size_t size)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
const struct fal_flash_dev *flash_dev = NULL;
|
||||||
|
|
||||||
|
assert(part);
|
||||||
|
assert(buf);
|
||||||
|
|
||||||
|
if (addr + size > part->len)
|
||||||
|
{
|
||||||
|
log_e("Partition write error! Partition address out of bound.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
flash_dev = fal_flash_device_find(part->flash_name);
|
||||||
|
if (flash_dev == NULL)
|
||||||
|
{
|
||||||
|
log_e("Partition write error! Don't found flash device(%s) of the partition(%s).", part->flash_name, part->name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = flash_dev->ops.write(part->offset + addr, buf, size);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
log_e("Partition write error! Flash device(%s) write error!", part->flash_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* erase partition data
|
||||||
|
*
|
||||||
|
* @param part partition
|
||||||
|
* @param addr relative address for partition
|
||||||
|
* @param size erase size
|
||||||
|
*
|
||||||
|
* @return >= 0: successful erased data size
|
||||||
|
* -1: error
|
||||||
|
*/
|
||||||
|
int fal_partition_erase(const struct fal_partition *part, uint32_t addr, size_t size)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
const struct fal_flash_dev *flash_dev = NULL;
|
||||||
|
|
||||||
|
assert(part);
|
||||||
|
|
||||||
|
if (addr + size > part->len)
|
||||||
|
{
|
||||||
|
log_e("Partition erase error! Partition address out of bound.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
flash_dev = fal_flash_device_find(part->flash_name);
|
||||||
|
if (flash_dev == NULL)
|
||||||
|
{
|
||||||
|
log_e("Partition erase error! Don't found flash device(%s) of the partition(%s).", part->flash_name, part->name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = flash_dev->ops.erase(part->offset + addr, size);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
log_e("Partition erase error! Flash device(%s) erase error!", part->flash_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* erase partition all data
|
||||||
|
*
|
||||||
|
* @param part partition
|
||||||
|
*
|
||||||
|
* @return >= 0: successful erased data size
|
||||||
|
* -1: error
|
||||||
|
*/
|
||||||
|
int fal_partition_erase_all(const struct fal_partition *part)
|
||||||
|
{
|
||||||
|
return fal_partition_erase(part, 0, part->len);
|
||||||
|
}
|
|
@ -0,0 +1,930 @@
|
||||||
|
/*
|
||||||
|
* File : fal_rtt.c
|
||||||
|
* This file is part of FAL (Flash Abstraction Layer) package
|
||||||
|
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Change Logs:
|
||||||
|
* Date Author Notes
|
||||||
|
* 2018-06-23 armink the first version
|
||||||
|
* 2019-08-22 MurphyZhao adapt to none rt-thread case
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <fal.h>
|
||||||
|
|
||||||
|
#ifdef RT_VER_NUM
|
||||||
|
#include <rtthread.h>
|
||||||
|
#include <rtdevice.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* ========================== block device ======================== */
|
||||||
|
struct fal_blk_device
|
||||||
|
{
|
||||||
|
struct rt_device parent;
|
||||||
|
struct rt_device_blk_geometry geometry;
|
||||||
|
const struct fal_partition *fal_part;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* RT-Thread device interface */
|
||||||
|
#if RTTHREAD_VERSION >= 30000
|
||||||
|
static rt_err_t blk_dev_control(rt_device_t dev, int cmd, void *args)
|
||||||
|
#else
|
||||||
|
static rt_err_t blk_dev_control(rt_device_t dev, rt_uint8_t cmd, void *args)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
struct fal_blk_device *part = (struct fal_blk_device*) dev;
|
||||||
|
|
||||||
|
assert(part != RT_NULL);
|
||||||
|
|
||||||
|
if (cmd == RT_DEVICE_CTRL_BLK_GETGEOME)
|
||||||
|
{
|
||||||
|
struct rt_device_blk_geometry *geometry;
|
||||||
|
|
||||||
|
geometry = (struct rt_device_blk_geometry *) args;
|
||||||
|
if (geometry == RT_NULL)
|
||||||
|
{
|
||||||
|
return -RT_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(geometry, &part->geometry, sizeof(struct rt_device_blk_geometry));
|
||||||
|
}
|
||||||
|
else if (cmd == RT_DEVICE_CTRL_BLK_ERASE)
|
||||||
|
{
|
||||||
|
rt_uint32_t *addrs = (rt_uint32_t *) args, start_addr = addrs[0], end_addr = addrs[1], phy_start_addr;
|
||||||
|
rt_size_t phy_size;
|
||||||
|
|
||||||
|
if (addrs == RT_NULL || start_addr > end_addr)
|
||||||
|
{
|
||||||
|
return -RT_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end_addr == start_addr)
|
||||||
|
{
|
||||||
|
end_addr++;
|
||||||
|
}
|
||||||
|
|
||||||
|
phy_start_addr = start_addr * part->geometry.bytes_per_sector;
|
||||||
|
phy_size = (end_addr - start_addr) * part->geometry.bytes_per_sector;
|
||||||
|
|
||||||
|
if (fal_partition_erase(part->fal_part, phy_start_addr, phy_size) < 0)
|
||||||
|
{
|
||||||
|
return -RT_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return RT_EOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static rt_size_t blk_dev_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
struct fal_blk_device *part = (struct fal_blk_device*) dev;
|
||||||
|
|
||||||
|
assert(part != RT_NULL);
|
||||||
|
|
||||||
|
ret = fal_partition_read(part->fal_part, pos * part->geometry.block_size, buffer, size * part->geometry.block_size);
|
||||||
|
|
||||||
|
if (ret != (int)(size * part->geometry.block_size))
|
||||||
|
{
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static rt_size_t blk_dev_write(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
struct fal_blk_device *part;
|
||||||
|
rt_off_t phy_pos;
|
||||||
|
rt_size_t phy_size;
|
||||||
|
|
||||||
|
part = (struct fal_blk_device*) dev;
|
||||||
|
assert(part != RT_NULL);
|
||||||
|
|
||||||
|
/* change the block device's logic address to physical address */
|
||||||
|
phy_pos = pos * part->geometry.bytes_per_sector;
|
||||||
|
phy_size = size * part->geometry.bytes_per_sector;
|
||||||
|
|
||||||
|
ret = fal_partition_erase(part->fal_part, phy_pos, phy_size);
|
||||||
|
|
||||||
|
if (ret == (int) phy_size)
|
||||||
|
{
|
||||||
|
ret = fal_partition_write(part->fal_part, phy_pos, buffer, phy_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret != (int) phy_size)
|
||||||
|
{
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef RT_USING_DEVICE_OPS
|
||||||
|
const static struct rt_device_ops blk_dev_ops =
|
||||||
|
{
|
||||||
|
RT_NULL,
|
||||||
|
RT_NULL,
|
||||||
|
RT_NULL,
|
||||||
|
blk_dev_read,
|
||||||
|
blk_dev_write,
|
||||||
|
blk_dev_control
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create RT-Thread block device by specified partition
|
||||||
|
*
|
||||||
|
* @param parition_name partition name
|
||||||
|
*
|
||||||
|
* @return != NULL: created block device
|
||||||
|
* NULL: created failed
|
||||||
|
*/
|
||||||
|
struct rt_device *fal_blk_device_create(const char *parition_name)
|
||||||
|
{
|
||||||
|
struct fal_blk_device *blk_dev;
|
||||||
|
const struct fal_partition *fal_part = fal_partition_find(parition_name);
|
||||||
|
const struct fal_flash_dev *fal_flash = NULL;
|
||||||
|
|
||||||
|
if (!fal_part)
|
||||||
|
{
|
||||||
|
log_e("Error: the partition name (%s) is not found.", parition_name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((fal_flash = fal_flash_device_find(fal_part->flash_name)) == NULL)
|
||||||
|
{
|
||||||
|
log_e("Error: the flash device name (%s) is not found.", fal_part->flash_name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
blk_dev = (struct fal_blk_device*) rt_malloc(sizeof(struct fal_blk_device));
|
||||||
|
if (blk_dev)
|
||||||
|
{
|
||||||
|
blk_dev->fal_part = fal_part;
|
||||||
|
blk_dev->geometry.bytes_per_sector = fal_flash->blk_size;
|
||||||
|
blk_dev->geometry.block_size = fal_flash->blk_size;
|
||||||
|
blk_dev->geometry.sector_count = fal_part->len / fal_flash->blk_size;
|
||||||
|
|
||||||
|
/* register device */
|
||||||
|
blk_dev->parent.type = RT_Device_Class_Block;
|
||||||
|
|
||||||
|
#ifdef RT_USING_DEVICE_OPS
|
||||||
|
blk_dev->parent.ops = &blk_dev_ops;
|
||||||
|
#else
|
||||||
|
blk_dev->parent.init = NULL;
|
||||||
|
blk_dev->parent.open = NULL;
|
||||||
|
blk_dev->parent.close = NULL;
|
||||||
|
blk_dev->parent.read = blk_dev_read;
|
||||||
|
blk_dev->parent.write = blk_dev_write;
|
||||||
|
blk_dev->parent.control = blk_dev_control;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* no private */
|
||||||
|
blk_dev->parent.user_data = RT_NULL;
|
||||||
|
|
||||||
|
log_i("The FAL block device (%s) created successfully", fal_part->name);
|
||||||
|
rt_device_register(RT_DEVICE(blk_dev), fal_part->name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STANDALONE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log_e("Error: no memory for create FAL block device");
|
||||||
|
}
|
||||||
|
|
||||||
|
return RT_DEVICE(blk_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================== MTD nor device ======================== */
|
||||||
|
#if defined(RT_USING_MTD_NOR)
|
||||||
|
|
||||||
|
struct fal_mtd_nor_device
|
||||||
|
{
|
||||||
|
struct rt_mtd_nor_device parent;
|
||||||
|
const struct fal_partition *fal_part;
|
||||||
|
};
|
||||||
|
|
||||||
|
static rt_size_t mtd_nor_dev_read(struct rt_mtd_nor_device* device, rt_off_t offset, rt_uint8_t* data, rt_uint32_t length)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
struct fal_mtd_nor_device *part = (struct fal_mtd_nor_device*) device;
|
||||||
|
|
||||||
|
assert(part != RT_NULL);
|
||||||
|
|
||||||
|
ret = fal_partition_read(part->fal_part, offset, data, length);
|
||||||
|
|
||||||
|
if (ret != (int)length)
|
||||||
|
{
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static rt_size_t mtd_nor_dev_write(struct rt_mtd_nor_device* device, rt_off_t offset, const rt_uint8_t* data, rt_uint32_t length)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
struct fal_mtd_nor_device *part;
|
||||||
|
|
||||||
|
part = (struct fal_mtd_nor_device*) device;
|
||||||
|
assert(part != RT_NULL);
|
||||||
|
|
||||||
|
ret = fal_partition_write(part->fal_part, offset, data, length);
|
||||||
|
|
||||||
|
if (ret != (int) length)
|
||||||
|
{
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static rt_err_t mtd_nor_dev_erase(struct rt_mtd_nor_device* device, rt_off_t offset, rt_uint32_t length)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
struct fal_mtd_nor_device *part;
|
||||||
|
|
||||||
|
part = (struct fal_mtd_nor_device*) device;
|
||||||
|
assert(part != RT_NULL);
|
||||||
|
|
||||||
|
ret = fal_partition_erase(part->fal_part, offset, length);
|
||||||
|
|
||||||
|
if (ret != length)
|
||||||
|
{
|
||||||
|
return -RT_ERROR;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return RT_EOK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct rt_mtd_nor_driver_ops _ops =
|
||||||
|
{
|
||||||
|
RT_NULL,
|
||||||
|
mtd_nor_dev_read,
|
||||||
|
mtd_nor_dev_write,
|
||||||
|
mtd_nor_dev_erase,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create RT-Thread MTD NOR device by specified partition
|
||||||
|
*
|
||||||
|
* @param parition_name partition name
|
||||||
|
*
|
||||||
|
* @return != NULL: created MTD NOR device
|
||||||
|
* NULL: created failed
|
||||||
|
*/
|
||||||
|
struct rt_device *fal_mtd_nor_device_create(const char *parition_name)
|
||||||
|
{
|
||||||
|
struct fal_mtd_nor_device *mtd_nor_dev;
|
||||||
|
const struct fal_partition *fal_part = fal_partition_find(parition_name);
|
||||||
|
const struct fal_flash_dev *fal_flash = NULL;
|
||||||
|
|
||||||
|
if (!fal_part)
|
||||||
|
{
|
||||||
|
log_e("Error: the partition name (%s) is not found.", parition_name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((fal_flash = fal_flash_device_find(fal_part->flash_name)) == NULL)
|
||||||
|
{
|
||||||
|
log_e("Error: the flash device name (%s) is not found.", fal_part->flash_name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mtd_nor_dev = (struct fal_mtd_nor_device*) rt_malloc(sizeof(struct fal_mtd_nor_device));
|
||||||
|
if (mtd_nor_dev)
|
||||||
|
{
|
||||||
|
mtd_nor_dev->fal_part = fal_part;
|
||||||
|
|
||||||
|
mtd_nor_dev->parent.block_start = 0;
|
||||||
|
mtd_nor_dev->parent.block_end = fal_part->len / fal_flash->blk_size;
|
||||||
|
mtd_nor_dev->parent.block_size = fal_flash->blk_size;
|
||||||
|
|
||||||
|
/* set ops */
|
||||||
|
mtd_nor_dev->parent.ops = &_ops;
|
||||||
|
|
||||||
|
log_i("The FAL MTD NOR device (%s) created successfully", fal_part->name);
|
||||||
|
rt_mtd_nor_register_device(fal_part->name, &mtd_nor_dev->parent);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log_e("Error: no memory for create FAL MTD NOR device");
|
||||||
|
}
|
||||||
|
|
||||||
|
return RT_DEVICE(&mtd_nor_dev->parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* defined(RT_USING_MTD_NOR) */
|
||||||
|
|
||||||
|
|
||||||
|
/* ========================== char device ======================== */
|
||||||
|
struct fal_char_device
|
||||||
|
{
|
||||||
|
struct rt_device parent;
|
||||||
|
const struct fal_partition *fal_part;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* RT-Thread device interface */
|
||||||
|
static rt_size_t char_dev_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
struct fal_char_device *part = (struct fal_char_device *) dev;
|
||||||
|
|
||||||
|
assert(part != RT_NULL);
|
||||||
|
|
||||||
|
if (pos + size > part->fal_part->len)
|
||||||
|
size = part->fal_part->len - pos;
|
||||||
|
|
||||||
|
ret = fal_partition_read(part->fal_part, pos, buffer, size);
|
||||||
|
|
||||||
|
if (ret != (int)(size))
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static rt_size_t char_dev_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
struct fal_char_device *part;
|
||||||
|
|
||||||
|
part = (struct fal_char_device *) dev;
|
||||||
|
assert(part != RT_NULL);
|
||||||
|
|
||||||
|
if (pos == 0)
|
||||||
|
{
|
||||||
|
fal_partition_erase_all(part->fal_part);
|
||||||
|
}
|
||||||
|
else if (pos + size > part->fal_part->len)
|
||||||
|
{
|
||||||
|
size = part->fal_part->len - pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = fal_partition_write(part->fal_part, pos, buffer, size);
|
||||||
|
|
||||||
|
if (ret != (int) size)
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef RT_USING_DEVICE_OPS
|
||||||
|
const static struct rt_device_ops char_dev_ops =
|
||||||
|
{
|
||||||
|
RT_NULL,
|
||||||
|
RT_NULL,
|
||||||
|
RT_NULL,
|
||||||
|
char_dev_read,
|
||||||
|
char_dev_write,
|
||||||
|
RT_NULL
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef RT_USING_POSIX
|
||||||
|
#include <dfs_posix.h>
|
||||||
|
|
||||||
|
/* RT-Thread device filesystem interface */
|
||||||
|
static int char_dev_fopen(struct dfs_fd *fd)
|
||||||
|
{
|
||||||
|
struct fal_char_device *part = (struct fal_char_device *) fd->data;
|
||||||
|
|
||||||
|
assert(part != RT_NULL);
|
||||||
|
|
||||||
|
switch (fd->flags & O_ACCMODE)
|
||||||
|
{
|
||||||
|
case O_RDONLY:
|
||||||
|
break;
|
||||||
|
case O_WRONLY:
|
||||||
|
case O_RDWR:
|
||||||
|
/* erase partition when device file open */
|
||||||
|
fal_partition_erase_all(part->fal_part);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fd->pos = 0;
|
||||||
|
|
||||||
|
return RT_EOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int char_dev_fread(struct dfs_fd *fd, void *buf, size_t count)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
struct fal_char_device *part = (struct fal_char_device *) fd->data;
|
||||||
|
|
||||||
|
assert(part != RT_NULL);
|
||||||
|
|
||||||
|
if (fd->pos + count > part->fal_part->len)
|
||||||
|
count = part->fal_part->len - fd->pos;
|
||||||
|
|
||||||
|
ret = fal_partition_read(part->fal_part, fd->pos, buf, count);
|
||||||
|
|
||||||
|
if (ret != (int)(count))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fd->pos += ret;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int char_dev_fwrite(struct dfs_fd *fd, const void *buf, size_t count)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
struct fal_char_device *part = (struct fal_char_device *) fd->data;
|
||||||
|
|
||||||
|
assert(part != RT_NULL);
|
||||||
|
|
||||||
|
if (fd->pos + count > part->fal_part->len)
|
||||||
|
count = part->fal_part->len - fd->pos;
|
||||||
|
|
||||||
|
ret = fal_partition_write(part->fal_part, fd->pos, buf, count);
|
||||||
|
|
||||||
|
if (ret != (int) count)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fd->pos += ret;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct dfs_file_ops char_dev_fops =
|
||||||
|
{
|
||||||
|
char_dev_fopen,
|
||||||
|
RT_NULL,
|
||||||
|
RT_NULL,
|
||||||
|
char_dev_fread,
|
||||||
|
char_dev_fwrite,
|
||||||
|
RT_NULL, /* flush */
|
||||||
|
RT_NULL, /* lseek */
|
||||||
|
RT_NULL, /* getdents */
|
||||||
|
RT_NULL,
|
||||||
|
};
|
||||||
|
#endif /* defined(RT_USING_POSIX) */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create RT-Thread char device by specified partition
|
||||||
|
*
|
||||||
|
* @param parition_name partition name
|
||||||
|
*
|
||||||
|
* @return != NULL: created char device
|
||||||
|
* NULL: created failed
|
||||||
|
*/
|
||||||
|
struct rt_device *fal_char_device_create(const char *parition_name)
|
||||||
|
{
|
||||||
|
struct fal_char_device *char_dev;
|
||||||
|
const struct fal_partition *fal_part = fal_partition_find(parition_name);
|
||||||
|
|
||||||
|
if (!fal_part)
|
||||||
|
{
|
||||||
|
log_e("Error: the partition name (%s) is not found.", parition_name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((fal_flash_device_find(fal_part->flash_name)) == NULL)
|
||||||
|
{
|
||||||
|
log_e("Error: the flash device name (%s) is not found.", fal_part->flash_name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char_dev = (struct fal_char_device *) rt_malloc(sizeof(struct fal_char_device));
|
||||||
|
if (char_dev)
|
||||||
|
{
|
||||||
|
char_dev->fal_part = fal_part;
|
||||||
|
|
||||||
|
/* register device */
|
||||||
|
char_dev->parent.type = RT_Device_Class_Char;
|
||||||
|
|
||||||
|
#ifdef RT_USING_DEVICE_OPS
|
||||||
|
char_dev->parent.ops = &char_dev_ops;
|
||||||
|
#else
|
||||||
|
char_dev->parent.init = NULL;
|
||||||
|
char_dev->parent.open = NULL;
|
||||||
|
char_dev->parent.close = NULL;
|
||||||
|
char_dev->parent.read = char_dev_read;
|
||||||
|
char_dev->parent.write = char_dev_write;
|
||||||
|
char_dev->parent.control = NULL;
|
||||||
|
/* no private */
|
||||||
|
char_dev->parent.user_data = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
rt_device_register(RT_DEVICE(char_dev), fal_part->name, RT_DEVICE_FLAG_RDWR);
|
||||||
|
log_i("The FAL char device (%s) created successfully", fal_part->name);
|
||||||
|
|
||||||
|
#ifdef RT_USING_POSIX
|
||||||
|
/* set fops */
|
||||||
|
char_dev->parent.fops = &char_dev_fops;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log_e("Error: no memory for create FAL char device");
|
||||||
|
}
|
||||||
|
|
||||||
|
return RT_DEVICE(char_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(RT_USING_FINSH) && defined(FINSH_USING_MSH)
|
||||||
|
|
||||||
|
#include <finsh.h>
|
||||||
|
extern int fal_init_check(void);
|
||||||
|
|
||||||
|
static void fal(uint8_t argc, char **argv) {
|
||||||
|
|
||||||
|
#define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ')
|
||||||
|
#define HEXDUMP_WIDTH 16
|
||||||
|
#define CMD_PROBE_INDEX 0
|
||||||
|
#define CMD_READ_INDEX 1
|
||||||
|
#define CMD_WRITE_INDEX 2
|
||||||
|
#define CMD_ERASE_INDEX 3
|
||||||
|
#define CMD_BENCH_INDEX 4
|
||||||
|
|
||||||
|
int result;
|
||||||
|
static const struct fal_flash_dev *flash_dev = NULL;
|
||||||
|
static const struct fal_partition *part_dev = NULL;
|
||||||
|
size_t i = 0, j = 0;
|
||||||
|
|
||||||
|
const char* help_info[] =
|
||||||
|
{
|
||||||
|
[CMD_PROBE_INDEX] = "fal probe [dev_name|part_name] - probe flash device or partition by given name",
|
||||||
|
[CMD_READ_INDEX] = "fal read addr size - read 'size' bytes starting at 'addr'",
|
||||||
|
[CMD_WRITE_INDEX] = "fal write addr data1 ... dataN - write some bytes 'data' starting at 'addr'",
|
||||||
|
[CMD_ERASE_INDEX] = "fal erase addr size - erase 'size' bytes starting at 'addr'",
|
||||||
|
[CMD_BENCH_INDEX] = "fal bench <blk_size> - benchmark test with per block size",
|
||||||
|
};
|
||||||
|
|
||||||
|
if (fal_init_check() != 1)
|
||||||
|
{
|
||||||
|
rt_kprintf("\n[Warning] FAL is not initialized or failed to initialize!\n\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc < 2)
|
||||||
|
{
|
||||||
|
rt_kprintf("Usage:\n");
|
||||||
|
for (i = 0; i < sizeof(help_info) / sizeof(char*); i++)
|
||||||
|
{
|
||||||
|
rt_kprintf("%s\n", help_info[i]);
|
||||||
|
}
|
||||||
|
rt_kprintf("\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const char *operator = argv[1];
|
||||||
|
uint32_t addr, size;
|
||||||
|
|
||||||
|
if (!strcmp(operator, "probe"))
|
||||||
|
{
|
||||||
|
if (argc >= 3)
|
||||||
|
{
|
||||||
|
char *dev_name = argv[2];
|
||||||
|
if ((flash_dev = fal_flash_device_find(dev_name)) != NULL)
|
||||||
|
{
|
||||||
|
part_dev = NULL;
|
||||||
|
}
|
||||||
|
else if ((part_dev = fal_partition_find(dev_name)) != NULL)
|
||||||
|
{
|
||||||
|
flash_dev = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rt_kprintf("Device %s NOT found. Probe failed.\n", dev_name);
|
||||||
|
flash_dev = NULL;
|
||||||
|
part_dev = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flash_dev)
|
||||||
|
{
|
||||||
|
rt_kprintf("Probed a flash device | %s | addr: %ld | len: %d |.\n", flash_dev->name,
|
||||||
|
flash_dev->addr, flash_dev->len);
|
||||||
|
}
|
||||||
|
else if (part_dev)
|
||||||
|
{
|
||||||
|
rt_kprintf("Probed a flash partition | %s | flash_dev: %s | offset: %ld | len: %d |.\n",
|
||||||
|
part_dev->name, part_dev->flash_name, part_dev->offset, part_dev->len);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rt_kprintf("No flash device or partition was probed.\n");
|
||||||
|
rt_kprintf("Usage: %s.\n", help_info[CMD_PROBE_INDEX]);
|
||||||
|
fal_show_part_table();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!flash_dev && !part_dev)
|
||||||
|
{
|
||||||
|
rt_kprintf("No flash device or partition was probed. Please run 'fal probe'.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!rt_strcmp(operator, "read"))
|
||||||
|
{
|
||||||
|
if (argc < 4)
|
||||||
|
{
|
||||||
|
rt_kprintf("Usage: %s.\n", help_info[CMD_READ_INDEX]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
addr = strtol(argv[2], NULL, 0);
|
||||||
|
size = strtol(argv[3], NULL, 0);
|
||||||
|
uint8_t *data = rt_malloc(size);
|
||||||
|
if (data)
|
||||||
|
{
|
||||||
|
if (flash_dev)
|
||||||
|
{
|
||||||
|
result = flash_dev->ops.read(addr, data, size);
|
||||||
|
}
|
||||||
|
else if (part_dev)
|
||||||
|
{
|
||||||
|
result = fal_partition_read(part_dev, addr, data, size);
|
||||||
|
}
|
||||||
|
if (result >= 0)
|
||||||
|
{
|
||||||
|
rt_kprintf("Read data success. Start from 0x%08X, size is %ld. The data is:\n", addr,
|
||||||
|
size);
|
||||||
|
rt_kprintf("Offset (h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n");
|
||||||
|
for (i = 0; i < size; i += HEXDUMP_WIDTH)
|
||||||
|
{
|
||||||
|
rt_kprintf("[%08X] ", addr + i);
|
||||||
|
/* dump hex */
|
||||||
|
for (j = 0; j < HEXDUMP_WIDTH; j++)
|
||||||
|
{
|
||||||
|
if (i + j < size)
|
||||||
|
{
|
||||||
|
rt_kprintf("%02X ", data[i + j]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rt_kprintf(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* dump char for hex */
|
||||||
|
for (j = 0; j < HEXDUMP_WIDTH; j++)
|
||||||
|
{
|
||||||
|
if (i + j < size)
|
||||||
|
{
|
||||||
|
rt_kprintf("%c", __is_print(data[i + j]) ? data[i + j] : '.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rt_kprintf("\n");
|
||||||
|
}
|
||||||
|
rt_kprintf("\n");
|
||||||
|
}
|
||||||
|
rt_free(data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rt_kprintf("Low memory!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!strcmp(operator, "write"))
|
||||||
|
{
|
||||||
|
if (argc < 4)
|
||||||
|
{
|
||||||
|
rt_kprintf("Usage: %s.\n", help_info[CMD_WRITE_INDEX]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
addr = strtol(argv[2], NULL, 0);
|
||||||
|
size = argc - 3;
|
||||||
|
uint8_t *data = rt_malloc(size);
|
||||||
|
if (data)
|
||||||
|
{
|
||||||
|
for (i = 0; i < size; i++)
|
||||||
|
{
|
||||||
|
data[i] = strtol(argv[3 + i], NULL, 0);
|
||||||
|
}
|
||||||
|
if (flash_dev)
|
||||||
|
{
|
||||||
|
result = flash_dev->ops.write(addr, data, size);
|
||||||
|
}
|
||||||
|
else if (part_dev)
|
||||||
|
{
|
||||||
|
result = fal_partition_write(part_dev, addr, data, size);
|
||||||
|
}
|
||||||
|
if (result >= 0)
|
||||||
|
{
|
||||||
|
rt_kprintf("Write data success. Start from 0x%08X, size is %ld.\n", addr, size);
|
||||||
|
rt_kprintf("Write data: ");
|
||||||
|
for (i = 0; i < size; i++)
|
||||||
|
{
|
||||||
|
rt_kprintf("%d ", data[i]);
|
||||||
|
}
|
||||||
|
rt_kprintf(".\n");
|
||||||
|
}
|
||||||
|
rt_free(data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rt_kprintf("Low memory!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!rt_strcmp(operator, "erase"))
|
||||||
|
{
|
||||||
|
if (argc < 4)
|
||||||
|
{
|
||||||
|
rt_kprintf("Usage: %s.\n", help_info[CMD_ERASE_INDEX]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
addr = strtol(argv[2], NULL, 0);
|
||||||
|
size = strtol(argv[3], NULL, 0);
|
||||||
|
if (flash_dev)
|
||||||
|
{
|
||||||
|
result = flash_dev->ops.erase(addr, size);
|
||||||
|
}
|
||||||
|
else if (part_dev)
|
||||||
|
{
|
||||||
|
result = fal_partition_erase(part_dev, addr, size);
|
||||||
|
}
|
||||||
|
if (result >= 0)
|
||||||
|
{
|
||||||
|
rt_kprintf("Erase data success. Start from 0x%08X, size is %ld.\n", addr, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!strcmp(operator, "bench"))
|
||||||
|
{
|
||||||
|
if (argc < 3)
|
||||||
|
{
|
||||||
|
rt_kprintf("Usage: %s.\n", help_info[CMD_BENCH_INDEX]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if ((argc > 3 && strcmp(argv[3], "yes")) || argc < 4)
|
||||||
|
{
|
||||||
|
rt_kprintf("DANGER: It will erase full chip or partition! Please run 'fal bench %d yes'.\n", strtol(argv[2], NULL, 0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* full chip benchmark test */
|
||||||
|
uint32_t start_time, time_cast;
|
||||||
|
size_t write_size = strtol(argv[2], NULL, 0), read_size = strtol(argv[2], NULL, 0), cur_read_size;
|
||||||
|
uint8_t *write_data = (uint8_t *)rt_malloc(write_size), *read_data = (uint8_t *)rt_malloc(read_size);
|
||||||
|
|
||||||
|
if (write_data && read_data)
|
||||||
|
{
|
||||||
|
memset(write_data, 0x55, write_size);
|
||||||
|
if (flash_dev)
|
||||||
|
{
|
||||||
|
size = flash_dev->len;
|
||||||
|
}
|
||||||
|
else if (part_dev)
|
||||||
|
{
|
||||||
|
size = part_dev->len;
|
||||||
|
}
|
||||||
|
/* benchmark testing */
|
||||||
|
rt_kprintf("Erasing %ld bytes data, waiting...\n", size);
|
||||||
|
start_time = rt_tick_get();
|
||||||
|
if (flash_dev)
|
||||||
|
{
|
||||||
|
result = flash_dev->ops.erase(0, size);
|
||||||
|
}
|
||||||
|
else if (part_dev)
|
||||||
|
{
|
||||||
|
result = fal_partition_erase(part_dev, 0, size);
|
||||||
|
}
|
||||||
|
if (result >= 0)
|
||||||
|
{
|
||||||
|
time_cast = rt_tick_get() - start_time;
|
||||||
|
rt_kprintf("Erase benchmark success, total time: %d.%03dS.\n", time_cast / RT_TICK_PER_SECOND,
|
||||||
|
time_cast % RT_TICK_PER_SECOND / ((RT_TICK_PER_SECOND * 1 + 999) / 1000));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rt_kprintf("Erase benchmark has an error. Error code: %d.\n", result);
|
||||||
|
}
|
||||||
|
/* write test */
|
||||||
|
rt_kprintf("Writing %ld bytes data, waiting...\n", size);
|
||||||
|
start_time = rt_tick_get();
|
||||||
|
for (i = 0; i < size; i += write_size)
|
||||||
|
{
|
||||||
|
if (flash_dev)
|
||||||
|
{
|
||||||
|
result = flash_dev->ops.write(i, write_data, write_size);
|
||||||
|
}
|
||||||
|
else if (part_dev)
|
||||||
|
{
|
||||||
|
result = fal_partition_write(part_dev, i, write_data, write_size);
|
||||||
|
}
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result >= 0)
|
||||||
|
{
|
||||||
|
time_cast = rt_tick_get() - start_time;
|
||||||
|
rt_kprintf("Write benchmark success, total time: %d.%03dS.\n", time_cast / RT_TICK_PER_SECOND,
|
||||||
|
time_cast % RT_TICK_PER_SECOND / ((RT_TICK_PER_SECOND * 1 + 999) / 1000));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rt_kprintf("Write benchmark has an error. Error code: %d.\n", result);
|
||||||
|
}
|
||||||
|
/* read test */
|
||||||
|
rt_kprintf("Reading %ld bytes data, waiting...\n", size);
|
||||||
|
start_time = rt_tick_get();
|
||||||
|
for (i = 0; i < size; i += read_size)
|
||||||
|
{
|
||||||
|
if (i + read_size <= size)
|
||||||
|
{
|
||||||
|
cur_read_size = read_size;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cur_read_size = size - i;
|
||||||
|
}
|
||||||
|
if (flash_dev)
|
||||||
|
{
|
||||||
|
result = flash_dev->ops.read(i, read_data, cur_read_size);
|
||||||
|
}
|
||||||
|
else if (part_dev)
|
||||||
|
{
|
||||||
|
result = fal_partition_read(part_dev, i, read_data, cur_read_size);
|
||||||
|
}
|
||||||
|
/* data check */
|
||||||
|
if (memcmp(write_data, read_data, cur_read_size))
|
||||||
|
{
|
||||||
|
result = -RT_ERROR;
|
||||||
|
rt_kprintf("Data check ERROR! Please check you flash by other command.\n");
|
||||||
|
}
|
||||||
|
/* has an error */
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result >= 0)
|
||||||
|
{
|
||||||
|
time_cast = rt_tick_get() - start_time;
|
||||||
|
rt_kprintf("Read benchmark success, total time: %d.%03dS.\n", time_cast / RT_TICK_PER_SECOND,
|
||||||
|
time_cast % RT_TICK_PER_SECOND / ((RT_TICK_PER_SECOND * 1 + 999) / 1000));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rt_kprintf("Read benchmark has an error. Error code: %d.\n", result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rt_kprintf("Low memory!\n");
|
||||||
|
}
|
||||||
|
rt_free(write_data);
|
||||||
|
rt_free(read_data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rt_kprintf("Usage:\n");
|
||||||
|
for (i = 0; i < sizeof(help_info) / sizeof(char*); i++)
|
||||||
|
{
|
||||||
|
rt_kprintf("%s\n", help_info[i]);
|
||||||
|
}
|
||||||
|
rt_kprintf("\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (result < 0) {
|
||||||
|
rt_kprintf("This operate has an error. Error code: %d.\n", result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MSH_CMD_EXPORT(fal, FAL (Flash Abstraction Layer) operate.);
|
||||||
|
|
||||||
|
#endif /* defined(RT_USING_FINSH) && defined(FINSH_USING_MSH) */
|
||||||
|
#endif /* RT_VER_NUM */
|
|
@ -0,0 +1,100 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Initialize interface.
|
||||||
|
*
|
||||||
|
* Some initialize interface for this library.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <flashdb.h>
|
||||||
|
#include <fdb_low_lvl.h>
|
||||||
|
|
||||||
|
#define FDB_LOG_TAG ""
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set database lock and unlock funtion.
|
||||||
|
*
|
||||||
|
* @param db database object
|
||||||
|
* @param lock lock function
|
||||||
|
* @param unlock lock function
|
||||||
|
*/
|
||||||
|
void fdb_lock_set(fdb_db_t db, void (*lock)(fdb_db_t db), void (*unlock)(fdb_db_t db))
|
||||||
|
{
|
||||||
|
FDB_ASSERT(db);
|
||||||
|
|
||||||
|
db->lock = lock;
|
||||||
|
db->unlock = unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the sector size for database.
|
||||||
|
*
|
||||||
|
* @note The sector size MUST align by partition block size.
|
||||||
|
* @note The sector size change MUST before database initialization.
|
||||||
|
*
|
||||||
|
* @param db database object
|
||||||
|
* @param sec_size
|
||||||
|
*/
|
||||||
|
void fdb_sec_size_set(fdb_db_t db, uint32_t sec_size)
|
||||||
|
{
|
||||||
|
FDB_ASSERT(db);
|
||||||
|
/* the sector size change MUST before database initialization */
|
||||||
|
FDB_ASSERT(db->init_ok == false);
|
||||||
|
|
||||||
|
db->sec_size = sec_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
fdb_err_t _fdb_init_ex(fdb_db_t db, const char *name, const char *part_name, fdb_db_type type, void *user_data)
|
||||||
|
{
|
||||||
|
size_t block_size;
|
||||||
|
|
||||||
|
FDB_ASSERT(db);
|
||||||
|
FDB_ASSERT(name);
|
||||||
|
FDB_ASSERT(part_name);
|
||||||
|
|
||||||
|
if (db->init_ok) {
|
||||||
|
return FDB_NO_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
db->name = name;
|
||||||
|
db->type = type;
|
||||||
|
db->user_data = user_data;
|
||||||
|
/* FAL (Flash Abstraction Layer) initialization */
|
||||||
|
fal_init();
|
||||||
|
/* check the flash partition */
|
||||||
|
if ((db->part = fal_partition_find(part_name)) == NULL) {
|
||||||
|
FDB_INFO("Error: Partition (%s) not found.\n", part_name);
|
||||||
|
return FDB_PART_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
block_size = fal_flash_device_find(db->part->flash_name)->blk_size;
|
||||||
|
if (db->sec_size == 0) {
|
||||||
|
db->sec_size = block_size;
|
||||||
|
} else {
|
||||||
|
/* must be aligned with block size */
|
||||||
|
FDB_ASSERT(db->sec_size % block_size == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return FDB_NO_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _fdb_init_finish(fdb_db_t db, fdb_err_t result)
|
||||||
|
{
|
||||||
|
static bool log_is_show = false;
|
||||||
|
if (result == FDB_NO_ERR) {
|
||||||
|
db->init_ok = true;
|
||||||
|
if (!log_is_show) {
|
||||||
|
FDB_INFO("FlashDB V%s is initialize success.\n", FDB_SW_VERSION);
|
||||||
|
FDB_INFO("You can get the latest version on https://github.com/armink/FlashDB .\n");
|
||||||
|
log_is_show = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FDB_INFO("Error: %s(%s) at partition %s is initialize fail(%d).\n", db->type == FDB_DB_TYPE_KV ? "KV" : "TS",
|
||||||
|
db->name, db->part->name, result);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,162 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Shell commands.
|
||||||
|
*
|
||||||
|
* RT-Thread Finsh/MSH command for EasyFlash.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <flashdb.h>
|
||||||
|
#include <rtthread.h>
|
||||||
|
|
||||||
|
extern struct fdb_kvdb _global_kvdb;
|
||||||
|
extern struct fdb_tsdb _global_tsdb;
|
||||||
|
|
||||||
|
#if defined(RT_USING_FINSH) && defined(FINSH_USING_MSH) && defined(FDB_USING_KVDB)
|
||||||
|
#include <finsh.h>
|
||||||
|
#if defined(FDB_USING_KVDB)
|
||||||
|
static void __setenv(uint8_t argc, char **argv) {
|
||||||
|
uint8_t i;
|
||||||
|
|
||||||
|
if (argc > 3) {
|
||||||
|
/* environment variable value string together */
|
||||||
|
for (i = 0; i < argc - 2; i++) {
|
||||||
|
argv[2 + i][rt_strlen(argv[2 + i])] = ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (argc == 1) {
|
||||||
|
rt_kprintf("Please input: setenv <key> [value]\n");
|
||||||
|
} else if (argc == 2) {
|
||||||
|
fdb_kv_set(&_global_kvdb, argv[1], NULL);
|
||||||
|
} else {
|
||||||
|
fdb_kv_set(&_global_kvdb, argv[1], argv[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MSH_CMD_EXPORT_ALIAS(__setenv, setenv, Set an envrionment variable.);
|
||||||
|
|
||||||
|
static void printenv(uint8_t argc, char **argv) {
|
||||||
|
fdb_kv_print(&_global_kvdb);
|
||||||
|
}
|
||||||
|
MSH_CMD_EXPORT(printenv, Print all envrionment variables.);
|
||||||
|
|
||||||
|
static void getvalue(uint8_t argc, char **argv) {
|
||||||
|
char *value = NULL;
|
||||||
|
value = fdb_kv_get(&_global_kvdb, argv[1]);
|
||||||
|
if (value) {
|
||||||
|
rt_kprintf("The %s value is %s.\n", argv[1], value);
|
||||||
|
} else {
|
||||||
|
rt_kprintf("Can't find %s.\n", argv[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MSH_CMD_EXPORT(getvalue, Get an envrionment variable by name.);
|
||||||
|
|
||||||
|
static void resetenv(uint8_t argc, char **argv) {
|
||||||
|
fdb_kv_set_default(&_global_kvdb);
|
||||||
|
}
|
||||||
|
MSH_CMD_EXPORT(resetenv, Reset all envrionment variable to default.);
|
||||||
|
|
||||||
|
#endif /* defined(FDB_USING_KVDB) */
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(FDB_USING_TSDB)
|
||||||
|
static bool tsl_cb(fdb_tsl_t tsl, void *arg)
|
||||||
|
{
|
||||||
|
struct fdb_blob blob;
|
||||||
|
char *log = rt_malloc(tsl->log_len);
|
||||||
|
size_t read_len;
|
||||||
|
|
||||||
|
if (log) {
|
||||||
|
fdb_blob_make(&blob, log, tsl->log_len);
|
||||||
|
read_len = fdb_blob_read((fdb_db_t)&_global_tsdb, fdb_tsl_to_blob(tsl, &blob));
|
||||||
|
|
||||||
|
rt_kprintf("TSL time: %d\n", tsl->time);
|
||||||
|
rt_kprintf("TSL blob content: %.*s\n", read_len, blob.buf);
|
||||||
|
rt_free(log);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool tsl_bench_cb(fdb_tsl_t tsl, void *arg)
|
||||||
|
{
|
||||||
|
rt_tick_t *end_tick = arg;
|
||||||
|
|
||||||
|
*end_tick = rt_tick_get();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tsl(uint8_t argc, char **argv) {
|
||||||
|
struct fdb_blob blob;
|
||||||
|
struct tm tm_from = { .tm_year = 1970 - 1900, .tm_mon = 0, .tm_mday = 1, .tm_hour = 0, .tm_min = 0, .tm_sec = 0 };
|
||||||
|
struct tm tm_to = { .tm_year = 2030 - 1900, .tm_mon = 0, .tm_mday = 1, .tm_hour = 0, .tm_min = 0, .tm_sec = 0 };
|
||||||
|
time_t from_time = mktime(&tm_from), to_time = mktime(&tm_to);
|
||||||
|
rt_tick_t start_tick = rt_tick_get(), end_tick;
|
||||||
|
|
||||||
|
if (!strcmp(argv[1], "add") && (argc > 2)) {
|
||||||
|
fdb_tsl_append(&_global_tsdb, fdb_blob_make(&blob, argv[2], strlen(argv[2])));
|
||||||
|
} else if (!strcmp(argv[1], "get") && (argc > 1)) {
|
||||||
|
fdb_tsl_iter_by_time(&_global_tsdb, from_time, to_time, tsl_cb, NULL);
|
||||||
|
// fdb_ts_iter_by_time(&_global_tsdb, atoi(argv[2]), atoi(argv[3]), ts_cb, NULL);
|
||||||
|
} else if (!strcmp(argv[1], "clean") && (argc > 1)) {
|
||||||
|
fdb_tsl_clean(&_global_tsdb);
|
||||||
|
} else if (!strcmp(argv[1], "query") && (argc > 2)) {
|
||||||
|
int status = atoi(argv[2]);
|
||||||
|
size_t count;
|
||||||
|
count = fdb_tsl_query_count(&_global_tsdb, from_time, to_time, status);
|
||||||
|
rt_kprintf("query count: %d\n", count);
|
||||||
|
} else if (!strcmp(argv[1], "bench") && (argc > 1)) {
|
||||||
|
#define BENCH_TIMEOUT (5*1000)
|
||||||
|
struct fdb_blob blob;
|
||||||
|
static char data[11], log[128];
|
||||||
|
size_t append_num = 0;
|
||||||
|
fdb_time_t start, end, cur;
|
||||||
|
rt_tick_t bench_start_tick, spent_tick, min_tick = 9999, max_tick = 0, total_tick = 0;
|
||||||
|
float temp;
|
||||||
|
|
||||||
|
fdb_tsl_clean(&_global_tsdb);
|
||||||
|
bench_start_tick = rt_tick_get();
|
||||||
|
start = _global_tsdb.get_time();
|
||||||
|
while (rt_tick_get() - bench_start_tick <= (rt_tick_t)rt_tick_from_millisecond(BENCH_TIMEOUT)) {
|
||||||
|
rt_snprintf(data, sizeof(data), "%d", append_num++);
|
||||||
|
fdb_tsl_append(&_global_tsdb, fdb_blob_make(&blob, data, rt_strnlen(data, sizeof(data))));
|
||||||
|
}
|
||||||
|
end = _global_tsdb.get_time();
|
||||||
|
temp = (float) append_num / (float)(BENCH_TIMEOUT / 1000);
|
||||||
|
snprintf(log, sizeof(log), "Append %d TSL in %d seconds, average: %.2f tsl/S, %.2f ms/per\n", append_num,
|
||||||
|
BENCH_TIMEOUT / 1000, temp, 1000.0f / temp);
|
||||||
|
rt_kprintf("%s", log);
|
||||||
|
cur = start;
|
||||||
|
while(cur < end) {
|
||||||
|
end_tick = bench_start_tick = rt_tick_get();
|
||||||
|
fdb_tsl_iter_by_time(&_global_tsdb, cur, cur, tsl_bench_cb, &end_tick);
|
||||||
|
// spent_tick = end_tick - bench_start_tick;
|
||||||
|
spent_tick = rt_tick_get() - bench_start_tick;
|
||||||
|
if (spent_tick < min_tick) {
|
||||||
|
min_tick = spent_tick;
|
||||||
|
}
|
||||||
|
if (spent_tick > max_tick) {
|
||||||
|
max_tick = spent_tick;
|
||||||
|
}
|
||||||
|
total_tick += spent_tick;
|
||||||
|
cur ++;
|
||||||
|
}
|
||||||
|
snprintf(log, sizeof(log), "Query total spent %u (tick) for %ld TSL, min %u, max %u, average: %.2f tick/per\n", total_tick, end - start, min_tick, max_tick,
|
||||||
|
(float) total_tick / (float) (end - start));
|
||||||
|
rt_kprintf("%s", log);
|
||||||
|
fdb_tsl_clean(&_global_tsdb);
|
||||||
|
} else {
|
||||||
|
rt_kprintf("Please input: tsl [add log content|get [from_s to_s]]\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
rt_kprintf("exec time: %d ticks\n", rt_tick_get() - start_tick);
|
||||||
|
}
|
||||||
|
MSH_CMD_EXPORT_ALIAS(tsl, tsl, Time series log. tsl [add log content|get [from_s to_s]|clean].);
|
||||||
|
#endif /* defined(FDB_USING_TSDB) */
|
||||||
|
|
||||||
|
#endif /* defined(RT_USING_FINSH) && defined(FINSH_USING_MSH) */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,712 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief TSDB feature.
|
||||||
|
*
|
||||||
|
* Time series log (like TSDB) feature implement source file.
|
||||||
|
*
|
||||||
|
* TSL is time series log, the TSDB saved many TSLs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <flashdb.h>
|
||||||
|
#include <fdb_low_lvl.h>
|
||||||
|
|
||||||
|
#define FDB_LOG_TAG "[tsl]"
|
||||||
|
/* rewrite log prefix */
|
||||||
|
#undef FDB_LOG_PREFIX2
|
||||||
|
#define FDB_LOG_PREFIX2() FDB_PRINT("[%s] ", db_name(db))
|
||||||
|
|
||||||
|
#if defined(FDB_USING_TSDB)
|
||||||
|
|
||||||
|
/* magic word(`T`, `S`, `L`, `0`) */
|
||||||
|
#define SECTOR_MAGIC_WORD 0x304C5354
|
||||||
|
|
||||||
|
#define TSL_STATUS_TABLE_SIZE FDB_STATUS_TABLE_SIZE(FDB_TSL_STATUS_NUM)
|
||||||
|
|
||||||
|
#define SECTOR_HDR_DATA_SIZE (FDB_WG_ALIGN(sizeof(struct sector_hdr_data)))
|
||||||
|
#define LOG_IDX_DATA_SIZE (FDB_WG_ALIGN(sizeof(struct log_idx_data)))
|
||||||
|
#define LOG_IDX_TS_OFFSET ((unsigned long)(&((struct log_idx_data *)0)->time))
|
||||||
|
#define SECTOR_MAGIC_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->magic))
|
||||||
|
#define SECTOR_START_TIME_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->start_time))
|
||||||
|
#define SECTOR_END0_TIME_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->end_info[0].time))
|
||||||
|
#define SECTOR_END0_IDX_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->end_info[0].index))
|
||||||
|
#define SECTOR_END0_STATUS_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->end_info[0].status))
|
||||||
|
#define SECTOR_END1_TIME_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->end_info[1].time))
|
||||||
|
#define SECTOR_END1_IDX_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->end_info[1].index))
|
||||||
|
#define SECTOR_END1_STATUS_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->end_info[1].status))
|
||||||
|
|
||||||
|
/* the next address is get failed */
|
||||||
|
#define FAILED_ADDR 0xFFFFFFFF
|
||||||
|
|
||||||
|
#define db_name(db) (((fdb_db_t)db)->name)
|
||||||
|
#define db_init_ok(db) (((fdb_db_t)db)->init_ok)
|
||||||
|
#define db_sec_size(db) (((fdb_db_t)db)->sec_size)
|
||||||
|
#define db_part_size(db) (((fdb_db_t)db)->part->len)
|
||||||
|
#define db_lock(db) \
|
||||||
|
do { \
|
||||||
|
if (((fdb_db_t)db)->lock) ((fdb_db_t)db)->lock((fdb_db_t)db); \
|
||||||
|
} while(0);
|
||||||
|
|
||||||
|
#define db_unlock(db) \
|
||||||
|
do { \
|
||||||
|
if (((fdb_db_t)db)->unlock) ((fdb_db_t)db)->unlock((fdb_db_t)db); \
|
||||||
|
} while(0);
|
||||||
|
|
||||||
|
#define _FDB_WRITE_STATUS(db, addr, status_table, status_num, status_index) \
|
||||||
|
do { \
|
||||||
|
result = _fdb_write_status((fdb_db_t)db, addr, status_table, status_num, status_index);\
|
||||||
|
if (result != FDB_NO_ERR) return result; \
|
||||||
|
} while(0);
|
||||||
|
|
||||||
|
#define FLASH_WRITE(db, addr, buf, size) \
|
||||||
|
do { \
|
||||||
|
result = _fdb_flash_write((fdb_db_t)db, addr, buf, size); \
|
||||||
|
if (result != FDB_NO_ERR) return result; \
|
||||||
|
} while(0);
|
||||||
|
|
||||||
|
struct sector_hdr_data {
|
||||||
|
uint8_t status[FDB_STORE_STATUS_TABLE_SIZE]; /**< sector store status @see fdb_sector_store_status_t */
|
||||||
|
uint32_t magic; /**< magic word(`T`, `S`, `L`, `0`) */
|
||||||
|
fdb_time_t start_time; /**< the first start node's timestamp */
|
||||||
|
struct {
|
||||||
|
fdb_time_t time; /**< the last end node's timestamp */
|
||||||
|
uint32_t index; /**< the last end node's index */
|
||||||
|
uint8_t status[TSL_STATUS_TABLE_SIZE]; /**< end node status, @see fdb_tsl_status_t */
|
||||||
|
} end_info[2];
|
||||||
|
uint32_t reserved;
|
||||||
|
};
|
||||||
|
typedef struct sector_hdr_data *sector_hdr_data_t;
|
||||||
|
|
||||||
|
/* time series log node index data */
|
||||||
|
struct log_idx_data {
|
||||||
|
uint8_t status_table[TSL_STATUS_TABLE_SIZE]; /**< node status, @see fdb_tsl_status_t */
|
||||||
|
fdb_time_t time; /**< node timestamp */
|
||||||
|
uint32_t log_len; /**< node total length (header + name + value), must align by FDB_WRITE_GRAN */
|
||||||
|
uint32_t log_addr; /**< node address */
|
||||||
|
};
|
||||||
|
typedef struct log_idx_data *log_idx_data_t;
|
||||||
|
|
||||||
|
struct query_count_args {
|
||||||
|
fdb_tsl_status_t status;
|
||||||
|
size_t count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct check_sec_hdr_cb_args {
|
||||||
|
fdb_tsdb_t db;
|
||||||
|
bool check_failed;
|
||||||
|
size_t empty_num;
|
||||||
|
uint32_t empty_addr;
|
||||||
|
};
|
||||||
|
|
||||||
|
static fdb_err_t read_tsl(fdb_tsdb_t db, fdb_tsl_t tsl)
|
||||||
|
{
|
||||||
|
struct log_idx_data idx;
|
||||||
|
/* read TSL index raw data */
|
||||||
|
_fdb_flash_read((fdb_db_t)db, tsl->addr.index, (uint32_t *) &idx, sizeof(struct log_idx_data));
|
||||||
|
tsl->status = (fdb_tsl_status_t) _fdb_get_status(idx.status_table, FDB_TSL_STATUS_NUM);
|
||||||
|
if (tsl->status == FDB_TSL_PRE_WRITE) {
|
||||||
|
tsl->log_len = db->max_len;
|
||||||
|
tsl->addr.log = FDB_DATA_UNUSED;
|
||||||
|
tsl->time = 0;
|
||||||
|
} else {
|
||||||
|
tsl->log_len = idx.log_len;
|
||||||
|
tsl->addr.log = idx.log_addr;
|
||||||
|
tsl->time = idx.time;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FDB_NO_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t get_next_sector_addr(fdb_tsdb_t db, tsdb_sec_info_t pre_sec, uint32_t traversed_len)
|
||||||
|
{
|
||||||
|
if (traversed_len + db_sec_size(db) <= db_part_size(db)) {
|
||||||
|
if (pre_sec->addr + db_sec_size(db) < db_part_size(db)) {
|
||||||
|
return pre_sec->addr + db_sec_size(db);
|
||||||
|
} else {
|
||||||
|
/* the next sector is on the top of the partition */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* finished */
|
||||||
|
return FAILED_ADDR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t get_next_tsl_addr(tsdb_sec_info_t sector, fdb_tsl_t pre_tsl)
|
||||||
|
{
|
||||||
|
uint32_t addr = FAILED_ADDR;
|
||||||
|
|
||||||
|
if (sector->status == FDB_SECTOR_STORE_EMPTY) {
|
||||||
|
return FAILED_ADDR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pre_tsl->addr.index + LOG_IDX_DATA_SIZE <= sector->end_idx) {
|
||||||
|
addr = pre_tsl->addr.index + LOG_IDX_DATA_SIZE;
|
||||||
|
} else {
|
||||||
|
/* no TSL */
|
||||||
|
return FAILED_ADDR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static fdb_err_t read_sector_info(fdb_tsdb_t db, uint32_t addr, tsdb_sec_info_t sector, bool traversal)
|
||||||
|
{
|
||||||
|
fdb_err_t result = FDB_NO_ERR;
|
||||||
|
struct sector_hdr_data sec_hdr;
|
||||||
|
|
||||||
|
FDB_ASSERT(sector);
|
||||||
|
|
||||||
|
/* read sector header raw data */
|
||||||
|
_fdb_flash_read((fdb_db_t)db, addr, (uint32_t *)&sec_hdr, sizeof(struct sector_hdr_data));
|
||||||
|
|
||||||
|
sector->addr = addr;
|
||||||
|
sector->magic = sec_hdr.magic;
|
||||||
|
|
||||||
|
/* check magic word */
|
||||||
|
if (sector->magic != SECTOR_MAGIC_WORD) {
|
||||||
|
sector->check_ok = false;
|
||||||
|
return FDB_INIT_FAILED;
|
||||||
|
}
|
||||||
|
sector->check_ok = true;
|
||||||
|
sector->status = (fdb_sector_store_status_t) _fdb_get_status(sec_hdr.status, FDB_SECTOR_STORE_STATUS_NUM);
|
||||||
|
sector->start_time = sec_hdr.start_time;
|
||||||
|
sector->end_info_stat[0] = (fdb_tsl_status_t) _fdb_get_status(sec_hdr.end_info[0].status, FDB_TSL_STATUS_NUM);
|
||||||
|
sector->end_info_stat[1] = (fdb_tsl_status_t) _fdb_get_status(sec_hdr.end_info[1].status, FDB_TSL_STATUS_NUM);
|
||||||
|
if (sector->end_info_stat[0] == FDB_TSL_WRITE) {
|
||||||
|
sector->end_time = sec_hdr.end_info[0].time;
|
||||||
|
sector->end_idx = sec_hdr.end_info[0].index;
|
||||||
|
} else if (sector->end_info_stat[1] == FDB_TSL_WRITE) {
|
||||||
|
sector->end_time = sec_hdr.end_info[1].time;
|
||||||
|
sector->end_idx = sec_hdr.end_info[1].index;
|
||||||
|
} else if (sector->end_info_stat[0] == FDB_TSL_PRE_WRITE && sector->end_info_stat[1] == FDB_TSL_PRE_WRITE) {
|
||||||
|
//TODO There is no valid end node info on this sector, need impl fast query this sector by fdb_tsl_iter_by_time
|
||||||
|
FDB_ASSERT(0);
|
||||||
|
}
|
||||||
|
/* traversal all TSL and calculate the remain space size */
|
||||||
|
sector->empty_idx = sector->addr + SECTOR_HDR_DATA_SIZE;
|
||||||
|
sector->empty_data = sector->addr + db_sec_size(db);
|
||||||
|
/* the TSL's data is saved from sector bottom, and the TSL's index saved from the sector top */
|
||||||
|
sector->remain = sector->empty_data - sector->empty_idx;
|
||||||
|
if (sector->status == FDB_SECTOR_STORE_USING && traversal) {
|
||||||
|
struct fdb_tsl tsl;
|
||||||
|
|
||||||
|
tsl.addr.index = sector->empty_idx;
|
||||||
|
while (read_tsl(db, &tsl) == FDB_NO_ERR) {
|
||||||
|
if (tsl.status == FDB_TSL_UNUSED) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sector->end_time = tsl.time;
|
||||||
|
sector->end_idx = tsl.addr.index;
|
||||||
|
sector->empty_idx += LOG_IDX_DATA_SIZE;
|
||||||
|
sector->empty_data -= FDB_WG_ALIGN(tsl.log_len);
|
||||||
|
tsl.addr.index += LOG_IDX_DATA_SIZE;
|
||||||
|
if (sector->remain > LOG_IDX_DATA_SIZE + FDB_WG_ALIGN(tsl.log_len)) {
|
||||||
|
sector->remain -= (LOG_IDX_DATA_SIZE + FDB_WG_ALIGN(tsl.log_len));
|
||||||
|
} else {
|
||||||
|
FDB_INFO("Error: this TSL (0x%08lX) size (%lu) is out of bound.\n", tsl.addr.index, tsl.log_len);
|
||||||
|
sector->remain = 0;
|
||||||
|
result = FDB_READ_ERR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static fdb_err_t format_sector(fdb_tsdb_t db, uint32_t addr)
|
||||||
|
{
|
||||||
|
fdb_err_t result = FDB_NO_ERR;
|
||||||
|
struct sector_hdr_data sec_hdr;
|
||||||
|
|
||||||
|
FDB_ASSERT(addr % db_sec_size(db) == 0);
|
||||||
|
|
||||||
|
result = _fdb_flash_erase((fdb_db_t)db, addr, db_sec_size(db));
|
||||||
|
if (result == FDB_NO_ERR) {
|
||||||
|
_FDB_WRITE_STATUS(db, addr, sec_hdr.status, FDB_SECTOR_STORE_STATUS_NUM, FDB_SECTOR_STORE_EMPTY);
|
||||||
|
/* set the magic */
|
||||||
|
sec_hdr.magic = SECTOR_MAGIC_WORD;
|
||||||
|
FLASH_WRITE(db, addr + SECTOR_MAGIC_OFFSET, &sec_hdr.magic, sizeof(sec_hdr.magic));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sector_iterator(fdb_tsdb_t db, tsdb_sec_info_t sector, fdb_sector_store_status_t status, void *arg1,
|
||||||
|
void *arg2, bool (*callback)(tsdb_sec_info_t sector, void *arg1, void *arg2), bool traversal)
|
||||||
|
{
|
||||||
|
uint32_t sec_addr = sector->addr, traversed_len = 0;
|
||||||
|
|
||||||
|
/* search all sectors */
|
||||||
|
do {
|
||||||
|
read_sector_info(db, sec_addr, sector, false);
|
||||||
|
if (status == FDB_SECTOR_STORE_UNUSED || status == sector->status) {
|
||||||
|
if (traversal) {
|
||||||
|
read_sector_info(db, sec_addr, sector, true);
|
||||||
|
}
|
||||||
|
/* iterator is interrupted when callback return true */
|
||||||
|
if (callback && callback(sector, arg1, arg2)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
traversed_len += db_sec_size(db);
|
||||||
|
} while ((sec_addr = get_next_sector_addr(db, sector, traversed_len)) != FAILED_ADDR);
|
||||||
|
}
|
||||||
|
|
||||||
|
static fdb_err_t write_tsl(fdb_tsdb_t db, fdb_blob_t blob, fdb_time_t time)
|
||||||
|
{
|
||||||
|
fdb_err_t result = FDB_NO_ERR;
|
||||||
|
struct log_idx_data idx;
|
||||||
|
uint32_t idx_addr = db->cur_sec.empty_idx;
|
||||||
|
|
||||||
|
idx.log_len = blob->size;
|
||||||
|
idx.time = time;
|
||||||
|
idx.log_addr = db->cur_sec.empty_data - FDB_WG_ALIGN(idx.log_len);
|
||||||
|
/* write the status will by write granularity */
|
||||||
|
_FDB_WRITE_STATUS(db, idx_addr, idx.status_table, FDB_TSL_STATUS_NUM, FDB_TSL_PRE_WRITE);
|
||||||
|
/* write other index info */
|
||||||
|
FLASH_WRITE(db, idx_addr + LOG_IDX_TS_OFFSET, &idx.time, sizeof(struct log_idx_data) - LOG_IDX_TS_OFFSET);
|
||||||
|
/* write blob data */
|
||||||
|
FLASH_WRITE(db, idx.log_addr, blob->buf, blob->size);
|
||||||
|
/* write the status will by write granularity */
|
||||||
|
_FDB_WRITE_STATUS(db, idx_addr, idx.status_table, FDB_TSL_STATUS_NUM, FDB_TSL_WRITE);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static fdb_err_t update_sec_status(fdb_tsdb_t db, tsdb_sec_info_t sector, fdb_blob_t blob, fdb_time_t cur_time)
|
||||||
|
{
|
||||||
|
fdb_err_t result = FDB_NO_ERR;
|
||||||
|
uint8_t status[FDB_STORE_STATUS_TABLE_SIZE];
|
||||||
|
|
||||||
|
if (sector->status == FDB_SECTOR_STORE_USING && sector->remain < LOG_IDX_DATA_SIZE + FDB_WG_ALIGN(blob->size)) {
|
||||||
|
uint8_t end_status[TSL_STATUS_TABLE_SIZE];
|
||||||
|
uint32_t end_index = sector->empty_idx - LOG_IDX_DATA_SIZE, new_sec_addr, cur_sec_addr = sector->addr;
|
||||||
|
/* save the end node index and timestamp */
|
||||||
|
if (sector->end_info_stat[0] == FDB_TSL_UNUSED) {
|
||||||
|
_FDB_WRITE_STATUS(db, cur_sec_addr + SECTOR_END0_STATUS_OFFSET, end_status, FDB_TSL_STATUS_NUM, FDB_TSL_PRE_WRITE);
|
||||||
|
FLASH_WRITE(db, cur_sec_addr + SECTOR_END0_TIME_OFFSET, (uint32_t * )&db->last_time, sizeof(fdb_time_t));
|
||||||
|
FLASH_WRITE(db, cur_sec_addr + SECTOR_END0_IDX_OFFSET, &end_index, sizeof(end_index));
|
||||||
|
_FDB_WRITE_STATUS(db, cur_sec_addr + SECTOR_END0_STATUS_OFFSET, end_status, FDB_TSL_STATUS_NUM, FDB_TSL_WRITE);
|
||||||
|
} else if (sector->end_info_stat[1] == FDB_TSL_UNUSED) {
|
||||||
|
_FDB_WRITE_STATUS(db, cur_sec_addr + SECTOR_END1_STATUS_OFFSET, end_status, FDB_TSL_STATUS_NUM, FDB_TSL_PRE_WRITE);
|
||||||
|
FLASH_WRITE(db, cur_sec_addr + SECTOR_END1_TIME_OFFSET, (uint32_t * )&db->last_time, sizeof(fdb_time_t));
|
||||||
|
FLASH_WRITE(db, cur_sec_addr + SECTOR_END1_IDX_OFFSET, &end_index, sizeof(end_index));
|
||||||
|
_FDB_WRITE_STATUS(db, cur_sec_addr + SECTOR_END1_STATUS_OFFSET, end_status, FDB_TSL_STATUS_NUM, FDB_TSL_WRITE);
|
||||||
|
}
|
||||||
|
/* change current sector to full */
|
||||||
|
_FDB_WRITE_STATUS(db, cur_sec_addr, status, FDB_SECTOR_STORE_STATUS_NUM, FDB_SECTOR_STORE_FULL);
|
||||||
|
/* calculate next sector address */
|
||||||
|
if (sector->addr + db_sec_size(db) < db_part_size(db)) {
|
||||||
|
new_sec_addr = sector->addr + db_sec_size(db);
|
||||||
|
} else {
|
||||||
|
new_sec_addr = 0;
|
||||||
|
}
|
||||||
|
read_sector_info(db, new_sec_addr, &db->cur_sec, false);
|
||||||
|
if (sector->status != FDB_SECTOR_STORE_EMPTY) {
|
||||||
|
/* calculate the oldest sector address */
|
||||||
|
if (new_sec_addr + db_sec_size(db) < db_part_size(db)) {
|
||||||
|
db->oldest_addr = new_sec_addr + db_sec_size(db);
|
||||||
|
} else {
|
||||||
|
db->oldest_addr = 0;
|
||||||
|
}
|
||||||
|
format_sector(db, new_sec_addr);
|
||||||
|
read_sector_info(db, new_sec_addr, &db->cur_sec, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sector->status == FDB_SECTOR_STORE_EMPTY) {
|
||||||
|
/* change the sector to using */
|
||||||
|
sector->status = FDB_SECTOR_STORE_USING;
|
||||||
|
sector->start_time = cur_time;
|
||||||
|
_FDB_WRITE_STATUS(db, sector->addr, status, FDB_SECTOR_STORE_STATUS_NUM, FDB_SECTOR_STORE_USING);
|
||||||
|
/* save the start timestamp */
|
||||||
|
FLASH_WRITE(db, sector->addr + SECTOR_START_TIME_OFFSET, (uint32_t *)&cur_time, sizeof(fdb_time_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static fdb_err_t tsl_append(fdb_tsdb_t db, fdb_blob_t blob)
|
||||||
|
{
|
||||||
|
fdb_err_t result = FDB_NO_ERR;
|
||||||
|
fdb_time_t cur_time = db->get_time();
|
||||||
|
|
||||||
|
FDB_ASSERT(blob->size <= db->max_len);
|
||||||
|
|
||||||
|
update_sec_status(db, &db->cur_sec, blob, cur_time);
|
||||||
|
|
||||||
|
/* write the TSL node */
|
||||||
|
result = write_tsl(db, blob, cur_time);
|
||||||
|
|
||||||
|
/* recalculate the current using sector info */
|
||||||
|
db->cur_sec.end_idx = db->cur_sec.empty_idx;
|
||||||
|
db->cur_sec.end_time = cur_time;
|
||||||
|
db->cur_sec.empty_idx += LOG_IDX_DATA_SIZE;
|
||||||
|
db->cur_sec.empty_data -= FDB_WG_ALIGN(blob->size);
|
||||||
|
db->cur_sec.remain -= LOG_IDX_DATA_SIZE + FDB_WG_ALIGN(blob->size);
|
||||||
|
|
||||||
|
if (cur_time >= db->last_time) {
|
||||||
|
db->last_time = cur_time;
|
||||||
|
} else {
|
||||||
|
FDB_INFO("Warning: current timestamp (%ld) is less than the last save timestamp (%ld)\n", cur_time, db->last_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append a new log to TSDB.
|
||||||
|
*
|
||||||
|
* @param db database object
|
||||||
|
* @param blob log blob data
|
||||||
|
*
|
||||||
|
* @return result
|
||||||
|
*/
|
||||||
|
fdb_err_t fdb_tsl_append(fdb_tsdb_t db, fdb_blob_t blob)
|
||||||
|
{
|
||||||
|
fdb_err_t result = FDB_NO_ERR;
|
||||||
|
|
||||||
|
if (!db_init_ok(db)) {
|
||||||
|
FDB_INFO("Error: TSL (%s) isn't initialize OK.\n", db_name(db));
|
||||||
|
return FDB_INIT_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
db_lock(db);
|
||||||
|
result = tsl_append(db, blob);
|
||||||
|
db_unlock(db);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The TSDB iterator for each TSL.
|
||||||
|
*
|
||||||
|
* @param db database object
|
||||||
|
* @param cb callback
|
||||||
|
* @param arg callback argument
|
||||||
|
*/
|
||||||
|
void fdb_tsl_iter(fdb_tsdb_t db, fdb_tsl_cb cb, void *arg)
|
||||||
|
{
|
||||||
|
struct tsdb_sec_info sector;
|
||||||
|
uint32_t sec_addr, traversed_len = 0;
|
||||||
|
struct fdb_tsl tsl;
|
||||||
|
|
||||||
|
if (!db_init_ok(db)) {
|
||||||
|
FDB_INFO("Error: TSL (%s) isn't initialize OK.\n", db_name(db));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cb == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sec_addr = db->oldest_addr;
|
||||||
|
/* search all sectors */
|
||||||
|
do {
|
||||||
|
if (read_sector_info(db, sec_addr, §or, false) != FDB_NO_ERR) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* sector has TSL */
|
||||||
|
if (sector.status == FDB_SECTOR_STORE_USING || sector.status == FDB_SECTOR_STORE_FULL) {
|
||||||
|
if (sector.status == FDB_SECTOR_STORE_USING) {
|
||||||
|
/* copy the current using sector status */
|
||||||
|
sector = db->cur_sec;
|
||||||
|
}
|
||||||
|
tsl.addr.index = sector.addr + SECTOR_HDR_DATA_SIZE;
|
||||||
|
/* search all TSL */
|
||||||
|
do {
|
||||||
|
read_tsl(db, &tsl);
|
||||||
|
/* iterator is interrupted when callback return true */
|
||||||
|
if (cb(&tsl, arg)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} while ((tsl.addr.index = get_next_tsl_addr(§or, &tsl)) != FAILED_ADDR);
|
||||||
|
}
|
||||||
|
traversed_len += db_sec_size(db);
|
||||||
|
} while ((sec_addr = get_next_sector_addr(db, §or, traversed_len)) != FAILED_ADDR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The TSDB iterator for each TSL by timestamp.
|
||||||
|
*
|
||||||
|
* @param db database object
|
||||||
|
* @param from starting timestap
|
||||||
|
* @param to ending timestap
|
||||||
|
* @param cb callback
|
||||||
|
* @param arg callback argument
|
||||||
|
*/
|
||||||
|
void fdb_tsl_iter_by_time(fdb_tsdb_t db, fdb_time_t from, fdb_time_t to, fdb_tsl_cb cb, void *cb_arg)
|
||||||
|
{
|
||||||
|
struct tsdb_sec_info sector;
|
||||||
|
uint32_t sec_addr, traversed_len = 0;
|
||||||
|
struct fdb_tsl tsl;
|
||||||
|
bool found_start_tsl = false;
|
||||||
|
|
||||||
|
if (!db_init_ok(db)) {
|
||||||
|
FDB_INFO("Error: TSL (%s) isn't initialize OK.\n", db_name(db));
|
||||||
|
}
|
||||||
|
|
||||||
|
// FDB_INFO("from %s", ctime((const time_t * )&from));
|
||||||
|
// FDB_INFO("to %s", ctime((const time_t * )&to));
|
||||||
|
|
||||||
|
if (cb == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sec_addr = db->oldest_addr;
|
||||||
|
/* search all sectors */
|
||||||
|
do {
|
||||||
|
if (read_sector_info(db, sec_addr, §or, false) != FDB_NO_ERR) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* sector has TSL */
|
||||||
|
if ((sector.status == FDB_SECTOR_STORE_USING || sector.status == FDB_SECTOR_STORE_FULL)) {
|
||||||
|
if (sector.status == FDB_SECTOR_STORE_USING) {
|
||||||
|
/* copy the current using sector status */
|
||||||
|
sector = db->cur_sec;
|
||||||
|
}
|
||||||
|
if ((!found_start_tsl && ((sector.start_time <= from && sector.end_time >= from) || (sector.start_time <= to)))
|
||||||
|
|| (found_start_tsl)) {
|
||||||
|
uint32_t start = sector.addr + SECTOR_HDR_DATA_SIZE, end = sector.end_idx;
|
||||||
|
|
||||||
|
found_start_tsl = true;
|
||||||
|
/* search start TSL address, using binary search algorithm */
|
||||||
|
while (start <= end) {
|
||||||
|
tsl.addr.index = start + ((end - start) / 2 + 1) / LOG_IDX_DATA_SIZE * LOG_IDX_DATA_SIZE;
|
||||||
|
read_tsl(db, &tsl);
|
||||||
|
if (tsl.time < from) {
|
||||||
|
start = tsl.addr.index + LOG_IDX_DATA_SIZE;
|
||||||
|
} else {
|
||||||
|
end = tsl.addr.index - LOG_IDX_DATA_SIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tsl.addr.index = start;
|
||||||
|
/* search all TSL */
|
||||||
|
do {
|
||||||
|
read_tsl(db, &tsl);
|
||||||
|
if (tsl.time >= from && tsl.time <= to) {
|
||||||
|
/* iterator is interrupted when callback return true */
|
||||||
|
if (cb(&tsl, cb_arg)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} while ((tsl.addr.index = get_next_tsl_addr(§or, &tsl)) != FAILED_ADDR);
|
||||||
|
}
|
||||||
|
} else if (sector.status == FDB_SECTOR_STORE_EMPTY) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
traversed_len += db_sec_size(db);
|
||||||
|
} while ((sec_addr = get_next_sector_addr(db, §or, traversed_len)) != FAILED_ADDR);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool query_count_cb(fdb_tsl_t tsl, void *arg)
|
||||||
|
{
|
||||||
|
struct query_count_args *args = arg;
|
||||||
|
|
||||||
|
if (tsl->status == args->status) {
|
||||||
|
args->count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query some TSL's count by timestamp and status.
|
||||||
|
*
|
||||||
|
* @param db database object
|
||||||
|
* @param from starting timestap
|
||||||
|
* @param to ending timestap
|
||||||
|
* @param status status
|
||||||
|
*/
|
||||||
|
size_t fdb_tsl_query_count(fdb_tsdb_t db, fdb_time_t from, fdb_time_t to, fdb_tsl_status_t status)
|
||||||
|
{
|
||||||
|
struct query_count_args arg = { 0 };
|
||||||
|
|
||||||
|
arg.status = status;
|
||||||
|
|
||||||
|
if (!db_init_ok(db)) {
|
||||||
|
FDB_INFO("Error: TSL (%s) isn't initialize OK.\n", db_name(db));
|
||||||
|
return FDB_INIT_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
fdb_tsl_iter_by_time(db, from, to, query_count_cb, &arg);
|
||||||
|
|
||||||
|
return arg.count;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the TSL status.
|
||||||
|
*
|
||||||
|
* @param db database object
|
||||||
|
* @param tsl TSL object
|
||||||
|
* @param status status
|
||||||
|
*
|
||||||
|
* @return result
|
||||||
|
*/
|
||||||
|
fdb_err_t fdb_tsl_set_status(fdb_tsdb_t db, fdb_tsl_t tsl, fdb_tsl_status_t status)
|
||||||
|
{
|
||||||
|
fdb_err_t result = FDB_NO_ERR;
|
||||||
|
uint8_t status_table[TSL_STATUS_TABLE_SIZE];
|
||||||
|
|
||||||
|
/* write the status will by write granularity */
|
||||||
|
_FDB_WRITE_STATUS(db, tsl->addr.index, status_table, FDB_TSL_STATUS_NUM, status);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert the TSL object to blob object
|
||||||
|
*
|
||||||
|
* @param tsl TSL object
|
||||||
|
* @param blob blob object
|
||||||
|
*
|
||||||
|
* @return new blob object
|
||||||
|
*/
|
||||||
|
fdb_blob_t fdb_tsl_to_blob(fdb_tsl_t tsl, fdb_blob_t blob)
|
||||||
|
{
|
||||||
|
blob->saved.addr = tsl->addr.log;
|
||||||
|
blob->saved.meta_addr = tsl->addr.index;
|
||||||
|
blob->saved.len = tsl->log_len;
|
||||||
|
|
||||||
|
return blob;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool check_sec_hdr_cb(tsdb_sec_info_t sector, void *arg1, void *arg2)
|
||||||
|
{
|
||||||
|
struct check_sec_hdr_cb_args *arg = arg1;
|
||||||
|
fdb_tsdb_t db = arg->db;
|
||||||
|
|
||||||
|
if (!sector->check_ok) {
|
||||||
|
FDB_INFO("Warning: Sector (0x%08lX) header check failed.\n", sector->addr);
|
||||||
|
(arg->check_failed) = true;
|
||||||
|
return true;
|
||||||
|
} else if (sector->status == FDB_SECTOR_STORE_USING) {
|
||||||
|
if (db->cur_sec.addr == FDB_DATA_UNUSED) {
|
||||||
|
memcpy(&db->cur_sec, sector, sizeof(struct tsdb_sec_info));
|
||||||
|
} else {
|
||||||
|
FDB_INFO("Warning: Sector status is wrong, there are multiple sectors in use.\n");
|
||||||
|
(arg->check_failed) = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (sector->status == FDB_SECTOR_STORE_EMPTY) {
|
||||||
|
(arg->empty_num) += 1;
|
||||||
|
arg->empty_addr = sector->addr;
|
||||||
|
if ((arg->empty_num) == 1 && db->cur_sec.addr == FDB_DATA_UNUSED) {
|
||||||
|
memcpy(&db->cur_sec, sector, sizeof(struct tsdb_sec_info));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
static bool format_all_cb(tsdb_sec_info_t sector, void *arg1, void *arg2)
|
||||||
|
{
|
||||||
|
fdb_tsdb_t db = arg1;
|
||||||
|
|
||||||
|
format_sector(db, sector->addr);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tsl_format_all(fdb_tsdb_t db)
|
||||||
|
{
|
||||||
|
struct tsdb_sec_info sector;
|
||||||
|
|
||||||
|
sector.addr = 0;
|
||||||
|
sector_iterator(db, §or, FDB_SECTOR_STORE_UNUSED, db, NULL, format_all_cb, false);
|
||||||
|
db->oldest_addr = 0;
|
||||||
|
db->cur_sec.addr = 0;
|
||||||
|
/* read the current using sector info */
|
||||||
|
read_sector_info(db, db->cur_sec.addr, &db->cur_sec, false);
|
||||||
|
|
||||||
|
FDB_INFO("All sector format finished.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean all the data in the TSDB.
|
||||||
|
*
|
||||||
|
* @note It's DANGEROUS. This operation is not reversible.
|
||||||
|
*
|
||||||
|
* @param db database object
|
||||||
|
*/
|
||||||
|
void fdb_tsl_clean(fdb_tsdb_t db)
|
||||||
|
{
|
||||||
|
db_lock(db);
|
||||||
|
tsl_format_all(db);
|
||||||
|
db_unlock(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The time series database initialization.
|
||||||
|
*
|
||||||
|
* @param db database object
|
||||||
|
* @param name database name
|
||||||
|
* @param part_name partition name
|
||||||
|
* @param get_time get current time function
|
||||||
|
* @param max_len maximum length of each log
|
||||||
|
* @param user_data user data
|
||||||
|
*
|
||||||
|
* @return result
|
||||||
|
*/
|
||||||
|
fdb_err_t fdb_tsdb_init(fdb_tsdb_t db, const char *name, const char *part_name, fdb_get_time get_time, size_t max_len, void *user_data)
|
||||||
|
{
|
||||||
|
fdb_err_t result = FDB_NO_ERR;
|
||||||
|
struct tsdb_sec_info sector;
|
||||||
|
struct check_sec_hdr_cb_args check_sec_arg = { db, false, 0, 0};
|
||||||
|
|
||||||
|
FDB_ASSERT(get_time);
|
||||||
|
|
||||||
|
result = _fdb_init_ex((fdb_db_t)db, name, part_name, FDB_DB_TYPE_TS, user_data);
|
||||||
|
|
||||||
|
db->get_time = get_time;
|
||||||
|
db->max_len = max_len;
|
||||||
|
db->oldest_addr = FDB_DATA_UNUSED;
|
||||||
|
db->cur_sec.addr = FDB_DATA_UNUSED;
|
||||||
|
/* must align with sector size */
|
||||||
|
FDB_ASSERT(db_part_size(db) % db_sec_size(db) == 0);
|
||||||
|
/* must have more than or equal 2 sector */
|
||||||
|
FDB_ASSERT(db_part_size(db) / db_sec_size(db) >= 2);
|
||||||
|
/* must less than sector size */
|
||||||
|
FDB_ASSERT(max_len < db_sec_size(db));
|
||||||
|
|
||||||
|
/* check all sector header */
|
||||||
|
sector.addr = 0;
|
||||||
|
sector_iterator(db, §or, FDB_SECTOR_STORE_UNUSED, &check_sec_arg, NULL, check_sec_hdr_cb, true);
|
||||||
|
/* format all sector when check failed */
|
||||||
|
if (check_sec_arg.check_failed) {
|
||||||
|
tsl_format_all(db);
|
||||||
|
} else {
|
||||||
|
uint32_t latest_addr;
|
||||||
|
if (check_sec_arg.empty_num > 0) {
|
||||||
|
latest_addr = check_sec_arg.empty_addr;
|
||||||
|
} else {
|
||||||
|
latest_addr = db->cur_sec.addr;
|
||||||
|
}
|
||||||
|
/* db->cur_sec is the latest sector, and the next is the oldest sector */
|
||||||
|
if (latest_addr + db_sec_size(db) >= db_part_size(db)) {
|
||||||
|
/* db->cur_sec is the the bottom of the partition */
|
||||||
|
db->oldest_addr = 0;
|
||||||
|
} else {
|
||||||
|
db->oldest_addr = latest_addr + db_sec_size(db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FDB_DEBUG("tsdb (%s) oldest sectors is 0x%08lX, current using sector is 0x%08lX.\n", db_name(db), db->oldest_addr,
|
||||||
|
db->cur_sec.addr);
|
||||||
|
/* read the current using sector info */
|
||||||
|
read_sector_info(db, db->cur_sec.addr, &db->cur_sec, true);
|
||||||
|
|
||||||
|
_fdb_init_finish((fdb_db_t)db, result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* defined(FDB_USING_TSDB) */
|
|
@ -0,0 +1,270 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief utils
|
||||||
|
*
|
||||||
|
* Some utils for this library.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <flashdb.h>
|
||||||
|
#include <fdb_low_lvl.h>
|
||||||
|
|
||||||
|
#define FDB_LOG_TAG "[utils]"
|
||||||
|
|
||||||
|
static const uint32_t crc32_table[] =
|
||||||
|
{
|
||||||
|
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
|
||||||
|
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
||||||
|
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
|
||||||
|
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
||||||
|
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
|
||||||
|
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
|
||||||
|
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
|
||||||
|
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
||||||
|
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
|
||||||
|
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
||||||
|
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
|
||||||
|
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
||||||
|
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
|
||||||
|
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
|
||||||
|
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
|
||||||
|
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
||||||
|
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
|
||||||
|
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
|
||||||
|
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
|
||||||
|
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
||||||
|
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
|
||||||
|
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
|
||||||
|
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
|
||||||
|
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
||||||
|
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
|
||||||
|
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
|
||||||
|
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
|
||||||
|
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
||||||
|
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
|
||||||
|
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
||||||
|
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
|
||||||
|
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
||||||
|
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
|
||||||
|
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
|
||||||
|
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
|
||||||
|
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
||||||
|
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
|
||||||
|
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
|
||||||
|
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
|
||||||
|
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
||||||
|
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
|
||||||
|
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||||
|
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the CRC32 value of a memory buffer.
|
||||||
|
*
|
||||||
|
* @param crc accumulated CRC32 value, must be 0 on first call
|
||||||
|
* @param buf buffer to calculate CRC32 value for
|
||||||
|
* @param size bytes in buffer
|
||||||
|
*
|
||||||
|
* @return calculated CRC32 value
|
||||||
|
*/
|
||||||
|
uint32_t fdb_calc_crc32(uint32_t crc, const void *buf, size_t size)
|
||||||
|
{
|
||||||
|
const uint8_t *p;
|
||||||
|
|
||||||
|
p = (const uint8_t *)buf;
|
||||||
|
crc = crc ^ ~0U;
|
||||||
|
|
||||||
|
while (size--) {
|
||||||
|
crc = crc32_table[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
return crc ^ ~0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t _fdb_set_status(uint8_t status_table[], size_t status_num, size_t status_index)
|
||||||
|
{
|
||||||
|
size_t byte_index = ~0UL;
|
||||||
|
/*
|
||||||
|
* | write garn | status0 | status1 | status2 |
|
||||||
|
* ---------------------------------------------------------------------------------
|
||||||
|
* | 1bit | 0xFF | 0x7F | 0x3F |
|
||||||
|
* | 8bit | 0xFFFF | 0x00FF | 0x0000 |
|
||||||
|
* | 32bit | 0xFFFFFFFF FFFFFFFF | 0x00FFFFFF FFFFFFFF | 0x00FFFFFF 00FFFFFF |
|
||||||
|
*/
|
||||||
|
memset(status_table, 0xFF, FDB_STATUS_TABLE_SIZE(status_num));
|
||||||
|
if (status_index > 0) {
|
||||||
|
#if (FDB_WRITE_GRAN == 1)
|
||||||
|
byte_index = (status_index - 1) / 8;
|
||||||
|
status_table[byte_index] &= ~(0x80 >> ((status_index - 1) % 8));
|
||||||
|
#else
|
||||||
|
byte_index = (status_index - 1) * (FDB_WRITE_GRAN / 8);
|
||||||
|
status_table[byte_index] = 0x00;
|
||||||
|
#endif /* FDB_WRITE_GRAN == 1 */
|
||||||
|
}
|
||||||
|
|
||||||
|
return byte_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t _fdb_get_status(uint8_t status_table[], size_t status_num)
|
||||||
|
{
|
||||||
|
size_t i = 0, status_num_bak = --status_num;
|
||||||
|
|
||||||
|
while (status_num --) {
|
||||||
|
/* get the first 0 position from end address to start address */
|
||||||
|
#if (FDB_WRITE_GRAN == 1)
|
||||||
|
if ((status_table[status_num / 8] & (0x80 >> (status_num % 8))) == 0x00) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#else /* (FDB_WRITE_GRAN == 8) || (FDB_WRITE_GRAN == 32) || (FDB_WRITE_GRAN == 64) */
|
||||||
|
if (status_table[status_num * FDB_WRITE_GRAN / 8] == 0x00) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif /* FDB_WRITE_GRAN == 1 */
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return status_num_bak - i;
|
||||||
|
}
|
||||||
|
|
||||||
|
fdb_err_t _fdb_write_status(fdb_db_t db, uint32_t addr, uint8_t status_table[], size_t status_num, size_t status_index)
|
||||||
|
{
|
||||||
|
fdb_err_t result = FDB_NO_ERR;
|
||||||
|
size_t byte_index;
|
||||||
|
|
||||||
|
FDB_ASSERT(status_index < status_num);
|
||||||
|
FDB_ASSERT(status_table);
|
||||||
|
|
||||||
|
/* set the status first */
|
||||||
|
byte_index = _fdb_set_status(status_table, status_num, status_index);
|
||||||
|
|
||||||
|
/* the first status table value is all 1, so no need to write flash */
|
||||||
|
if (byte_index == ~0UL) {
|
||||||
|
return FDB_NO_ERR;
|
||||||
|
}
|
||||||
|
#if (FDB_WRITE_GRAN == 1)
|
||||||
|
result = _fdb_flash_write(db, addr + byte_index, (uint32_t *)&status_table[byte_index], 1);
|
||||||
|
#else /* (FDB_WRITE_GRAN == 8) || (FDB_WRITE_GRAN == 32) || (FDB_WRITE_GRAN == 64) */
|
||||||
|
/* write the status by write granularity
|
||||||
|
* some flash (like stm32 onchip) NOT supported repeated write before erase */
|
||||||
|
result = _fdb_flash_write(db, addr + byte_index, (uint32_t *) &status_table[byte_index], FDB_WRITE_GRAN / 8);
|
||||||
|
#endif /* FDB_WRITE_GRAN == 1 */
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t _fdb_read_status(fdb_db_t db, uint32_t addr, uint8_t status_table[], size_t total_num)
|
||||||
|
{
|
||||||
|
FDB_ASSERT(status_table);
|
||||||
|
|
||||||
|
_fdb_flash_read(db, addr, (uint32_t *) status_table, FDB_STATUS_TABLE_SIZE(total_num));
|
||||||
|
|
||||||
|
return _fdb_get_status(status_table, total_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* find the continue 0xFF flash address to end address
|
||||||
|
*/
|
||||||
|
uint32_t _fdb_continue_ff_addr(fdb_db_t db, uint32_t start, uint32_t end)
|
||||||
|
{
|
||||||
|
uint8_t buf[32], last_data = 0x00;
|
||||||
|
size_t i, addr = start, read_size;
|
||||||
|
|
||||||
|
for (; start < end; start += sizeof(buf)) {
|
||||||
|
if (start + sizeof(buf) < end) {
|
||||||
|
read_size = sizeof(buf);
|
||||||
|
} else {
|
||||||
|
read_size = end - start;
|
||||||
|
}
|
||||||
|
_fdb_flash_read(db, start, (uint32_t *) buf, read_size);
|
||||||
|
for (i = 0; i < read_size; i++) {
|
||||||
|
if (last_data != 0xFF && buf[i] == 0xFF) {
|
||||||
|
addr = start + i;
|
||||||
|
}
|
||||||
|
last_data = buf[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last_data == 0xFF) {
|
||||||
|
return FDB_WG_ALIGN(addr);
|
||||||
|
} else {
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a blob object.
|
||||||
|
*
|
||||||
|
* @param blob blob object
|
||||||
|
* @param value_buf value buffer
|
||||||
|
* @param buf_len buffer length
|
||||||
|
*
|
||||||
|
* @return new blob object
|
||||||
|
*/
|
||||||
|
fdb_blob_t fdb_blob_make(fdb_blob_t blob, const void *value_buf, size_t buf_len)
|
||||||
|
{
|
||||||
|
blob->buf = (void *)value_buf;
|
||||||
|
blob->size = buf_len;
|
||||||
|
|
||||||
|
return blob;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the blob object in database.
|
||||||
|
*
|
||||||
|
* @param db database object
|
||||||
|
* @param blob blob object
|
||||||
|
*
|
||||||
|
* @return read length
|
||||||
|
*/
|
||||||
|
size_t fdb_blob_read(fdb_db_t db, fdb_blob_t blob)
|
||||||
|
{
|
||||||
|
size_t read_len = blob->size;
|
||||||
|
|
||||||
|
if (read_len > blob->saved.len) {
|
||||||
|
read_len = blob->saved.len;
|
||||||
|
}
|
||||||
|
_fdb_flash_read(db, blob->saved.addr, blob->buf, read_len);
|
||||||
|
|
||||||
|
return read_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
fdb_err_t _fdb_flash_read(fdb_db_t db, uint32_t addr, void *buf, size_t size)
|
||||||
|
{
|
||||||
|
fdb_err_t result = FDB_NO_ERR;
|
||||||
|
|
||||||
|
fal_partition_read(db->part, addr, (uint8_t *)buf, size);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
fdb_err_t _fdb_flash_erase(fdb_db_t db, uint32_t addr, size_t size)
|
||||||
|
{
|
||||||
|
fdb_err_t result = FDB_NO_ERR;
|
||||||
|
|
||||||
|
if (fal_partition_erase(db->part, addr, size) < 0)
|
||||||
|
{
|
||||||
|
result = FDB_ERASE_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
fdb_err_t _fdb_flash_write(fdb_db_t db, uint32_t addr, const void *buf, size_t size)
|
||||||
|
{
|
||||||
|
fdb_err_t result = FDB_NO_ERR;
|
||||||
|
|
||||||
|
if (fal_partition_write(db->part, addr, (uint8_t *)buf, size) < 0)
|
||||||
|
{
|
||||||
|
result = FDB_WRITE_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief blob KV samples.
|
||||||
|
*
|
||||||
|
* Key-Value Database blob type KV feature samples
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <flashdb.h>
|
||||||
|
|
||||||
|
static uint32_t boot_count = 0;
|
||||||
|
static time_t boot_time[10] = {0, 1, 2, 3};
|
||||||
|
/* default KV nodes */
|
||||||
|
static struct fdb_default_kv_node default_kv_table[] = {
|
||||||
|
{"username", "armink", 0}, /* string KV */
|
||||||
|
{"password", "123456", 0}, /* string KV */
|
||||||
|
{"boot_count", &boot_count, sizeof(boot_count)}, /* int type KV */
|
||||||
|
{"boot_time", &boot_time, sizeof(boot_time)}, /* int array type KV */
|
||||||
|
};
|
||||||
|
/* KVDB object */
|
||||||
|
static struct fdb_kvdb kvdb = { 0 };
|
||||||
|
|
||||||
|
static void lock(fdb_db_t db)
|
||||||
|
{
|
||||||
|
/* YOUR CODE HERE */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unlock(fdb_db_t db)
|
||||||
|
{
|
||||||
|
/* YOUR CODE HERE */
|
||||||
|
}
|
||||||
|
|
||||||
|
void kvdb_type_blob_sample(void)
|
||||||
|
{
|
||||||
|
fdb_err_t result;
|
||||||
|
struct fdb_blob blob;
|
||||||
|
|
||||||
|
{ /* database initialization */
|
||||||
|
struct fdb_default_kv default_kv;
|
||||||
|
|
||||||
|
default_kv.kvs = default_kv_table;
|
||||||
|
default_kv.num = sizeof(default_kv_table) / sizeof(default_kv_table[0]);
|
||||||
|
/* set the lock and unlock function if you want */
|
||||||
|
fdb_lock_set((fdb_db_t)&kvdb, lock, unlock);
|
||||||
|
/* Key-Value database initialization
|
||||||
|
*
|
||||||
|
* &kvdb: database object
|
||||||
|
* "env": database name
|
||||||
|
* "fdb_kvdb1": The flash partition name base on FAL. Please make sure it's in FAL partition table.
|
||||||
|
* Please change to YOUR partition name.
|
||||||
|
* &default_kv: The default KV nodes. It will auto add to KVDB when first initialize successfully.
|
||||||
|
* NULL: The user data if you need, now is empty.
|
||||||
|
*/
|
||||||
|
result = fdb_kvdb_init(&kvdb, "env", "fdb_kvdb1", &default_kv, NULL);
|
||||||
|
|
||||||
|
if (result != FDB_NO_ERR) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{ /* CREATE new Key-Value */
|
||||||
|
int temp_data = 36;
|
||||||
|
|
||||||
|
/* It will create new KV node when "temp" KV not in database.
|
||||||
|
* fdb_blob_make: It's a blob make function, and it will return the blob when make finish.
|
||||||
|
*/
|
||||||
|
fdb_kv_set_blob(&kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
{ /* GET the KV value */
|
||||||
|
int temp_data = 0;
|
||||||
|
|
||||||
|
/* get the "temp" KV value */
|
||||||
|
fdb_kv_get_blob(&kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data)));
|
||||||
|
/* the blob.saved.len is more than 0 when get the value successful */
|
||||||
|
if (blob.saved.len > 0) {
|
||||||
|
FDB_PRINT("temp_data: %d\n", temp_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{ /* CHANGE the KV value */
|
||||||
|
int temp_data = 38;
|
||||||
|
|
||||||
|
/* change the "temp" KV's value to 38.1 */
|
||||||
|
fdb_kv_set_blob(&kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
{ /* DELETE the KV by name */
|
||||||
|
fdb_kv_del(&kvdb, "temp");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief string KV samples.
|
||||||
|
*
|
||||||
|
* Key-Value Database string type KV feature samples source file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <flashdb.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static uint32_t boot_count = 0;
|
||||||
|
static time_t boot_time[10] = {0, 1, 2, 3};
|
||||||
|
/* default KV nodes */
|
||||||
|
static struct fdb_default_kv_node default_kv_table[] = {
|
||||||
|
{"username", "armink", 0}, /* string KV */
|
||||||
|
{"password", "123456", 0}, /* string KV */
|
||||||
|
{"boot_count", &boot_count, sizeof(boot_count)}, /* int type KV */
|
||||||
|
{"boot_time", &boot_time, sizeof(boot_time)}, /* int array type KV */
|
||||||
|
};
|
||||||
|
/* KVDB object */
|
||||||
|
static struct fdb_kvdb kvdb = { 0 };
|
||||||
|
|
||||||
|
static void lock(fdb_db_t db)
|
||||||
|
{
|
||||||
|
/* YOUR CODE HERE */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unlock(fdb_db_t db)
|
||||||
|
{
|
||||||
|
/* YOUR CODE HERE */
|
||||||
|
}
|
||||||
|
|
||||||
|
void kvdb_type_string_sample(void)
|
||||||
|
{
|
||||||
|
fdb_err_t result;
|
||||||
|
|
||||||
|
{ /* database initialization */
|
||||||
|
struct fdb_default_kv default_kv;
|
||||||
|
|
||||||
|
default_kv.kvs = default_kv_table;
|
||||||
|
default_kv.num = sizeof(default_kv_table) / sizeof(default_kv_table[0]);
|
||||||
|
/* set the lock and unlock function if you want */
|
||||||
|
fdb_lock_set((fdb_db_t)&kvdb, lock, unlock);
|
||||||
|
/* Key-Value database initialization
|
||||||
|
*
|
||||||
|
* &kvdb: database object
|
||||||
|
* "env": database name
|
||||||
|
* "fdb_kvdb1": The flash partition name base on FAL. Please make sure it's in FAL partition table.
|
||||||
|
* Please change to YOUR partition name.
|
||||||
|
* &default_kv: The default KV nodes. It will auto add to KVDB when first initialize successfully.
|
||||||
|
* NULL: The user data if you need, now is empty.
|
||||||
|
*/
|
||||||
|
result = fdb_kvdb_init(&kvdb, "env", "fdb_kvdb1", &default_kv, NULL);
|
||||||
|
|
||||||
|
if (result != FDB_NO_ERR) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{ /* CREATE new Key-Value */
|
||||||
|
char temp_data[10] = "36";
|
||||||
|
|
||||||
|
/* It will create new KV node when "temp" KV not in database. */
|
||||||
|
fdb_kv_set(&kvdb, "temp", temp_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ /* GET the KV value */
|
||||||
|
char *return_value, temp_data[10] = { 0 };
|
||||||
|
|
||||||
|
/* Get the "temp" KV value.
|
||||||
|
* NOTE: The return value saved in fdb_kv_get's buffer. Please copy away as soon as possible.
|
||||||
|
*/
|
||||||
|
return_value = fdb_kv_get(&kvdb, "temp");
|
||||||
|
/* the return value is NULL when get the value failed */
|
||||||
|
if (return_value != NULL) {
|
||||||
|
strncpy(temp_data, return_value, sizeof(temp_data));
|
||||||
|
FDB_PRINT("temp_data: %s\n", temp_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{ /* CHANGE the KV value */
|
||||||
|
char temp_data[10] = "38";
|
||||||
|
|
||||||
|
/* change the "temp" KV's value to "38.1" */
|
||||||
|
fdb_kv_set(&kvdb, "temp", temp_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ /* DELETE the KV by name */
|
||||||
|
fdb_kv_del(&kvdb, "temp");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,130 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief TSDB samples.
|
||||||
|
*
|
||||||
|
* Time series log (like TSDB) feature samples source file.
|
||||||
|
*
|
||||||
|
* TSL is time series log, the TSDB saved many TSLs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <flashdb.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
struct env_status {
|
||||||
|
int temp;
|
||||||
|
int humi;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* TSDB object */
|
||||||
|
static struct fdb_tsdb tsdb = { 0 };
|
||||||
|
|
||||||
|
static bool query_cb(fdb_tsl_t tsl, void *arg);
|
||||||
|
static bool set_status_cb(fdb_tsl_t tsl, void *arg);
|
||||||
|
|
||||||
|
static void lock(fdb_db_t db)
|
||||||
|
{
|
||||||
|
/* YOUR CODE HERE */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unlock(fdb_db_t db)
|
||||||
|
{
|
||||||
|
/* YOUR CODE HERE */
|
||||||
|
}
|
||||||
|
static fdb_time_t get_time(void)
|
||||||
|
{
|
||||||
|
return time(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tsdb_sample(void)
|
||||||
|
{
|
||||||
|
fdb_err_t result;
|
||||||
|
struct fdb_blob blob;
|
||||||
|
|
||||||
|
{ /* database initialization */
|
||||||
|
/* set the lock and unlock function if you want */
|
||||||
|
fdb_lock_set((fdb_db_t)&tsdb, lock, unlock);
|
||||||
|
/* Time series database initialization
|
||||||
|
*
|
||||||
|
* &tsdb: database object
|
||||||
|
* "log": database name
|
||||||
|
* "fdb_tsdb1": The flash partition name base on FAL. Please make sure it's in FAL partition table.
|
||||||
|
* Please change to YOUR partition name.
|
||||||
|
* get_time: The get current timestamp function.
|
||||||
|
* 128: maximum length of each log
|
||||||
|
* NULL: The user data if you need, now is empty.
|
||||||
|
*/
|
||||||
|
result = fdb_tsdb_init(&tsdb, "log", "fdb_tsdb1", get_time, 128, NULL);
|
||||||
|
|
||||||
|
if (result != FDB_NO_ERR) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{ /* APPEND new TSL (time series log) */
|
||||||
|
struct env_status status;
|
||||||
|
|
||||||
|
/* append new log to TSDB */
|
||||||
|
status.temp = 36;
|
||||||
|
status.humi = 85;
|
||||||
|
fdb_tsl_append(&tsdb, fdb_blob_make(&blob, &status, sizeof(status)));
|
||||||
|
|
||||||
|
status.temp = 38;
|
||||||
|
status.humi = 90;
|
||||||
|
fdb_tsl_append(&tsdb, fdb_blob_make(&blob, &status, sizeof(status)));
|
||||||
|
}
|
||||||
|
|
||||||
|
{ /* QUERY the TSDB */
|
||||||
|
/* query all TSL in TSDB by iterator */
|
||||||
|
fdb_tsl_iter(&tsdb, query_cb, &tsdb);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ /* QUERY the TSDB by time */
|
||||||
|
/* prepare query time (from 1970-01-01 00:00:00 to 2020-05-05 00:00:00) */
|
||||||
|
struct tm tm_from = { .tm_year = 1970 - 1900, .tm_mon = 0, .tm_mday = 1, .tm_hour = 0, .tm_min = 0, .tm_sec = 0 };
|
||||||
|
struct tm tm_to = { .tm_year = 2020 - 1900, .tm_mon = 4, .tm_mday = 5, .tm_hour = 0, .tm_min = 0, .tm_sec = 0 };
|
||||||
|
time_t from_time = mktime(&tm_from), to_time = mktime(&tm_to);
|
||||||
|
size_t count;
|
||||||
|
/* query all TSL in TSDB by time */
|
||||||
|
fdb_tsl_iter_by_time(&tsdb, from_time, to_time, query_cb, &tsdb);
|
||||||
|
/* query all FDB_TSL_WRITE status TSL's count in TSDB by time */
|
||||||
|
count = fdb_tsl_query_count(&tsdb, from_time, to_time, FDB_TSL_WRITE);
|
||||||
|
FDB_PRINT("query count: %lu\n", count);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ /* SET the TSL status */
|
||||||
|
/* Change the TSL status by iterator or time iterator
|
||||||
|
* set_status_cb: the change operation will in this callback
|
||||||
|
*
|
||||||
|
* NOTE: The actions to modify the state must be in order.
|
||||||
|
* FDB_TSL_WRITE -> FDB_TSL_USER_STATUS1 -> FDB_TSL_DELETED -> FDB_TSL_USER_STATUS2
|
||||||
|
*/
|
||||||
|
fdb_tsl_iter(&tsdb, set_status_cb, &tsdb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool query_cb(fdb_tsl_t tsl, void *arg)
|
||||||
|
{
|
||||||
|
struct fdb_blob blob;
|
||||||
|
struct env_status status;
|
||||||
|
fdb_tsdb_t db = arg;
|
||||||
|
|
||||||
|
fdb_blob_read((fdb_db_t) db, fdb_tsl_to_blob(tsl, fdb_blob_make(&blob, &status, sizeof(status))));
|
||||||
|
FDB_PRINT("time: %d, temp: %d, humi: %d\n", tsl->time, status.temp, status.humi);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool set_status_cb(fdb_tsl_t tsl, void *arg)
|
||||||
|
{
|
||||||
|
fdb_tsdb_t db = arg;
|
||||||
|
|
||||||
|
fdb_tsl_set_status(db, tsl, FDB_TSL_USER_STATUS1);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
|
@ -0,0 +1,187 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief KVDB testcases.
|
||||||
|
*
|
||||||
|
* This testcases is be used in RT-Thread Utest framework.
|
||||||
|
* If you want run it, please add it to RT-Thread project.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "utest.h"
|
||||||
|
#include <flashdb.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define TEST_TS_PART_NAME "fdb_kvdb1"
|
||||||
|
#define TEST_KV_BLOB_NAME "kv_blob_test"
|
||||||
|
#define TEST_KV_NAME "kv_test"
|
||||||
|
|
||||||
|
#if defined(RT_USING_UTEST) && defined(FDB_USING_KVDB)
|
||||||
|
|
||||||
|
static struct fdb_default_kv_node default_kv_set[] = {
|
||||||
|
{"iap_need_copy_app", "0"},
|
||||||
|
{"iap_need_crc32_check", "0"},
|
||||||
|
{"iap_copy_app_size", "0"},
|
||||||
|
{"stop_in_bootloader", "0"},
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct fdb_kvdb test_kvdb;
|
||||||
|
|
||||||
|
static void test_easyflash_init(void)
|
||||||
|
{
|
||||||
|
struct fdb_default_kv default_kv;
|
||||||
|
|
||||||
|
default_kv.kvs = default_kv_set;
|
||||||
|
default_kv.num = sizeof(default_kv_set) / sizeof(default_kv_set[0]);
|
||||||
|
uassert_true(fdb_kvdb_init(&test_kvdb, "test_kv", "fdb_kvdb1", &default_kv, NULL) == FDB_NO_ERR);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_fdb_create_kv_blob(void)
|
||||||
|
{
|
||||||
|
fdb_err_t result = FDB_NO_ERR;
|
||||||
|
rt_tick_t tick = rt_tick_get(), read_tick;
|
||||||
|
size_t read_len;
|
||||||
|
struct fdb_kv kv_obj;
|
||||||
|
struct fdb_blob blob;
|
||||||
|
uint8_t value_buf[sizeof(tick)];
|
||||||
|
|
||||||
|
result = fdb_kv_set_blob(&test_kvdb, TEST_KV_BLOB_NAME, fdb_blob_make(&blob, &tick, sizeof(tick)));
|
||||||
|
uassert_true(result == FDB_NO_ERR);
|
||||||
|
|
||||||
|
read_len = fdb_kv_get_blob(&test_kvdb, TEST_KV_BLOB_NAME, fdb_blob_make(&blob, &read_tick, sizeof(read_tick)));
|
||||||
|
uassert_int_equal(blob.saved.len, sizeof(read_tick));
|
||||||
|
uassert_int_equal(blob.saved.len, read_len);
|
||||||
|
uassert_int_equal(tick, read_tick);
|
||||||
|
|
||||||
|
uassert_true(fdb_kv_get_obj(&test_kvdb, TEST_KV_BLOB_NAME, &kv_obj) != NULL);
|
||||||
|
|
||||||
|
fdb_blob_make(&blob, value_buf, sizeof(value_buf));
|
||||||
|
read_len = fdb_blob_read((fdb_db_t)&test_kvdb, fdb_kv_to_blob(&kv_obj, &blob));
|
||||||
|
uassert_int_equal(read_len, sizeof(value_buf));
|
||||||
|
uassert_buf_equal(&tick, value_buf, sizeof(value_buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_fdb_change_kv_blob(void)
|
||||||
|
{
|
||||||
|
fdb_err_t result = FDB_NO_ERR;
|
||||||
|
rt_tick_t tick = rt_tick_get(), read_tick;
|
||||||
|
size_t read_len;
|
||||||
|
struct fdb_blob blob_obj, *blob = &blob_obj;
|
||||||
|
|
||||||
|
read_len = fdb_kv_get_blob(&test_kvdb, TEST_KV_BLOB_NAME, fdb_blob_make(&blob_obj, &read_tick, sizeof(read_tick)));
|
||||||
|
uassert_int_equal(blob->saved.len, sizeof(read_tick));
|
||||||
|
uassert_int_equal(blob->saved.len, read_len);
|
||||||
|
uassert_int_not_equal(tick, read_tick);
|
||||||
|
|
||||||
|
result = fdb_kv_set_blob(&test_kvdb, TEST_KV_BLOB_NAME, fdb_blob_make(&blob_obj, &tick, sizeof(tick)));
|
||||||
|
uassert_true(result == FDB_NO_ERR);
|
||||||
|
|
||||||
|
read_len = fdb_kv_get_blob(&test_kvdb, TEST_KV_BLOB_NAME, fdb_blob_make(&blob_obj, &read_tick, sizeof(read_tick)));
|
||||||
|
uassert_int_equal(blob->saved.len, sizeof(read_tick));
|
||||||
|
uassert_int_equal(blob->saved.len, read_len);
|
||||||
|
uassert_int_equal(tick, read_tick);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_fdb_del_kv_blob(void)
|
||||||
|
{
|
||||||
|
fdb_err_t result = FDB_NO_ERR;
|
||||||
|
rt_tick_t tick = rt_tick_get(), read_tick;
|
||||||
|
size_t read_len;
|
||||||
|
struct fdb_blob blob;
|
||||||
|
|
||||||
|
read_len = fdb_kv_get_blob(&test_kvdb, TEST_KV_BLOB_NAME, fdb_blob_make(&blob, &read_tick, sizeof(read_tick)));
|
||||||
|
uassert_int_equal(blob.saved.len, sizeof(read_tick));
|
||||||
|
uassert_int_equal(blob.saved.len, read_len);
|
||||||
|
uassert_int_not_equal(tick, read_tick);
|
||||||
|
|
||||||
|
result = fdb_kv_set_blob(&test_kvdb, TEST_KV_BLOB_NAME, fdb_blob_make(&blob, NULL, 0));
|
||||||
|
uassert_true(result == FDB_NO_ERR);
|
||||||
|
|
||||||
|
read_len = fdb_kv_get_blob(&test_kvdb, TEST_KV_BLOB_NAME, fdb_blob_make(&blob, &read_tick, sizeof(read_tick)));
|
||||||
|
uassert_int_equal(blob.saved.len, 0);
|
||||||
|
uassert_int_equal(read_len, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_fdb_create_kv(void)
|
||||||
|
{
|
||||||
|
fdb_err_t result = FDB_NO_ERR;
|
||||||
|
rt_tick_t tick = rt_tick_get(), read_tick;
|
||||||
|
char value_buf[14], *read_value;
|
||||||
|
|
||||||
|
snprintf(value_buf, sizeof(value_buf), "%d", tick);
|
||||||
|
result = fdb_kv_set(&test_kvdb, TEST_KV_NAME, value_buf);
|
||||||
|
uassert_true(result == FDB_NO_ERR);
|
||||||
|
|
||||||
|
read_value = fdb_kv_get(&test_kvdb, TEST_KV_NAME);
|
||||||
|
uassert_not_null(read_value);
|
||||||
|
read_tick = atoi(read_value);
|
||||||
|
uassert_int_equal(tick, read_tick);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_fdb_change_kv(void)
|
||||||
|
{
|
||||||
|
fdb_err_t result = FDB_NO_ERR;
|
||||||
|
rt_tick_t tick = rt_tick_get(), read_tick;
|
||||||
|
char value_buf[14], *read_value;
|
||||||
|
|
||||||
|
read_value = fdb_kv_get(&test_kvdb, TEST_KV_NAME);
|
||||||
|
uassert_not_null(read_value);
|
||||||
|
read_tick = atoi(read_value);
|
||||||
|
uassert_int_not_equal(tick, read_tick);
|
||||||
|
|
||||||
|
snprintf(value_buf, sizeof(value_buf), "%d", tick);
|
||||||
|
result = fdb_kv_set(&test_kvdb, TEST_KV_NAME, value_buf);
|
||||||
|
uassert_true(result == FDB_NO_ERR);
|
||||||
|
|
||||||
|
read_value = fdb_kv_get(&test_kvdb, TEST_KV_NAME);
|
||||||
|
uassert_not_null(read_value);
|
||||||
|
read_tick = atoi(read_value);
|
||||||
|
uassert_int_equal(tick, read_tick);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_fdb_del_kv(void)
|
||||||
|
{
|
||||||
|
fdb_err_t result = FDB_NO_ERR;
|
||||||
|
rt_tick_t tick = rt_tick_get(), read_tick;
|
||||||
|
char *read_value;
|
||||||
|
|
||||||
|
read_value = fdb_kv_get(&test_kvdb, TEST_KV_NAME);
|
||||||
|
uassert_not_null(read_value);
|
||||||
|
read_tick = atoi(read_value);
|
||||||
|
uassert_int_not_equal(tick, read_tick);
|
||||||
|
|
||||||
|
result = fdb_kv_del(&test_kvdb, TEST_KV_NAME);
|
||||||
|
uassert_true(result == FDB_NO_ERR);
|
||||||
|
|
||||||
|
read_value = fdb_kv_get(&test_kvdb, TEST_KV_NAME);
|
||||||
|
uassert_null(read_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static rt_err_t utest_tc_init(void)
|
||||||
|
{
|
||||||
|
return RT_EOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static rt_err_t utest_tc_cleanup(void)
|
||||||
|
{
|
||||||
|
return RT_EOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testcase(void)
|
||||||
|
{
|
||||||
|
UTEST_UNIT_RUN(test_easyflash_init);
|
||||||
|
UTEST_UNIT_RUN(test_fdb_create_kv_blob);
|
||||||
|
UTEST_UNIT_RUN(test_fdb_change_kv_blob);
|
||||||
|
UTEST_UNIT_RUN(test_fdb_del_kv_blob);
|
||||||
|
UTEST_UNIT_RUN(test_fdb_create_kv);
|
||||||
|
UTEST_UNIT_RUN(test_fdb_change_kv);
|
||||||
|
UTEST_UNIT_RUN(test_fdb_del_kv);
|
||||||
|
}
|
||||||
|
UTEST_TC_EXPORT(testcase, "packages.tools.flashdb.kvdb", utest_tc_init, utest_tc_cleanup, 20);
|
||||||
|
|
||||||
|
#endif /* defined(RT_USING_UTEST) && defined(FDB_USING_TSDB) */
|
|
@ -0,0 +1,156 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief TSDB testcases.
|
||||||
|
*
|
||||||
|
* This testcases is be used in RT-Thread Utest framework.
|
||||||
|
* If you want run it, please add it to RT-Thread project.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "utest.h"
|
||||||
|
#include <flashdb.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#if defined(RT_USING_UTEST) && defined(FDB_USING_TSDB)
|
||||||
|
|
||||||
|
#define TEST_TS_PART_NAME "fdb_tsdb1"
|
||||||
|
#define TEST_TS_COUNT 256
|
||||||
|
#define TEST_TS_USER_STATUS1_COUNT (TEST_TS_COUNT/2)
|
||||||
|
#define TEST_TS_DELETED_COUNT (TEST_TS_COUNT - TEST_TS_USER_STATUS1_COUNT)
|
||||||
|
|
||||||
|
static char log[10];
|
||||||
|
|
||||||
|
static struct fdb_tsdb test_tsdb;
|
||||||
|
static int cur_times = 0;
|
||||||
|
|
||||||
|
static fdb_time_t get_time(void)
|
||||||
|
{
|
||||||
|
return cur_times ++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_fdb_tsdb_init_ex(void)
|
||||||
|
{
|
||||||
|
uassert_true(fdb_tsdb_init(&test_tsdb, "test_ts", TEST_TS_PART_NAME, get_time, 128, NULL) == FDB_NO_ERR);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_fdb_tsl_append(void)
|
||||||
|
{
|
||||||
|
struct fdb_blob blob;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < TEST_TS_COUNT; ++i) {
|
||||||
|
rt_snprintf(log, sizeof(log), "%d", i);
|
||||||
|
uassert_true(fdb_tsl_append(&test_tsdb, fdb_blob_make(&blob, log, rt_strnlen(log, sizeof(log)))) == FDB_NO_ERR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool test_fdb_tsl_iter_cb(fdb_tsl_t tsl, void *arg)
|
||||||
|
{
|
||||||
|
struct fdb_blob blob;
|
||||||
|
char data[sizeof(log)];
|
||||||
|
size_t read_len;
|
||||||
|
|
||||||
|
fdb_blob_make(&blob, data, tsl->log_len);
|
||||||
|
read_len = fdb_blob_read((fdb_db_t) &test_tsdb, fdb_tsl_to_blob(tsl, &blob));
|
||||||
|
|
||||||
|
data[read_len] = '\0';
|
||||||
|
uassert_true(tsl->time == atoi(data));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_fdb_tsl_iter(void)
|
||||||
|
{
|
||||||
|
fdb_tsl_iter(&test_tsdb, test_fdb_tsl_iter_cb, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_fdb_tsl_iter_by_time(void)
|
||||||
|
{
|
||||||
|
fdb_time_t from = 0, to = TEST_TS_COUNT -1;
|
||||||
|
|
||||||
|
fdb_tsl_iter_by_time(&test_tsdb, from, to, test_fdb_tsl_iter_cb, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_fdb_tsl_query_count(void)
|
||||||
|
{
|
||||||
|
fdb_time_t from = 0, to = TEST_TS_COUNT -1;
|
||||||
|
|
||||||
|
uassert_true(fdb_tsl_query_count(&test_tsdb, from, to, FDB_TSL_WRITE) == TEST_TS_COUNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool est_fdb_tsl_set_status_cb(fdb_tsl_t tsl, void *arg)
|
||||||
|
{
|
||||||
|
fdb_tsdb_t db = arg;
|
||||||
|
|
||||||
|
if (tsl->time >= 0 && tsl->time < TEST_TS_USER_STATUS1_COUNT) {
|
||||||
|
uassert_true(fdb_tsl_set_status(db, tsl, FDB_TSL_USER_STATUS1) == FDB_NO_ERR);
|
||||||
|
} else {
|
||||||
|
uassert_true(fdb_tsl_set_status(db, tsl, FDB_TSL_DELETED) == FDB_NO_ERR);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_fdb_tsl_set_status(void)
|
||||||
|
{
|
||||||
|
fdb_time_t from = 0, to = TEST_TS_COUNT -1;
|
||||||
|
|
||||||
|
fdb_tsl_iter_by_time(&test_tsdb, from, to, est_fdb_tsl_set_status_cb, &test_tsdb);
|
||||||
|
|
||||||
|
uassert_true(fdb_tsl_query_count(&test_tsdb, from, to, FDB_TSL_USER_STATUS1) == TEST_TS_USER_STATUS1_COUNT);
|
||||||
|
uassert_true(fdb_tsl_query_count(&test_tsdb, from, to, FDB_TSL_DELETED) == TEST_TS_DELETED_COUNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool test_fdb_tsl_clean_cb(fdb_tsl_t tsl, void *arg)
|
||||||
|
{
|
||||||
|
size_t *count = arg;
|
||||||
|
|
||||||
|
(*count) ++;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_fdb_tsl_clean(void)
|
||||||
|
{
|
||||||
|
size_t count = 0;
|
||||||
|
|
||||||
|
fdb_tsl_clean(&test_tsdb);
|
||||||
|
|
||||||
|
fdb_tsl_iter(&test_tsdb, test_fdb_tsl_clean_cb, &count);
|
||||||
|
|
||||||
|
uassert_true(count == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static rt_err_t utest_tc_init(void)
|
||||||
|
{
|
||||||
|
cur_times = 0;
|
||||||
|
rt_memset(&test_tsdb, 0, sizeof(struct fdb_tsdb));
|
||||||
|
|
||||||
|
return RT_EOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static rt_err_t utest_tc_cleanup(void)
|
||||||
|
{
|
||||||
|
return RT_EOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testcase(void)
|
||||||
|
{
|
||||||
|
UTEST_UNIT_RUN(test_fdb_tsdb_init_ex);
|
||||||
|
UTEST_UNIT_RUN(test_fdb_tsl_clean);
|
||||||
|
UTEST_UNIT_RUN(test_fdb_tsl_append);
|
||||||
|
UTEST_UNIT_RUN(test_fdb_tsl_iter);
|
||||||
|
UTEST_UNIT_RUN(test_fdb_tsl_iter_by_time);
|
||||||
|
UTEST_UNIT_RUN(test_fdb_tsl_query_count);
|
||||||
|
UTEST_UNIT_RUN(test_fdb_tsl_set_status);
|
||||||
|
UTEST_UNIT_RUN(test_fdb_tsl_clean);
|
||||||
|
}
|
||||||
|
UTEST_TC_EXPORT(testcase, "packages.tools.flashdb.tsdb", utest_tc_init, utest_tc_cleanup, 20);
|
||||||
|
|
||||||
|
#endif /* defined(RT_USING_UTEST) && defined(FDB_USING_TSDB) */
|
Loading…
Reference in New Issue