[文章]基于OpenHarmony3.1的LittleFS文件系统hdf驱动实现

阅读量0
0
0
一、简介
LittleFS是一个小型的Flash文件系统,它结合日志结构(log-structured)文件系统和COW(copy-on-write)文件系统的思想,以日志结构存储元数据,以COW结构存储数据。这种特殊的存储方式,使LittleFS具有强大的掉电恢复能力(power-loss resilience)。分配COW数据块时LittleFS采用了名为统计损耗均衡的动态损耗均衡算法,使Flash设备的寿命得到有效保障。同时LittleFS针对资源紧缺的小型设备进行设计,具有极其有限的ROM和RAM占用,并且所有RAM的使用都通过一个可配置的固定大小缓冲区进行分配,不会随文件系统的扩大占据更多的系统资源。当在一个资源非常紧缺的小型设备上,寻找一个具有掉电恢复能力并支持损耗均衡的Flash文件系统时,LittleFS是一个比较好的选择。

LittleFS在嵌入式开发过程中经常遇到,但是如何在OpenHarmony中使用呢?本文基于OpenHarmony 3.1Release + 小凌派-RK2206开发板做LittleFS文件系统移植,现将相关移植经验发布,分享给大家。文中如有问题,请大家帮忙指正。

二、LittleFS移植过程
本文基于OpenHarmony3.1Release做LittleFS移植,小凌派-RK2206开发板内部Flash有8MB大小,其中4~8MB区间为空闲区域。我将4M~5M作为LittleFS文件系统的/data目录挂载硬件设备。具体移植过程主要如下所示:

1、hcs配置
1.1、hdf.hcs
创建/device/soc/rockchip/rk2206/hcs_config/hdf.hcs文件,具体如下:
  1. #include "device_info/device_info.hcs"
  2. #include "fs/fs_config.hcs"
  3. #include "gpio/gpio_config.hcs"
  4. #include "i2c/i2c_config.hcs"
  5. #include "spi/spi_config.hcs"
  6. root {
  7.     module = "rockchip,rk2206_chip";
  8. }
复制代码

如上所述,我将在device_info/device_info.hcs添加LittleFS设备,并在fs/fs_config.hcs添加LittleFS具体信息。

1.2、BUILD.gn
新建//device/soc/rockchip/rk2206/hdf_config/BUILD.gn,具体代码如下所示:
  1. import("//drivers/adapter/khdf/liteos_m/hdf.gni")
  2. module_switch = defined(LOSCFG_DRIVERS_HDF)
  3. module_name = get_path_info(rebase_path("."), "name")
  4. hdf_driver(module_name) {
  5.   hcs_sources = [ "hdf.hcs" ]
  6. }
复制代码
上述代码将在编译OpenHarmony3.1Rlease时,将编译hdf.hcs。

1.3、device_info.hcs
创建/device/soc/rockchip/rk2206/hcs_config/device_info/device_info.hcs文件,在文件中添加LittleFS设备,具体代码如下所示:
  1. device_fs :: device {
  2.                 device0 :: deviceNode {
  3.                     policy = 0;
  4.                     priority = 5;
  5.                     permission = 0777;
  6.                     moduleName = "HDF_PLATFORM_FS_LITTLEFS";
  7.                     serviceName = "littlefs_config";
  8.                     deviceMatchAttr = "rockchip_rk2206_fs_littlefs";
  9.                 }
  10.             }
复制代码
上述代码表示建设一个设备驱动,该驱动的模块名称(即moduleName)为“HDF_PLATFORM_FS_LITTLEFS”,OpenHamrony系统依据该名称匹配驱动程序;设备匹配信息(即deviceMatchAttr)添加小凌派开发板Flash特殊信息(比如:分区信息,挂载目录名、起始地址、结束地址等)。

1.4、fs_config.hcs
新建//device/soc/rockchip/rk2206/hdf_config/fs/fs_config.hcs文件,该文件主要写清楚设备挂载信息,具体如下:
  1. root {
  2.     platform {
  3.         fs_config {
  4.             template fs_controller {
  5.                 match_attr = "";
  6.                 mount_points = [];
  7.                 block_size = [];
  8.                 block_start = [];
  9.                 block_count = [];
  10.             }

  11.             fs_littefs :: fs_controller {
  12.                 match_attr = "rockchip_rk2206_fs_littlefs";
  13.                 mount_points = ["/data"];
  14.                 block_size = [4096];
  15.                 block_start = [1024];
  16.                 block_count = [256];
  17.             }
  18.         }
  19.     }
  20. }
复制代码

(1)points:挂载目录。
(2)block_size:Flash的擦除块大小。
(3)block_start:该挂载Flash区域的起始块地址,实际Flash地址为block_size * block_start。
(4)block_count:该挂载Flash区域的存储块总数。
注意:match_attr所表示的字符串要与device_info.hcs所表示的字符串要一致。

2、hdf驱动
新建//device/soc/rockchip/rk2206/hdf_driver/fs文件夹,文件夹下创建2个文件,具体如下所示:
2.1、fs_driver.c
2.1.1、添加必要的头文件
  1. #include <sys/mount.h>
  2. #include <string.h>
  3. #include "los_config.h"
  4. #include "hdf_log.h"
  5. #include "hdf_device_desc.h"
  6. #include "device_resource_if.h"
  7. #include "osal_mem.h"
  8. #include "lfs_api.h"
复制代码
2.1.2、添加HDF驱动
添加LittleFS匹配的hdf驱动,具体代码如下所示:
  1. static struct HdfDriverEntry g_fsDriverEntry = {
  2.     .moduleVersion = 1,
  3.     .moduleName = "HDF_PLATFORM_FS_LITTLEFS",
  4.     .Bind = fs_driver_bind,
  5.     .Init = fs_driver_init,
  6.     .Release = fs_driver_release,
  7. };
复制代码
HDF_INIT(g_fsDriverEntry);
其中,moduleName必须要与device_info.hcs中的moduleName保持一致。


2.1.3、fs_driver_init函数
fs_driver_init为hdf驱动加载函数。OpenHarmony启动时,将根据hcs的编写匹配对应的驱动程序,并运行fs_driver_init函数。该函数负责:
(1)读取hcs文件的配置参数。
(2)初始化Flash。
(3)适配LittleFS对应的read、write、erase和sync函数,并适配LittleFS相关参数。
(4)挂载LittleFS。

具体代码如下所示:
  1. static int32_t fs_driver_init(struct HdfDeviceObject *device)
  2. {
  3.     int result;
  4.     int32_t ret;
  5.     struct FileOpInfo *file_op_info = NULL;

  6.     if (device == NULL) {
  7.         PRINT_ERR("device is nulln");
  8.         return HDF_ERR_INVALID_OBJECT;
  9.     }
  10.     if (device->property == NULL)
  11.     {
  12.         PRINT_ERR("device is nulln");
  13.         return HDF_ERR_INVALID_OBJECT;
  14.     }

  15.     /* Flash设备初始化 */
  16.     FlashInit();

  17. /* 读取hcs参数 */
  18.     ret = fs_driver_readdrs(device->property, &m_fs_cfg[0]);
  19.     if (ret != HDF_SUCCESS)
  20.     {
  21.         PRINT_ERR("%s: fs_driver_readdrs failed(%d)n", ret);
  22.         return ret;
  23.     }
  24.    
  25. /* 适配LitteleFS对应的函数和参数 */
  26.     for (int i = 0; i < sizeof(m_fs_cfg) / sizeof(m_fs_cfg[0]); i++) {
  27.         if (m_fs_cfg[i].mount_point == NULL)
  28.         {
  29.             PRINT_LOG("m_fs_cfg[%d].mount_point is nulln", i);
  30.             continue;
  31.         }

  32.         m_fs_cfg[i].lfs_cfg.read = flash_littlefs_read;
  33.         m_fs_cfg[i].lfs_cfg.prog = flash_littlefs_write;
  34.         m_fs_cfg[i].lfs_cfg.erase = flash_littlefs_erase;
  35.         m_fs_cfg[i].lfs_cfg.sync = flash_littlefs_sync;

  36.         m_fs_cfg[i].lfs_cfg.read_size = 4;
  37.         m_fs_cfg[i].lfs_cfg.prog_size = 4;
  38.         m_fs_cfg[i].lfs_cfg.cache_size = 256;
  39.         m_fs_cfg[i].lfs_cfg.lookahead_size = 64;
  40.         m_fs_cfg[i].lfs_cfg.block_cycles = 1000;

  41.         m_fs_cfg[i].lfs_cfg.file_max = LFS_FILE_MAX;
  42.         m_fs_cfg[i].lfs_cfg.name_max = LFS_NAME_MAX;
  43.         
  44. /* 准备挂载 */
  45.         result = SetDefaultMountPath(i, m_fs_cfg[i].mount_point);
  46.         if (result != VFS_OK)
  47.         {
  48.             PRINT_ERR("SetDefaultMountPath(%d, %d) failed(%d)n", i, m_fs_cfg[i].mount_point, result);
  49.             continue;
  50.         }
  51.         
  52. /* 挂载目录 */
  53.         result = mount(NULL, m_fs_cfg[i].mount_point, "littlefs", 0, &m_fs_cfg[i].lfs_cfg);
  54.         printf("%s: mount fs on '%s' %sn", __func__, m_fs_cfg[i].mount_point, (result == 0) ? "succeed" : "failed");
  55.         if (CheckPathIsMounted(m_fs_cfg[i].mount_point, &file_op_info) == TRUE) {
  56.             int lfs_ret = lfs_mkdir(&file_op_info->lfsInfo, m_fs_cfg[i].mount_point);
  57.             if (lfs_ret == LFS_ERR_OK) {
  58.                 PRINT_LOG("create root dir(%s) success.n", m_fs_cfg[i].mount_point);
  59.             } else if (lfs_ret == LFS_ERR_EXIST) {
  60.                 PRINT_LOG("root dir(%s) exist.n", m_fs_cfg[i].mount_point);
  61.             } else {
  62.                 PRINT_LOG("create root dir(%s) failed.", m_fs_cfg[i].mount_point);
  63.             }
  64.         }
  65.     }
  66.    
  67.     return HDF_SUCCESS;
  68. }
复制代码
2.2、BUILD.gn
BUILD.gn负责将fs_driver.c编译到内核中,具体源代码如下所示:
  1. import("//drivers/adapter/khdf/liteos_m/hdf.gni")
  2. module_switch = defined(LOSCFG_SOC_SERIES_RK22XX) && defined(LOSCFG_DRIVERS_HDF_PLATFORM) && defined(LOSCFG_FS_LITTLEFS)
  3. module_name = get_path_info(rebase_path("."), "name")
  4. hdf_driver(module_name) {
  5.   sources = [
  6.     "fs_driver.c",
  7.   ]
  8.   include_dirs = [ "." ]
  9. }
复制代码
3、测试程序
我在main函数中添加一个任务,负责每隔5秒读写文件。具体代码如下所示:
  1. /* 文件系统测试 */
  2. static void file_rw()
  3. {
  4.     static unsigned int cur = 0;
  5.     char file_name[] = "/data/a.txt";
  6.     int fd_w, fd_r;
  7.     unsigned char buffer[256];

  8.     /* 写操作 */
  9.     fd_w = open(file_name, O_WRONLY | O_CREAT);
  10.     if (fd_w == -1)
  11.     {
  12.         printf("write: %s open failed!n", file_name);
  13.         return;
  14.     }
  15.     memset(buffer, 0, sizeof(buffer));
  16.     snprintf(buffer, sizeof(buffer), "Hello World and %dn", cur++);
  17.     printf("write: %s", buffer);
  18.     write(fd_w, buffer, strlen(buffer));
  19.     close(fd_w);

  20.     /* 读操作 */
  21.     fd_r = open(file_name, O_RDONLY);
  22.     if (fd_r == -1)
  23.     {
  24.         printf("read: %s open failed!n", file_name);
  25.         return;
  26.     }
  27.     lseek(fd_r, 0, SEEK_SET);
  28.     memset(buffer, 0, sizeof(buffer));
  29.     read(fd_r, buffer, sizeof(buffer));
  30.     printf("read: %s", buffer);
  31.     close(fd_r);
  32. }

  33. static void IotProcess(void *arg)
  34. {
  35.     static const unsigned int SLEEP_MAXSEC = 5;
  36.    
  37.     while (1)
  38.     {
  39.         printf("%s: sleep %d sec!n", __func__, SLEEP_MAXSEC);
  40.         
  41.         /* 文件系统测试 */
  42.         file_rw();

  43.         
  44.         LOS_Msleep(SLEEP_MAXSEC * 1000);
  45.     }
  46. }
复制代码

三、实验结果
程序编译烧写到开发板后,按下开发板的RESET按键,通过串口软件查看日志如下:
  1. [MAIN:D]Main: OpenHarmony start schedule...
  2. Entering scheduler
  3. IotProcess: sleep 5 sec!
  4. write: Hello World and 0
  5. read: Hello World and 0
  6. IotProcess: sleep 5 sec!
  7. write: Hello World and 1
  8. read: Hello World and 1
  9. IotProcess: sleep 5 sec!
  10. write: Hello World and 2
  11. read: Hello World and 2
  12. ......
复制代码

回帖

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容图片侵权或者其他问题,请联系本站作侵删。 侵权投诉
链接复制成功,分享给好友