first commit for chrg
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
|
||||
# Kconfig file for package fal
|
||||
menuconfig RT_USING_FAL
|
||||
bool "FAL: flash abstraction layer"
|
||||
default n
|
||||
|
||||
if RT_USING_FAL
|
||||
config FAL_USING_DEBUG
|
||||
bool "Enable debug log output"
|
||||
default y if RT_USING_DEBUG
|
||||
default n
|
||||
|
||||
config FAL_PART_HAS_TABLE_CFG
|
||||
bool "FAL partition table config has defined on 'fal_cfg.h'"
|
||||
default y
|
||||
help
|
||||
If defined partition table on 'fal_cfg.h' please enable this option.
|
||||
When this option is disable, it will auto find and load the partition table
|
||||
on a specified location in flash partition.
|
||||
|
||||
if !FAL_PART_HAS_TABLE_CFG
|
||||
|
||||
config FAL_PART_TABLE_FLASH_DEV_NAME
|
||||
string "The flash device which saving partition table"
|
||||
default "onchip"
|
||||
help
|
||||
It will auto find the partition table on this flash device.
|
||||
|
||||
config FAL_PART_TABLE_END_OFFSET
|
||||
int "The patition table end address relative to flash device offset."
|
||||
default 65536
|
||||
help
|
||||
The auto find and load the partition table process is forward from this
|
||||
offset address on flash.
|
||||
|
||||
endif
|
||||
|
||||
config FAL_USING_SFUD_PORT
|
||||
bool "FAL uses SFUD drivers"
|
||||
default n
|
||||
help
|
||||
The fal_flash_sfud_port.c in the samples\porting directory will be used.
|
||||
|
||||
if FAL_USING_SFUD_PORT
|
||||
config FAL_USING_NOR_FLASH_DEV_NAME
|
||||
string "The name of the device used by FAL"
|
||||
default "norflash0"
|
||||
endif
|
||||
|
||||
endif
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
from building import *
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
src = Glob('src/*.c')
|
||||
CPPPATH = [cwd + '/inc']
|
||||
|
||||
if GetDepend(['FAL_USING_SFUD_PORT']):
|
||||
src += Glob('samples/porting/fal_flash_sfud_port.c')
|
||||
|
||||
group = DefineGroup('Fal', src, depend = ['RT_USING_FAL'], CPPPATH = CPPPATH)
|
||||
|
||||
Return('group')
|
||||
@@ -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(rt_size_t *len)
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
| :----- | :----------------------- |
|
||||
| len | 分区表的长度 |
|
||||
| return | 分区表 |
|
||||
|
||||
## 临时设置分区表
|
||||
|
||||
FAL 初始化时会自动装载默认分区表。使用该设置将临时修改分区表,重启后会 **丢失** 该设置
|
||||
|
||||
```C
|
||||
void fal_set_partition_table_temp(struct fal_partition *table, rt_size_t len)
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
| :----- | :----------------------- |
|
||||
| table | 分区表 |
|
||||
| len | 分区表的长度 |
|
||||
|
||||
## 从分区读取数据
|
||||
|
||||
```C
|
||||
int fal_partition_read(const struct fal_partition *part, rt_uint32_t addr, rt_uint8_t *buf, rt_size_t size)
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
| :----- | :----------------------- |
|
||||
| part | 分区对象 |
|
||||
| addr | 相对分区的偏移地址 |
|
||||
| buf | 存放待读取数据的缓冲区 |
|
||||
| size | 待读取数据的大小 |
|
||||
| return | 返回实际读取的数据大小 |
|
||||
|
||||
## 往分区写入数据
|
||||
|
||||
```C
|
||||
int fal_partition_write(const struct fal_partition *part, rt_uint32_t addr, const rt_uint8_t *buf, rt_size_t size)
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
| :----- | :----------------------- |
|
||||
| part | 分区对象 |
|
||||
| addr | 相对分区的偏移地址 |
|
||||
| buf | 存放待写入数据的缓冲区 |
|
||||
| size | 待写入数据的大小 |
|
||||
| return | 返回实际写入的数据大小 |
|
||||
|
||||
## 擦除分区数据
|
||||
|
||||
```C
|
||||
int fal_partition_erase(const struct fal_partition *part, rt_uint32_t addr, rt_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 后,还可以通过 open/read/write 函数操作分区。
|
||||
|
||||
```C
|
||||
struct rt_device *fal_char_device_create(const char *parition_name)
|
||||
```
|
||||
|
||||
| 参数 | 描述 |
|
||||
| :------------ | :----------------------------------------- |
|
||||
| parition_name | 分区名称 |
|
||||
| return | 创建成功,则返回对应的字符设备,失败返回空 |
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
# FAL API
|
||||
|
||||
## Find Flash device
|
||||
|
||||
```C
|
||||
const struct fal_flash_dev *fal_flash_device_find(const char *name)
|
||||
```
|
||||
|
||||
| Parameters | Description |
|
||||
| :----- | :----------------------- |
|
||||
| name | Flash device name |
|
||||
| return | If the search is successful, the Flash device object will be returned, and if the search fails, it will return NULL |
|
||||
|
||||
## Find Flash Partition
|
||||
|
||||
```C
|
||||
const struct fal_partition *fal_partition_find(const char *name)
|
||||
```
|
||||
|
||||
| Parameters | Description |
|
||||
| :----- | :----------------------- |
|
||||
| name | Flash partition name |
|
||||
| return | If the search is successful, the Flash partition object will be returned, and if the search fails, it will return NULL |
|
||||
|
||||
## Get the partition table
|
||||
|
||||
```C
|
||||
const struct fal_partition *fal_get_partition_table(rt_size_t *len)
|
||||
```
|
||||
|
||||
| Parameters | Description |
|
||||
| :----- | :----------------------- |
|
||||
| len | The length of the partition table |
|
||||
| return | Partition table |
|
||||
|
||||
## Temporarily set the partition table
|
||||
|
||||
The default partition table will be automatically loaded when FAL is initialized. Using this setting will temporarily modify the partition table and will **lost** this setting after restarting
|
||||
|
||||
```C
|
||||
void fal_set_partition_table_temp(struct fal_partition *table, rt_size_t len)
|
||||
```
|
||||
|
||||
| Parameters | Description |
|
||||
| :----- | :----------------------- |
|
||||
| table | Partition table |
|
||||
| len | Length of the partition table |
|
||||
|
||||
## Read data from partition
|
||||
|
||||
```C
|
||||
int fal_partition_read(const struct fal_partition *part, rt_uint32_t addr, rt_uint8_t *buf, rt_size_t size)
|
||||
```
|
||||
|
||||
| Parameters | Description |
|
||||
| :----- | :----------------------- |
|
||||
| part | Partition object |
|
||||
| addr | Relative partition offset address |
|
||||
| buf | Buffer to store the data to be read |
|
||||
| size | The size of the data to be read |
|
||||
| return | Return the actual read data size |
|
||||
|
||||
## Write data to partition
|
||||
|
||||
```C
|
||||
int fal_partition_write(const struct fal_partition *part, rt_uint32_t addr, const rt_uint8_t *buf, rt_size_t size)
|
||||
```
|
||||
|
||||
| Parameters | Description |
|
||||
| :----- | :----------------------- |
|
||||
| part | Partition object |
|
||||
| addr | Relative partition offset address |
|
||||
| buf | Buffer to store data to be written |
|
||||
| size | The size of the data to be written |
|
||||
| return | Return the actual written data size |
|
||||
|
||||
## Erase partition data
|
||||
|
||||
```C
|
||||
int fal_partition_erase(const struct fal_partition *part, rt_uint32_t addr, rt_size_t size)
|
||||
```
|
||||
|
||||
| Parameters | Description |
|
||||
| :----- | :----------------------- |
|
||||
| part | Partition object |
|
||||
| addr | Relative partition offset address |
|
||||
| size | The size of the erased area |
|
||||
| return | Return the actual erased area size |
|
||||
|
||||
## Erase the entire partition data
|
||||
|
||||
```C
|
||||
int fal_partition_erase_all(const struct fal_partition *part)
|
||||
```
|
||||
|
||||
| Parameters | Description |
|
||||
| :----- | :----------------------- |
|
||||
| part | Partition object |
|
||||
| return | Return the actual erased area size |
|
||||
|
||||
## Print partition table
|
||||
|
||||
```c
|
||||
void fal_show_part_table(void)
|
||||
```
|
||||
|
||||
## Create block device
|
||||
|
||||
This function can create the corresponding block device according to the specified partition name, so as to mount the file system on the specified partition
|
||||
|
||||
```C
|
||||
struct rt_device *fal_blk_device_create(const char *parition_name)
|
||||
```
|
||||
|
||||
| Parameters | Description |
|
||||
| :----- | :----------------------- |
|
||||
| parition_name | partition name |
|
||||
| return | If the creation is successful, the corresponding block device will be returned, and if it fails, empty |
|
||||
|
||||
## Create MTD Nor Flash device
|
||||
|
||||
This function can create the corresponding MTD Nor Flash device according to the specified partition name, so as to mount the file system on the specified partition
|
||||
|
||||
```C
|
||||
struct rt_device *fal_mtd_nor_device_create(const char *parition_name)
|
||||
```
|
||||
|
||||
| Parameters | Description |
|
||||
| :------------ | :---------------------------------- ------------------ |
|
||||
| parition_name | Partition name |
|
||||
| return | If the creation is successful, the corresponding MTD Nor Flash device will be returned, otherwise empty |
|
||||
|
||||
## Create a character device
|
||||
|
||||
This function can create the corresponding character device according to the specified partition name to facilitate the operation of the partition through the deivice interface or the devfs interface. After POSIX is turned on, the partition can also be operated through the open/read/write function.
|
||||
|
||||
```C
|
||||
struct rt_device *fal_char_device_create(const char *parition_name)
|
||||
```
|
||||
|
||||
| Parameters | Description |
|
||||
| :------------ | :---------------------------------- ------- |
|
||||
| parition_name | partition name |
|
||||
| return | If the creation is successful, the corresponding character device will be returned, otherwise empty |
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 54 KiB |
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: 24 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2018, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2018-05-17 armink the first version
|
||||
*/
|
||||
|
||||
#ifndef _FAL_H_
|
||||
#define _FAL_H_
|
||||
|
||||
#include <rtconfig.h>
|
||||
#include <fal_cfg.h>
|
||||
#include "fal_def.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* 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(rt_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, rt_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, rt_uint32_t addr, rt_uint8_t *buf, rt_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, rt_uint32_t addr, const rt_uint8_t *buf, rt_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, rt_uint32_t addr, rt_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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _FAL_H_ */
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2018, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2018-05-17 armink the first version
|
||||
*/
|
||||
|
||||
#ifndef _FAL_DEF_H_
|
||||
#define _FAL_DEF_H_
|
||||
|
||||
#include <rtthread.h>
|
||||
|
||||
/* FAL flash and partition device name max length */
|
||||
#ifndef FAL_DEV_NAME_MAX
|
||||
#define FAL_DEV_NAME_MAX 24
|
||||
#endif
|
||||
|
||||
#ifndef FAL_DEV_BLK_MAX
|
||||
#define FAL_DEV_BLK_MAX 6
|
||||
#endif
|
||||
|
||||
struct flash_blk
|
||||
{
|
||||
rt_size_t size;
|
||||
rt_size_t count;
|
||||
};
|
||||
|
||||
struct fal_flash_dev
|
||||
{
|
||||
char name[FAL_DEV_NAME_MAX];
|
||||
|
||||
/* flash device start address and len */
|
||||
rt_uint32_t addr;
|
||||
rt_size_t len;
|
||||
/* the block size in the flash for erase minimum granularity */
|
||||
rt_size_t blk_size;
|
||||
|
||||
struct
|
||||
{
|
||||
int (*init)(void);
|
||||
int (*read)(long offset, rt_uint8_t *buf, rt_size_t size);
|
||||
int (*write)(long offset, const rt_uint8_t *buf, rt_size_t size);
|
||||
int (*erase)(long offset, rt_size_t size);
|
||||
} ops;
|
||||
|
||||
/* write minimum granularity, unit: bit.
|
||||
1(nor flash)/ 8(stm32f2/f4)/ 32(stm32f1)/ 64(stm32l4)
|
||||
0 will not take effect. */
|
||||
rt_size_t write_gran;
|
||||
struct flash_blk blocks[FAL_DEV_BLK_MAX];
|
||||
};
|
||||
typedef struct fal_flash_dev *fal_flash_dev_t;
|
||||
|
||||
/**
|
||||
* FAL partition
|
||||
*/
|
||||
struct fal_partition
|
||||
{
|
||||
rt_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;
|
||||
rt_size_t len;
|
||||
|
||||
rt_uint32_t reserved;
|
||||
};
|
||||
typedef struct fal_partition *fal_partition_t;
|
||||
|
||||
#endif /* _FAL_DEF_H_ */
|
||||
@@ -0,0 +1,4 @@
|
||||
| 文件夹 | 说明 |
|
||||
| :------ | :----------------------- |
|
||||
| porting | 移植相关的示例代码及文档 |
|
||||
|
||||
@@ -0,0 +1,327 @@
|
||||
# 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, rt_uint8_t *buf, rt_size_t size)`:读取操作
|
||||
|
||||
|参数 |描述|
|
||||
|:----- |:----|
|
||||
|offset |读取数据的 Flash 偏移地址|
|
||||
|buf |存放待读取数据的缓冲区|
|
||||
|size |待读取数据的大小|
|
||||
|return |返回实际读取的数据大小|
|
||||
|
||||
- `static int write(long offset, const rt_uint8_t *buf, rt_size_t size)` :写入操作
|
||||
|
||||
| 参数 | 描述 |
|
||||
| :----- | :------------------------ |
|
||||
| offset | 写入数据的 Flash 偏移地址 |
|
||||
| buf | 存放待写入数据的缓冲区 |
|
||||
| size | 待写入数据的大小 |
|
||||
| return | 返回实际写入的数据大小 |
|
||||
|
||||
- `static int erase(long offset, rt_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` (目前用于保留功能)
|
||||
## 3、如何实现读写擦除等操作
|
||||
我们以fal_norflash_port.c为例,简单介绍一下。
|
||||
首先 介绍一下这两个宏定义
|
||||
```C
|
||||
#define FAL_ALIGN_UP( size, align ) \
|
||||
( ( ( size ) + ( align ) - 1 ) - ( ( ( size ) + ( align ) - 1 ) % ( align ) ) )
|
||||
#define FAL_ALIGN_DOWN( size, align ) ( ( ( size ) / ( align ) ) * ( align ) )
|
||||
```
|
||||
ALIGN_UP(16,4)=16 ALIGN_UP(15,4)=16 ALIGN_UP(17,4)=20
|
||||
ALIGN_DOWN(16,4)=16 ALIGN_DOWN(15,4)=12 ALIGN_DOWN(17,4)=16
|
||||
不难看出 ALIGN_UP是一个size向上取整到align的倍数,ALIGN_DOWN则是向下取整到align的倍数。
|
||||
然后 介绍FLASH的特性
|
||||
FLASH都是按块擦除 norflash的块大小一般为4K 单片机内部FLASH的块大小为1K,2K,16K不等
|
||||
同时有最少写入数据的限制
|
||||
norflash中 是按页写入 一次最少写256个字节数据 超过则覆盖起始数据 如第257个数据会覆盖第1个数据的位置
|
||||
单片机内部flash中 一次最少写2个字节数据(STM32F105RC) 且只能将地址2字节对齐写入 只写一个字节时 给后面的字节补成FF
|
||||
实现擦除
|
||||
```C
|
||||
static int32_t get_sector( uint32_t address );//获取当前属于第一个扇区
|
||||
extern void norflash_erase_sector( uint32_t saddr );//负责擦除单个扇区的全部数据
|
||||
//FLASH都是按块擦除 我们假定在调用擦除函数时 用户知道自己将会擦除扇区内的全部数据
|
||||
static int erase( long offset, size_t size )
|
||||
{
|
||||
int32_t cur_erase_sector;
|
||||
uint32_t addr = FLASH_START_ADDR + offset;
|
||||
uint32_t addr_down = FAL_ALIGN_DOWN( addr, FLASH_SECTOR_SIZE );
|
||||
|
||||
uint32_t addr_end = addr + size;
|
||||
uint32_t addr_end_up = FAL_ALIGN_UP( addr_end, FLASH_SECTOR_SIZE );
|
||||
uint32_t cur_addr = addr_down;
|
||||
|
||||
while ( cur_addr < addr_end_up ) {
|
||||
cur_erase_sector = get_sector( cur_addr );
|
||||
if ( cur_erase_sector == -1 ) {//获取第几个扇区失败 说明地址超出范围
|
||||
return cur_addr - addr;
|
||||
}
|
||||
norflash_erase_sector( cur_erase_sector );
|
||||
cur_addr += FLASH_SECTOR_SIZE;//这里如果每个扇区的大小不同 需要实现从当前地址获取扇区实际大小的函数
|
||||
}
|
||||
return size;
|
||||
}
|
||||
```
|
||||
实现读取
|
||||
```c
|
||||
//这个比较简单 直接调用norflash_read即可
|
||||
static int read( long offset, uint8_t* buf, size_t size )
|
||||
{
|
||||
norflash_read( buf, offset + FLASH_START_ADDR, size );
|
||||
return size;
|
||||
}
|
||||
```
|
||||
最后 也是最关键的一步 实现写入
|
||||
```c
|
||||
/* 写入任意长数据到NOR Flash函数 */
|
||||
static int write( long offset, const uint8_t* buf, size_t size )
|
||||
{
|
||||
// 计算实际物理地址(相对于Flash起始地址的偏移)
|
||||
uint32_t addr = FLASH_START_ADDR + offset;
|
||||
// 计算起始地址的扇区向上对齐地址(例如0x1007 -> 0x2000 当扇区大小4K)
|
||||
uint32_t addr_up = FAL_ALIGN_UP( addr, FLASH_SECTOR_SIZE );
|
||||
// 计算起始地址的扇区向下对齐地址(例如0x1007 -> 0x1000)
|
||||
uint32_t addr_down = FAL_ALIGN_DOWN( addr, FLASH_SECTOR_SIZE );
|
||||
|
||||
// 计算写入结束地址
|
||||
uint32_t addr_end = addr + size;
|
||||
// 结束地址的扇区向上对齐地址
|
||||
uint32_t addr_end_up = FAL_ALIGN_UP( addr_end, FLASH_SECTOR_SIZE );
|
||||
// 结束地址的扇区向下对齐地址
|
||||
uint32_t addr_end_down = FAL_ALIGN_DOWN( addr_end, FLASH_SECTOR_SIZE );
|
||||
uint32_t cur_addr = addr_down; // 当前处理的扇区起始地址
|
||||
|
||||
uint32_t max_write_len = 0; // 单次最大可写入长度
|
||||
uint32_t write_len = 0; // 实际写入长度
|
||||
|
||||
// 地址有效性检查:结束地址超过Flash范围 或 起始地址在Flash区域外
|
||||
if ( addr_end_up > FLASH_END_ADDR || ( int )addr_end_down < FLASH_START_ADDR ) return -1;
|
||||
|
||||
// 分配扇区大小的缓冲区(用于处理部分写入时需要保存原始数据的情况)
|
||||
uint8_t* read_sector_buf = FAL_MALLOC( FLASH_SECTOR_SIZE );
|
||||
if ( read_sector_buf == RT_NULL ) {
|
||||
return -2; // 内存分配失败
|
||||
}
|
||||
|
||||
// 按扇区逐个处理(从起始扇区到结束扇区)
|
||||
while ( cur_addr < addr_end_up ) {
|
||||
/* 情况1:处理起始地址不在扇区边界的情况(首扇区部分写入) */
|
||||
if ( cur_addr < addr ) {
|
||||
// 读取整个扇区原始数据到缓冲区
|
||||
read( cur_addr - FLASH_START_ADDR, read_sector_buf, FLASH_SECTOR_SIZE );
|
||||
|
||||
// 计算首扇区可写入的最大长度(从起始地址到扇区末尾)
|
||||
max_write_len = ( addr_up - addr );
|
||||
// 确定实际写入长度(不超过剩余数据大小)
|
||||
write_len = size >= max_write_len ? max_write_len : size;
|
||||
|
||||
// 判断是否需要擦除(检查目标区域是否包含需要从0->1的位)
|
||||
if ( judge_whether_erase( read_sector_buf + addr - cur_addr, write_len ) ){
|
||||
// 需要擦除时:执行擦除->修改缓冲区->写入整个扇区
|
||||
norflash_erase_sector( get_sector( cur_addr ) );
|
||||
// 将新数据合并到缓冲区对应位置
|
||||
FAL_MEMCPY( read_sector_buf + ( addr - cur_addr ), buf, write_len );
|
||||
// 写入整个扇区
|
||||
write_sector( cur_addr, read_sector_buf, FLASH_SECTOR_SIZE );
|
||||
}
|
||||
else {
|
||||
// 无需擦除时直接写入数据(NOR Flash允许直接写入0位)
|
||||
write_sector( addr, buf, write_len );
|
||||
}
|
||||
buf += write_len; // 移动数据指针
|
||||
}
|
||||
/* 情况2:处理结束地址不在扇区边界的情况(末扇区部分写入) */
|
||||
else if ( cur_addr == addr_end_down ) {
|
||||
// 读取整个扇区原始数据
|
||||
read( cur_addr - FLASH_START_ADDR, read_sector_buf, FLASH_SECTOR_SIZE );
|
||||
|
||||
// 计算最大可写入长度(整个扇区)
|
||||
max_write_len = FLASH_SECTOR_SIZE;
|
||||
// 计算实际需要写入的长度(从扇区起始到结束地址)
|
||||
write_len = addr_end - cur_addr;
|
||||
write_len = write_len >= max_write_len ? max_write_len : write_len;
|
||||
|
||||
// 判断是否需要擦除
|
||||
if ( judge_whether_erase( read_sector_buf, write_len ) ) {
|
||||
// 需要擦除时:合并数据->擦除->写入整个扇区
|
||||
FAL_MEMCPY( read_sector_buf, buf, write_len );
|
||||
norflash_erase_sector( get_sector( cur_addr ) );
|
||||
write_sector( cur_addr, read_sector_buf, FLASH_SECTOR_SIZE );
|
||||
}
|
||||
else {
|
||||
// 直接写入数据
|
||||
write_sector( cur_addr, buf, write_len );
|
||||
}
|
||||
}
|
||||
/* 情况3:完整扇区写入(中间扇区) */
|
||||
else {
|
||||
// 直接擦除整个扇区(完整覆盖不需要保留数据)
|
||||
norflash_erase_sector( get_sector( cur_addr ) );
|
||||
// 写入整个扇区数据
|
||||
write_sector( cur_addr, buf, FLASH_SECTOR_SIZE );
|
||||
buf += FLASH_SECTOR_SIZE; // 移动数据指针
|
||||
}
|
||||
cur_addr += FLASH_SECTOR_SIZE; // 移动到下一个扇区
|
||||
}
|
||||
FAL_FREE( read_sector_buf ); // 释放缓冲区内存
|
||||
return size; // 返回成功写入的字节数
|
||||
}
|
||||
```
|
||||
关键逻辑说明:
|
||||
地址对齐处理:通过向上/向下对齐计算确定实际需要操作的扇区范围
|
||||
三种写入场景:
|
||||
首扇区部分写入:需要读取原始数据,合并新数据后判断擦除必要性
|
||||
中间完整扇区:直接擦除后全量写入,提高效率
|
||||
末扇区部分写入:处理方式类似首扇区,但数据位置不同
|
||||
擦除判断:通过judge_whether_erase函数检测是否需要执行擦除操作(基于NOR Flash的特性,只有需要将0变为1时才必须擦除)
|
||||
数据合并:使用临时缓冲区保存原始数据,仅修改需要写入的部分,最大限度减少擦除操作
|
||||
内存管理:动态分配扇区大小的缓冲区,处理完成后立即释放
|
||||
到这里 工作似乎做完了 但是 我们没有写入扇区的函数 只有页写入函数 norflash_write_page
|
||||
扇区写入逻辑和任意写入逻辑基本相同
|
||||
下面实现扇区写入函数
|
||||
```c
|
||||
/* 扇区写入函数:处理按页对齐的NOR Flash写入操作 */
|
||||
static int write_sector( long offset, const uint8_t* buf, size_t size )
|
||||
{
|
||||
// 计算实际物理地址(FLASH起始地址 + 偏移量)
|
||||
uint32_t addr = FLASH_START_ADDR + offset;
|
||||
|
||||
// 计算地址的页对齐上边界和下边界(按FLASH_PAGE_SIZE对齐)
|
||||
uint32_t addr_up = FAL_ALIGN_UP( addr, FLASH_PAGE_SIZE );
|
||||
uint32_t addr_down = FAL_ALIGN_DOWN( addr, FLASH_PAGE_SIZE );
|
||||
|
||||
// 计算写入结束地址及其页对齐边界
|
||||
uint32_t addr_end = addr + size;
|
||||
uint32_t addr_end_up = FAL_ALIGN_UP( addr_end, FLASH_PAGE_SIZE );
|
||||
uint32_t addr_end_down = FAL_ALIGN_DOWN( addr_end, FLASH_PAGE_SIZE );
|
||||
|
||||
// 初始化当前处理地址和长度变量
|
||||
uint32_t cur_addr = addr_down; // 从页对齐起始地址开始处理
|
||||
uint32_t max_write_len = 0; // 单次最大可写入长度
|
||||
uint32_t write_len = 0; // 实际写入长度
|
||||
|
||||
// 循环处理所有需要写入的页
|
||||
while ( cur_addr < addr_end_up ) {
|
||||
// 处理起始未对齐部分(跨页起始边界)
|
||||
if ( cur_addr < addr ) {
|
||||
// 计算当前页剩余可写空间(页结束地址 - 实际起始地址)
|
||||
max_write_len = ( addr_up - addr );
|
||||
// 取实际剩余长度和总长度的最小值
|
||||
write_len = size >= max_write_len ? max_write_len : size;
|
||||
|
||||
// 执行页写入:参数依次是数据指针、物理地址、写入长度
|
||||
norflash_write_page( buf, addr, write_len );
|
||||
buf += write_len; // 移动数据指针
|
||||
}
|
||||
// 处理结束未对齐部分(跨页结束边界)
|
||||
else if ( cur_addr == addr_end_down ) {
|
||||
// 单页最大写入长度
|
||||
max_write_len = FLASH_PAGE_SIZE;
|
||||
// 计算实际需要写入的长度(结束地址 - 当前页起始地址)
|
||||
write_len = addr_end - cur_addr;
|
||||
// 确保不超过页最大长度
|
||||
write_len = write_len >= max_write_len ? max_write_len : write_len;
|
||||
|
||||
// 执行页写入
|
||||
norflash_write_page( buf, cur_addr, write_len );
|
||||
}
|
||||
// 处理完整页写入
|
||||
else {
|
||||
// 整页写入(FLASH_PAGE_SIZE长度)
|
||||
norflash_write_page( buf, cur_addr, FLASH_PAGE_SIZE );
|
||||
buf += FLASH_PAGE_SIZE; // 移动数据指针整页长度
|
||||
}
|
||||
|
||||
// 移动到下一页起始地址
|
||||
cur_addr += FLASH_PAGE_SIZE;
|
||||
}
|
||||
return size; // 返回成功写入的总字节数
|
||||
}
|
||||
```
|
||||
至此 我们就完成了Flash驱动的移植 实现了读写擦除等操作 上面的思路对于大部分flash驱动来说是通用的
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2018, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2018-05-17 armink the first version
|
||||
*/
|
||||
|
||||
#ifndef _FAL_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,96 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2018, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* 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 <dev_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, rt_uint8_t *buf, rt_size_t size);
|
||||
static int write(long offset, const rt_uint8_t *buf, rt_size_t size);
|
||||
static int erase(long offset, rt_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, rt_uint8_t *buf, rt_size_t size)
|
||||
{
|
||||
RT_ASSERT(sfud_dev);
|
||||
RT_ASSERT(sfud_dev->init_ok);
|
||||
sfud_read(sfud_dev, nor_flash0.addr + offset, size, buf);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static int write(long offset, const rt_uint8_t *buf, rt_size_t size)
|
||||
{
|
||||
RT_ASSERT(sfud_dev);
|
||||
RT_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, rt_size_t size)
|
||||
{
|
||||
RT_ASSERT(sfud_dev);
|
||||
RT_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,198 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2018, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* 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 ((rt_uint32_t)0x08000000) /* Base address of Sector 0, 16 K bytes */
|
||||
#define ADDR_FLASH_SECTOR_1 ((rt_uint32_t)0x08004000) /* Base address of Sector 1, 16 K bytes */
|
||||
#define ADDR_FLASH_SECTOR_2 ((rt_uint32_t)0x08008000) /* Base address of Sector 2, 16 K bytes */
|
||||
#define ADDR_FLASH_SECTOR_3 ((rt_uint32_t)0x0800C000) /* Base address of Sector 3, 16 K bytes */
|
||||
#define ADDR_FLASH_SECTOR_4 ((rt_uint32_t)0x08010000) /* Base address of Sector 4, 64 K bytes */
|
||||
#define ADDR_FLASH_SECTOR_5 ((rt_uint32_t)0x08020000) /* Base address of Sector 5, 128 K bytes */
|
||||
#define ADDR_FLASH_SECTOR_6 ((rt_uint32_t)0x08040000) /* Base address of Sector 6, 128 K bytes */
|
||||
#define ADDR_FLASH_SECTOR_7 ((rt_uint32_t)0x08060000) /* Base address of Sector 7, 128 K bytes */
|
||||
#define ADDR_FLASH_SECTOR_8 ((rt_uint32_t)0x08080000) /* Base address of Sector 8, 128 K bytes */
|
||||
#define ADDR_FLASH_SECTOR_9 ((rt_uint32_t)0x080A0000) /* Base address of Sector 9, 128 K bytes */
|
||||
#define ADDR_FLASH_SECTOR_10 ((rt_uint32_t)0x080C0000) /* Base address of Sector 10, 128 K bytes */
|
||||
#define ADDR_FLASH_SECTOR_11 ((rt_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 rt_uint32_t stm32_get_sector(rt_uint32_t address)
|
||||
{
|
||||
rt_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 rt_uint32_t stm32_get_sector_size(rt_uint32_t sector) {
|
||||
RT_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, rt_uint8_t *buf, rt_size_t size)
|
||||
{
|
||||
rt_size_t i;
|
||||
rt_uint32_t addr = stm32f2_onchip_flash.addr + offset;
|
||||
for (i = 0; i < size; i++, addr++, buf++)
|
||||
{
|
||||
*buf = *(rt_uint8_t *) addr;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static int write(long offset, const rt_uint8_t *buf, rt_size_t size)
|
||||
{
|
||||
rt_size_t i;
|
||||
rt_uint32_t read_data;
|
||||
rt_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 = *(rt_uint8_t *) addr;
|
||||
/* check data */
|
||||
if (read_data != *buf)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
FLASH_Lock();
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static int erase(long offset, rt_size_t size)
|
||||
{
|
||||
FLASH_Status flash_status;
|
||||
rt_size_t erased_size = 0;
|
||||
rt_uint32_t cur_erase_sector;
|
||||
rt_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,196 @@
|
||||
#include <fal.h>
|
||||
#include "fal_def.h"
|
||||
|
||||
#define FLASH_SECTOR_SIZE ( 4 * 1024 )
|
||||
#define FLASH_START_ADDR 0U
|
||||
#define FLASH_END_ADDR 0x01000000U // 16*1024*1024
|
||||
|
||||
#define FLASH_PROGRAM_MIN_SIZE 256 // 256 bytes
|
||||
//每次对falsh写入时 底层可以写入的最大字节数为 FLASH_PAGE_SIZE
|
||||
#define FLASH_PAGE_SIZE FLASH_PROGRAM_MIN_SIZE // 256 bytes
|
||||
|
||||
/**
|
||||
* @brief 需要实现以下函数
|
||||
*/
|
||||
extern int norflash_init( void );
|
||||
extern void norflash_read( uint8_t* pbuf, uint32_t addr, uint16_t datalen );
|
||||
extern void norflash_write_page( uint8_t* pbuf, uint32_t addr, uint16_t datalen );
|
||||
extern void norflash_erase_sector( uint32_t saddr );
|
||||
|
||||
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 );
|
||||
|
||||
#define FAL_ALIGN_UP( size, align ) \
|
||||
( ( ( size ) + ( align ) - 1 ) - ( ( ( size ) + ( align ) - 1 ) % ( align ) ) )
|
||||
#define FAL_ALIGN_DOWN( size, align ) ( ( ( size ) / ( align ) ) * ( align ) )
|
||||
|
||||
static int32_t get_sector( uint32_t address )
|
||||
{
|
||||
uint32_t sector = 0;
|
||||
if ( address < FLASH_END_ADDR && address >= FLASH_START_ADDR ) {
|
||||
address -= FLASH_START_ADDR;
|
||||
sector = address / FLASH_SECTOR_SIZE;
|
||||
return sector;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int init( void )
|
||||
{
|
||||
norflash_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int read( long offset, uint8_t* buf, size_t size )
|
||||
{
|
||||
norflash_read( buf, offset + FLASH_START_ADDR, size );
|
||||
return size;
|
||||
}
|
||||
|
||||
static uint32_t judge_whether_erase( uint8_t* sector_buf, uint16_t len )
|
||||
{
|
||||
uint8_t* p = sector_buf;
|
||||
for ( size_t i = 0; i < len; i++ ) {
|
||||
if ( p[ i ] != 0xFF ) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* @param offset 绝对地址
|
||||
* @param buf 读出来的扇区数据缓存
|
||||
* @param size 从扇区开始要写的长度 小于扇区大小
|
||||
* @return int
|
||||
*/
|
||||
static int write_sector( long offset, const uint8_t* buf, size_t size )
|
||||
{
|
||||
uint32_t addr = FLASH_START_ADDR + offset;
|
||||
uint32_t addr_up = FAL_ALIGN_UP( addr, FLASH_PAGE_SIZE );
|
||||
uint32_t addr_down = FAL_ALIGN_DOWN( addr, FLASH_PAGE_SIZE );
|
||||
|
||||
uint32_t addr_end = addr + size;
|
||||
uint32_t addr_end_up = FAL_ALIGN_UP( addr_end, FLASH_PAGE_SIZE );
|
||||
uint32_t addr_end_down = FAL_ALIGN_DOWN( addr_end, FLASH_PAGE_SIZE );
|
||||
|
||||
uint32_t cur_addr = addr_down;
|
||||
uint32_t max_write_len = 0;
|
||||
uint32_t write_len = 0;
|
||||
while ( cur_addr < addr_end_up ) {
|
||||
if ( cur_addr < addr ) {
|
||||
max_write_len = ( addr_up - addr );
|
||||
write_len = size >= max_write_len ? max_write_len : size;
|
||||
norflash_write_page( buf, addr, write_len );
|
||||
buf += write_len;
|
||||
}
|
||||
else if ( cur_addr == addr_end_down ) {
|
||||
max_write_len = FLASH_PAGE_SIZE;
|
||||
write_len = addr_end - cur_addr;
|
||||
write_len = write_len >= max_write_len ? max_write_len : write_len;
|
||||
norflash_write_page( buf, cur_addr, write_len );
|
||||
}
|
||||
else {
|
||||
norflash_write_page( buf, cur_addr, FLASH_PAGE_SIZE );
|
||||
buf += FLASH_PAGE_SIZE;
|
||||
}
|
||||
|
||||
cur_addr += FLASH_PAGE_SIZE;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
static int write( long offset, const uint8_t* buf, size_t size )
|
||||
{
|
||||
uint32_t addr = FLASH_START_ADDR + offset;
|
||||
uint32_t addr_up = FAL_ALIGN_UP( addr, FLASH_SECTOR_SIZE );
|
||||
uint32_t addr_down = FAL_ALIGN_DOWN( addr, FLASH_SECTOR_SIZE );
|
||||
|
||||
uint32_t addr_end = addr + size;
|
||||
uint32_t addr_end_up = FAL_ALIGN_UP( addr_end, FLASH_SECTOR_SIZE );
|
||||
uint32_t addr_end_down = FAL_ALIGN_DOWN( addr_end, FLASH_SECTOR_SIZE );
|
||||
uint32_t cur_addr = addr_down;
|
||||
|
||||
uint32_t max_write_len = 0;
|
||||
uint32_t write_len = 0;
|
||||
|
||||
if ( addr_end_up > FLASH_END_ADDR || ( int )addr_end_down < FLASH_START_ADDR ) return -1;
|
||||
//如果不使用内存分配可以定义一个static FLASH_SECTOR_SIZE 长度的buf
|
||||
uint8_t* read_sector_buf = FAL_MALLOC( FLASH_SECTOR_SIZE );
|
||||
if ( read_sector_buf == RT_NULL ) {
|
||||
return -2;
|
||||
}
|
||||
while ( cur_addr < addr_end_up ) {
|
||||
// 首次扇区写
|
||||
if ( cur_addr < addr ) {
|
||||
read( cur_addr - FLASH_START_ADDR, read_sector_buf, FLASH_SECTOR_SIZE );
|
||||
max_write_len = ( addr_up - addr );
|
||||
write_len = size >= max_write_len ? max_write_len : size;
|
||||
if ( judge_whether_erase( read_sector_buf + addr - cur_addr, write_len ) ){
|
||||
norflash_erase_sector( get_sector( cur_addr ) );
|
||||
FAL_MEMCPY( read_sector_buf + ( addr - cur_addr ), buf, write_len );
|
||||
write_sector( cur_addr, read_sector_buf, FLASH_SECTOR_SIZE );
|
||||
}
|
||||
else {
|
||||
write_sector( addr, buf, write_len );
|
||||
}
|
||||
buf += write_len;
|
||||
}
|
||||
//最后一次扇区写
|
||||
else if ( cur_addr == addr_end_down ) {
|
||||
read( cur_addr - FLASH_START_ADDR, read_sector_buf, FLASH_SECTOR_SIZE );
|
||||
max_write_len = FLASH_SECTOR_SIZE;
|
||||
write_len = addr_end - cur_addr;
|
||||
write_len = write_len >= max_write_len ? max_write_len : write_len;
|
||||
if ( judge_whether_erase( read_sector_buf, write_len ) ) {
|
||||
FAL_MEMCPY( read_sector_buf, buf, write_len );
|
||||
norflash_erase_sector( get_sector( cur_addr ) );
|
||||
write_sector( cur_addr, read_sector_buf, FLASH_SECTOR_SIZE );
|
||||
}
|
||||
else {
|
||||
write_sector( cur_addr, buf, write_len );
|
||||
}
|
||||
}
|
||||
//中间扇区写 直接擦除
|
||||
else {
|
||||
norflash_erase_sector( get_sector( cur_addr ) );
|
||||
write_sector( cur_addr, buf, FLASH_SECTOR_SIZE );
|
||||
buf += FLASH_SECTOR_SIZE;
|
||||
}
|
||||
cur_addr += FLASH_SECTOR_SIZE;
|
||||
}
|
||||
FAL_FREE( read_sector_buf );
|
||||
return size;
|
||||
}
|
||||
|
||||
static int erase( long offset, size_t size )
|
||||
{
|
||||
int32_t cur_erase_sector;
|
||||
uint32_t addr = FLASH_START_ADDR + offset;
|
||||
uint32_t addr_down = FAL_ALIGN_DOWN( addr, FLASH_SECTOR_SIZE );
|
||||
|
||||
uint32_t addr_end = addr + size;
|
||||
uint32_t addr_end_up = FAL_ALIGN_UP( addr_end, FLASH_SECTOR_SIZE );
|
||||
uint32_t cur_addr = addr_down;
|
||||
|
||||
while ( cur_addr < addr_end_up ) {
|
||||
cur_erase_sector = get_sector( cur_addr );
|
||||
if ( cur_erase_sector == -1 ) {
|
||||
return cur_addr - addr;
|
||||
}
|
||||
norflash_erase_sector( cur_erase_sector );
|
||||
cur_addr += FLASH_SECTOR_SIZE;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
const struct fal_flash_dev norflash0 = {
|
||||
.name = "norflash0",
|
||||
.addr = FLASH_START_ADDR,
|
||||
.len = FLASH_END_ADDR - FLASH_START_ADDR,
|
||||
.blk_size = FLASH_SECTOR_SIZE,
|
||||
.ops = { init, read, write, erase },
|
||||
.write_gran = 1,
|
||||
};
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2018, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2018-05-17 armink the first version
|
||||
*/
|
||||
|
||||
#include <fal.h>
|
||||
#include <rtdevice.h>
|
||||
|
||||
#define DBG_TAG "FAL"
|
||||
#ifdef FAL_USING_DEBUG
|
||||
#define DBG_LVL DBG_LOG
|
||||
#else
|
||||
#define DBG_LVL DBG_WARNING
|
||||
#endif
|
||||
#include <rtdbg.h>
|
||||
|
||||
static rt_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 initialize success.");
|
||||
}
|
||||
else if(result <= 0)
|
||||
{
|
||||
init_ok = 0;
|
||||
LOG_E("RT-Thread Flash Abstraction Layer initialize failed.");
|
||||
}
|
||||
|
||||
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,106 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2024 RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2018-05-17 armink the first version
|
||||
*/
|
||||
|
||||
#include <fal.h>
|
||||
#include <string.h>
|
||||
|
||||
#define DBG_TAG "FAL"
|
||||
#ifdef FAL_USING_DEBUG
|
||||
#define DBG_LVL DBG_LOG
|
||||
#else
|
||||
#define DBG_LVL DBG_WARNING
|
||||
#endif
|
||||
#include <rtdbg.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 rt_size_t device_table_len = sizeof(device_table) / sizeof(device_table[0]);
|
||||
static rt_uint8_t init_ok = 0;
|
||||
|
||||
/**
|
||||
* Initialize all flash device on FAL flash table
|
||||
*
|
||||
* @return result
|
||||
*/
|
||||
int fal_flash_init(void)
|
||||
{
|
||||
rt_size_t i, j, offset;
|
||||
|
||||
if (init_ok)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < device_table_len; i++)
|
||||
{
|
||||
RT_ASSERT(device_table[i]->ops.read);
|
||||
RT_ASSERT(device_table[i]->ops.write);
|
||||
RT_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);
|
||||
offset = 0;
|
||||
for (j = 0; j < FAL_DEV_BLK_MAX; j ++)
|
||||
{
|
||||
const struct flash_blk *blk = &device_table[i]->blocks[j];
|
||||
rt_size_t blk_len = blk->count * blk->size;
|
||||
if (blk->count == 0 || blk->size == 0)
|
||||
break;
|
||||
|
||||
if(offset > device_table[i]->len)
|
||||
{
|
||||
LOG_I("Flash device %*.*s: add block failed, offset %d > len %d.",
|
||||
FAL_DEV_NAME_MAX, FAL_DEV_NAME_MAX, device_table[i]->name, device_table[i]->addr, offset, device_table[i]->len);
|
||||
break;
|
||||
}
|
||||
|
||||
LOG_D(" blk%2d | addr: 0x%08lx | len: 0x%08x | blk_size: 0x%08x |initialized finish.",
|
||||
j, device_table[i]->addr + offset, blk_len, blk->size);
|
||||
offset += blk_len;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
RT_ASSERT(init_ok);
|
||||
RT_ASSERT(name);
|
||||
|
||||
rt_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,530 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2018, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2018-05-17 armink the first version
|
||||
*/
|
||||
|
||||
#include <fal.h>
|
||||
#include <string.h>
|
||||
|
||||
#define DBG_TAG "FAL"
|
||||
#ifdef FAL_USING_DEBUG
|
||||
#define DBG_LVL DBG_LOG
|
||||
#else
|
||||
#define DBG_LVL DBG_WARNING
|
||||
#endif
|
||||
#include <rtdbg.h>
|
||||
|
||||
|
||||
/* partition magic word */
|
||||
#define FAL_PART_MAGIC_WORD 0x45503130
|
||||
#define FAL_PART_MAGIC_WORD_H 0x4550L
|
||||
#define FAL_PART_MAGIC_WORD_L 0x3130L
|
||||
#define FAL_PART_MAGIC_WROD 0x45503130
|
||||
|
||||
struct part_flash_info
|
||||
{
|
||||
const struct fal_flash_dev *flash_dev;
|
||||
};
|
||||
|
||||
/**
|
||||
* FAL partition table config has defined on 'fal_cfg.h'.
|
||||
* When this option is disable, it will auto find the partition table on a specified location in flash partition.
|
||||
*/
|
||||
#ifdef FAL_PART_HAS_TABLE_CFG
|
||||
|
||||
/* check partition table definition */
|
||||
#if !defined(FAL_PART_TABLE)
|
||||
#error "You must defined FAL_PART_TABLE on 'fal_cfg.h'"
|
||||
#endif
|
||||
|
||||
/* partition table definition */
|
||||
static const struct fal_partition partition_table_def[] = FAL_PART_TABLE;
|
||||
static const struct fal_partition *partition_table = NULL;
|
||||
/* partition and flash object information cache table */
|
||||
static struct part_flash_info part_flash_cache[sizeof(partition_table_def) / sizeof(partition_table_def[0])] = { 0 };
|
||||
|
||||
#else /* FAL_PART_HAS_TABLE_CFG */
|
||||
|
||||
#if !defined(FAL_PART_TABLE_FLASH_DEV_NAME)
|
||||
#error "You must defined FAL_PART_TABLE_FLASH_DEV_NAME on 'fal_cfg.h'"
|
||||
#endif
|
||||
|
||||
/* check partition table end offset address definition */
|
||||
#if !defined(FAL_PART_TABLE_END_OFFSET)
|
||||
#error "You must defined FAL_PART_TABLE_END_OFFSET on 'fal_cfg.h'"
|
||||
#endif
|
||||
|
||||
static struct fal_partition *partition_table = NULL;
|
||||
static struct part_flash_info *part_flash_cache = NULL;
|
||||
#endif /* FAL_PART_HAS_TABLE_CFG */
|
||||
|
||||
static rt_uint8_t init_ok = 0;
|
||||
static rt_size_t partition_table_len = 0;
|
||||
|
||||
/**
|
||||
* print the partition table
|
||||
*/
|
||||
void fal_show_part_table(void)
|
||||
{
|
||||
char *item1 = "name", *item2 = "flash_dev";
|
||||
rt_size_t i, part_name_max = strlen(item1), flash_dev_name_max = strlen(item2);
|
||||
const struct fal_partition *part;
|
||||
|
||||
if (partition_table_len)
|
||||
{
|
||||
for (i = 0; i < partition_table_len; i++)
|
||||
{
|
||||
part = &partition_table[i];
|
||||
if (strlen(part->name) > part_name_max)
|
||||
{
|
||||
part_name_max = strlen(part->name);
|
||||
}
|
||||
if (strlen(part->flash_name) > flash_dev_name_max)
|
||||
{
|
||||
flash_dev_name_max = strlen(part->flash_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG_I("==================== FAL partition table ====================");
|
||||
LOG_I("| %-*.*s | %-*.*s | offset | length |", part_name_max, FAL_DEV_NAME_MAX, item1, flash_dev_name_max,
|
||||
FAL_DEV_NAME_MAX, item2);
|
||||
LOG_I("-------------------------------------------------------------");
|
||||
for (i = 0; i < partition_table_len; i++)
|
||||
{
|
||||
|
||||
#ifdef FAL_PART_HAS_TABLE_CFG
|
||||
part = &partition_table[i];
|
||||
#else
|
||||
part = &partition_table[partition_table_len - i - 1];
|
||||
#endif
|
||||
|
||||
LOG_I("| %-*.*s | %-*.*s | 0x%08lx | 0x%08x |", part_name_max, FAL_DEV_NAME_MAX, part->name, flash_dev_name_max,
|
||||
FAL_DEV_NAME_MAX, part->flash_name, part->offset, part->len);
|
||||
}
|
||||
LOG_I("=============================================================");
|
||||
}
|
||||
|
||||
static int check_and_update_part_cache(const struct fal_partition *table, rt_size_t len)
|
||||
{
|
||||
const struct fal_flash_dev *flash_dev = NULL;
|
||||
rt_size_t i;
|
||||
|
||||
#ifndef FAL_PART_HAS_TABLE_CFG
|
||||
if (part_flash_cache)
|
||||
{
|
||||
rt_free(part_flash_cache);
|
||||
}
|
||||
part_flash_cache = rt_malloc(len * sizeof(struct part_flash_info));
|
||||
if (part_flash_cache == NULL)
|
||||
{
|
||||
LOG_E("Initialize failed! No memory for partition table cache");
|
||||
return -2;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
flash_dev = fal_flash_device_find(table[i].flash_name);
|
||||
if (flash_dev == NULL)
|
||||
{
|
||||
LOG_D("Warning: Do NOT found the flash device(%s).", table[i].flash_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (table[i].offset >= (long)flash_dev->len)
|
||||
{
|
||||
LOG_E("Initialize failed! Partition(%s) offset address(%ld) out of flash bound(<%d).",
|
||||
table[i].name, table[i].offset, flash_dev->len);
|
||||
partition_table_len = 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
part_flash_cache[i].flash_dev = flash_dev;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize all flash partition on FAL partition table
|
||||
*
|
||||
* @return partitions total number
|
||||
*/
|
||||
int fal_partition_init(void)
|
||||
{
|
||||
|
||||
if (init_ok)
|
||||
{
|
||||
return partition_table_len;
|
||||
}
|
||||
|
||||
#ifdef FAL_PART_HAS_TABLE_CFG
|
||||
partition_table = &partition_table_def[0];
|
||||
partition_table_len = sizeof(partition_table_def) / sizeof(partition_table_def[0]);
|
||||
#else
|
||||
/* load partition table from the end address FAL_PART_TABLE_END_OFFSET, error return 0 */
|
||||
long part_table_offset = FAL_PART_TABLE_END_OFFSET;
|
||||
rt_size_t table_num = 0, table_item_size = 0;
|
||||
rt_uint8_t part_table_find_ok = 0;
|
||||
rt_uint32_t read_magic_word;
|
||||
fal_partition_t new_part = NULL;
|
||||
rt_size_t i;
|
||||
const struct fal_flash_dev *flash_dev = NULL;
|
||||
|
||||
flash_dev = fal_flash_device_find(FAL_PART_TABLE_FLASH_DEV_NAME);
|
||||
if (flash_dev == NULL)
|
||||
{
|
||||
LOG_E("Initialize failed! Flash device (%s) NOT found.", FAL_PART_TABLE_FLASH_DEV_NAME);
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
/* check partition table offset address */
|
||||
if (part_table_offset < 0 || part_table_offset >= (long) flash_dev->len)
|
||||
{
|
||||
LOG_E("Setting partition table end offset address(%ld) out of flash bound(<%d).", part_table_offset, flash_dev->len);
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
table_item_size = sizeof(struct fal_partition);
|
||||
new_part = (fal_partition_t)rt_malloc(table_item_size);
|
||||
if (new_part == NULL)
|
||||
{
|
||||
LOG_E("Initialize failed! No memory for table buffer.");
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
/* find partition table location */
|
||||
{
|
||||
rt_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), (rt_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) rt_realloc(partition_table, table_item_size * (table_num + 1));
|
||||
if (partition_table == NULL)
|
||||
{
|
||||
LOG_E("Initialize failed! No memory for partition table");
|
||||
table_num = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(partition_table + table_num, new_part, table_item_size);
|
||||
|
||||
table_num++;
|
||||
};
|
||||
|
||||
if (table_num == 0)
|
||||
{
|
||||
LOG_E("Partition table NOT found on flash: %s (len: %d) from offset: 0x%08x.", FAL_PART_TABLE_FLASH_DEV_NAME,
|
||||
FAL_DEV_NAME_MAX, FAL_PART_TABLE_END_OFFSET);
|
||||
goto _exit;
|
||||
}
|
||||
else
|
||||
{
|
||||
partition_table_len = table_num;
|
||||
}
|
||||
#endif /* FAL_PART_HAS_TABLE_CFG */
|
||||
|
||||
/* check the partition table device exists */
|
||||
if (check_and_update_part_cache(partition_table, partition_table_len) != 0)
|
||||
{
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
init_ok = 1;
|
||||
|
||||
_exit:
|
||||
|
||||
#ifdef FAL_USING_DEBUG
|
||||
fal_show_part_table();
|
||||
#endif /* FAL_USING_DEBUG */
|
||||
|
||||
#ifndef FAL_PART_HAS_TABLE_CFG
|
||||
if (new_part)
|
||||
{
|
||||
rt_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)
|
||||
{
|
||||
if (!init_ok)
|
||||
return NULL;
|
||||
|
||||
rt_size_t i;
|
||||
|
||||
for (i = 0; i < partition_table_len; i++)
|
||||
{
|
||||
if (!strcmp(name, partition_table[i].name))
|
||||
{
|
||||
return &partition_table[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const struct fal_flash_dev *flash_device_find_by_part(const struct fal_partition *part)
|
||||
{
|
||||
RT_ASSERT(part >= partition_table);
|
||||
RT_ASSERT(part <= &partition_table[partition_table_len - 1]);
|
||||
|
||||
return part_flash_cache[part - partition_table].flash_dev;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the partition table
|
||||
*
|
||||
* @param len return the partition table length
|
||||
*
|
||||
* @return partition table
|
||||
*/
|
||||
const struct fal_partition *fal_get_partition_table(rt_size_t *len)
|
||||
{
|
||||
RT_ASSERT(len);
|
||||
|
||||
if (!init_ok)
|
||||
return NULL;
|
||||
|
||||
*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, rt_size_t len)
|
||||
{
|
||||
RT_ASSERT(table);
|
||||
|
||||
if (!init_ok)
|
||||
{
|
||||
LOG_E("FAL NOT initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
check_and_update_part_cache(table, len);
|
||||
|
||||
partition_table_len = len;
|
||||
partition_table = table;
|
||||
}
|
||||
|
||||
/**
|
||||
* read data from partition
|
||||
*
|
||||
* @param part partition
|
||||
* @param addr relative address for partition
|
||||
* @param buf read buffer
|
||||
* @param size read size
|
||||
*
|
||||
* @return >= 0: successful read data size
|
||||
* -1: error
|
||||
*/
|
||||
int fal_partition_read(const struct fal_partition *part, rt_uint32_t addr, rt_uint8_t *buf, rt_size_t size)
|
||||
{
|
||||
int ret = 0;
|
||||
const struct fal_flash_dev *flash_dev = NULL;
|
||||
|
||||
RT_ASSERT(part);
|
||||
RT_ASSERT(buf);
|
||||
|
||||
if (addr + size > part->len)
|
||||
{
|
||||
LOG_E("Partition read error! Partition(%s) address(0x%08x) out of bound(0x%08x).", part->name, addr + size, part->len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
flash_dev = flash_device_find_by_part(part);
|
||||
if (flash_dev == NULL)
|
||||
{
|
||||
LOG_E("Partition read error! Don't found flash device(%s) of the partition(%s).", part->flash_name, part->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
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, rt_uint32_t addr, const rt_uint8_t *buf, rt_size_t size)
|
||||
{
|
||||
int ret = 0;
|
||||
const struct fal_flash_dev *flash_dev = NULL;
|
||||
|
||||
RT_ASSERT(part);
|
||||
RT_ASSERT(buf);
|
||||
|
||||
if (addr + size > part->len)
|
||||
{
|
||||
LOG_E("Partition write error! Partition address out of bound.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
flash_dev = flash_device_find_by_part(part);
|
||||
if (flash_dev == NULL)
|
||||
{
|
||||
LOG_E("Partition write error! Don't found flash device(%s) of the partition(%s).", part->flash_name, part->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = flash_dev->ops.write(part->offset + addr, buf, size);
|
||||
if (ret < 0)
|
||||
{
|
||||
LOG_E("Partition write error! Flash device(%s) write error!", part->flash_name);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* erase partition data
|
||||
*
|
||||
* @param part partition
|
||||
* @param addr relative address for partition
|
||||
* @param size erase size
|
||||
*
|
||||
* @return >= 0: successful erased data size
|
||||
* -1: error
|
||||
*/
|
||||
int fal_partition_erase(const struct fal_partition *part, rt_uint32_t addr, rt_size_t size)
|
||||
{
|
||||
int ret = 0;
|
||||
const struct fal_flash_dev *flash_dev = NULL;
|
||||
|
||||
RT_ASSERT(part);
|
||||
|
||||
if (addr + size > part->len)
|
||||
{
|
||||
LOG_E("Partition erase error! Partition address out of bound.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
flash_dev = flash_device_find_by_part(part);
|
||||
if (flash_dev == NULL)
|
||||
{
|
||||
LOG_E("Partition erase error! Don't found flash device(%s) of the partition(%s).", part->flash_name, part->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = flash_dev->ops.erase(part->offset + addr, size);
|
||||
if (ret < 0)
|
||||
{
|
||||
LOG_E("Partition erase error! Flash device(%s) erase error!", part->flash_name);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* erase partition all data
|
||||
*
|
||||
* @param part partition
|
||||
*
|
||||
* @return >= 0: successful erased data size
|
||||
* -1: error
|
||||
*/
|
||||
int fal_partition_erase_all(const struct fal_partition *part)
|
||||
{
|
||||
return fal_partition_erase(part, 0, part->len);
|
||||
}
|
||||
@@ -0,0 +1,944 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2018-06-23 armink the first version
|
||||
* 2019-08-22 MurphyZhao adapt to none rt-thread case
|
||||
*/
|
||||
|
||||
#include <fal.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <rtdevice.h>
|
||||
|
||||
#define DBG_TAG "FAL"
|
||||
#ifdef FAL_USING_DEBUG
|
||||
#define DBG_LVL DBG_LOG
|
||||
#else
|
||||
#define DBG_LVL DBG_WARNING
|
||||
#endif
|
||||
#include <rtdbg.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;
|
||||
|
||||
RT_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;
|
||||
}
|
||||
|
||||
rt_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_ssize_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;
|
||||
|
||||
RT_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_ssize_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;
|
||||
RT_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_ssize_t mtd_nor_dev_read(struct rt_mtd_nor_device* device, rt_off_t offset, rt_uint8_t* data, rt_size_t length)
|
||||
{
|
||||
int ret = 0;
|
||||
struct fal_mtd_nor_device *part = (struct fal_mtd_nor_device*) device;
|
||||
|
||||
RT_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_ssize_t mtd_nor_dev_write(struct rt_mtd_nor_device* device, rt_off_t offset, const rt_uint8_t* data, rt_size_t length)
|
||||
{
|
||||
int ret = 0;
|
||||
struct fal_mtd_nor_device *part;
|
||||
|
||||
part = (struct fal_mtd_nor_device*) device;
|
||||
RT_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_size_t length)
|
||||
{
|
||||
int ret = 0;
|
||||
struct fal_mtd_nor_device *part;
|
||||
|
||||
part = (struct fal_mtd_nor_device*) device;
|
||||
RT_ASSERT(part != RT_NULL);
|
||||
|
||||
ret = fal_partition_erase(part->fal_part, offset, length);
|
||||
|
||||
if (ret != (int)length || ret < 0)
|
||||
{
|
||||
return -RT_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
return RT_EOK;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct rt_mtd_nor_driver_ops _ops =
|
||||
{
|
||||
RT_NULL, /* read_id */
|
||||
mtd_nor_dev_read, /* read */
|
||||
mtd_nor_dev_write, /* write*/
|
||||
mtd_nor_dev_erase, /* erase_block */
|
||||
};
|
||||
|
||||
/**
|
||||
* 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_ssize_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;
|
||||
|
||||
RT_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_ssize_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;
|
||||
RT_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_DEVIO
|
||||
#include <dfs_file.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h> /* rename() */
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statfs.h> /* statfs() */
|
||||
|
||||
/* RT-Thread device filesystem interface */
|
||||
static int char_dev_fopen(struct dfs_file *fd)
|
||||
{
|
||||
struct fal_char_device *part = (struct fal_char_device *) fd->vnode->data;
|
||||
|
||||
RT_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;
|
||||
}
|
||||
DFS_FILE_POS(fd) = 0;
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static int char_dev_fread(struct dfs_file *fd, void *buf, rt_size_t count)
|
||||
{
|
||||
int ret = 0;
|
||||
struct fal_char_device *part = (struct fal_char_device *) fd->vnode->data;
|
||||
|
||||
RT_ASSERT(part != RT_NULL);
|
||||
|
||||
if (DFS_FILE_POS(fd) + count > part->fal_part->len)
|
||||
count = part->fal_part->len - DFS_FILE_POS(fd);
|
||||
|
||||
ret = fal_partition_read(part->fal_part, DFS_FILE_POS(fd), buf, count);
|
||||
|
||||
if (ret != (int)(count))
|
||||
return 0;
|
||||
|
||||
DFS_FILE_POS(fd) += ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int char_dev_fwrite(struct dfs_file *fd, const void *buf, rt_size_t count)
|
||||
{
|
||||
int ret = 0;
|
||||
struct fal_char_device *part = (struct fal_char_device *) fd->vnode->data;
|
||||
|
||||
RT_ASSERT(part != RT_NULL);
|
||||
|
||||
if (DFS_FILE_POS(fd) + count > part->fal_part->len)
|
||||
count = part->fal_part->len - DFS_FILE_POS(fd);
|
||||
|
||||
ret = fal_partition_write(part->fal_part, DFS_FILE_POS(fd), buf, count);
|
||||
|
||||
if (ret != (int) count)
|
||||
return 0;
|
||||
|
||||
DFS_FILE_POS(fd) += 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_DEVIO) */
|
||||
|
||||
/**
|
||||
* 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_DEVIO
|
||||
/* 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(rt_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 = 0;
|
||||
static const struct fal_flash_dev *flash_dev = NULL;
|
||||
static const struct fal_partition *part_dev = NULL;
|
||||
rt_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];
|
||||
rt_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);
|
||||
rt_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;
|
||||
rt_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 */
|
||||
rt_uint32_t start_time, time_cast;
|
||||
rt_size_t write_size = strtol(argv[2], NULL, 0), read_size = strtol(argv[2], NULL, 0), cur_op_size;
|
||||
rt_uint8_t *write_data = (rt_uint8_t *)rt_malloc(write_size), *read_data = (rt_uint8_t *)rt_malloc(read_size);
|
||||
|
||||
if (write_data && read_data)
|
||||
{
|
||||
for (i = 0; i < write_size; i ++) {
|
||||
write_data[i] = i & 0xFF;
|
||||
}
|
||||
if (flash_dev)
|
||||
{
|
||||
size = flash_dev->len;
|
||||
}
|
||||
else if (part_dev)
|
||||
{
|
||||
size = part_dev->len;
|
||||
}
|
||||
/* benchmark testing */
|
||||
rt_kprintf("Erasing %ld bytes data, waiting...\n", size);
|
||||
start_time = rt_tick_get();
|
||||
if (flash_dev)
|
||||
{
|
||||
result = flash_dev->ops.erase(0, size);
|
||||
}
|
||||
else if (part_dev)
|
||||
{
|
||||
result = fal_partition_erase(part_dev, 0, size);
|
||||
}
|
||||
if (result >= 0)
|
||||
{
|
||||
time_cast = rt_tick_get() - start_time;
|
||||
rt_kprintf("Erase benchmark success, total time: %d.%03dS.\n", time_cast / RT_TICK_PER_SECOND,
|
||||
time_cast % RT_TICK_PER_SECOND / ((RT_TICK_PER_SECOND * 1 + 999) / 1000));
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("Erase benchmark has an error. Error code: %d.\n", result);
|
||||
}
|
||||
/* write test */
|
||||
rt_kprintf("Writing %ld bytes data, waiting...\n", size);
|
||||
start_time = rt_tick_get();
|
||||
for (i = 0; i < size; i += write_size)
|
||||
{
|
||||
if (i + write_size <= size)
|
||||
{
|
||||
cur_op_size = write_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
cur_op_size = size - i;
|
||||
}
|
||||
if (flash_dev)
|
||||
{
|
||||
result = flash_dev->ops.write(i, write_data, cur_op_size);
|
||||
}
|
||||
else if (part_dev)
|
||||
{
|
||||
result = fal_partition_write(part_dev, i, write_data, cur_op_size);
|
||||
}
|
||||
if (result < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result >= 0)
|
||||
{
|
||||
time_cast = rt_tick_get() - start_time;
|
||||
rt_kprintf("Write benchmark success, total time: %d.%03dS.\n", time_cast / RT_TICK_PER_SECOND,
|
||||
time_cast % RT_TICK_PER_SECOND / ((RT_TICK_PER_SECOND * 1 + 999) / 1000));
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("Write benchmark has an error. Error code: %d.\n", result);
|
||||
}
|
||||
/* read test */
|
||||
rt_kprintf("Reading %ld bytes data, waiting...\n", size);
|
||||
start_time = rt_tick_get();
|
||||
for (i = 0; i < size; i += read_size)
|
||||
{
|
||||
if (i + read_size <= size)
|
||||
{
|
||||
cur_op_size = read_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
cur_op_size = size - i;
|
||||
}
|
||||
if (flash_dev)
|
||||
{
|
||||
result = flash_dev->ops.read(i, read_data, cur_op_size);
|
||||
}
|
||||
else if (part_dev)
|
||||
{
|
||||
result = fal_partition_read(part_dev, i, read_data, cur_op_size);
|
||||
}
|
||||
/* data check */
|
||||
for (rt_size_t index = 0; index < cur_op_size; index ++)
|
||||
{
|
||||
if (write_data[index] != read_data[index])
|
||||
{
|
||||
rt_kprintf("%d %d %02x %02x.\n", i, index, write_data[index], read_data[index]);
|
||||
}
|
||||
}
|
||||
|
||||
if (memcmp(write_data, read_data, cur_op_size))
|
||||
{
|
||||
result = -RT_ERROR;
|
||||
rt_kprintf("Data check ERROR! Please check you flash by other command.\n");
|
||||
}
|
||||
/* has an error */
|
||||
if (result < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result >= 0)
|
||||
{
|
||||
time_cast = rt_tick_get() - start_time;
|
||||
rt_kprintf("Read benchmark success, total time: %d.%03dS.\n", time_cast / RT_TICK_PER_SECOND,
|
||||
time_cast % RT_TICK_PER_SECOND / ((RT_TICK_PER_SECOND * 1 + 999) / 1000));
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("Read benchmark has an error. Error code: %d.\n", result);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("Low memory!\n");
|
||||
}
|
||||
rt_free(write_data);
|
||||
rt_free(read_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("Usage:\n");
|
||||
for (i = 0; i < sizeof(help_info) / sizeof(char*); i++)
|
||||
{
|
||||
rt_kprintf("%s\n", help_info[i]);
|
||||
}
|
||||
rt_kprintf("\n");
|
||||
return;
|
||||
}
|
||||
if (result < 0) {
|
||||
rt_kprintf("This operate has an error. Error code: %d.\n", result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MSH_CMD_EXPORT(fal, FAL (Flash Abstraction Layer) operate.);
|
||||
|
||||
#endif /* defined(RT_USING_FINSH) && defined(FINSH_USING_MSH) */
|
||||
Reference in New Issue
Block a user