EMMC
对接块设备驱动及挂载文件系统
步骤
EMMC
驱动对接块设备驱动- 挂载文件系统
对接块设备驱动
- 修改
Kconfig
文件 - 修改
SConscript
文件 - 添加 emmc 驱动源码(
drv_emmc.c
和drv_emmc.h
)
Kconfig
文件
修改当前驱动配置文件的 Kconfig
,使 emmc
驱动可选的加入工程,例如楼主的配置文件为 temp/project/board/Kconfig
添加如下内容
menuconfig BSP_USING_EMMC
bool "Enable emmc"
default n
添加完成之后 menuconfig
配置界面如下图(linux:scons --menuconfig)
使能 emmc
选项之后,rtconfig.h
文件会定义 BSP_USING_EMMC
宏,后续文件会使用到。
修改 SConscript
文件
在驱动文件的 SConscript
文件中添加 BSP_USING_EMMC
判断加入 emmc
驱动源码。
注意这里的宏需要和上面 Kconfig
定义的宏保持一致,如果定义了 BSP_USING_EMMC
宏,那么 drv_emmc.c
源码就参与编译。
if GetDepend('BSP_USING_EMMC'):
src += ['drv_emmc.c']
实现块设备驱动
新建 drv_emmc.h
和 drv_emmc.c
文件,头文件中主要定义 emmc
驱动所需要的宏以及包含所需要的头文件。
drv_emmc.h
文件
/*
* Copyright (c) 2006-2020, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-03-23 tyustli first version
*/
#ifndef __DRV_EMMC_H_
#define __DRV_EMMC_H_
#include "hal_emmc.h"
#define EMMC_BLOCK_SIZE (512)
#define EMMC_BYTE_PER_SECTOR (512)
#define EMMC_BLOCK_CNT (65535 * 64)
#define EMMC_BLOCK_NAME "emmc"
#ifdef __cplusplus
extern "C" {
#endif
int rt_hw_emmc_init(void);
#ifdef __cplusplus
}
#endif
#endif /* not defined __DRV_EMMC_H_ */
/***************** end of file ****************/
- EMMC_BLOCK_SIZE 表示 emmc 块的大小
- EMMC_BYTE_PER_SECTOR 表示每个 sector 包含多少字节
- EMMC_BLOCK_CNT 表示一共有多少个块
- EMMC_BLOCK_NAME 块设备名称
前三个宏一般 emmc
型号确定之后该数字也就确定了,块设备名称可以用户自己指定,但是使用的时候需要使用同一个宏。
drv_emmc.c
文件
/*
* Copyright (c) 2006-2020, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-03-23 tyustli first version
*/
#include "rtconfig.h"
#ifdef BSP_USING_EMMC
#include "drv_emmc.h"
#include "board.h"
static struct rt_device emmc_device;
static struct mmc_host *emmc_host;
static rt_err_t rtt_emmc_init(rt_device_t dev)
{
return RT_EOK;
}
static rt_err_t rtt_emmc_open(rt_device_t dev, rt_uint16_t oflag)
{
return RT_EOK;
}
static rt_err_t rtt_emmc_close(rt_device_t dev)
{
return RT_EOK;
}
static rt_size_t rtt_emmc_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
int ret = MMC_NO_ERR;
ret = hal_emmc_read(emmc_host, pos, (void *)buffer, size);
if(ret != MMC_NO_ERR)
{
rt_kprintf("%s %d failed:%d\r\n", __func__, __LINE__, ret);
return -1;
}
return size;
}
static rt_size_t rtt_emmc_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
int ret = MMC_NO_ERR;
ret = hal_emmc_write(emmc_host, pos, (void *)buffer, size);
if(ret != MMC_NO_ERR)
{
rt_kprintf("%s %d failed:%d\r\n", __func__, __LINE__, ret);
return -1;
}
return size;
}
static rt_err_t rtt_emmc_control(rt_device_t dev, int cmd, void *args)
{
RT_ASSERT(dev != RT_NULL);
if (cmd == RT_DEVICE_CTRL_BLK_GETGEOME)
{
struct rt_device_blk_geometry *geometry;
geometry = (struct rt_device_blk_geometry *)args;
geometry->bytes_per_sector = EMMC_BLOCK_SIZE;
geometry->block_size = EMMC_BYTE_PER_SECTOR;
geometry->sector_count = EMMC_BLOCK_CNT;
rt_kprintf("bytes_per_sector:%d, block_size:%d sector_count:%d\r\n", geometry->bytes_per_sector, geometry->block_size, geometry->sector_count);
}
return RT_EOK;
}
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops emmc_ops =
{
.init = rtt_emmc_init,
.open = rtt_emmc_open,
.close = rtt_emmc_close,
.read = rtt_emmc_read,
.write = rtt_emmc_write,
.control = rtt_emmc_control
};
#endif /* RT_USING_DEVICE_OPS */
int rt_hw_emmc_init(void)
{
rt_err_t ret = RT_EOK;
/* 调用 HAL 层接口初始化 emmc */
hal_emmc_init(emmc_host);
RT_ASSERT(emmc_host != RT_NULL);
/* 设备类型为块设备类型 */
emmc_device.type = RT_Device_Class_Block;
emmc_device.rx_indicate = RT_NULL;
emmc_device.tx_complete = RT_NULL;
#ifdef RT_USING_DEVICE_OPS
emmc_device.ops = &emmc_ops;
#else /* RT_USING_DEVICE_OPS */
emmc_device.init = rtt_emmc_init;
emmc_device.open = rtt_emmc_open;
emmc_device.close = rtt_emmc_close;
emmc_device.read = rtt_emmc_read;
emmc_device.write = rtt_emmc_write;
emmc_device.control = rtt_emmc_control;
#endif /* not define RT_USING_DEVICE_OPS */
emmc_device.user_data = RT_NULL;
/* register a character device */
return rt_device_register(&emmc_device, EMMC_BLOCK_NAME, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STANDALONE);
}
INIT_DEVICE_EXPORT(rt_hw_emmc_init);
#endif /* BSP_USING_EMMC */
/********************** end of file *******************/
驱动文件有几个需要注意的地方
hal_emmc_init
函数可能不同平台实现不同,所以注册设备之前需要初始化自己的emmc
外设- 块设备的读写接口需要调用
hal
层的hal_emmc_read/write
读写接口,该接口不同平台实现不同 control
接口中需要获取emmc
的相关信息- 注册的设备类型为块设备
实现 drv_emmc.c
文件之后使用 list_device
命令就可以看到一个 emmc
的块设备
挂载文件系统
使能 elm
文件系统
menuconfig
使能文件系统
这里需要注意 emmc
的设置的 sector size
大小需要和文件系统配置的一致
挂载 elm
文件系统
#include "drv_emmc.h"
int mnt_init(void)
{
if (dfs_mount(EMMC_BLOCK_NAME, "/", "elm", 0, RT_NULL) < 0)
{
dfs_mkfs("elm", EMMC_BLOCK_NAME);
if (dfs_mount(EMMC_BLOCK_NAME, "/", "elm", 0, RT_NULL) < 0)
{
rt_kprintf("mount / failed\r\n");
return -1;
}
}
return 1;
}
dfs_mount
函数各个参数含义:
- EMMC_BLOCK_NAME:设备名称,即驱动中注册的块设备
- /:挂载点
- elm:文件系统类型
- 0:读写标志,一般为 0
- RT_NULL:保留数据,未使用