... ...
if (mrq->done) mrq->done(mrq); }
这里用到了完成量completion,LDD3上是这样说的,完成量是一个任务的轻量级机制,允许一个线程告知另一个线程工作已经完成。这里的一个线程就是内部中断处理函数,它是结束主控制器与SD卡间通信的线程,通过mrq->done(mrq); 即complete(mrq->done_data);告知另一个线程-回调(*request)实现主控制器与SD卡进行通信的线程,通信已经完毕。 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); }
static void mmc_wait_done(struct mmc_request *mrq) {
complete(mrq->done_data); }
2) 探测引脚引起的中断
if (host->pdata && host->pdata->init)
host->pdata->init(&pdev->dev, pxamci_detect_irq, mmc);
该init()回调函数就是刚才提到的系统初始化时通过saar_mci_init()实现的, static int saar_mci_init(struct device *dev, irq_handler_t saar_detect_int, void *data) {
request_irq(cd_irq, saar_detect_int, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, \ ... ... }
其中,cd_irq是通过GPIO转换得到的中断号,pxamci_detect_irq便是该中断实现的函数,之前已经提到过mmc_detect_change,它将最终调用queue_delayed_work执行工作队列里的mmc_rescan函数。
static irqreturn_t pxamci_detect_irq(int irq, void *devid) {
struct pxamci_host *host = mmc_priv(devid);
mmc_detect_change(devid, host->pdata->detect_delay); return IRQ_HANDLED; }
当有SD卡插入或拔出时,硬件主控制器芯片的探测pin脚产生外部中断,进入中断处理函数,执行工作队列里的mmc_rescan,扫描SD总线,对插入或拔出SD卡作相应的处理。下文协议层将讨论mmc_rescan()。 3. 协议层
Linux在内核源码的drivers/mmc/core文件夹下为我们的提供了一系列SD卡的接口服务函数。可以查看Makefile如下,
可见,core文件夹下有针对总线的服务bus.c,针对主控制器的服务host.c,针对SD卡的服务sd.c, sd_ops.c等等。
其中,最为核心的一个函数便是之前提到的位于core.c的mmc_rescan,概括来讲,主要完成两项任务,即
? ?
扫描SD总线,插入SD卡 扫描SD总线,拔出SD卡
3.1 插入SD卡
插入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; } ... ...
}
插入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()的实现过程,
int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) {
struct mmc_command cmd;
cmd.opcode = SD_APP_OP_COND; /* #define SD_APP_OP_COND 41 */ mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES); ... ... }
int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card, struct mmc_command *cmd, int retries) {
mmc_app_cmd(host, card); /* #define MMC_APP_CMD 55 */ mrq.cmd = cmd; cmd->data = NULL;
mmc_wait_for_req(host, &mrq); ... ... }
这里的指令SD_APP_OP_COND只有SD2.0的协议支持,也就是说,只有普通SD卡支持,所以也只有普通SD卡能够成功上电。
如果上电成功,就开始进行总线与SD卡的绑定,通过mmc_attach_sd(),绑定过程可分为四步,
? ? ? ?
注册SD总线上的操作函数 - struct mmc_bus_ops mmc_sd_ops 设置主控制器的时钟和总线方式 - 通过回调函数host->ops->set_ios(); 启动SD卡 - 根据协议,完成SD卡启动的各个步骤 注册SD卡设备驱动
3.1.1 注册总线上的操作函数
int mmc_attach_sd(struct mmc_host *host, u32 ocr) {
mmc_sd_attach_bus_ops(host); ... ... }
static void mmc_sd_attach_bus_ops(struct mmc_host *host) {
const struct mmc_bus_ops *bus_ops;