备注:
1. Kernel版本:5.4
2. 使用工具:Source Insight 4.0
3. 参考博客:
Linux MMC framework(1)_软件架构
1. [mmc subsystem] 概念与框架
mmc有很多种意义,具体如下:
卡与主控制器间串行传送,工作时钟频率范围为0~200 MHz。
mmc总线上最多可识别64 K个mmc设备,在总线上不超过10个卡时,可运行到最高频率。
使用mmc接口规范(MCI, Multimedia Card Interface)的设备都可以称之为mmc设备。
可分成三个种类,如下:
(1)标准mmc卡:闪存卡的一种,使用mmc标准。
(2)emmc:Embedded MultiMediaCard,是MMC协会所制定的内嵌式存储器标准规格,带有mmc接口,是具备mmc协议的芯片。
(1)sd卡:SD卡为Secure Digital Memory Card, 即安全数码卡。它在MMC的基础上发展而来,增加了两个主要特色:SD卡强调数据的安全,可以设定所储存的使用权限,防止数据被他人复制。兼容mmc接口规范。
(1)sdio设备:SDIO是在SD标准上定义了一种外设接口,它和SD卡规范间的一个重要区别是增加了低速标准。在SDIO卡只需要SPI和1位SD传输模式。低速卡的目标应用是以最小的硬件开销支持低速IO能力。常见的sdio设备有Wi-Fi card、Bluetooth card等等。
注意,这几种类型的card统称为mmc card。
类似i2c协议、spi协议,mmc总线上也有一套自己的通讯规范。通信规范后续在说明。
而上述mmc设备基于上mmc总线通讯规范上由自身硬件特性设置了自己的一套协议。
jedec的协议规范可以去jedec的官网上下载。
<1> mmc4.0
<2> mmc4.1《MultiMediaCard (MMC) Electrical Standard, Standard Capacity (MMCA, 4.1)》
<3>mmc4.2《MultiMediaCard (MMC) Electrical Standard, High Capacity (MMCA, 4.2)》
<1> emmc4.41《Embedded MultiMediaCard(e•MMC) e•MMC/Card Product Standard, High Capacity, including Reliable Write, Boot, Sleep Modes, Dual Data Rate, Multiple Partitions Supports, Security Enhancement, Background Operation and High Priority Interrupt (MMCA, 4.41)》
<2> emmc4.51《Embedded Multimedia Card (e•MMC), Electrical Standard 4.51》
<3> emmc5.0《Embedded Multi-Media Card (e•MMC) Electrical Standard (5.01)》
<4> emmc5.1《Embedded Multi-Media Card (e•MMC) Electrical Standard (5.1)》
<1> sd1.0《SD Host Controller Simplified Specification》
<2> sd2.0《SD Host Controller Simplified Specification》
<3> sd3.0《SD Host Controller Simplified Specification》
<4> sdio4.2《SD Host Controller Simplified Specification》
<1> sdio1.0《SDIO Simplified Specification》
<2> sdio1.1《SDIO Simplified Specification》
<3> sdio2.0《SDIO Simplified Specification》
<4> sdio3.0《SDIO Simplified Specification》
MMC framework分别有“从左到右”和“从下到上”两种层次结构。
host,负责驱动Host controller,提供诸如访问card的寄存器、检测card的插拔、读写card等操作方法。从设备模型的角度看,host会检测卡的插入,并向bus注册MMC card设备;
bus,是MMC bus的虚拟抽象,以标准设备模型的方式,收纳MMC card(device)以及对应的MMC driver(driver);
card,抽象具体的MMC卡,由对应的MMC driver驱动(从这个角度看,可以忽略MMC的技术细节,只需关心一个个具有特定功能的卡设备,如存储卡、WIFI卡、GPS卡等等)。
MMC framework从下到上也有3个层次(老生常谈了):
MMC core位于中间,是MMC framework的核心实现,负责抽象host、bus、card等软件实体,负责向底层提供统一、便利的编写Host controller driver的API;
MMC host controller driver位于底层,基于MMC core提供的框架,驱动具体的硬件(MMC controller);
MMC card driver位于最上面,负责驱动MMC core抽象出来的虚拟的card设备,并对接内核其它的framework(例如块设备、TTY、wireless等),实现具体的功能。
这里补充说明,sdhci并不是实际的host驱动,而是上述说明的sdhc标准的host的驱动部分,如:sdhci-msm和sdhci-s3c都使用了SDHC标准。
core(核心层)
主要子文件:bus.h/.c、core.h/.c、host.h/.c、mmc.c、sdio.c、sd.c、block.c、queue.h、queue.c等。
作用一:主要是按照 LINUX 块设备驱动程序的框架实现一个卡的块设备驱动;
作用二:核心层封装了 MMC/SD 卡的命令,例如存储卡的识别,设置,读写;
文件说明:
block.c: 在 该文件当中我们可以看到写一个块设备驱动程序时需要的 block_device_operations 结构体变量的定义,其中有 open/release/ioctl 函数的实现;
queue.c :则是对内核提供的请求队列的封装,我们暂时不用深入理解它,只需要知道一个块设备需要一个请求队列就可以了。
bus:总线相关操作,连接驱动和设备的桥梁;
core:提供存储卡的相关操作,core.c由 sd.c、mmc.c 两个文件支撑的, core.c 把 MMC 卡、 SD 卡的共性抽象出来,它们的差别由 sd.c 和 sd_ops.c 、 mmc.c 和 mmc_ops.c 来完成。
host(主机控制层)
主机控制器则是依赖于不同的平台的,例如 s3c2410 的卡控制器和 atmel 的卡控制器必定是不一样的,所以要针对不同的控制器来实现。
以 s3cmci.c 为例,它首先要进行一些设置,例如中断函数注册,全能控制器等等。然后它会向 core 层注册一个主机( host ),用结构 mmc_host_ops 描述,这样核心层就可以拿着这个 host 来操作 s3c24xx 的卡控制器了,而具体是 s3c24xx 的卡控制器还是 atmel 的卡控制器, core 层是不用知道的。
mmc bus节点的对应路径为/sys/bus/mmc。在mmc_register_bus中生成。
在devices目录下有如下节点
/sys/bus/mmc/devices/mmc0:5048
其中mmc0:5048就是mmc core抽象出来的card设备,对应于我们板子上的sd。
对应代码参考mmc_alloc_card & mmc_add_card。
在drivers目录下有如下节点
/sys/bus/mmc/drivers/mmcblk
其中mmcblk就是block.c中实现的card driver。
对应代码参考mmc_register_driver。
在devices目录下有如下节点
/sys/bus/sdio/devices/mmc1:0001:1
其中mmc1:0001:1就是mmc core抽象出来的card设备,对应于我们板子上的sdio wifi。
对应代码参考mmc_alloc_card & mmc_add_card。
在drivers目录下有如下节点
/sys/bus/sdio/drivers/rtl8189fs
mmc core实现了一个class用于维护和管理mmc host。其对应路径为/sys/class/mmc_host。mmc core会为每个注册到mmc core中的mmc host在该class目录下添加一个对应的节点。在mmc_add_host中生成。
示例如下:
创建class的代码参考mmc_register_host_class
在/sys/class/mmc_host下有如下目录:
/sys/class/mmc_host/mmc0
/sys/class/mmc_host/mmc1
mmc0就是对应alias序号为0的host,而mmc1就是对应alias序号为1的host。 具体代码参考mmc_alloc_host。
/sys/class/mmc_host/mmc0下有如下属性:
device mmc0:5048 power subsystem uevent
/sys/class/mmc_host/mmc1下有如下属性:
device mmc1:0001 power subsystem uevent
mmc core把mmc设备抽象为card设备。
从两个地方可以找到其对应的sys节点。
简单示例如下:
以mmc0:5048设备为例,mmc0表示这个card挂载mmc0这个host上,0001表示card设备地址为5048(也就是RCA,协议的东西,后续会说明)
(1)/sys/bus/mmc/devices/mmc0:5048(因为是挂在mmc bus上)
(2)/sys/class/mmc_host/mmc0/mmc0:5048(因为card的parent device为mmc host的class device)
具体代码参考mmc_alloc_card & mmc_add_card。
其有如下属性:
block hwrev scr
cid manfid serial
csd name ssr
date ocr subsystem
driver oemid type
dsr power uevent
erase_size preferred_erase_size
fwrev rca
mmc core为每个注册到core中的host创建了对应的debug节点。
以mmc0这个host为例,其对应节点路径为/sys/kernel/debug/mmc0。
/sys/kernel/debug/mmc0有如下属性:
caps caps2 clock ios mmc0:5048
具体代码参考mmc_add_host——》mmc_add_host_debugfs
mmc0:5048下有如下debug属性:
state status
源码路径:drivers/mmc/core/core.c
static int __init mmc_init(void)
{
int ret;
ret = mmc_register_bus();//注册mmc bus
if (ret)
return ret;
ret = mmc_register_host_class(); //注册mmc_host class
if (ret)
goto unregister_bus;
ret = sdio_register_bus(); //注册sdio bus
if (ret)
goto unregister_host_class;
return 0;
unregister_host_class:
mmc_unregister_host_class();
unregister_bus:
mmc_unregister_bus();
return ret;
}
static void __exit mmc_exit(void)
{
sdio_unregister_bus();
mmc_unregister_host_class();
mmc_unregister_bus();
}
subsys_initcall(mmc_init);
module_exit(mmc_exit);
MODULE_LICENSE("GPL");
mmc bus注册:
源码路径:drivers/mmc/core/bus.c
static struct bus_type mmc_bus_type = {
.name = "mmc",
.dev_groups = mmc_dev_groups,
.match = mmc_bus_match,
.uevent = mmc_bus_uevent,
.probe = mmc_bus_probe,
.remove = mmc_bus_remove,
.shutdown = mmc_bus_shutdown,
.pm = &mmc_bus_pm_ops,
};
int mmc_register_bus(void)
{
return bus_register(&mmc_bus_type);
}
sdio bus注册:
源码路径:drivers/mmc/core/sdio_bus.c
static struct bus_type sdio_bus_type = {
.name = "sdio",
.dev_groups = sdio_dev_groups,
.match = sdio_bus_match,
.uevent = sdio_bus_uevent,
.probe = sdio_bus_probe,
.remove = sdio_bus_remove,
.pm = &sdio_bus_pm_ops,
};
int sdio_register_bus(void)
{
return bus_register(&sdio_bus_type);
}
mmc_host class注册:
源码路径:drivers/mmc/core/host.c
static struct class mmc_host_class = {
.name = "mmc_host",
.dev_release = mmc_host_classdev_release,
};
int mmc_register_host_class(void)
{
return class_register(&mmc_host_class);
}