2022-08-17
共荣开源生态 共筑万物互联 ——汇顶科技揽获Open Harmony多项荣誉
写技术文章
精选推荐
东风永恒
在最新的GR5xx SDK中,实现了一套文件系统框架APP FS, 类似于Linux下的虚拟文件系统VFS, 向上提供一套标准的文件操作接口,向下提供适配方法。
一个具体的文件系统(如littlefs、fatfs)需要被支持,只需按照框架的接口规范提供其底层文件系统操作函数,并注册到APP
FS。
APP FS支持的特性:
APP FS源码存放于:“{SDK}/app/components/libraries/app_fs”。
2. 标准接口
APP FS向用户提供了统一的文件操作API, 可分为磁盘操作、文件操作、目录操作三大类。
操作函数 | 函数功能 |
---|---|
int app_fs_mount(const char *target, const char *fs_name, uint32_t mountflags, const void *fs_ops) | 挂载文件系统 |
int app_fs_umount(const char *target) | 卸载文件系统 |
int app_fs_format(const char *dev, const char *fs_name, void *data) | 格式化磁盘 |
int app_fs_fdisk(const char *dev, const char *fs_name, int *length_array, int partnum) | 获取磁盘信息 |
操作函数 | 函数功能 |
---|---|
int app_fs_open(const char *path, int flags) | 打开文件 |
int app_fs_close(int fd) | 关闭文件 |
size_t app_fs_read(int fd, char *buff, size_t bytes) | 从指定文件中读数据 |
size_t app_fs_write(int fd, const void *buff, size_t bytes) | 向指定文件写数据 |
size_t app_fs_lseek(int fd, int32_t off, int whence) | 定位到文件索引 |
int app_fs_stat(const char *path, fs_stat_t *stat) | 获取文件信息 |
int app_fs_unlink(const char *path) | 删除指定路径文件 |
int app_fs_rename(const char *old, const char *new) | 文件重命名 |
int app_fs_sync(int fd) | 文件操作同步 |
操作函数 | 函数功能 |
---|---|
fs_dir_t *app_fs_opendir(const char *path) | 打开指定路径目录 |
struct dirent *app_fs_readdir(fs_dir_t *d) | 读取目录信息 |
int app_fs_closedir(fs_dir_t *d) | 关闭一个目录 |
int app_fs_mkdir(const char *path, int mode) | 创建一个目录 |
int app_fs_rmdir(const char *path) | 删除指定路径目录 |
3. 文件系统适配
APP
FS底层不限制具体的文件系统种类,也不限制文件系统的数量,因为一个系统中可能需要存在多种不同的文件系统。本章节主要以在小型嵌入式设备上使用非常广泛的Littlefs为例,指导如何将其适配到APP FS中。
3.1 Littlefs简介
Littlefs是一个为微控制器设计的安全文件系统,常用于nor flash上,具有以下特点:
3.2 Littlefs适配
Littlefs开源地址:https://github.com/littlefs-project/littlefs/tags
Littlefs目前最新版本为v2.5.0,GR5xx SDK使用v2.5.0的源码并存放于“{SDK}/app/external/littlefs”。
3.2.1 抽象文件系统操作函数
APP FS对底层文件操作进行了抽象,分别定义了文件操作、挂载操作、磁盘管理三大类文件系统作操函数的operation结构体。
typedef struct mount_ops {
int (*mount)(struct fs_mp *mp, unsigned long mountflags, const void *ops);
int (*umount)(struct fs_mp *mp);
int (*statfs)(const char *path, fs_statfs_t *buf);
} mount_ops_t;
typedef struct file_ops {
int (*open)(struct file *, const char *, int);
int (*close)(struct file *);
size_t (*read)(struct file *, char *, size_t);
size_t (*write)(struct file *, const char *, size_t);
size_t (*lseek)(struct file *, int32_t, int);
int (*stat)(struct fs_mp *, const char *, struct _stat *);
int (*unlink)(struct fs_mp *, const char *);
int (*rename)(struct fs_mp *, const char *, const char *);
int (*ioctl)(struct file *, int, unsigned long);
int (*sync)(struct file *);
int (*opendir)(struct dir *, const char *);
int (*readdir)(struct dir *, struct dirent *);
int (*closedir)(struct dir *);
int (*mkdir)(struct fs_mp *, const char *);
int (*rmdir)(struct fs_mp *, const char *);
} file_ops_t;
typedef struct mgmt_ops {
int (*fdisk)(const char *dev, int *length_array, int part_num);
int (*format)(const char *dev, void *data);
} mgmt_ops_t;
APP FS提供一个文件系统的注册函数,函数原型如下:
int app_fs_register(const char *fs_name, mount_ops_t *mount_ops, file_ops_t *file_ops, mgmt_ops_t *mgmt_ops)
用户在适配完成一个文件系统后,需要调用app_fs_register函数将operation结构体信息注册到文件系统信息链表中,当应用程序调用app_fs文件系统操作API时,内部会从文件系统信息链表中找到operation结构体对应的文件系统操作函数,以app_fs_open接口为例,整个链路如下图所示:
3.2.2 适配步骤
上一小节主要介绍了对抽象函数operation结构体的认识,实际上文件系统的适配工作就是对这些抽象函数进行具体化实现。
3.2.2.1 文件操作函数实现
新建一个lfs_adapter.c文件,分别定义g_lfs_mount_ops、g_lfs_file_ops、g_lfs_mgmt,实现如下所示 :
static struct mount_ops g_lfs_mount_ops = {
.mount = __lfs_mount,
.umount = __lfs_umount,
.statfs = NULL,
};
static struct file_ops g_lfs_file_ops = {
.open = __lfs_open,
.close = __lfs_close,
.read = __lfs_read,
.write = __lfs_write,
.lseek = __lfs_seek,
.stat = __lfs_stat,
.unlink = __lfs_unlink,
.rename = __lfs_rename,
.ioctl = NULL, /* not support */
.sync = __lfs_sync,
.opendir = __lfs_open_dir,
.readdir = __lfs_read_dir,
.closedir = __lfs_close_dir,
.mkdir = __lfs_mkdir,
.rmdir = __lfs_rmdir,
};
static struct mgmt_ops g_lfs_mgmt = {
.fdisk = NULL,
.format = NULL,
};
根据littlefs的文件系统操作API,封装实现__lfs_mount、__lfs_open等操作函数,如下代码段所示:
static int __lfs_open(struct file *file, const char *path_name, int flag)
{
int ret;
if ((path_name == NULL) || (file == NULL) || (file->mp == NULL) ||
(file->mp->data == NULL))
{
return FS_ERROR;
}
lfs_file_t *lfs_file = (lfs_file_t *)fs_mem_malloc(sizeof(lfs_file_t));
if (file == NULL)
return FS_ERROR;
int lfs_open_flag = convert_to_lfs_flag(flag);
ret = lfs_file_open((lfs_t *)file->mp->data, lfs_file, path_name, lfs_open_flag);
if (ret != 0)
{
fs_mem_free(lfs_file);
}
else
{
file->data = (void *)lfs_file;
}
return ret;
}
3.2.2.2 文件系统注册
APP FS支持挂载多个文件系统,适配的文件系统信息需要注册到内部的管理链表,需调用app_fs_register进行注册。
void lfs_init(void)
{
app_fs_register("littlefs", &g_lfs_mount_ops, &g_lfs_file_ops, &g_lfs_mgmt);
}
3.2.2.3 底层存储介质操作
文件系统会将文件的数据内容存储在存储介质上,例如flash、nand、SD卡等,而这些存储介质的底层操作实现又有很大的差异。为了和APP FS框架解耦,将存储介质的读、写、擦操作函数放在应用层代码实现,函数地址和一些配置参数封装在 lfs_ops结构体中,在app_fs_mount进行文件系统挂载的时候传入文件系统内部。
Littlefs定义了一个lfs_config结构体,结构体成员函数指针需要指向用户适配的存储介质读、写、擦操作函数,并配置存储介质操作块大小、块个数、和cache size等参数。
struct lfs_config {
void *context;
int (*read)(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size);
int (*prog)(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size);
int (*erase)(const struct lfs_config *c, lfs_block_t block);
int (*sync)(const struct lfs_config *c);
…
lfs_size_t read_size;
lfs_size_t prog_size;
lfs_size_t block_size;
lfs_size_t block_count;
};
在lfs_adapter.h中定义lfs_ops结构体, cfg记录Littlefs的配置信息,start_addr记录当前存储介质起始地址:
typedef struct {
struct lfs_config cfg; /**< Littlfs config structures */
uint32_t start_addr; /**< The starting address of the storage space by the file system */
} lfs_ops_t;
操作存储介质的读、写、擦实现可参考fs_demo示例工程,介质读取函数实现如下代码段所示:
int lfs_block_read(const struct lfs_config *cfg, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size)
{
uint32_t addr;
lfs_ops_t *lfs_ops = (lfs_ops_t *)cfg;
addr = lfs_ops->start_addr + block * cfg->block_size + off;
return size == hal_flash_read(addr, (uint8_t*)buffer, size) ? LFS_ERR_OK : LFS_ERR_IO;
}
应用程序在挂载文件系统时,将lfs_ops传入底层的Littlefs文件系统:
static lfs_ops_t s_lfs0_ops;
void fs_mount_point0(const char *target)
{
s_lfs0_ops.cfg.read = lfs_block_read;
s_lfs0_ops.cfg.prog = lfs_block_prog;
s_lfs0_ops.cfg.erase = lfs_block_erase;
s_lfs0_ops.cfg.sync = lfs_device_sync;
s_lfs0_ops.cfg.read_size = 16;
s_lfs0_ops.cfg.prog_size = 16;
s_lfs0_ops.cfg.block_size = 4096;
s_lfs0_ops.cfg.block_count = LFS0_FLASH_SIZE / 4096;
s_lfs0_ops.cfg.block_cycles = 500;
s_lfs0_ops.cfg.cache_size = 16;
s_lfs0_ops.cfg.lookahead_size = 32;
s_lfs0_ops.start_addr = LFS0_FLASH_START_ADDR;
app_fs_mount(target, "littlefs", MS_FORMAT, &s_lfs0_ops);
}
4. APP FS应用
应用层使用APP FS,主要关注两点:
APP FS使用流程如下图所示:
更详尽的APP FS示例Demo请参见“{SDK}/app/projects/test/components/app_fs_rtos”。
打开微信,使用“扫一扫”即可关注