一、SD卡系列简介
这些都是网上找出来的,权当作为开场白了。

MMC卡全称Multi Media Card,由西门子公司和SanDisk公司1997年推出的多媒体记忆卡标准。MMC卡尺寸为32mm x24mm x 1.4mm,它将存贮单元和控制器一同做到了卡上,智能的控制器使得MMC保证兼容性和灵活性。MMC卡具有MMC和SPI两种工作模式,MMC模式是默认工作模式,具有MMC的全部特性。而SPI模式则是MMC协议的一个子集,主要用于低速系统。

SD卡全称Secure Digital Memory Card,由松下、东芝和SanDisk公司于1999年8月共同开发的新一代记忆卡标准,已完全兼容MMC标准。SD卡比MMC卡多了一个进行数据著作权保护的暗号认证功能,读写速度比MMC卡快4倍。尺寸为32mm x 24mm x2.1mm,长宽和MMC卡一样,只是比MMC卡厚了0.7mm,以容纳更大容量的存贮单元。SD卡与MMC卡保持向上兼容,也就是说,MMC卡可以被新的设有SD卡插槽的设备存取,但是SD卡却不可以被设有MMC插槽的设备存取。

SDIO全称Secure Digital Input and Output Card,SDIO是在SD标准上定义了一种外设接口,它使用SD的I/O接口来连接外围设备,并通过SD上的I/O数据接口与这些外围设备传输数据。现在已经有很多手持设备支持SDIO功能,而且许多SDIO外设也被开发出来,目前常见的SDIO外设有:WIFI Card、GPS Card、 Bluetooth Card等等。

eMMC全称Embedded Multi MediaCard,是MMC协会所制定的内嵌式存储器标准规格,主要应用于智能手机和移动嵌入式产品等。eMMC是一种嵌入式非易失性存储系统,由闪存和闪存控制器两部分组成,它的一个明显优势是在封装中集成了一个闪存控制器,它采用JEDEC标准BGA封装,并采用统一闪存接口管理闪存。eMMC结构由一个嵌入式存储解决方案组成,带有MMC接口、快闪存储设备及主控制器,所有这些由一个小型BGA封装。由于采用标准封装,eMMC也很容易升级,并不用改变硬件结构。

二.驱动程序分析

首先,说明一下EMMC驱动涉及到的文件。另外,我们重点是分析驱动程序的基本构架,所以不同内核版本的差异并不是很大。 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 卡驱动程序流程类似。

host/

s3cmci.c/s3cmci.h 以 S3C24XX 的 MMC/SD 卡控制器为例,其它类型的控制器类似。

LINUX 当中对目录的划分是很有讲究的,这些文件被分布在 3 个目录下,正好对应 MMC/SD 驱动程序的 3 个层次(关于层的划分这里浏览一下,有个概念即可,当我们分析完了后再回头来看,你会觉得很形象):

(1) 区块层

主要是按照 LINUX 块设备驱动程序的框架实现一个卡的块设备驱动,这 block.c 当中我们可以看到写一个块设备驱动程序时需要的 block_device_operations 结构体变量的定义,其中有 open/release/request 函数的实现,而 queue.c 则是对内核提供的请求队列的封装,我们暂时不用深入理解它,只需要知道一个块设备需要一个请求队列就可以了。

(2) 核心层

核心层封装了 MMC/SD 卡的命令,例如存储卡的识别,设置,读写。例如不管什么卡都应该有一些识别,设置,和读写的命令,这些流程都是必须要有的,只是具体对于不同的卡会有一些各自特有的操作。 Core.c 文件是由 sd.c 、 mmc.c 两个文件支撑的, core.c 把 MMC 卡、 SD 卡的共性抽象出来,它们的差别由 sd.c 和 sd_ops.c 、 mmc.c 和 mmc_ops.c 来完成。

(3) 主机控制器层

主机控制器则是依赖于不同的平台的,例如 s3c2410 的卡控制器和 atmel 的卡控制器必定是不一样的,所以要针对不同的控制器来实现。以 s3cmci.c 为例,它首先要进行一些设置,例如中断函数注册,全能控制器等等。然后它会向 core 层注册一个主机( host ),用结构 mmc_host_ops 描述,这样核心层就可以拿着这个 host 来操作 s3c24xx 的卡控制器了,而具体是 s3c24xx 的卡控制器还是 atmel 的卡控制器, core 层是不用知道的。

对这几个目录有一个大概认识以后,我们来看几个重要的数据结构:

struct mmc_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(&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 = {

       .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

{

       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;

……

       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;

……

}

SD主控制器驱动程序的初始化函数probe(struct platform_device *pdev),概括地讲,主要完成五大任务,

初始化设备的数据结构,并将数据挂载到pdev->dev.driver_data下
实现设备驱动的功能函数,如mmc->ops = &pxamci_ops;
申请中断函数request_irq()
注册设备(mmc_alloc_host 、 mmc_add_host ),即注册kobject,建立sys文件,发送uevent等
其他需求,如在/proc/driver下建立用户交互文件等

这个函数很长,做的事件也很多,但我们关心的整个驱动的构架 / 流程,所以过滤掉一些细节的东西,只看 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

{

       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:

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 ,那么它到底干了些什么呢?看一下就知道了:

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)

{

       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

{

       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 :

// 来自 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 */

……

       /*

         * 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...

         */

       err = mmc_send_app_op_cond(host, 0, &ocr);

       if (!err) {

              if (mmc_attach_sd(host, ocr))

                     mmc_power_off(host);

              goto out;

       }

 

       /*

         * ...and finally MMC.

         */

       err = mmc_send_op_cond(host, 0, &ocr);

       if (!err) {

              if (mmc_attach_mmc(host, ocr))

                     mmc_power_off(host);

              goto out;

       }

 

       mmc_release_host(host);

       mmc_power_off(host);

 

out:

       if (host->caps & MMC_CAP_NEEDS_POLL)

              mmc_schedule_delayed_work(&host->detect, HZ);

}

浏览一个这个函数,看看函数名,再看看注释,知道什么了吗?它是在检测是不是有卡插入了卡控制器,如果有卡挺入就要采取相应的行动了。这里要明白一点,我们平时用的 SD/MMC 卡就是一个卡,如果要操作它得用 SD/MMC 卡控制器才行,所以可以看到有 struct mmc_card,struct mmc_host 的区分。

到这里了,来回忆一下 s3cmci_probe 这个函数做的事情,大概就是准备一个 mmc_host 结构,然后添加一个主控制器设备到内核,最后又调用了一下 mmc_rescan 来检测是不是有卡插入了。
如果有卡插入了还好,可以去操作卡了,那如果没有卡插入呢? mmc_rescan 不是白调用了一次吗?是啊,的确是白调用了一次。可是卡插入时为什么 PC 还是能检测到呢?在 s3cmci_probe 里面还注册了一个中断函数 s3cmci_irq_cd( 函数名的意思应该是 irq card detect) ,就是这个了,看看这个函数先:

s3cmci_probe :
			if (request_irq(host->irq_cd, s3cmci_irq_cd,
					IRQF_TRIGGER_RISING |
					IRQF_TRIGGER_FALLING,
					DRIVER_NAME, host)) 
					
					
static irqreturn_t s3cmci_irq_cd(int irq, void *dev_id)   // host/s3cmci.c

{

       struct s3cmci_host *host = (struct s3cmci_host *)dev_id;

 

       mmc_detect_change(host->mmc, msecs_to_jiffies(500));

 

       return IRQ_HANDLED;

}

看到这个函数想都不用想,直接跳到 mmc_rescan 里面去看就行了。前面已经知道了 mmc_rescan 里面就是在检测卡是不是插入了,既然卡随时插入我们都能检测到了,那就来看卡插入后都做了些什么动作吧。

前面讲到s3cmci_probe函数里面还做了一件事就是设置操作函数集

s3cmci_probe{
mmc->ops 	= &s3cmci_ops;
}

static struct mmc_host_ops s3cmci_ops = {
	.request	= s3cmci_request,
	.set_ios	= s3cmci_set_ios,
	.get_ro		= s3cmci_get_ro,
	.get_cd		= s3cmci_card_present,
	.enable_sdio_irq = s3cmci_enable_sdio_irq,
};

其中,(*set_ios)为主控制器设置总线和时钟等配置,(*get_ro)得到只读属性,(*enable_sdio_irq)开启sdio中断,本文重点讨论(*request)这个回调函数,它是整个SD主控制器驱动的核心,实现了SD主控制器能与SD卡进行通信的能力。
s3cmci_request函数里面分别进行的是通过操作寄存器进行数据和指令的传输
议层里,每条指令都会通过mmc_wait_for_req(host, &mrq)调用到host->ops->request(host, mrq)来发送指令和数据。

/* core/core.c */
 
void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
 {
  DECLARE_COMPLETION_ONSTACK(complete);
 
 mrq->done_data = &complete;
  mrq->done = mmc_wait_done;
 
 mmc_start_request(host, mrq);
 
 wait_for_completion(&complete);
 }
 
mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
 {
  ... ...
 
 host->ops->request(host, mrq);
 }

申请中断
s3cmci_probe(struct platform_device *pdev)中有两个中断,一个为SD主控制器芯片内电路固有的内部中断,另一个为探测引脚的探测到外部有SD卡插拔引起的中断。

  1. 由主控芯片内部电路引起的中断 request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host)) 回顾一下,host->irq就是刚才从platform_device里得到的中断号, irq = platform_get_irq(pdev, 0); host->irq = irq; 接下来,s3cmci_irq便是为该中断host->irq申请的中断函数, 当调用(*request),即host->ops->request(host, mrq),即上文中的pxamci_request()后,控制器与SD卡之间开始进行一次指令或数据传输,通信完毕后,主控芯片将产生一个内部中断,以告知此次指令或数据传输完毕。这个中断的具体值将保存在一个名为MMC_I_REG的中断寄存器中以供读取,中断寄存器MMC_I_REG中相关描述如下,

emmc坏了怎么办 emmc discard_ci

  1. 探测引脚引起的中断
    就是前面所说的外部中断s3cmci_irq_cd,当有SD卡插入或拔出时,硬件主控制器芯片的探测pin脚产生外部中断,进入中断处理函数,执行工作队列里的mmc_rescan,扫描SD总线,对插入或拔出SD卡作相应的处理。下文协议层将讨论mmc_rescan()。

第二阶段:
插入SD卡,主控制器产生中断,进入中断处理函数,处理工作队列,执行mmc_rescan。

void mmc_rescan(struct work_struct *work)
 {
  struct mmc_host *host = container_of(work, struct mmc_host, detect.work); // 得到mmc_host的数据
 /*
   * 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...
   */
  err = mmc_send_app_op_cond(host, 0, &ocr);
  if (!err) {
   if (mmc_attach_sd(host, ocr))
    mmc_power_off(host);
   goto out;
  }
 
 /*
   * ...and finally MMC.
   */
  err = mmc_send_op_cond(host, 0, &ocr);
  if (!err) {
   if (mmc_attach_mmc(host, ocr))
    mmc_power_off(host);
   goto out;
  }
 
  ... ...
 
}

mmc_rescan 里面既要检测 sd 卡,又要检测 mmc 卡的,我们就照着一个往下走,假定有个人插入了 MMC 卡,那就应该走下面这几行:
插入SD卡,mmc_rescan扫描SD总线上是否存在SD卡,具体的实现方法就是通过向SD卡上电,看是否能成功,以普通SD卡为例,为普通SD卡上电的函数mmc_send_app_op_cond(host, 0, &ocr);如果上电成功,则返回0,即执行if()里的mmc_attach_sd()进行总线与SD卡的绑定。如果上电失败,则返回非0值,跳过if(),尝试其他上电的方法。那么,上电方法究竟有何不同呢?具体看看mmc_send_app_op_cond()的实现过程,

err = mmc_send_op_cond(host, 0, &ocr);

       if (!err) {

              if (mmc_attach_mmc(host, ocr))

                     mmc_power_off(host);

              goto out;

       }

如果取这个值没有错误的话就得进 mmc_attach_mmc 了:

/*

  * Starting point for MMC card init.

  */

int mmc_attach_mmc(struct mmc_host *host, u32 ocr)  // core/mmc.c

{

       int err;

……

       mmc_attach_bus_ops(host);         // 这个与总线的电源管理有关,暂时跳过

……

       /*

         * Detect and init the card.

         */

       err = mmc_init_card(host, host->ocr, NULL);

       if (err)

              goto err;

……

       mmc_release_host(host);

 

       err = mmc_add_card(host->card);

       if (err)

              goto remove_card;

 

       return 0;

 

remove_card:

……

err:

……

       return err;

}

如果上电成功,就开始进行总线与SD卡的绑定,通过mmc_attach_sd(),绑定过程可分为四步,

注册SD总线上的操作函数 - struct mmc_bus_ops mmc_sd_ops
设置主控制器的时钟和总线方式 - 通过回调函数host->ops->set_ios();
启动SD卡mmc_init_card - 根据协议,完成SD卡启动的各个步骤
注册SD卡设备驱动mmc_add_card

还是找几个关键函数来看 mmc_init_card 从函数名来看就是初始化一个 card ,这个 card 就用 struct mmc_card 结构来描述,然后又调用 mmc_add_card 将卡设备添加到了内核,先来看 mmc_init_card 都做了些什么事情:

static int mmc_init_card(struct mmc_host *host, u32 ocr,

       struct mmc_card *oldcard)

{

       struct mmc_card *card;

       int err;

       u32 cid[4];

       unsigned int max_dtr;

……

              /*

                * Allocate card structure.

                */

              card = mmc_alloc_card(host, &mmc_type);

              if (IS_ERR(card)) {

                     err = PTR_ERR(card);

                     goto err;

              }

 

              card->type = MMC_TYPE_MMC;

              card->rca = 1;

              memcpy(card->raw_cid, cid, sizeof(card->raw_cid));

……

              host->card = card;

 

       return 0;

 

free_card:

……

err:

……

       return err;

}

将与硬件操作相关的全部删掉,最后对我们有用的也就这几行了 mmc_alloc_card 申请了一个 struct mmc_card 结构,然后给 card->type 赋上 MMC_TYPE_MMC ,最后将 card 又赋给了 host->card ,这和具体硬件还是挺像的,因为一个主控制器一般就插一个卡,有卡时 host->card 有值,没有卡时 host->card 自己就是 NULL 了。

钻进 mmc_alloc_card 里面来看看:

/*

  * Allocate and initialise a new MMC card structure.

  */

struct mmc_card *mmc_alloc_card(struct mmc_host *host, struct device_type *type)

{

       struct mmc_card *card;

 

       card = kzalloc(sizeof(struct mmc_card), GFP_KERNEL);

       if (!card)

              return ERR_PTR(-ENOMEM);

 

       card->host = host;

 

       device_initialize(&card->dev);

 

       card->dev.parent = mmc_classdev(host);

       card->dev.bus = &mmc_bus_type;

       card->dev.release = mmc_release_card;

       card->dev.type = type;

 

       return card;

}

Struct mmc_card 结构里面包含了一个 struct device 结构, mmc_alloc_card 不但申请了内存,而且还填充了 struct device 中的几个成员,尤其 card->dev.bus = &mmc_bus_type; 这一句要重点对待。

申请一个 mmc_card 结构,并简单初始化后, mmc_init_card 的使命就完成了,

2)mmc_add_card

然后再调用 mmc_add_card 将这个 card 设备添加到内核。 mmc_add_card 其实很简单,就是调用 device_add 将 card->dev 添加到内核当中去。
知道总线模型这个东西的人都明白,理到 device_add 里面总线就应该有动作了,具体是哪个总线呢?那就得看你调用 device_add 时送的那个 dev 里面指定的是哪个总线了,我们送的 card->dev ,那么 card->dev.bus 具体指向什么呢?很明现是那个 mmc_bus_type :

static struct bus_type mmc_bus_type = {

       .name             = "mmc",

       .dev_attrs       = mmc_dev_attrs,

       .match           = mmc_bus_match,

       .uevent           = mmc_bus_uevent,

       .probe            = mmc_bus_probe,

       .remove          = mmc_bus_remove,

       .suspend  = mmc_bus_suspend,

       .resume          = mmc_bus_resume,

};

在 device_add 里面,设备对应的总线会拿着你这个设备和挂在这个总线上的所有驱动程序去匹配( match ),此时会调用 match 函数,如果匹配到了就会调用总线的 probe 函数或驱动的 probe 函数,那我们看一下这里的 mmc_bus_match 是如何进行匹配的:

static int mmc_bus_match(struct device *dev, struct device_driver *drv)

{

       return 1;

}

看来 match 永远都能成功,那就去执行 probe 吧:

static int mmc_bus_probe(struct device *dev)

{

       struct mmc_driver *drv = to_mmc_driver(dev->driver);

       struct mmc_card *card = dev_to_mmc_card(dev);

 

       return drv->probe(card);

}

这里就有点麻烦了,在这个函数里面又调用了一下 drv->probe() ,那这个 drv 是什么呢?上面有: struct mmc_driver *drv = to_mmc_driver(dev->driver);

match 函数总是返回 1 ,那看来只要是挂在这条总线上的 driver 都有可能跑到这里来了,事实的确也是这样的,不过好在挂在这条总线上的 driver 只有一个,它是这样定义的:

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,

};

看到这里时, card/core/host 几个已经全部被扯进来了,边看 mmc_driver 中的几个函数,他们几个如何联系起来也就慢慢明白了。那我们继续吧。

第三阶段:

前面已经看到了,在总线的 probe 里面调用了 drv->probe, 而这个函数就对应的是 mmc_blk_probe ,具体这个 mmc_driver 是怎么挂到 mmc_bus 上的,自己去看 mmc_blk_init() ,就几行代码,应该不难。

static int mmc_blk_probe(struct mmc_card *card) // 来自 card/block.c

{

       struct mmc_blk_data *md;

       int err;

……

       md = mmc_blk_alloc(card);

       if (IS_ERR(md))

              return PTR_ERR(md);

……

       add_disk(md->disk);

       return 0;

 

  out:

       mmc_blk_put(md);

 

       return err;

}

还是捡重要的函数看,一看到这个函数最后调用了 add_disk ,你应该可以想到些什么吧?如果你不知道我在说些什么,那我估计你没有看过 LDD3 ,或者看了也是走马观花了。我来告诉你:如果看到 add_disk ,那说明前面一定会有 alloc_disk 和初始化队列的动作,在 mmc_blk_probe 时面没有体现出来,那就看 mmc_blk_alloc(card) 那一行:

static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)

{

       struct mmc_blk_data *md;

       int devidx, ret;

 

       devidx = find_first_zero_bit(dev_use, MMC_NUM_MINORS);

       if (devidx >= MMC_NUM_MINORS)

              return ERR_PTR(-ENOSPC);

       __set_bit(devidx, dev_use);

 

       md = kzalloc(sizeof(struct mmc_blk_data), GFP_KERNEL);

       if (!md) {

              ret = -ENOMEM;

              goto out;

       }

 

 

       /*

         * Set the read-only status based on the supported commands

         * and the write protect switch.

         */

       md->read_only = mmc_blk_readonly(card);

 

       md->disk = alloc_disk(1 << MMC_SHIFT);

       if (md->disk == NULL) {

              ret = -ENOMEM;

              goto err_kfree;

       }

 

       spin_lock_init(&md->lock);

       md->usage = 1;

 

       ret = mmc_init_queue(&md->queue, card, &md->lock);

       if (ret)

              goto err_putdisk;

 

       md->queue.issue_fn = mmc_blk_issue_rq;

       md->queue.data = md;

 

       md->disk->major   = MMC_BLOCK_MAJOR;

       md->disk->first_minor = devidx << MMC_SHIFT;

       md->disk->fops = &mmc_bdops;

       md->disk->private_data = md;

       md->disk->queue = md->queue.queue;

       md->disk->driverfs_dev = &card->dev;

 

       /*

         * As discussed on lkml, GENHD_FL_REMOVABLE should:

         *

         * - be set for removable media with permanent block devices

         * - be unset for removable block devices with permanent media

         *

         * Since MMC block devices clearly fall under the second

         * case, we do not set GENHD_FL_REMOVABLE.  Userspace

         * should use the block device creation/destruction hotplug

         * messages to tell when the card is present.

         */

 

       sprintf(md->disk->disk_name, "mmcblk%d", devidx);

 

       blk_queue_logical_block_size(md->queue.queue, 512);

 

       if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) {

              /*

                * The EXT_CSD sector count is in number or 512 byte

                * sectors.

                */

              set_capacity(md->disk, card->ext_csd.sectors);

       } else {

              /*

                * The CSD capacity field is in units of read_blkbits.

                * set_capacity takes units of 512 bytes.

                */

              set_capacity(md->disk,

                     card->csd.capacity << (card->csd.read_blkbits - 9));

       }

       return md;

 

  err_putdisk:

       put_disk(md->disk);

  err_kfree:

       kfree(md);

  out:

       return ERR_PTR(ret);

}

看到这个函数的代码,我们自然就回忆起了块设备驱动的整个套路了:

分配、初始化请求队列,并绑定请求队列和请求函数。
分配,初始化 gendisk ,给 gendisk 的 major , fops , queue 等成员赋值,最后添加 gendisk 。
注册块设备驱动。

我们看看 MMC 卡驱动程序有没有按这个套路走,

1 、 mmc_init_queue 初始了队列,并将 mmc_blk_issue_rq; 函数绑定成请求函数;

2 、 alloc_disk 分配了 gendisk 结构,并初始化了 major , fops ,和 queue ;

3 、最后调用 add_disk 将块设备加到 KERNEL 中去。

到这里虽然 mmc_blk_probe 已经结束了,但我们别停下来。记得 LDD3 上在讲 sbull 实例时说过, add_disk 的调用标志着一个块设备驱动将被激活,所以在这之前必须把其它所有准备工作全部做好,作者为什么会这样说是有理由的,因为在 add_disk 里面 kernel 会去调用你绑定到队列中的请求函数,目的是去你的块设备上读分区表。而且是在 add_disk 内部就要做的,而不是 add_disk 返回后再做,具体为什么会这样,去看 add_disk 的代码实现就知道了。

既然要调用请求函数去读,那我们就来看看请求函数: mmc_blk_issue_rq

static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)

{

       struct mmc_blk_data *md = mq->data;

       struct mmc_card *card = md->queue.card;

       struct mmc_blk_request brq;

       int ret = 1, disable_multi = 0;

 

       do {

 

              mmc_wait_for_req(card->host, &brq.mrq);

              /*

                * A block was successfully transferred.

                */

              spin_lock_irq(&md->lock);

              ret = __blk_end_request(req, 0, brq.data.bytes_xfered);

              spin_unlock_irq(&md->lock);

       } while (ret);

 

       return 1;

}

这个函数实在太长了,好在我们不用全部看,大部分读数据的准备代码和出错处理的代码已经被我删掉了,只要知道读数据都是在这里完成的就够了。看不懂这个函数的,拿上 LDD3 找个人少的地方,将 sbull 研究透了也就明白这个函数了。不过这个函数里涉及的东西还挺不少,“散列表”,“回弹”都在这里出现了,有时间慢慢去研究吧。

在块设备驱动当中你只需要抓住请求队列和请求函数就可以了,具体那些 block_device_operations 里面赋值的函数可不像字符设备驱动里面那么受关注了。

分析到这里, MMC/SD 卡的驱动整个构架基本也就很明析了,说简单了就是做了两件事:

  1. 卡的检测;
  2. 卡数据的读取。

最后再将这两个过程大概串一下:

  1. 卡的检测:

S3cmci_probe(host/s3cmci.c)

Mmc_alloc_host(core/core.c)

          Mmc_rescan(core/core.c)

                 Mmc_attach_mmc(core/mmc.c)

                        Mmc_init_card(core/mmc.c)

                        mmc_add_card(core/bus.c)

                               device_add

                                      mmc_bus_match(core/bus.c)

                                      mmc_bus_probe(core/bus.c)

                                             mmc_blk_probe(card/block.c)

                                                    alloc_disk/add_disk
  1. 读写数据:

mmc_blk_issue_rq ( card/block.c )

mmc_wait_for_req(core/core.c)

          mmc_start_request(core/core.c)

                 host->ops->request(host, mrq)   // s3cmci 中 s3cmci_request

MMC/SD 卡的驱动分析完了,是不是有些复杂,不过这样设计的目的是为了分层,让具体平台的驱动编写更加省事。