eMMC 驱动架构分析

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
(1) 区块层
主要是按照 LINUX 块设备驱动程序的框架实现一个卡的块设备驱动,这 block.c 当中我 们可以看到写一个块设备驱动程序时需要的 block_device_operations 结构体变量的定义, 其中有 open/release/request 函数的实现,而 queue.c 则是对内核提供的请求队列的封 装,我们暂时不用深入理解它,只需要知道一个块设备需要一个请求队列就可以了。
…… ret = mmc_add_host (mmc); if (ret) { dev_err(&pdev->dev, "failed to add mmc host./n"); goto free_dmabuf; }
…… platform_set_drvdata(pdev, mmc); return 0;
第一阶段: 从 s3cmci_init 开始往下看
static int __init s3cmci_init(void) { platform_driver_register(&s3cmci_driver_2410); }
有 platform_driver_register 函数,根据设备模型的知识,我们知道那一定会有对应的 platform_device_register 函数的,可是在哪里呢?没有看到,那是不是这个 s3cmci_driver_2410 当中给的 probe 函数就不执行了???当然不是, mci 接口一般都 是硬件做好的(我认为是这样),所以在系统启动时一定会有调用 platform_device_register 对板上的资源进行注册,如果没有这个硬件资源,那我们这个驱动也就没有用了。好,我们 就假定是有 mci 接口的,而且也有与 s3cmci_driver_2410 对应的硬件资源注册了,那自 己就会去跑 probe 函数。来看一下 s3cmci_driver_2410: static struct platform_driver s3cmci_driver_2410 = {
{
return queue_delayed_work(workqueue, work, delay); }
mmc_detect_change 又跳了一下,最后调用了 queue_delayed_work ,不知道这个函数 功能的去查一下〈〈 LDD3 〉〉和〈〈深入理解 LINUX 内核〉〉,这几个代码告诉我们 在 workqueue 这个工作队列当中添加一个延迟的工作任务,而这个工作任务就是由 host->detect 来描述的,在随后的 delay 个 jiffies 后会有一个记录在 host->detect 里面 的函数被执行,那么到这里 s3cmci_probe 这个函数算是结束了,但事情还没有完, workqueue 这个工作队列还在忙,不一会儿它就会调用 host->detect 里面那个函数,这个 函数到底是哪个函数,到底是用来干什么的呢?好像没有看到, detect 包含在 host 里面, 那估计是在刚才那个申请的地方设置的那个函数,回过头来看一下 mmc_alloc_host: struct mmc_host *mmc_alloc_host(int extra, struct device *dev) // 来自 core/host.c {
(3) 主机控制器层
主机控制器则是依赖于不同的平台的,例如 s3c2410 的卡控制器和 atmel 的卡控制器必 定是不一样的,所以要针对不同的控制器来实现。以 s3cmci.c 为例,它首先要进行一些设 置,例如中断函数注册,全能控制器等等。然后它会向 core 层注册一个主机( host ), 用结构 mmc_host_ops 描述,这样核心层就可以拿着这个 host 来操作 s3c24xx 的卡控 制器了,而具体是 sபைடு நூலகம்c24xx 的卡控制器还是 atmel 的卡控制器, core 层是不用知道的。
eMMc 驱动分析
块设备是 Linux 最复杂的设备之一,但是作为固执于知其然的 Geek,我们总会把代码 翻个遍,把道理弄个透。当然了,快速地学习一种新的东西,方法是最重要的,个人觉得: 内核当中 MMC/SD 卡驱动程序构架是学习 EMMC 驱动程序的重点,只有理解了它才能真 正理解该块设备驱动程序,同时才能真正理解 LINUX 块设备驱动程序。
void mmc_detect_change(struct mmc_host *host, unsigned long delay) // core/core.c
{
mmc_schedule_delayed_work(&host->detect, delay);
}
static int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay)
host.c/host.h
mmc.c
mmc_ops.c/mmc_ops.h 拿 MMC 卡来分析, SD 卡驱动程序流程类似。
host/
s3cmci.c/s3cmci.h 以 S3C24XX 的 MMC/SD 卡控制器为例,其它类型的控制器类 似。
LINUX 当中对目录的划分是很有讲究的,这些文件被分布在 3 个目录下,正好对应 MMC/SD 驱动程序的 3 个层次(关于层的划分这里浏览一下,有个概念即可,当我们分 析完了后再回头来看,你会觉得很形象):
int err; ……
err = device_add(&host->class_dev);
if (err) return err;
mmc_start_host(host); return 0; }
很简单,就是增加了一个 device ,然后就调用 mmc_start_host 了,那就先跳过 device_add 这个动作,来看 mmc_start_host:
(2) 核心层
核心层封装了 MMC/SD 卡的命令,例如存储卡的识别,设置,读写。例如不管什么卡都 应该有一些识别,设置,和读写的命令,这些流程都是必须要有的,只是具体对于不同的卡 会有一些各自特有的操作。 Core.c 文件是由 sd.c 、 mmc.c 两个文件支撑的, core.c 把 MMC 卡、 SD 卡的共性抽象出来,它们的差别由 sd.c 和 sd_ops.c 、 mmc.c 和 mmc_ops.c 来完成。
* First we search for SDIO... */ err = mmc_send_io_op_cond(host, 0, &ocr); if (!err) {
if (mmc_attach_sdio(host, ocr)) mmc_power_off(host);
goto out; }
/* * ...then normal SD...
对这几个目录有一个大概认识以后,我们来看几个重要的数据结构: struct mmc_host 用来描述卡控制器 struct mmc_card 用来描述卡 struct mmc_driver 用来描述 mmc 卡驱动 struct mmc_host_ops 用来描述卡控制器操作集,用于从主机控制器层向 core 层注册操作 函数,从而将 core 层与具体的主机控制器隔离。也就是说 core 要操作主机控制器,就用 这个 ops 当中给的函数指针操作,不能直接调用具体主控制器的函数。
*/ err = mmc_send_app_op_cond(host, 0, &ocr); if (!err) {
if (mmc_attach_sd(host, ocr)) mmc_power_off(host);
.driver.name = "s3c2410-sdi",
.probe
= s3cmci_probe_2410,
.remove
= s3cmci_remove,
.suspend = s3cmci_suspend,
.resume
= s3cmci_resume,
};
我们到 s3cmci_probe_2410 函数中看,还是干脆直接看 s3cmci_probe 算了: static int s3cmci_probe(struct platform_device *pdev, int is2440) // 来自 /host/s3cmci.c {
理功能的 controler。
二.驱动程序分析 首先,说明一下 EMMC 驱动涉及到的文件。另外,我们重点是分析驱动程序的基本构
架,所以不同内核版本的差异并不是很大。 MMC/SD 卡驱动程序位于 drivers/mmc 目录 下 Card/
block.c queue.c/queue.h core/ bus.c/bus.h core.c/core.h
…… }
这个函数很长,做的事件也很多,但我们关心的整个驱动的构架 / 流程,所以过滤掉一些 细节的东西,只看 2 个最重要的函数: mmc_alloc_host 、 mmc_add_host 。函数命名 已经很形象了,前者是申请一个 mmc_host ,而后者是添加一个 mmc_host 。中间还有 一个操作,就是给 mmc 的 ops 成员赋上了 s3cmci_ops 这个值。申请 mmc_host 当然 很简单,就是申请一个结构体(我们暂且这样认为,因为他里面还做的其它事情,后面会看 到),而添加又是添加到哪里去呢?看 mmc_add_host 函数: int mmc_add_host(struct mmc_host *host) // 来自 core/host.c {
// 来自 core/host.c void mmc_rescan(struct work_struct *work) // // 来自 core/host.c {
struct mmc_host *host = container_of(work, struct mmc_host, detect.work); u32 ocr; int err; …… /* detect a newly inserted card */ …… /*
struct mmc_host *host;
host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL); if (!host)
return NULL;
INIT_DELAYED_WORK(&host->detect, mmc_rescan); return host; } 如果你看了 queue_delayed_work 这个函数功能介绍,相信对 INIT_DELAYED_WORK 也不会陌生了吧。不废话了,来看 mmc_rescan :
一.需要的基础知识: 1. LINUX 设备驱动的基本结构。 2. 块设备驱动程序的基本构架(相信研究过 LDD3 当中的 sbull 的人应该都不成问 题,如果只是走马观花的话,那可得好好再补补了)
3. LINUX 设备驱动模型。
4.
EMMC 的原理,是 Nand Flash 的基础上加上一个负责:ECC、负载均衡和坏块管
struct mmc_host *mmc; struct s3cmci_host *host;
int ret; ……
mmc = mmc_alloc_host (sizeof(struct s3cmci_host), &pdev->dev); if (!mmc) {
ret = -ENOMEM; goto probe_out; } …… mmc->ops = &s3cmci_ops;
void mmc_start_host(struct mmc_host *host) // 来自 /host/core.c
{ mmc_power_off(host);
// 掉电一下
mmc_detect_change(host, 0);
// ???
}
看上去只有两行代码,不过浓缩才是精华, mmc_power_off(host) 光看名子都知道是在干 什么,先跳过,来看 mmc_detect_change ,那么它到底干了些什么呢?看一下就知道了:
相关文档
最新文档