linux mmc 框架源码分析
文章基于
- 内核版本
- linux-3.2.0x
- CPU
- 芯唐 的 NUC972
- 源码路径 linux-3.10.x\drivers\mmc
文章仅分析 sd卡这类设备在 Linux mmc 框架中的源码
本文基于个人的理解与思考, 如有错误的地方欢迎指正.
linux mmc 框架代码目录结构
源码路径 kernel-3.2\drivers\mmc
mmc/
|-- card
| |-- block.c
| |-- block.o
| |-- queue.c
| |-- queue.h
| `-- sdio_uart.c
|-- core
| |-- bus.c
| |-- bus.h
| |-- core.c
| |-- core.h
| |-- host.c
| |-- host.h
| |-- quirks.c
| |-- sd.c
| |-- sd.h
| |-- sd_ops.c
| `-- sd_ops.h
`-- host
|-- nuc970_emmc.c
`-- nuc970_sd.c
- card
- mmc card 层代码, 实现块设备的各种功能接口
- core
- mmc 核心层代码, 实现检测外接存储(如sd卡)和生成对应设备的一整个流程
- host
- mmc host 层代码,对应不同的cpu有不同的 host 驱动,通过对 cpu mmc 相关寄存器控制实现与外接sd卡的命令/数据收发
- 目录下的 nuc970_sd.c 就是 NUC972 芯片的针对 sd 类的设备的适配器驱动
- 而 nuc970_emmc.c 就是 NUC972 芯片的针对 emmc 类的设备的适配器驱动
linux mmc 框架
linux mmc 分为三层,分别是:
- mmc card 层
- mmc 核心层
- mmc host 层
mmc 核心层
mmc 核心层提供一下功能:
- 提供 mmc host 注册接口: int mmc_add_host(struct mmc_host *host)
- 提供 mmc card 驱动注册接口: int mmc_register_driver(struct mmc_driver *drv)
- 负责外接sd卡的检测和检测到sd卡接入后生成对应总线设备的一整个过程
mmc host 层
- 调用核心层接口 mmc_add_host() 注册一个 mmc host
- 提供最外接sd卡的一系列操作函数, 即 struct mmc_host_ops 结构体的一系列函数, 如 .get_cd 函数实现检测sd卡是否在位
- 实现检测sd卡插入/拔出的机制(如中断)
struct mmc_host_ops 结构体包含了一系列对外接sd卡的操作函数, 结构体中比较常用的回调有以下几个:
struct mmc_host_ops {
...
void (*request)(struct mmc_host *host, struct mmc_request *req);
void (*set_ios)(struct mmc_host *host, struct mmc_ios *ios);
int (*get_ro)(struct mmc_host *host);
int (*get_cd)(struct mmc_host *host);
void (*enable_sdio_irq)(struct mmc_host *host, int enable);
...
};
其中(与sd卡的数据交互包括两种, 一种是命令 CMD, 一种是数据 DATA)
request 函数
: 进行sd卡数据读写时的回调, 需要控制 mmc 寄存器实现数据发送接收
- set_ios 函数
- 进行sd卡命令收发时的回调, 需要控制 mmc 寄存器实现命令发送接收
- get_ro 函数
- 检测sd卡是否为只读模式的回调
- get_cd 函数
- 检测sd卡接入/拔出状态的的回调
- enable_sdio_irq 函数
- 使能/关闭 cpu mmc 中断功能的回调
mmc card 层
- 调用核心层接口 mmc_register_driver() 注册一个 mmc crad 层的平台驱动, 用来与平台设备匹配之后生成sd卡对应的块设备
- 实现块设备的分区管理和读写管理
mmc host层
host层平台设备
源码: linux-3.10.x\arch\arm\mach-nuc970\dev.c
添加平台设备
void __init nuc970_platform_init(struct platform_device **device, int size)
platform_add_devices(nuc970_public_dev, ARRAY_SIZE(nuc970_public_dev)); /*添加平台设备*/
static struct platform_device *nuc970_public_dev[] __initdata = {
...
&nuc970_device_sdh,
...
}
struct platform_device nuc970_device_sdh = {
.name = "nuc970-sdh",
.id = -1,
.num_resources = ARRAY_SIZE(nuc970_sdh_resource),
.resource = nuc970_sdh_resource,
.dev = {
.dma_mask = &nuc970_device_sdh_dmamask,
.coherent_dma_mask = 0xffffffffUL
}
};
static struct resource nuc970_sdh_resource[] = {
[0] = {
.start = NUC970_PA_SDH,
.end = NUC970_PA_SDH + NUC970_SZ_SDH - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_SDH,
.end = IRQ_SDH,
.flags = IORESOURCE_IRQ,
}
};
host层平台驱动
源码: linux-3.10.x\drivers\mmc\host\nuc970_sd.c
注册平台驱动
static struct platform_driver nuc970_sd_driver = {
.probe = nuc970_sd_probe,
...
.driver = {
.name = "nuc970-sdh",
.owner = THIS_MODULE,
},
};
module_platform_driver(nuc970_sd_driver);
注册平台驱动和设备后
平台总线根据 name 为 “nuc970-sdh” 进行平台驱动/设备的匹配
匹配后回调进入 .probe = nuc970_sd_probe
static int nuc970_sd_probe(struct platform_device *pdev)
/*获取平台资源*/
platform_get_resource(pdev, IORESOURCE_MEM, 0);
request_mem_region(res->start, res->end - res->start + 1, DRIVER_NAME)
sema_init(&sdh_fmi_sem, 1); /*信号量初始化*/
devm_pinctrl_get_select(&pdev->dev, "sd0"); /*引脚初始化*/
/*时钟初始化*/
clk_prepare(clk_get(NULL, "sdh_hclk"));
...
/*
* 申请 mmc_host 结构体,并申请一段私有数据段存放 struct nuc970_sd_host 结构体
* mmc_host 结构体描述了 mmc 的工作方式
* nuc970_sd_host 结构体是根据 芯唐nuc970平台 host 层驱动逻辑需求多定义的一个结构体
*/
mmc = mmc_alloc_host(sizeof(struct nuc970_sd_host), &pdev->dev);
->进入 mmc 核心层,进入 struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL); /*申请一段内存存放 struct mmc_host*/
host->rescan_disable = 1; /*这个变量在 mmc 扫描是否外接sd卡时用到, 由于mmc host 还没初始化完成,暂时停止扫描*/
...
dev_set_name(&host->class_dev, "mmc%d", host->index); /*设置设备名*/
...
host->class_dev.class = &mmc_host_class; /*设置设备 class 为 mmc_host_class*/
device_initialize(&host->class_dev); /*初始化设备*/
/*设置 host 的时钟门控
* 通过动态调整 mmc 工作时钟,节省功耗
* 例如sd卡空闲时,关闭 mmc 的时钟
* 这部分功能逻辑这里不作分析
*/
mmc_host_clk_init(host);
/*一些锁的初始化*/
...
init_waitqueue_head(&host->wq); /*初始化一个等待队列 host->wq*/
INIT_DELAYED_WORK(&host->detect, mmc_rescan); /*初始化一个延时任务 mmc_rescan, mmc_rescan 用于检测是否有外接设备, 如sd卡*/
/*初始化主机控制器 host 结构体, 填充好默认参数*/
host->max_segs = 1;
host->max_seg_size = PAGE_CACHE_SIZE;
host->max_req_size = PAGE_CACHE_SIZE;
host->max_blk_size = 512;
host->max_blk_count = PAGE_CACHE_SIZE / 512;
/*根据cpu的sd硬件接口,填充 mmc_host 结构体
* struct mmc_host 结构体是 mmc 核心层处理相关逻辑时使用到的
*/
mmc->ops = &nuc970_sd_ops; /*注册 mmc_host 的功能接口,由核心层回调*/
->nuc970_sd_ops 结构体定义
static const struct mmc_host_ops nuc970_sd_ops = {
.request = nuc970_sd_request, /*处理 mmc 请求, 进行外接 mmc 设备的读写, 如sd卡的读写*/
.set_ios = nuc970_sd_set_ios, /*配置 mmc 硬件接口, 根据传进的 ios 信息, 配置 mmc 的开关/mmc 宽度等信息*/
.get_ro = nuc970_sd_get_ro, /*检测外接 mmc 设备是否处于写保护模式*/
.get_cd = nuc970_sd_card_detect, /*通过sd卡检测脚判断是否有sd卡接入*/
.enable_sdio_irq = nuc900_sd_enable_sdio_irq, /*使能/关闭 mmc 的中断寄存器*/
};
mmc->f_min = 300000; /*mmc 控制器支持的最小频率*/
mmc->f_max = 50000000; /*mmc 控制器支持的最大频率*/
mmc->ocr_avail = MMC_VDD_27_28|MMC_VDD_28_29|MMC_VDD_29_30|MMC_VDD_30_31|MMC_VDD_31_32|MMC_VDD_32_33 | MMC_VDD_33_34; /*mmc 控制器支持的有效电压范围*/
mmc->caps |= (MMC_CAP_4_BIT_DATA|MMC_CAP_SDIO_IRQ|MMC_CAP_SD_HIGHSPEED|MMC_CAP_MMC_HIGHSPEED); /*mmc 控制器支持的功能,如4位数据收发等*/
mmc->max_blk_size = MCI_MAXBLKSIZE;
mmc->max_blk_count = MCI_BLKATONCE;
mmc->max_req_size = MCI_BUFSIZE;
mmc->max_segs = MCI_BLKATONCE;
mmc->max_seg_size = MCI_BUFSIZE;
/*填充 struct nuc970_sd_host *host 结构体
* struct nuc970_sd_host 结构体是 mmc 控制器层驱动内部进行逻辑处理中使用到的
*/
host = mmc_priv(mmc); /*前面申请了一段私有数据段存放 struct nuc970_sd_host 结构体, 这里取出来*/
...
host->buffer = dma_alloc_coherent(&pdev->dev, MCI_BUFSIZE, &host->physical_address, GFP_KERNEL); /*申请一段 dma 内存地址*/
...
/*关闭后使能 cpu 的 sd 接口,属于硬件寄存器操作*/
nuc970_sd_disable(host);
nuc970_sd_enable(host);
host->irq = platform_get_irq(pdev, 0);
ret = request_irq(host->irq, nuc970_sd_irq, IRQF_SHARED, mmc_hostname(mmc), host); /*mmc 的中断申请*/
...
nuc970_sd_write(REG_SDIER, nuc970_sd_read(REG_SDIER) | SDIER_CD0_IE | SDIER_CD0SRC); /*使能 mmc 和中断*/
mmc_add_host(mmc);
->进入 mmc 核心层, 添加一个 mmc host, 代码分析参考 mmc 核心层相关的分析
mmc 核心层
源码: linux-3.10.x\drivers\mmc\core\core.c
核心层初始化
- 创建工作队列管理 mmc 相关工作队列, 管理 mmc 核心层用到的一些 work
- 注册 mmc 总线和 sdio 总线, mmc card 层的平台设备和驱动就是注册到对应的总线中
static int __init mmc_init(void)
workqueue = alloc_ordered_workqueue("kmmcd", 0);
ret = mmc_register_bus(); /*注册一条总线, 名为 mmc*/
->进入 int mmc_register_bus(void)
return bus_register(&mmc_bus_type);
-> static struct bus_type mmc_bus_type 定义
static struct bus_type mmc_bus_type = {
.name = "mmc",
.dev_attrs = mmc_dev_attrs,
.match = mmc_bus_match,
.probe = mmc_bus_probe,
...
};
ret = mmc_register_host_class();
ret = sdio_register_bus(); /*注册一条总线, 名为 sdio*/
->进入 int sdio_register_bus(void)
return bus_register(&sdio_bus_type);
-> static struct bus_type sdio_bus_type 定义
static struct bus_type sdio_bus_type = {
.name = "sdio",
.dev_attrs = sdio_dev_attrs,
.match = sdio_bus_match,
.probe = sdio_bus_probe,
...
};
mmc_add_host
注册一个 mmc host, 注册完成后会调用一次扫描 mmc_rescan, 检测外部是否接入了sd卡
int mmc_add_host(struct mmc_host *host)
->进入 int mmc_add_host(struct mmc_host *host)
device_add(&host->class_dev); /*将主机控制器设备添加到系统设备表里面*/
/*led灯的相关控制,例如在读写sd卡时闪烁led灯
* 这部分功能代码不作分析
*/
led_trigger_register_simple(dev_name(&host->class_dev), &host->led);
...
mmc_start_host(host); /*开始启用 mmc 主机控制器*/
->进入 void mmc_start_host(struct mmc_host *host)
host->f_init = max(freqs[0], host->f_min);
host->rescan_disable = 0; /*mmc host 已经初始化完成,允许开始扫描*/
mmc_power_up(host); /*控制 mmc 硬件 power up*/
mmc_detect_change(host, 0); /*检测mmc 主机控制器有没有外接设备,如 sd卡*/
->进入 void mmc_detect_change(struct mmc_host *host, unsigned long delay)
...
host->detect_change = 1;
mmc_schedule_delayed_work(&host->detect, delay);
->进入 static int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay)
queue_delayed_work(workqueue, work, delay); /*将延时任务加入到任务队列中,延时结束后将调度执行该任务,即 mmc_rescan*/
register_pm_notifier(&host->pm_notify); /*跟电源管理有关,这部分代码不作分析*/
mmc_register_driver
注册一个 mmc card 层总线驱动
int mmc_register_driver(struct mmc_driver *drv)
->进入 int mmc_register_driver(struct mmc_driver *drv)
drv->drv.bus = &mmc_bus_type; /*设置驱动的总线类型为 mmc 总线, 也就是该驱动归属于 mmc 总线*/
-> static struct bus_type mmc_bus_type 定义
static struct bus_type mmc_bus_type = {
.name = "mmc",
.dev_attrs = mmc_dev_attrs,
.match = mmc_bus_match,
.probe = mmc_bus_probe,
...
};
return driver_register(&drv->drv); /*注册一个驱动到对应总线, 即 mmc 总线*/
mmc_rescan
mmc 核心层扫描函数, 检测外部有没有接sd卡
针对 sd卡在两种情况下会进行扫描:
- mmc 主控制器驱动加载时会进行一次扫描, 判断sd卡是否在位
- sd卡插入是会触发中断,中断中进行一次扫描, 判断sd卡是否在位
检测过程中会以不同的频率检测三种有可能的外接设备接口: SDIO, SD, MMC, sd卡用到的接口是 sd
如果检测到外接了sd卡, 则调用 mmc_add_card 创建一个 mmc card层的总线设备
void mmc_rescan(struct work_struct *work)
/*若 host->rescan_disable 为 1, 则说明主机控制器还没初始化好,直接返回
* 避免主机控制器还没初始化好时因为中断进入这个扫描函数时出错
*/
if (host->rescan_disable)
return;
/*如果是不可移除的外接设备,如emmc,则只进行一次 mmc 扫描*/
if ((host->caps & MMC_CAP_NONREMOVABLE) && host->rescan_entered)
return;
host->rescan_entered = 1;
...
host->detect_change = 0;
...
/*回调 mmc 控制器层注册好的 get_cd 函数,判断是否能检测到设备接入,如 sd卡 接入,没有则直接退出*/
if (host->ops->get_cd && host->ops->get_cd(host) == 0) {
...
goto out;
}
mmc_claim_host(host); /*声明占用 mmc 总线,如果已被占用则进入睡眠,等待别人释放*/
->进入 int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
__mmc_claim_host(host, NULL);
->进入 int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
DECLARE_WAITQUEUE(wait, current); /*初始化一个等待队列*/
...
add_wait_queue(&host->wq, &wait); /*将等待队列加到等待队列头*/
...
while (1) {
set_current_state(TASK_UNINTERRUPTIBLE); /*设置进程状态为不可中断状态*/
/*符合三个条件其中一个则停止进程睡眠, 否则进入休眠状态, 等待别人释放总线
* 传入的 abort 添加符合
* host->claimed 为0, 即 mmc 总线未被占用
* host->claimer == current, 即占用 mmc 总线的是进程本身
*/
stop = abort ? atomic_read(abort) : 0;
if (stop || !host->claimed || host->claimer == current)
break;
/*不符合上面三个添加的话则进行调度,进程进入休眠*/
schedule();
}
set_current_state(TASK_RUNNING); /*设置进程状态为运行状态*/
if (!stop) {
/*由当前进程占用 mmc 总线*/
host->claimed = 1;
host->claimer = current;
host->claim_cnt += 1;
} else
wake_up(&host->wq);
remove_wait_queue(&host->wq, &wait); /*删除等待队列*/
/*回调 mmc 控制器层的 enable 函数*/
if (host->ops->enable && !stop && host->claim_cnt == 1)
host->ops->enable(host);
/*这里以从大到小不同频率去驱动 mmc 控制器外接的设备,如sd卡
* 默认的频率为 unsigned freqs[] = { 400000, 300000, 200000, 100000 };
*/
for (i = 0; i < ARRAY_SIZE(freqs); i++) {
mmc_rescan_try_freq(host, max(freqs[i], host->f_min));
->进入 static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
host->f_init = freq;
mmc_power_up(host); /*初始化 mmc 总线,配置好总线频率等*/
->进入 static void mmc_power_up(struct mmc_host *host)
if (host->ios.power_mode == MMC_POWER_ON) /*如果已处于 MMC_POWER_ON 状态, 直接返回*/
return;
...
/*设置 sd卡 需要的电压
* 如果 ocr 已经配置,则直接使用,否则从 ocr_avail 中选择合适的
*/
if (host->ocr)
bit = ffs(host->ocr) - 1;
else
bit = fls(host->ocr_avail) - 1;
host->ios.vdd = bit;
host->ios.chip_select = MMC_CS_DONTCARE; /*这里只分析sd卡,不关心片选设置*/
host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; /*总线模式*/
host->ios.power_mode = MMC_POWER_UP; /*上电状态设置为已上电*/
host->ios.bus_width = MMC_BUS_WIDTH_1; /*总线宽度设置为1*/
host->ios.timing = MMC_TIMING_LEGACY; /*总线时序,默认*/
mmc_set_ios(host); /*设置到 mmc 主控制器中*/
->static inline void mmc_set_ios(struct mmc_host *host)
...
host->ops->set_ios(host, ios); /*回调到 mmc 控制器层的 set_ios 函数,按配置设置好相关寄存器*/
__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330); /*设置信号线的电压为3.3伏,也是回调 mmc 控制器层的函数*/
mmc_delay(10); /*配置了电压后延时一会,等待电压变化*/
/*设置 mmc 总线的频率*/
host->ios.clock = host->f_init;
host->ios.power_mode = MMC_POWER_ON;
mmc_set_ios(host);
...
mmc_hw_reset_for_init(host);
->进入 static void mmc_hw_reset_for_init(struct mmc_host *host)
host->ops->hw_reset(host); /*回调 mmc 控制器层注册好的 hw_reset 函数,对外接设备进行一次复位*/
sdio_reset(host); /*发送复位命令, sd卡忽略此操作*/
mmc_go_idle(host); /*发送 CMD0 命令, 进入 GO_IDLE_STATE 状态*/
mmc_send_if_cond(host, host->ocr_avail); /*发送 CMD8 命令,返回成功说明支持 SD 2.0 协议,否则支持 SD 1.0*/
/*这里按顺序分别探测 SDIO/SD/MMC 三种接口
* 如果检测到对应接口的设备,则返回,否则更换别的总线频率进行检测
* 只分析sd卡的探测流程
*/
if (!mmc_attach_sd(host))...
->进入 int mmc_attach_sd(struct mmc_host *host)
err = mmc_send_app_op_cond(host, 0, &ocr); /*发送 CMD41 获取外接sd卡支持的工作电压*/
mmc_sd_attach_bus_ops(host); /*设置总线对应的操作函数集*/
->进入 mmc_sd_attach_bus_ops(struct mmc_host *host)
/*根据 mmc 外接设备可不可以移除, 设置不同操作函数集*/
bus_ops = &mmc_sd_ops;
-> struct mmc_bus_ops mmc_sd_ops 定义, 操作函数集包含了 sd卡的各种动作对应的回调, 如检测到sd卡插入/sd卡拔出
static const struct mmc_bus_ops mmc_sd_ops = {
.remove = mmc_sd_remove,
.detect = mmc_sd_detect,
.suspend = NULL,
.resume = NULL,
.power_restore = mmc_sd_power_restore,
.alive = mmc_sd_alive,
};
mmc_attach_bus(host, bus_ops); /*这里将操作函数集注册到 mmc 控制器*/
/*这里排除掉一些过低的不合适的电压*/
...
host->ocr = mmc_select_voltage(host, ocr); /*选择cpu支持的和sd卡支持的合适范围内的sd卡电压*/
->进入 mmc_select_voltage(struct mmc_host *host, u32 ocr)
ocr &= host->ocr_avail; /*选择cpu支持输入的和sd卡同时支持的电压*/
bit = ffs(ocr);
...
host->ios.vdd = bit;
mmc_set_ios(host); /*将支持的电压配置下去, cpu的mmc接口将输出合适的电压*/
...
...
err = mmc_sd_init_card(host, host->ocr, NULL); /*初始化一个 mmc 总线设备*/
->进入 mmc_sd_init_card(struct mmc_host *host, u32 ocr, struct mmc_card *oldcard)
err = mmc_sd_get_cid(host, ocr, cid, &rocr); /*读取sd卡 CID 寄存器信息*/
...
card = mmc_alloc_card(host, &sd_type); /*申请一个总线设备, 归属于 mmc 总线*/
->进入 struct mmc_card *mmc_alloc_card(struct mmc_host *host, struct device_type *type)
card = kzalloc(sizeof(struct mmc_card), GFP_KERNEL); /*申请内存存放 struct mmc_card*/
card->host = host;
device_initialize(&card->dev);
card->dev.parent = mmc_classdev(host);
card->dev.bus = &mmc_bus_type; /*配置改设备归属于 mmc 总线设备*/
-> static struct bus_type mmc_bus_type 定义
static struct bus_type mmc_bus_type = {
.name = "mmc", /*总线名称*/
...
};
card->dev.release = mmc_release_card;
card->dev.type = type; /*配置 mmc card 设备类型*/
card->type = MMC_TYPE_SD; /*配置 mmc card 的类型为sd卡*/
...
err = mmc_send_relative_addr(host, &card->rca); /*发送 CMD3 读取 RCA 寄存器信息*/
err = mmc_sd_get_csd(host, card); /*发送 CMD9 读取 CSD 寄存器信息*/
err = mmc_select_card(card); /*发送 CMD7 选中外接的sd卡*/
err = mmc_sd_setup_card(host, card, oldcard != NULL); /*初始化外接sd卡的基本配置*/
...
/*如果支持高速,则配置外接sd卡和cpu mmc接口为高速模式*/
err = mmc_sd_switch_hs(card);
mmc_sd_go_highspeed(card);
mmc_set_clock(host, mmc_sd_get_max_clock(card));
/*如果支持4线传输,则配置外接sd卡和cpu mmc接口 数据传输宽度为4*/
err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
host->card = card;
mmc_release_host(host); /*释放总线*/
err = mmc_add_card(host->card); /*将 MMC 总线设备 注册进内核设备列表*/
->进入 int mmc_add_card(struct mmc_card *card)
...
dev_set_name(&card->dev, "%s:%04x", mmc_hostname(card->host), card->rca); /*设置 mmc 总线设备名称*/
...
mmc_init_context_info(card->host);
ret = device_add(&card->dev); /*添加 mmc 总线设备进内核*/
mmc_card_set_present(card);
mmc_claim_host(host); /*占用总线*/
if (freqs[i] <= host->f_min) /*各种 mmc 总线频率都尝试完了就退出*/
break;
}
mmc_release_host(host); /*释放总线*/
}
mmc card层
mmc card层总线设备
mmc card层总线设备在核心层进行 mmc 总线扫描时生成
在核心层的 mmc_rescan 函数中, 会去检测外接的 mmc 设备, 如sd卡, 如果检测到外接了sd卡, 就会创建一个 mmc 总线设备
mmc card层总线驱动
源码: linux-3.10.x\drivers\mmc\card\block.c
mmc card层初始化
- 注册 mmc card层驱动到 mmc 总线上
static int __init mmc_blk_init(void)
...
res = register_blkdev(MMC_BLOCK_MAJOR, "mmc"); /*注册一个新的块设备, 名为 mmc*/
res = mmc_register_driver(&mmc_driver); /*注册一个 mmc card层总线驱动*/
static struct mmc_driver mmc_driver = {
.drv = {
.name = "mmcblk",
},
.probe = mmc_blk_probe,
.remove = mmc_blk_remove,
.suspend = mmc_blk_suspend,
.resume = mmc_blk_resume,
};
当 mmc card设备与 mmc card驱动匹配时, 回调进入 .probe = mmc_bus_probe
进行 sd卡块设备分区的管理
static int mmc_blk_probe(struct mmc_card *card)
...
md = mmc_blk_alloc(card); /*申请一个 mmc 块设备*/
->进入 static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
md = mmc_blk_alloc_req(card, &card->dev, size, false, NULL, MMC_BLK_DATA_AREA_MAIN);
->进入 static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, struct device *parent, sector_t size, ...)
md = kzalloc(sizeof(struct mmc_blk_data), GFP_KERNEL); /*申请一段内存存放 struct mmc_blk_data 结构体*/
/*初始化 struct mmc_blk_data 结构体*/
...
md->disk = alloc_disk(perdev_minors); /*申请一个块设备*/
...
/*初始化给 mmc 块设备用的请求队列, 创建一个内核线程处理 mmc 块设备请求*/
ret = mmc_init_queue(&md->queue, card, &md->lock, subname);
->进入 int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock, const char *subname)
mq->card = card;
mq->queue = blk_init_queue(mmc_request_fn, lock); /*初始化一个块设备请求队列, 当应用进行块设备读写时, 内核将组成一个请求, 然后回调到 mmc_request_fn*/
...
blk_queue_prep_rq(mq->queue, mmc_prep_request); /*初始化一个 blk_queue_prep_rq, 在处理块设备请求队列之前调用, 初始化一些标志位*/
...
mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd/%d%s", host->index, subname ? subname : ""); /*创建一个内核线程, 处理 mmc 块设备请求*/
md->queue.issue_fn = mmc_blk_issue_rq; /*初始化一个回调, 在内核线程里面调用的, 用来处理请求*/
/*初始化申请到的块设备*/
md->disk->major = MMC_BLOCK_MAJOR;
md->disk->fops = &mmc_bdops; /*配置块设备文件操作接口*/
md->disk->queue = md->queue.queue; /*将请求队列与块设备对应起来*/
...
snprintf(md->disk->disk_name, sizeof(md->disk->disk_name), "mmcblk%d%s", md->name_idx, subname ? subname : ""); /*初始化块设备名称*/
...
/*
* 为卡的每个分区申请对应的块设备
* 这里针对的是 mmc 卡, 对于sd卡进入后直接返回了
*/
if (mmc_blk_alloc_parts(card, md))
goto out;
...
mmc_add_disk(md) /*将申请好的块设备注册进系统*/
->进入 static int mmc_add_disk(struct mmc_blk_data *md)
add_disk(md->disk); /*将块设备添加到内核列表中*/
/*注册对应的 sysfs 文件系统接口*/
...
sysfs_attr_init(&md->force_ro.attr);
...
ret = device_create_file(disk_to_dev(md->disk), &md->force_ro);
/*
* 将申请好的每个分区对应的块设备注册进系统
* 这里针对的是 mmc 卡, 对于sd卡不会进入
*/
list_for_each_entry(part_md, &md->part, part) {
if (mmc_add_disk(part_md))
goto out;
}
当应用进行块设备读写时, 如拷贝数据到sd卡, 内核将组成一个请求, 然后回调到 mmc_request_fn
函数里面唤醒了 mq->thread 内核线程
static void mmc_request_fn(struct request_queue *q)
/*
* 新的块设备请求进来时
* 如果还有块设备请求还没处理完, 则等待处理完
* 如果之前的块设备请求已经处理完, 则唤醒内核线程处理新的块设备请求
*/
if (!mq->mqrq_cur->req && mq->mqrq_prev->req) {
...
wake_up_interruptible(&cntx->wait);
} else if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)
wake_up_process(mq->thread);
mmc card层驱动创建了一个内核线程 mq->thread, 负责处理块设备的 request
没有 request 需要处理的时候计入休眠
当有 request 过来时则被 mmc_request_fn 唤醒, 进行 request 的处理
static int mmc_queue_thread(void *d)
do {
set_current_state(TASK_INTERRUPTIBLE); /*设置该内核线程为可中断唤醒状态*/
req = blk_fetch_request(q); /*取出一个块设备请求*/
mq->mqrq_cur->req = req;
if (req || mq->mqrq_prev->req) { /*如果请求不为空, 则进入进行块设备请求处理*/
set_current_state(TASK_RUNNING); /*设置该内核线程为运行状态*/
mq->issue_fn(mq, req); /*回调 .issue_fn = mmc_blk_issue_rq 函数发送请求*/
...
} else { /*如果块设备请求为空, 则进入休眠等待唤醒*/
...
schedule(); /*调度, 该内核线程进入休眠, 等待有新的块设备请求时唤醒*/
...
}
} while (1);
static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
/*处理第一个块设备请求时, 占用总线*/
if (req && !mq->mqrq_prev->req)
mmc_claim_host(card->host);
...
ret = mmc_blk_issue_rw_rq(mq, req); /*处理块设备请求*/
->进入 static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
...
/*循环处理全部请求*/
do {
...
areq = mmc_start_req(card->host, areq, (int *) &status); /*开始处理块设备请求*/
->进入核心层, 进入 struct mmc_async_req *mmc_start_req(struct mmc_host *host, struct mmc_async_req *areq, int *error)
mmc_pre_req(host, areq->mrq, !host->areq); /*块设备请求发送前准备*/
->进入 static void mmc_pre_req(struct mmc_host *host, struct mmc_request *mrq, bool is_first_req)
host->ops->pre_req(host, mrq, is_first_req); /*回调进入 mmc host层 .pre_req 函数, 该回调接口一般可不实现*/
...
start_err = __mmc_start_data_req(host, areq->mrq); /*块设备请求发送*/
->进入 static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq)
mmc_start_request(host, mrq);
->进入 mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
host->ops->request(host, mrq); /*回调进入 mmc host层 .request 函数, 将请求发送出去*/
...
mmc_post_req(host, host->areq->mrq, 0); /*块设备请求发送后处理*/
->进入 static void mmc_post_req(struct mmc_host *host, struct mmc_request *mrq, int err)
host->ops->post_req(host, mrq, err); /*回调进入 mmc host层 .post_req 函数, 该回调接口一般可不实现*/
/*处理块设备请求成功/失败的一些处理*/
switch (status) {
case MMC_BLK_SUCCESS:
case MMC_BLK_PARTIAL:
...
case MMC_BLK_CMD_ERR:
...
case MMC_BLK_RETRY:
...
case MMC_BLK_ABORT:
...
case MMC_BLK_DATA_ERR:
...
case MMC_BLK_ECC_ERR:
...
case MMC_BLK_NOMEDIUM:
...
default:
...
}
...
} while (ret);
/*处理完全部请求/discard/flush 后释放总线*/
if ((!req && !(mq->flags & MMC_QUEUE_NEW_REQUEST)) || (cmd_flags & MMC_REQ_SPECIAL_MASK))
mmc_release_host(card->host);