掉电安全文件系统littlefs移植

前言

通过查看oneOS中对littlefs的移植工作,发现,littlefs源码本身,有用的就4个:

  • lfs.c
  • lfs.h
  • lfs_util.c
  • lfs_util.h

剩下的就是适配层:

  • dfs_lfs.c
  • lfs_config.h(和lfs_util.h差不多)
  • lfs_crc.c(和lfs_util.c差不多)

然后再对比其源码,发现,littlefs的源码完全没有更改,更改的只有是适配层,因此,接下来重点看看适配层到底改了什么!

适配层更改

先看lfs_crc.c:

他和lfs_util.c几乎一样,唯一区别在于:

如果没有定义宏LFS_CONFIG,那么将使用lfs_util.c中的lfs_crc接口;

如果定义了宏LFS_CONFIG,那么将使用lfs_crc.c中的lfs_crc接口。

oneOS在sconscript中是定义了该宏的:

CPPDEFINES = ['LFS_CONFIG=lfs_config.h']

另外该宏在lfs_util.h中有被用到作为一个判断:

  • 用户可以通过定义一个头文件来包含自己的配置来覆盖 lfs_util.h,通过定义 LFS_CONFIG 为一个头文件来包含(-DLFS_CONFIG=lfs_config.h)。
  • 如果使用了 LFS_CONFIG,那么默认的lfs_util.h将不会被输出,必须由配置文件提供。建议复制 lfs_util.h 并根据需要修改。
#ifdef LFS_CONFIG
#define LFS_STRINGIZE(x) LFS_STRINGIZE2(x)
#define LFS_STRINGIZE2(x) #x
#include LFS_STRINGIZE(LFS_CONFIG)
#else
 ... ...
#endif

可见,是在lfs_util.h文件中,直接include lfs_config.h

再看lfs_config.h

同样,他就是lfs_util.h改过来的,并没有太大区别。主要看点在于:

  • 如果没有定义LFS_ASSERT&&LFS_NO_ASSERTutil.h用的是assert.h里面的函数assert,而config用的是oneOS自带的os_assert
  • 如果没有定义LFS_NO_MALLOC,说明lfsmalloc,这时候util.h用的是stdlib.h里面的函数mallocfree,而config用的是oneOS自带的os_mallocos_free
适配dfs_lfs.c

最重要的适配层vfs_lfs.c,我把她单独拎出来,作为最高的敬意。

其实她的逻辑很清晰,最开始就是基本的配置,然后是littlefs需要用到的底层函数的适配,这两个都是对*struct* lfs_config结构体成员的赋值,最后则是对接vfs层的接口适配。

基本配置
#ifndef RT_DEF_LFS_DRIVERS
    #define RT_DEF_LFS_DRIVERS 1
#endif

#if (RT_DEF_LFS_DRIVERS < 1)
    #error "#define RT_DEF_LFS_DRIVERS must > 0"
#endif

#ifndef LFS_READ_SIZE
    #define LFS_READ_SIZE 256
#endif

#ifndef LFS_PROG_SIZE
    #define LFS_PROG_SIZE 256
#endif

#ifndef LFS_BLOCK_SIZE
    #define LFS_BLOCK_SIZE 4096
#endif

#ifndef LFS_CACHE_SIZE
    #define LFS_CACHE_SIZE LFS_PROG_SIZE
#endif

#ifndef LFS_BLOCK_CYCLES
    #define LFS_BLOCK_CYCLES (-1)
#endif

#ifndef LFS_LOOKAHEAD_MAX
    #define LFS_LOOKAHEAD_MAX 128
#endif
lfs底层函数适配
static void _lfs_load_config(struct lfs_config* lfs_cfg, struct rt_mtd_nor_device* mtd_nor)
{
    uint64_t mtd_size;

    lfs_cfg->context = (void*)mtd_nor;

    lfs_cfg->read_size = LFS_READ_SIZE;
    lfs_cfg->prog_size = LFS_PROG_SIZE;

    lfs_cfg->block_size = mtd_nor->block_size;
    if (lfs_cfg->block_size < LFS_BLOCK_SIZE)
    {
        lfs_cfg->block_size = LFS_BLOCK_SIZE;
    }

    lfs_cfg->cache_size = LFS_CACHE_SIZE;
    lfs_cfg->block_cycles = LFS_BLOCK_CYCLES;

    mtd_size = mtd_nor->block_end - mtd_nor->block_start;
    mtd_size *= mtd_nor->block_size;
    lfs_cfg->block_count = mtd_size / lfs_cfg->block_size;

    lfs_cfg->lookahead_size = 32 * ((lfs_cfg->block_count + 31) / 32);
    if (lfs_cfg->lookahead_size > LFS_LOOKAHEAD_MAX)
    {
        lfs_cfg->lookahead_size = LFS_LOOKAHEAD_MAX;
    }
#ifdef LFS_THREADSAFE
    lfs_cfg->lock = _lfs_lock;        //如果定义了线程安全,则使用mutex互斥锁
    lfs_cfg->unlock = _lfs_unlock;
#endif
    lfs_cfg->read = _lfs_flash_read;  //littlefs用到的底层函数read
    lfs_cfg->prog = _lfs_flash_prog;  //littlefs用到的底层函数write
    lfs_cfg->erase = _lfs_flash_erase;//littlefs用到的底层函数erase 直接返回OK
    lfs_cfg->sync = _lfs_flash_sync;  //littlefs用到的底层函数sync  直接返回OK
}
vfs对接层

主要是为了对接vfs层,让vfs的接口可以直接使用底层littfs函数。

static int _lfs_result_to_dfs(int result)  //错误码转换
static int _vfs_lfs_mount(struct vfs_filesystem* vfs, unsigned long rwflag, const void* data)  //挂载
static int _vfs_lfs_unmount(struct vfs_filesystem* vfs) //卸载
static int _vfs_lfs_open(struct vfs_file* file)         //打开文件
... ...
static int _vfs_lfs_getdents(struct vfs_file* file, struct dirent* dirp, uint32_t count) //获得目录
适配总结

由此可见,适配lfs最重要的几步:

  • 如果定义了LFS_CONFIG,要自己做好配置
  • 基本的配置要做好,底层函数要适配,归结起来就是:struct lfs_config中的结构体成员要初始化好
  • vfs对接层要做好,这是每一个底层文件系统到vfs层必须要做的
oneos-lfs对照
    part_info->dev_info.dev    = (void *)dev;

    part_info->config.context = &part_info->dev_info;
    part_info->config.read    = g_lfs_dev_ops.read;
    part_info->config.prog    = g_lfs_dev_ops.prog;
    part_info->config.erase   = g_lfs_dev_ops.erase;
    part_info->config.sync    = g_lfs_dev_ops.sync;
#ifdef LFS_THREADSAFE
    part_info->config.lock    = g_lfs_dev_ops.lock;
    part_info->config.unlock  = g_lfs_dev_ops.unlock;
#endif

    part_info->config.read_size    = geometry.block_size;//最小读字节数,试了下不能乱改,sector
    part_info->config.prog_size    = geometry.block_size;//最小写字节数,试了下不能乱改,sector
    part_info->config.block_size   = geometry.block_size;//硬件块大小,试了下不能乱改,blocksize
    part_info->config.block_count  = geometry.capacity / geometry.block_size;//不用手动配
    part_info->config.block_cycles = -1;                //禁用块级磨损均衡
    part_info->config.cache_size    = geometry.block_size; //块缓存的大小,该值必须是读取和编程大小的倍数,并且是块大小的因数,就是要和block_size一样就好!!!

    part_info->config.lookahead_size= LFS_LOOKAHEAD_SIZE; //块分配时的预测深度(分配块时每次步进多少个块),这个数值必须为8的整数倍,这个可以改!!!
    
    part_info->config.read_buffer      = OS_NULL; //lfs_init中会分配!
    part_info->config.prog_buffer      = OS_NULL; //lfs_init中会分配!
    part_info->config.lookahead_buffer = OS_NULL; //lfs_init中会分配!
  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
文件系统移植SDIO是指将文件系统移植到SDIO(Secure Digital Input Output)接口上,以实现在SD卡或者其他SDIO设备上进行文件读写操作的功能。移植文件系统到SDIO上需要进行如下步骤: 首先,需要了解目标平台的硬件架构和SDIO接口的技术规范。SDIO接口是一种用于连接外部设备的接口,需要了解其数据传输协议、寄存器配置以及接口驱动程序的接口规范。 其次,需要准备适配文件系统的驱动程序。常见的文件系统包括FAT32、EXT4等,需要根据目标平台的操作系统和文件系统的特性来选择合适的文件系统驱动程序,并进行移植和适配。 接着,需要进行文件系统在SDIO接口上的初始化和配置。这包括SDIO控制器的初始化、数据传输模式的设置、寄存器的配置等操作,以确保文件系统可以正确地在SDIO接口上进行数据读写操作。 最后,进行文件系统的测试和调试。在移植文件系统到SDIO接口上之后,需要进行充分的测试和调试,确保文件读写操作的稳定性和可靠性。这包括对文件系统的读写性能、数据完整性和错误处理能力等方面进行全面的验证。 总的来说,文件系统移植到SDIO接口上需要深入了解硬件架构和接口规范,进行文件系统驱动程序的适配和移植,对SDIO接口进行初始化和配置,并进行充分的测试和调试。移植成功后,就可以在SD卡或其他SDIO设备上实现文件的读写操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值