《mmc卡驱动分析》由会员分享,可在线阅读,更多相关《mmc卡驱动分析(20页珍藏版)》请在金锄头文库上搜索。
1、MMC 卡驱动分析 最近花时间研究了一下 MMC 卡驱动程序,开始在网上找了很多关于 MMC 卡驱动的分析文章,但大都是 在描述各个层,这对于初学者来讲帮助并不大,所以我就打算把自己的理解写下来,希望对大家有用。 个人觉得理解LINUX 内核当中 MMC/SD 卡驱动程序构架是学习 MMC 卡驱动程序的重点,只有理解了 它的基本框架或流程才能真正理解一个块设备驱动程序的写法,同时才能真正理解 LINUX 设备驱动模型 是如何发挥作用的。 一需要的基础知识: 1. LINUX 设备驱动的基本结构。 2. 块设备驱动程序的基本构架(相信研究过 LDD3 当中的 sbull 的人应该都不成问题,如果
2、只是走马 观花的话,那可得好好再补补了) 3. LINUX 设备驱动模型。 二驱动程序分析 首先,来明确一下我们需要分析的文件。下面的文件均来自 linux-2.6.24 源码,我们重点是分析驱 动程序的基本构架,所以不同内核版本的差异并不是很大。 MMC/SD 卡驱动程序位于 drivers/mmc 目录 下,我们只列出我们分析过程涉及到的几个文件: Card/ block.c queue.c/queue.h core/ bus.c/bus.h core.c/core.h host.c/host.h mmc.c mmc_ops.c/mmc_ops.h 拿 MMC 卡来分析, SD 卡驱动程序
3、流程类似。 host/ s3cmci.c/s3cmci.h 以 S3C24XX 的 MMC/SD 卡控制器为例,其它类型的控制器类似。 LINUX 当中对目录的划分是很有讲究的,这些文件被分布在 3 个目录下,正好对应 MMC/SD 驱动程序 的 3 个层次(关于层的划分这里浏览一下,有个概念即可,当我们分析完了后再回头来看,你会觉得很 形象): (1) 区块层 主要是按照 LINUX 块设备驱动程序的框架实现一个卡的块设备驱动,这 block.c 当中我们可以看到写一 个块设备驱动程序时需要的 block_device_operations 结构体变量的定义,其中有 open/release
4、/request 函数的实现,而queue.c 则是对内核提供的请求队列的封装,我们暂时不用深入理解它,只需要知道一 个块设备需要一个请求队列就可以了。 (2) 核心层 核心层封装了 MMC/SD 卡的命令,例如存储卡的识别,设置,读写。例如不管什么卡都应该有一些识别, 设置,和读写的命令,这些流程都是必须要有的,只是具体对于不同的卡会有一些各自特有的操作。 Cor e.c 文件是由 sd.c 、 mmc.c 两个文件支撑的, core.c 把 MMC 卡、 SD 卡的共性抽象出来,它们的差别 由 sd.c 和sd_ops.c 、 mmc.c 和 mmc_ops.c 来完成。 (3) 主机控制
5、器层 主机控制器则是依赖于不同的平台的,例如 s3c2410 的卡控制器和 atmel 的卡控制器必定是不一样的, 所以要针对不同的控制器来实现。以 s3cmci.c 为例,它首先要进行一些设置,例如中断函数注册,全能 控制器等等。然后它会向 core 层注册一个主机( host ),用结构 mmc_host_ops 描述,这样核心层就 可以拿着这个 host 来操作 s3c24xx 的卡控制器了,而具体是 s3c24xx 的卡控制器还是 atmel 的卡控制器, core 层是不用知道的。 驱动程序层次图 好了,对这几个目录有一个大概认识以后,我们来看几个重要的数据结构: struct mm
6、c_host 用来描述卡控制器 struct mmc_card 用来描述卡 struct mmc_driver 用来描述 mmc 卡驱动 struct mmc_host_ops 用来描述卡控制器操作集,用于从主机控制器层向 core 层注册操作函数,从 而将core 层与具体的主机控制器隔离。也就是说 core 要操作主机控制器,就用这个 ops 当中给的函数 指针操作,不能直接调用具体主控制器的函数。 第一阶段: 从 s3cmci_init 开始往下看 static int _init s3cmci_init(void) platform_driver_register( 有 platfor
7、m_driver_register 函数,根据设备模型的知识,我们知道那一定会有对应的 platform_device_r egister 函数的,可是在哪里呢?没有看到,那是不是这个 s3cmci_driver_2410 当中给的 probe 函数就不 执行了?当然不是, mci 接口一般都是硬件做好的(我认为是这样),所以在系统启动时一定会有 调用 platform_device_register 对板上的资源进行注册,如果没有这个硬件资源,那我们这个驱动也就没 有用了。好,我们就假定是有mci 接口的,而且也有与 s3cmci_driver_2410 对应的硬件资源注册了,那 自己就会去
8、跑 probe 函数。来看一下 s3cmci_driver_2410: static struct platform_driver s3cmci_driver_2410 = .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(
9、struct platform_device *pdev, int is2440) / 来自 /host/s3cmci.c struct mmc_host *mmc; struct s3cmci_host *host; int ret; mmc = mmc_alloc_host (sizeof(struct s3cmci_host), if (!mmc) ret = -ENOMEM; goto probe_out; mmc-ops = ret = mmc_add_host (mmc); if (ret) dev_err( goto free_dmabuf; platform_set_drvda
10、ta(pdev, mmc); return 0; 这个函数很长,做的事件也很多,但我们关心的整个驱动的构架 / 流程,所以过滤掉一些细节的东西,只 看 2 个最重要的函数: mmc_alloc_host 、 mmc_add_host 。函数命名已经很形象了,前者是申请一个 mmc_host ,而后者是添加一个 mmc_host 。中间还有一个操作,就是给 mmc 的 ops 成员赋上了 s3cm ci_ops 这个值。申请mmc_host 当然很简单,就是申请一个结构体(我们暂且这样认为,因为他里面还 做的其它事情,后面会看到),而添加又是添加到哪里去呢?看 mmc_add_host 函数:
11、int mmc_add_host(struct mmc_host *host) / 来自 core/host.c int err; err = device_add( if (err) return err; mmc_start_host(host); return 0; 很简单,就是增加了一个 device ,然后就调用 mmc_start_host 了,那就先跳过 device_add 这个动作, 来看mmc_start_host: void mmc_start_host(struct mmc_host *host) / 来自 /host/core.c mmc_power_off(host
12、); / 掉电一下 mmc_detect_change(host, 0); / ? 看上去只有两行代码,不过浓缩才是精华, mmc_power_off(host) 光看名子都知道是在干什么,先跳过, 来看mmc_detect_change ,那么它到底干了些什么呢?看一下就知道了: void mmc_detect_change(struct mmc_host *host, unsigned long delay) / core/core.c mmc_schedule_delayed_work( static int mmc_schedule_delayed_work(struct delaye
13、d_work *work, unsigned long delay) return queue_delayed_work(workqueue, work, delay); mmc_detect_change 又跳了一下,最后调用了 queue_delayed_work ,不知道这个函数功能的去查一下 LDD3 和深入理解 LINUX 内核,这几个代码告诉我们在 workqueue 这个工作队列当中 添加一个延迟的工作任务,而这个工作任务就是由 host-detect 来描述的,在随后的 delay 个 jiffies 后会 有一个记录在host-detect 里面的函数被执行,那么到这里 s3
14、cmci_probe 这个函数算是结束了,但事情 还没有完,workqueue 这个工作队列还在忙,不一会儿它就会调用 host-detect 里面那个函数,这个函 数到底是哪个函数,到底是用来干什么的呢?好像没有看到, detect 包含在 host 里面,那估计是在刚才 那个申请的地方设置的那个函数,回过头来看一下 mmc_alloc_host: struct mmc_host *mmc_alloc_host(int extra, struct device *dev) / 来自 core/host.c struct mmc_host *host; host = kzalloc(sizeo
15、f(struct mmc_host) + extra, GFP_KERNEL); if (!host) return NULL; INIT_DELAYED_WORK( return host; 如果你看了 queue_delayed_work 这个函数功能介绍,相信对 INIT_DELAYED_WORK 也不会陌生了吧。 不废话了,来看 mmc_rescan : / 来自 core/host.c void mmc_rescan(struct work_struct *work) / / 来自 core/host.c struct mmc_host *host = container_of(wo
16、rk, struct mmc_host, detect.work); u32 ocr; int err; /* detect a newly inserted card */ /* * First we search for SDIO. */ err = mmc_send_io_op_cond(host, 0, if (!err) if (mmc_attach_sdio(host, ocr) mmc_power_off(host); goto out; /* * .then normal SD. */ err = mmc_send_app_op_cond(host, 0, if (!err) if (mmc_attach_sd(host, ocr) mmc_power_off(host