• 掉电安全文件系统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中会分配!
    
  • 相关阅读:
    RTS游戏开发:基于四叉树的平面管理系统【加源码】
    MindSpore GPU版本安装指导
    工业异常检测顶会论文及源码合集,含2023最新
    网络安全笔记--文件上传1
    【老生谈算法】matlab实现DEA交叉评价源码——DEA算法
    如何避免在编码层面产生质量事故
    Hadoop HA (二) --------- HDFS-HA 手动模式
    智慧停车视频解决方案:如何让AI助力停车管理升级?
    Vue - 标准开发方式、组件(全局、局部、props、事件传递)、插槽的使用
    微电网优化调度|农村农业区可再生能源微电网优化调度(Python代码实现)
  • 原文地址:https://blog.csdn.net/xuxuluo/article/details/139587301