备注:
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); }