分享

sd卡驱动分析之core

 WUCANADA 2016-04-06

core层处理(linux/driver/mmc/core)

1. core层初始化

一切变化逃不出Kconfig/Makefile的魔爪,这话一点也不假。同样core层的故事也将从这里拉开帷幕。二话不说先还是进到core目录下瞧瞧…
与以往所见到的Kconfig相比这里的显然少了几分生机和活力,貌似整个文件看完也难以发现令我们眼前发亮的字眼。也罢,少一个config也许就意味这我们少看几千行代码。再看看Makefile:
[core/Makefile]
5 ifeq
($(CONFIG_MMC_DEBUG),y)
6 EXTRA_CFLAGS
+= -DDEBUG
7 endif
8
9 obj-$(CONFIG_MMC)
+= mmc_core.o
10 mmc_core-y
:= core.o bus.o host.o \
11
mmc.o mmc_ops.o sd.o sd_ops.o \
12
sdio.o sdio_ops.o sdio_bus.o \
13
sdio_cis.o sdio_io.o sdio_irq.o
14
15 mmc_core-$(CONFIG_DEBUG_FS)
+= debugfs.o
看到这里我们再也兴奋不起来了,好像处理debugfs.c这个文件我们可以不怎么关注外,其他的文件都是我们研究的重点了。命运本该如此,不能改变就学着去接受吧.....
知道了是那些个文件再去找入口也许就方便多了,前面我们说过module_init和subsys_initcall永远是linux内核中最“忠实”的奸臣。当然也还有其他的乱臣贼子,一张口就道出内核入口的,这里就不在一一列出了。前面说到card目录的时候,入口显然是module_init,记性稍微好点的哥们可能还记得当时是因为mmc_bus_type这条总线才让我们card的故事得以延续的。换句话来说,如果这条总线都还尚未注册,那么请问card目录又将如何利用总线mmc_bus_type的probe方法最终走向mmc_driver->probe?无花却有果,那才真是奇了怪了。说来这么多无非是想证明其实core目录是早于card目录而生的,而我们又知道对于subsys_initcall是早于module_init调用的,那么也就不难想到core很有可能就是利用subsys_initcall来入口的了。
是不是这个理搜索一下内核代码就知道了,直接在/mmc/core目录下搜索subsys_initcall关键字,出来的不是别人真是core.c这个文件。不信邪的可以去搜索module_init,要是能搜索出东西来,那就是出了鬼了,说不好多半是上辈子造下的孽,这辈子该还了。好了,是时候进入正题了,先看subsys_initcall(mmc_init)如下:
[mmc/core/core.c]
1315 static
int __init mmc_init(void)
1316 {
1317 int
ret;
1318
1319 workqueue
= create_singlethread_workqueue("kmmcd");
1320 if
(!workqueue)
1321 return
-ENOMEM;
1322
1323 ret
= mmc_register_bus();
1324 if
(ret)
1325 goto
destroy_workqueue;
1326
1327 ret
= mmc_register_host_class();
1328 if
(ret)
1329 goto
unregister_bus;
1330
1331 ret
= sdio_register_bus();
1332 if
(ret)
1333 goto
unregister_host_class;
1334
1335 return
0;
1336
1337 unregister_host_class:
1338 mmc_unregister_host_class();
1339 unregister_bus:
1340 mmc_unregister_bus();
1341 destroy_workqueue:
1342 destroy_workqueue(workqueue);
1343
1344 return
ret;
1345 }
1319行内核时间处理机制中大名鼎鼎的工作队列就被使用在这里了。我们知道每个工作队列有一个或多个专用的进程("内核线程"),它运行提交给这个队列的函数。通常我们使用create_workqueue来创建一个工作队列,实际上他可能创建了多个线程运行在系统不同的处理器上。然而在很多情况下,我们提交的任务可能是些简单的单线程就能够完成的工作,这时候使用create_singlethread_workqueue来代替创建工作队列时在适用不过了。这里就是直接使用create_singlethread_workqueue创建一个单线程的工作队列。
1323行之前分析内核入口的时候一而再再而三的提到mmc_bus_type这么一条总线,现在她终于是有机会抛头露面了。可以猜测mmc_register_bus注册的不是别人正是垂涎已久的mmc_bus_type,不信你就来看代码。
[mmc/core/bus.c]
150 int
mmc_register_bus(void)
151 {
152 return
bus_register(&mmc_bus_type);
153 }
看这代码清晰简单,比起小葱拌豆腐还一清二白。如果你硬是说不认识bus_register那我就没辙了,回去翻翻设备模型估计第一页就有讲述他老人家的风骚故事。
1327行这句话看不看能,如果你硬是对sys目录下的那点东西怎么来的感兴趣的话,就去瞅两眼吧。一眼就够了,太多了伤身体。
1331行又来个sdio_bus注册,本来一个mmc_bus_type就折腾的够烦人的了,现在又来个sdio_bus是个什么东东,无形中给我们增加压力。不知道是什么东西就百度百科一下吧,谁知道塞翁失马焉知非福,下面来点百度的东西。其实SDIO是目前我们比较关心的技术,SDIO故名思义,就是 SD的 I/O接口(interface)的意思,不过这样解释可能还有点抽像。更具体的说明,SD本来是记忆卡的标准,但是现在也可以把 SD 拿来插上一些外围接口使用,这样的技术便是 SDIO。所以 SDIO本身是一种相当单纯的技术,透过 SD的 I/O接脚来连接外部外围,并且透过 SD上的 I/O数据接位与这些外围传输数据,而且 SD协会会员也推出很完整的 SDIO
stack驱动程序,使得 SDIO外围(我们称为 SDIO卡)的开发与应用变得相当热门。现在已经有非常多的手机或是手持装置都支持 SDIO的功能(SD标准原本就是针对 mobile
device而制定),而且许多 SDIO外围也都被开发出来,让手机外接外围更加容易,并且开发上更有弹性(不需要内建外围)。目前常见的 SDIO外围(SDIO卡)有:Wi-Fi
card(无线网络卡)、CMOS sensor card(照相模块)、GPS
card
GSM/GPRS modem card、Bluetooth
card、Radio/TV card。SDIO的应用将是未来嵌入式系统最重要的接口技术之一,并且也会取代目前 GPIO式的 SPI 接口。
看完这堆科普知识,是不是有点柳暗花明又一村的感觉,其实这儿注册个sdio_bus就是为那些sdio外设服务的,就像前面我们分析的card目录使用mmc_bus,说不准哪天又多出个什么wi-fi卡就要依附在这条sdio总线上,那时我们就可以像mmc_bus一样拿来用了。至少这里我们现在还不用管他,这也就是说刚才core中见到的若干个文件,只要名字带了个sdio的头的,我们八成都不用再来管他了。你说这是福还是祸,是福跑不了,是祸躲不过。
mmc_init比较简短,说到这里也就算是结束了。一般来说core层所做的初始化的工作较少,多半是为整个子系统的工作提供必要的接口,就像前面分析块层设备驱动一样。另外,前面我们说过在card层给我们core的分析留下了一些线索,下面我们就来按之前遗留下来的函数的顺序对其一一进行分析。

2. mmc_claim_host

mmc_claim_host定义在/mmc/core/core.h中实际的代码是由__
mmc_claim_host来完成的,具体的实现如下:
150 static
inline void mmc_claim_host(struct mmc_host *host)
151 {
152 __mmc_claim_host(host,
NULL);
153 }
函数以非终止的方式调用,传递的abort实参为NULL。关于__
mmc_claim_host的内容将做详细分析,具体代码如下:
[mmc/core/core.c]
451 int
__mmc_claim_host(struct mmc_host *host, atomic_t *abort)
452 {
453 DECLARE_WAITQUEUE(wait,
current);
454 unsigned
long flags;
455 int
stop;
456
457 might_sleep();
458
459 add_wait_queue(&host->wq,
&wait);
460 spin_lock_irqsave(&host->lock,
flags);
461 while
(1) {
462 set_current_state(TASK_UNINTERRUPTIBLE);
463 stop
= abort ? atomic_read(abort) : 0;
464 if
(stop || !host->claimed || host->claimer == current)
465 break;
466 spin_unlock_irqrestore(&host->lock,
flags);
467 schedule();
468 spin_lock_irqsave(&host->lock,
flags);
469 }
470 set_current_state(TASK_RUNNING);
471 if
(!stop) {
472 host->claimed
= 1;
473 host->claimer
= current;
474 host->claim_cnt
+= 1;
475 }
else
476 wake_up(&host->wq);
477 spin_unlock_irqrestore(&host->lock,
flags);
478 remove_wait_queue(&host->wq,
&wait);
479 if
(!stop)
480 mmc_host_enable(host);
481 return
stop;
482 }
453行初始化一个等待节点,后面我们将看到他的作用。
457行might_sleep宏其实就是检查是否需要重新调度,如果是,则进行调度。一般都用在可能引发睡眠的上下文中,完成任务的抢占。
459行将新的等待节点加入到主机的host->wq等待对列中,主要这里只是说加入进来
464行满足以下条件之一该线程将不会睡眠直接跳出while循环,分别是设置了终止、host空闲可用或者拥有该host的是本线程。当符合以上三条中的任何一种情况,程序会跳转到470行,重新设置线程状态为运行态。下面我们在abort=0的条件下分两种情况来讨论上面的整段代码的执行情况:
l 情况一:如果此时host不是空闲状态,host->
claimed=1且host->claimer != current此时按常理来说本线程将等待,459行,462行以及467行这三行代码使得线程睡眠在host->wq等待队列上,什么时候唤醒后面说到。
l 情况二:此时host处于空闲状态host->
claimed=0,或者host拥有者就是当前这个线程,这时程序直接跳转到470行,重新将进程状态设为运行态,当然这时的471-475行也就得以执行了。这三个变量的重新赋值意味着本线程得到了Host的控制权,当然478行的等待队列中的元素也就该删除了,480行host开始工作,具体实现后面再分析。
最后,来看看这段代码的一个最特殊的情况abort!=0时,这种情况本身就是个bug,既要claime然后又让人家abort,这一点我是看不穿想不透了,不过476行的代码倒是有几分的人性化,如果像前面所说的有线程睡眠在了host->wq上了,那么这个时候wake_up(&host->wq);一旦发出就回唤醒那些等待host的线程重新申请资源,毕竟这个host较少,资源还是相当紧张的。当然这是这种变态社会中的一种极其变态的情况,真正正常的wake_up可能还是要等到mmc_release_host的时候,在这里只是先提一下。
最后的最后要说的是current这个东西,这个指的不是别人,正是前面一节我们谈到的那个块请求处理的内核线程,当然早在card的prob中有个mmc_blk_set_blksize里面也使用到了这个她,那个时候到底指向的谁就确实不知道了。
另外,刚才说到了mmc_host_enable这个函数,定义在/mmc/core/core.c中,还是来简单的看一下:
[mmc/core/core.c]
346 /**
347 *
mmc_host_enable - enable a host.
348 *
@host: mmc host to enable
349 *
350 *
Hosts that support power saving can use the 'enable' and 'disable'
351 *
methods to exit and enter power saving states. For more information
352 *
see comments for struct mmc_host_ops.
353 */
354 int
mmc_host_enable(struct mmc_host *host)
355 {
356 if
(!(host->caps & MMC_CAP_DISABLE))
357 return
0;
358
359 if
(host->en_dis_recurs)
360 return
0;
361
362 if
(host->nesting_cnt++)
363 return
0;
364
365 cancel_delayed_work_sync(&host->disable);
366
367 if
(host->enabled)
368 return
0;
369
370 if
(host->ops->enable) {
371 int
err;
372
373 host->en_dis_recurs
= 1;
374 err
= host->ops->enable(host);
375 host->en_dis_recurs
= 0;
376
377 if
(err) {
378 pr_debug("%s:
enable error %d\n",
379 mmc_hostname(host),
err);
380 return
err;
381 }
382 }
383 host->enabled
= 1;
384 return
0;
385 }
365行这实际上是内核维护的一个全局的工作队列,使用时不需要定义工作队列结构体,全局工作队列创建的时候可以使用如下方法:
int schedule_work(struct
work_struct *work ); 

int schedule_work_on(intCPU,struct
work_struct *work ); 

int scheduled_delayed_work(struct
delayed_work *dwork,unsigned long delay); 

int scheduled_delayed_work_on(int
cpu,struct delayed_work *dwork,unsigned long delay); 
如果任务被延迟,调用cancel_delayed_work_sync将会终止队列中的任务或者阻塞任务直到回调结束(如果处理程序已经在处理该任务)。从这里不难发现主机处理任务的方式很有可能是利用的全局工作队列,具体如何等见到创建队列任务的时候再说。
370-382行就是调用host所提供的mmc_host_ops方法来设置了,其实整个过程是和低功耗相关的,这里就不再深入研究了。
看完mmc_claim_host,我们乘热打铁把他的孪生兄弟mmc_release_host也给解决了。

3. mmc_release_host

mmc_release_host与mmc_claim_host一起出生入死,始终是成对出现,执行的过程肯能在顺序上有点颠倒,上点源码如下:
[mmc/core/core.c]
575 void
mmc_release_host(struct mmc_host *host)
576 {
577 WARN_ON(!host->claimed);
578
579 mmc_host_lazy_disable(host);
580
581 mmc_do_release_host(host);
582 }
579行跟进源码:
[mmc/core/core.c]
545 int
mmc_host_lazy_disable(struct mmc_host *host)
546 {
547 if
(!(host->caps & MMC_CAP_DISABLE))
548 return
0;
549
550 if
(host->en_dis_recurs)
551 return
0;
552
553 if
(--host->nesting_cnt)
554 return
0;
555
556 if
(!host->enabled)
557 return
0;
558
559 if
(host->disable_delay) {
560 mmc_schedule_delayed_work(&host->disable,
561
msecs_to_jiffies(host->disable_delay));
562 return
0;
563 }
else
564 return
mmc_host_do_disable(host, 1);
565 }
整个过程与上面说说的mmc_host_enable正好相反。
559-560行如果主机进入低功耗有个延时过程,那么就通过全局工作队列来进行延时调度,其中mmc_schedule_delayed_work调用的不是别人正是queue_delayed_work(workqueue,
work, delay),至于host->disable里面放着什么内容这是host那边的事,谈到了在论。
564行如果主机没有这个癖好直接可以disable那再好不过了,mmc_host_do_disable(host,
1);为您解决一切后顾之忧。
[mmc/core/core.c]
388 static
int mmc_host_do_disable(struct mmc_host *host, int lazy)
389 {
390 if
(host->ops->disable) {
391 int
err;
392
393 host->en_dis_recurs
= 1;
394 err
= host->ops->disable(host, lazy);
395 host->en_dis_recurs
= 0;
396
397 if
(err < 0) {
398 pr_debug("%s:
disable error %d\n",
399 mmc_hostname(host),
err);
400 return
err;
401 }
402 if
(err > 0) {
403 unsigned
long delay = msecs_to_jiffies(err);
404
405
mmc_schedule_delayed_work(&host->disable, delay);
406 }
407 }
408 host->enabled
= 0;
409 return
0;
410 }
由此可见这个忧患也并没达到什么程度,394行爽快调用了host提供的disable方法,但是405行又再次出现一个延时调用,这个解释要想合理只有等到明年春暖花开的季节我们分析host的时候了。总之,当等到了那一天一切真相都会水落石出。
回到mmc_release_host还剩下最后一行,mmc_do_release_host(host)。
[mmc/core/core.c]
509 static
void mmc_do_release_host(struct mmc_host *host)
510 {
511 unsigned
long flags;
512
513 spin_lock_irqsave(&host->lock,
flags);
514 if
(--host->claim_cnt) {
515 /*
Release for nested claim */
516 spin_unlock_irqrestore(&host->lock,
flags);
517 }
else {
518 host->claimed
= 0;
519 host->claimer
= NULL;
520 spin_unlock_irqrestore(&host->lock,
flags);
521 wake_up(&host->wq);
522 }
523 }
这段代码逻辑异常清晰,格式异常工整,一切犹如行云流水一般。环环相扣,句句入理。这么多的优点面前我只说一句,省的大煞风景。
521行再见了wake_up,前面说过一个变态的,这里出现的这个可是他家的正统血脉哦,一个主机控制器的release,wake_up了一批抢占他的线程,这里就是个强有力的证明。
好了,废话加白话总之没有一句真话的把mmc_release_host讲完了,接下来该步入整个SD卡故事得正题了,别笑的太早,到时候有你好受。

4. mmc_wait_for_req

总算是轮到他了,不是不想说他,是说起他来估计说到天黑还没个底。但是无论怎样,天塌下来内核源代码都还是要看的。废话少说,先上源码:
[mmc/core/core.c]
184 /**
185 *
mmc_wait_for_req - start a request and wait for completion
186 *
@host: MMC host to start command
187 *
@mrq: MMC request to start
188 *
189 *
Start a new MMC custom command request for a host, and wait
190 *
for the command to complete. Does not attempt to parse the
191 *
response.
192 */
193 void
mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
194 {
195 DECLARE_COMPLETION_ONSTACK(complete);
196
197 mrq->done_data
= &complete;
198 mrq->done
= mmc_wait_done;
199
200 mmc_start_request(host,
mrq);
201
202 wait_for_completion(&complete);
203 }
我汗,这啥意思,咋整个代码比潘长江还短。罢了不管他,研究下代码。
200行mmc_start_request搞了半天才开始请求,你说这急人不急人,好了看你怎个start法。
[mmc/core/core.c]
121 static
void
122 mmc_start_request(struct
mmc_host *host, struct mmc_request *mrq)
123 {
124 #ifdef
CONFIG_MMC_DEBUG
125 unsigned
int i, sz;
126 struct
scatterlist *sg;
127 #endif
128
129 pr_debug("%s:
starting CMD%u arg %08x flags %08x\n",
130 mmc_hostname(host),
mrq->cmd->opcode,
131 mrq->cmd->arg,
mrq->cmd->flags);
132
133 if
(mrq->data) {
134 pr_debug("%s:
blksz %d blocks %d flags %08x "
135 tsac
%d ms nsac %d\n,
136 mmc_hostname(host),
mrq->data->blksz,
137 mrq->data->blocks,
mrq->data->flags,
138 mrq->data->timeout_ns
/ 1000000,
139 mrq->data->timeout_clks);
140 }
141
142 if
(mrq->stop) {
143 pr_debug("%s:
CMD%u arg %08x flags %08x\n",
144 mmc_hostname(host),
mrq->stop->opcode,
145 mrq->stop->arg,
mrq->stop->flags);
146 }
147
148 WARN_ON(!host->claimed);
149
150 led_trigger_event(host->led,
LED_FULL);
151
152 mrq->cmd->error
= 0;
153 mrq->cmd->mrq
= mrq;
154 if
(mrq->data) {
155 BUG_ON(mrq->data->blksz
> host->max_blk_size);
156 BUG_ON(mrq->data->blocks
> host->max_blk_count);
157 BUG_ON(mrq->data->blocks
* mrq->data->blksz >
158 host->max_req_size);
159
160 #ifdef
CONFIG_MMC_DEBUG
161 sz
= 0;
162 for_each_sg(mrq->data->sg,
sg, mrq->data->sg_len, i)
163 sz
+= sg->length;
164 BUG_ON(sz
!= mrq->data->blocks * mrq->data->blksz);
165 #endif
166
167 mrq->cmd->data
= mrq->data;
168 mrq->data->error
= 0;
169 mrq->data->mrq
= mrq;
170 if
(mrq->stop) {
171 mrq->data->stop
= mrq->stop;
172 mrq->stop->error
= 0;
173 mrq->stop->mrq
= mrq;
174 }
175 }
176 host->ops->request(host,
mrq);
177 }
这不看不大紧,一看乐死人。149行以前全是debug要用的,本来就不长的代码这一缩水可真没啥分量了。
150行啥玩意,LED?都啥时候了还有这闲工夫玩这玩意。搞硬件的兄弟们总喜欢在板子上搞几个LED,弄的像不整几个上去不足以展示自己实力似的,这也就苦了这帮子写内核的哥们,既然有了个状态指示灯,总不能让它没反应吧,省的那些不懂硬件的人怀疑哪位画板子的哥们把个LED的原理图给画错了,这就麻烦大了,会死人的。好吧就加段小代码让他工作起来吧,也就有了这个led_trigger_event,当然是可配置的。如果您确实认为有必要研究一番,就劳驾自己去观摩吧,我在这里就先失陪了。
167-173行这几行什么意思我始终没能明白,也许真的到了春暖花开的季节才能明白他的良苦用心吧。没办法,现在还不能说,那就等待等待在等待吧....
176行不用我多说肯定都知道这是个什么意思,不错调用的真是host边的接口,好了不能再说了,再说就有人告我侵犯领土完整了。
前面说了这个函数很复杂的,怎么?放心好戏在后头。好了回到mmc_wait_for_req...
202行wait_for_completion(&complete);典型的complete机制,不会吧没听说过,那只能说明你out了。内核同步机制中complete可是也占了部分江山的呀,wait_for_completion以后的结果就是调用线程你可以进入冬眠了,什么时候我这边做完了会利用complete来解脱你的。好了话都说到这份上了,您自己说这个等待的过程是不是很漫长,何况也没有个期限,即使一万年也得等啊。
197-198行mmc_wait_done等待完成,进去看看
[mmc/core/core.c]
179 static
void mmc_wait_done(struct mmc_request *mrq)
180 {
181 complete(mrq->done_data);
182 }
181行确是complete,而且complete就是wait_for_completion的那个对象,好了大胆想象吧,N年后的某个地方我们肯定会与mmc_wait_done在聚首。不信就走着瞧....

5. mmc_wait_for_cmd

是时候进入mmc_wait_for_cmd了,不过遗憾的是这个函数也确实没有太多吸引人眼球的地方,还是贴出他的源码来吧。
[mmc/core/core.c]
217 int
mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries)
218 {
219 struct
mmc_request mrq;
220
221 WARN_ON(!host->claimed);
222
223 memset(&mrq,
0, sizeof(struct mmc_request));
224
225 memset(cmd->resp,
0, sizeof(cmd->resp));
226 cmd->retries
= retries;
227
228 mrq.cmd
= cmd;
229 cmd->data
= NULL;
230
231 mmc_wait_for_req(host,
&mrq);
232
233 return
cmd->error;
234 }
命令的提交形式与数据请求的有点区别,至少没能构建一个完整的struct
mmc_request结构。但是最终却都是调用了mmc_wait_for_req。至于这些个struct
mmc_command 、
struct mmc_reques现在确实不方便也没办法说清楚,说到真正作用在硬件上的传输过程之时,也将是揭开他神秘面纱之日。
故事发展到这里我们断了线索,但是有一点是肯定的core为我们所干的远不止这些,至少到目前为止我们只分析到了呈上所做的工作,至于启下会干那些工作等我们看完host再来给他画上圆满的句号,我想只有这样才能表达我们对这个吃苦耐劳的core最崇高的敬意。
http://m.blog.csdn.net/blog/rain0993/8476755

core层处理(linux/driver/mmc/core)

1. core层初始化

一切变化逃不出Kconfig/Makefile的魔爪,这话一点也不假。同样core层的故事也将从这里拉开帷幕。二话不说先还是进到core目录下瞧瞧…
与以往所见到的Kconfig相比这里的显然少了几分生机和活力,貌似整个文件看完也难以发现令我们眼前发亮的字眼。也罢,少一个config也许就意味这我们少看几千行代码。再看看Makefile:
[core/Makefile]
5 ifeq
($(CONFIG_MMC_DEBUG),y)
6 EXTRA_CFLAGS
+= -DDEBUG
7 endif
8
9 obj-$(CONFIG_MMC)
+= mmc_core.o
10 mmc_core-y
:= core.o bus.o host.o \
11
mmc.o mmc_ops.o sd.o sd_ops.o \
12
sdio.o sdio_ops.o sdio_bus.o \
13
sdio_cis.o sdio_io.o sdio_irq.o
14
15 mmc_core-$(CONFIG_DEBUG_FS)
+= debugfs.o
看到这里我们再也兴奋不起来了,好像处理debugfs.c这个文件我们可以不怎么关注外,其他的文件都是我们研究的重点了。命运本该如此,不能改变就学着去接受吧.....
知道了是那些个文件再去找入口也许就方便多了,前面我们说过module_init和subsys_initcall永远是linux内核中最“忠实”的奸臣。当然也还有其他的乱臣贼子,一张口就道出内核入口的,这里就不在一一列出了。前面说到card目录的时候,入口显然是module_init,记性稍微好点的哥们可能还记得当时是因为mmc_bus_type这条总线才让我们card的故事得以延续的。换句话来说,如果这条总线都还尚未注册,那么请问card目录又将如何利用总线mmc_bus_type的probe方法最终走向mmc_driver->probe?无花却有果,那才真是奇了怪了。说来这么多无非是想证明其实core目录是早于card目录而生的,而我们又知道对于subsys_initcall是早于module_init调用的,那么也就不难想到core很有可能就是利用subsys_initcall来入口的了。
是不是这个理搜索一下内核代码就知道了,直接在/mmc/core目录下搜索subsys_initcall关键字,出来的不是别人真是core.c这个文件。不信邪的可以去搜索module_init,要是能搜索出东西来,那就是出了鬼了,说不好多半是上辈子造下的孽,这辈子该还了。好了,是时候进入正题了,先看subsys_initcall(mmc_init)如下:
[mmc/core/core.c]
1315 static
int __init mmc_init(void)
1316 {
1317 int
ret;
1318
1319 workqueue
= create_singlethread_workqueue("kmmcd");
1320 if
(!workqueue)
1321 return
-ENOMEM;
1322
1323 ret
= mmc_register_bus();
1324 if
(ret)
1325 goto
destroy_workqueue;
1326
1327 ret
= mmc_register_host_class();
1328 if
(ret)
1329 goto
unregister_bus;
1330
1331 ret
= sdio_register_bus();
1332 if
(ret)
1333 goto
unregister_host_class;
1334
1335 return
0;
1336
1337 unregister_host_class:
1338 mmc_unregister_host_class();
1339 unregister_bus:
1340 mmc_unregister_bus();
1341 destroy_workqueue:
1342 destroy_workqueue(workqueue);
1343
1344 return
ret;
1345 }
1319行内核时间处理机制中大名鼎鼎的工作队列就被使用在这里了。我们知道每个工作队列有一个或多个专用的进程("内核线程"),它运行提交给这个队列的函数。通常我们使用create_workqueue来创建一个工作队列,实际上他可能创建了多个线程运行在系统不同的处理器上。然而在很多情况下,我们提交的任务可能是些简单的单线程就能够完成的工作,这时候使用create_singlethread_workqueue来代替创建工作队列时在适用不过了。这里就是直接使用create_singlethread_workqueue创建一个单线程的工作队列。
1323行之前分析内核入口的时候一而再再而三的提到mmc_bus_type这么一条总线,现在她终于是有机会抛头露面了。可以猜测mmc_register_bus注册的不是别人正是垂涎已久的mmc_bus_type,不信你就来看代码。
[mmc/core/bus.c]
150 int
mmc_register_bus(void)
151 {
152 return
bus_register(&mmc_bus_type);
153 }
看这代码清晰简单,比起小葱拌豆腐还一清二白。如果你硬是说不认识bus_register那我就没辙了,回去翻翻设备模型估计第一页就有讲述他老人家的风骚故事。
1327行这句话看不看能,如果你硬是对sys目录下的那点东西怎么来的感兴趣的话,就去瞅两眼吧。一眼就够了,太多了伤身体。
1331行又来个sdio_bus注册,本来一个mmc_bus_type就折腾的够烦人的了,现在又来个sdio_bus是个什么东东,无形中给我们增加压力。不知道是什么东西就百度百科一下吧,谁知道塞翁失马焉知非福,下面来点百度的东西。其实SDIO是目前我们比较关心的技术,SDIO故名思义,就是 SD的 I/O接口(interface)的意思,不过这样解释可能还有点抽像。更具体的说明,SD本来是记忆卡的标准,但是现在也可以把 SD 拿来插上一些外围接口使用,这样的技术便是 SDIO。所以 SDIO本身是一种相当单纯的技术,透过 SD的 I/O接脚来连接外部外围,并且透过 SD上的 I/O数据接位与这些外围传输数据,而且 SD协会会员也推出很完整的 SDIO
stack驱动程序,使得 SDIO外围(我们称为 SDIO卡)的开发与应用变得相当热门。现在已经有非常多的手机或是手持装置都支持 SDIO的功能(SD标准原本就是针对 mobile
device而制定),而且许多 SDIO外围也都被开发出来,让手机外接外围更加容易,并且开发上更有弹性(不需要内建外围)。目前常见的 SDIO外围(SDIO卡)有:Wi-Fi
card(无线网络卡)、CMOS sensor card(照相模块)、GPS
card
GSM/GPRS modem card、Bluetooth
card、Radio/TV card。SDIO的应用将是未来嵌入式系统最重要的接口技术之一,并且也会取代目前 GPIO式的 SPI 接口。
看完这堆科普知识,是不是有点柳暗花明又一村的感觉,其实这儿注册个sdio_bus就是为那些sdio外设服务的,就像前面我们分析的card目录使用mmc_bus,说不准哪天又多出个什么wi-fi卡就要依附在这条sdio总线上,那时我们就可以像mmc_bus一样拿来用了。至少这里我们现在还不用管他,这也就是说刚才core中见到的若干个文件,只要名字带了个sdio的头的,我们八成都不用再来管他了。你说这是福还是祸,是福跑不了,是祸躲不过。
mmc_init比较简短,说到这里也就算是结束了。一般来说core层所做的初始化的工作较少,多半是为整个子系统的工作提供必要的接口,就像前面分析块层设备驱动一样。另外,前面我们说过在card层给我们core的分析留下了一些线索,下面我们就来按之前遗留下来的函数的顺序对其一一进行分析。

2. mmc_claim_host

mmc_claim_host定义在/mmc/core/core.h中实际的代码是由__
mmc_claim_host来完成的,具体的实现如下:
150 static
inline void mmc_claim_host(struct mmc_host *host)
151 {
152 __mmc_claim_host(host,
NULL);
153 }
函数以非终止的方式调用,传递的abort实参为NULL。关于__
mmc_claim_host的内容将做详细分析,具体代码如下:
[mmc/core/core.c]
451 int
__mmc_claim_host(struct mmc_host *host, atomic_t *abort)
452 {
453 DECLARE_WAITQUEUE(wait,
current);
454 unsigned
long flags;
455 int
stop;
456
457 might_sleep();
458
459 add_wait_queue(&host->wq,
&wait);
460 spin_lock_irqsave(&host->lock,
flags);
461 while
(1) {
462 set_current_state(TASK_UNINTERRUPTIBLE);
463 stop
= abort ? atomic_read(abort) : 0;
464 if
(stop || !host->claimed || host->claimer == current)
465 break;
466 spin_unlock_irqrestore(&host->lock,
flags);
467 schedule();
468 spin_lock_irqsave(&host->lock,
flags);
469 }
470 set_current_state(TASK_RUNNING);
471 if
(!stop) {
472 host->claimed
= 1;
473 host->claimer
= current;
474 host->claim_cnt
+= 1;
475 }
else
476 wake_up(&host->wq);
477 spin_unlock_irqrestore(&host->lock,
flags);
478 remove_wait_queue(&host->wq,
&wait);
479 if
(!stop)
480 mmc_host_enable(host);
481 return
stop;
482 }
453行初始化一个等待节点,后面我们将看到他的作用。
457行might_sleep宏其实就是检查是否需要重新调度,如果是,则进行调度。一般都用在可能引发睡眠的上下文中,完成任务的抢占。
459行将新的等待节点加入到主机的host->wq等待对列中,主要这里只是说加入进来
464行满足以下条件之一该线程将不会睡眠直接跳出while循环,分别是设置了终止、host空闲可用或者拥有该host的是本线程。当符合以上三条中的任何一种情况,程序会跳转到470行,重新设置线程状态为运行态。下面我们在abort=0的条件下分两种情况来讨论上面的整段代码的执行情况:
l 情况一:如果此时host不是空闲状态,host->
claimed=1且host->claimer != current此时按常理来说本线程将等待,459行,462行以及467行这三行代码使得线程睡眠在host->wq等待队列上,什么时候唤醒后面说到。
l 情况二:此时host处于空闲状态host->
claimed=0,或者host拥有者就是当前这个线程,这时程序直接跳转到470行,重新将进程状态设为运行态,当然这时的471-475行也就得以执行了。这三个变量的重新赋值意味着本线程得到了Host的控制权,当然478行的等待队列中的元素也就该删除了,480行host开始工作,具体实现后面再分析。
最后,来看看这段代码的一个最特殊的情况abort!=0时,这种情况本身就是个bug,既要claime然后又让人家abort,这一点我是看不穿想不透了,不过476行的代码倒是有几分的人性化,如果像前面所说的有线程睡眠在了host->wq上了,那么这个时候wake_up(&host->wq);一旦发出就回唤醒那些等待host的线程重新申请资源,毕竟这个host较少,资源还是相当紧张的。当然这是这种变态社会中的一种极其变态的情况,真正正常的wake_up可能还是要等到mmc_release_host的时候,在这里只是先提一下。
最后的最后要说的是current这个东西,这个指的不是别人,正是前面一节我们谈到的那个块请求处理的内核线程,当然早在card的prob中有个mmc_blk_set_blksize里面也使用到了这个她,那个时候到底指向的谁就确实不知道了。
另外,刚才说到了mmc_host_enable这个函数,定义在/mmc/core/core.c中,还是来简单的看一下:
[mmc/core/core.c]
346 /**
347 *
mmc_host_enable - enable a host.
348 *
@host: mmc host to enable
349 *
350 *
Hosts that support power saving can use the 'enable' and 'disable'
351 *
methods to exit and enter power saving states. For more information
352 *
see comments for struct mmc_host_ops.
353 */
354 int
mmc_host_enable(struct mmc_host *host)
355 {
356 if
(!(host->caps & MMC_CAP_DISABLE))
357 return
0;
358
359 if
(host->en_dis_recurs)
360 return
0;
361
362 if
(host->nesting_cnt++)
363 return
0;
364
365 cancel_delayed_work_sync(&host->disable);
366
367 if
(host->enabled)
368 return
0;
369
370 if
(host->ops->enable) {
371 int
err;
372
373 host->en_dis_recurs
= 1;
374 err
= host->ops->enable(host);
375 host->en_dis_recurs
= 0;
376
377 if
(err) {
378 pr_debug("%s:
enable error %d\n",
379 mmc_hostname(host),
err);
380 return
err;
381 }
382 }
383 host->enabled
= 1;
384 return
0;
385 }
365行这实际上是内核维护的一个全局的工作队列,使用时不需要定义工作队列结构体,全局工作队列创建的时候可以使用如下方法:
int schedule_work(struct
work_struct *work ); 

int schedule_work_on(intCPU,struct
work_struct *work ); 

int scheduled_delayed_work(struct
delayed_work *dwork,unsigned long delay); 

int scheduled_delayed_work_on(int
cpu,struct delayed_work *dwork,unsigned long delay); 
如果任务被延迟,调用cancel_delayed_work_sync将会终止队列中的任务或者阻塞任务直到回调结束(如果处理程序已经在处理该任务)。从这里不难发现主机处理任务的方式很有可能是利用的全局工作队列,具体如何等见到创建队列任务的时候再说。
370-382行就是调用host所提供的mmc_host_ops方法来设置了,其实整个过程是和低功耗相关的,这里就不再深入研究了。
看完mmc_claim_host,我们乘热打铁把他的孪生兄弟mmc_release_host也给解决了。

3. mmc_release_host

mmc_release_host与mmc_claim_host一起出生入死,始终是成对出现,执行的过程肯能在顺序上有点颠倒,上点源码如下:
[mmc/core/core.c]
575 void
mmc_release_host(struct mmc_host *host)
576 {
577 WARN_ON(!host->claimed);
578
579 mmc_host_lazy_disable(host);
580
581 mmc_do_release_host(host);
582 }
579行跟进源码:
[mmc/core/core.c]
545 int
mmc_host_lazy_disable(struct mmc_host *host)
546 {
547 if
(!(host->caps & MMC_CAP_DISABLE))
548 return
0;
549
550 if
(host->en_dis_recurs)
551 return
0;
552
553 if
(--host->nesting_cnt)
554 return
0;
555
556 if
(!host->enabled)
557 return
0;
558
559 if
(host->disable_delay) {
560 mmc_schedule_delayed_work(&host->disable,
561
msecs_to_jiffies(host->disable_delay));
562 return
0;
563 }
else
564 return
mmc_host_do_disable(host, 1);
565 }
整个过程与上面说说的mmc_host_enable正好相反。
559-560行如果主机进入低功耗有个延时过程,那么就通过全局工作队列来进行延时调度,其中mmc_schedule_delayed_work调用的不是别人正是queue_delayed_work(workqueue,
work, delay),至于host->disable里面放着什么内容这是host那边的事,谈到了在论。
564行如果主机没有这个癖好直接可以disable那再好不过了,mmc_host_do_disable(host,
1);为您解决一切后顾之忧。
[mmc/core/core.c]
388 static
int mmc_host_do_disable(struct mmc_host *host, int lazy)
389 {
390 if
(host->ops->disable) {
391 int
err;
392
393 host->en_dis_recurs
= 1;
394 err
= host->ops->disable(host, lazy);
395 host->en_dis_recurs
= 0;
396
397 if
(err < 0) {
398 pr_debug("%s:
disable error %d\n",
399 mmc_hostname(host),
err);
400 return
err;
401 }
402 if
(err > 0) {
403 unsigned
long delay = msecs_to_jiffies(err);
404
405
mmc_schedule_delayed_work(&host->disable, delay);
406 }
407 }
408 host->enabled
= 0;
409 return
0;
410 }
由此可见这个忧患也并没达到什么程度,394行爽快调用了host提供的disable方法,但是405行又再次出现一个延时调用,这个解释要想合理只有等到明年春暖花开的季节我们分析host的时候了。总之,当等到了那一天一切真相都会水落石出。
回到mmc_release_host还剩下最后一行,mmc_do_release_host(host)。
[mmc/core/core.c]
509 static
void mmc_do_release_host(struct mmc_host *host)
510 {
511 unsigned
long flags;
512
513 spin_lock_irqsave(&host->lock,
flags);
514 if
(--host->claim_cnt) {
515 /*
Release for nested claim */
516 spin_unlock_irqrestore(&host->lock,
flags);
517 }
else {
518 host->claimed
= 0;
519 host->claimer
= NULL;
520 spin_unlock_irqrestore(&host->lock,
flags);
521 wake_up(&host->wq);
522 }
523 }
这段代码逻辑异常清晰,格式异常工整,一切犹如行云流水一般。环环相扣,句句入理。这么多的优点面前我只说一句,省的大煞风景。
521行再见了wake_up,前面说过一个变态的,这里出现的这个可是他家的正统血脉哦,一个主机控制器的release,wake_up了一批抢占他的线程,这里就是个强有力的证明。
好了,废话加白话总之没有一句真话的把mmc_release_host讲完了,接下来该步入整个SD卡故事得正题了,别笑的太早,到时候有你好受。

4. mmc_wait_for_req

总算是轮到他了,不是不想说他,是说起他来估计说到天黑还没个底。但是无论怎样,天塌下来内核源代码都还是要看的。废话少说,先上源码:
[mmc/core/core.c]
184 /**
185 *
mmc_wait_for_req - start a request and wait for completion
186 *
@host: MMC host to start command
187 *
@mrq: MMC request to start
188 *
189 *
Start a new MMC custom command request for a host, and wait
190 *
for the command to complete. Does not attempt to parse the
191 *
response.
192 */
193 void
mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
194 {
195 DECLARE_COMPLETION_ONSTACK(complete);
196
197 mrq->done_data
= &complete;
198 mrq->done
= mmc_wait_done;
199
200 mmc_start_request(host,
mrq);
201
202 wait_for_completion(&complete);
203 }
我汗,这啥意思,咋整个代码比潘长江还短。罢了不管他,研究下代码。
200行mmc_start_request搞了半天才开始请求,你说这急人不急人,好了看你怎个start法。
[mmc/core/core.c]
121 static
void
122 mmc_start_request(struct
mmc_host *host, struct mmc_request *mrq)
123 {
124 #ifdef
CONFIG_MMC_DEBUG
125 unsigned
int i, sz;
126 struct
scatterlist *sg;
127 #endif
128
129 pr_debug("%s:
starting CMD%u arg %08x flags %08x\n",
130 mmc_hostname(host),
mrq->cmd->opcode,
131 mrq->cmd->arg,
mrq->cmd->flags);
132
133 if
(mrq->data) {
134 pr_debug("%s:
blksz %d blocks %d flags %08x "
135 tsac
%d ms nsac %d\n,
136 mmc_hostname(host),
mrq->data->blksz,
137 mrq->data->blocks,
mrq->data->flags,
138 mrq->data->timeout_ns
/ 1000000,
139 mrq->data->timeout_clks);
140 }
141
142 if
(mrq->stop) {
143 pr_debug("%s:
CMD%u arg %08x flags %08x\n",
144 mmc_hostname(host),
mrq->stop->opcode,
145 mrq->stop->arg,
mrq->stop->flags);
146 }
147
148 WARN_ON(!host->claimed);
149
150 led_trigger_event(host->led,
LED_FULL);
151
152 mrq->cmd->error
= 0;
153 mrq->cmd->mrq
= mrq;
154 if
(mrq->data) {
155 BUG_ON(mrq->data->blksz
> host->max_blk_size);
156 BUG_ON(mrq->data->blocks
> host->max_blk_count);
157 BUG_ON(mrq->data->blocks
* mrq->data->blksz >
158 host->max_req_size);
159
160 #ifdef
CONFIG_MMC_DEBUG
161 sz
= 0;
162 for_each_sg(mrq->data->sg,
sg, mrq->data->sg_len, i)
163 sz
+= sg->length;
164 BUG_ON(sz
!= mrq->data->blocks * mrq->data->blksz);
165 #endif
166
167 mrq->cmd->data
= mrq->data;
168 mrq->data->error
= 0;
169 mrq->data->mrq
= mrq;
170 if
(mrq->stop) {
171 mrq->data->stop
= mrq->stop;
172 mrq->stop->error
= 0;
173 mrq->stop->mrq
= mrq;
174 }
175 }
176 host->ops->request(host,
mrq);
177 }
这不看不大紧,一看乐死人。149行以前全是debug要用的,本来就不长的代码这一缩水可真没啥分量了。
150行啥玩意,LED?都啥时候了还有这闲工夫玩这玩意。搞硬件的兄弟们总喜欢在板子上搞几个LED,弄的像不整几个上去不足以展示自己实力似的,这也就苦了这帮子写内核的哥们,既然有了个状态指示灯,总不能让它没反应吧,省的那些不懂硬件的人怀疑哪位画板子的哥们把个LED的原理图给画错了,这就麻烦大了,会死人的。好吧就加段小代码让他工作起来吧,也就有了这个led_trigger_event,当然是可配置的。如果您确实认为有必要研究一番,就劳驾自己去观摩吧,我在这里就先失陪了。
167-173行这几行什么意思我始终没能明白,也许真的到了春暖花开的季节才能明白他的良苦用心吧。没办法,现在还不能说,那就等待等待在等待吧....
176行不用我多说肯定都知道这是个什么意思,不错调用的真是host边的接口,好了不能再说了,再说就有人告我侵犯领土完整了。
前面说了这个函数很复杂的,怎么?放心好戏在后头。好了回到mmc_wait_for_req...
202行wait_for_completion(&complete);典型的complete机制,不会吧没听说过,那只能说明你out了。内核同步机制中complete可是也占了部分江山的呀,wait_for_completion以后的结果就是调用线程你可以进入冬眠了,什么时候我这边做完了会利用complete来解脱你的。好了话都说到这份上了,您自己说这个等待的过程是不是很漫长,何况也没有个期限,即使一万年也得等啊。
197-198行mmc_wait_done等待完成,进去看看
[mmc/core/core.c]
179 static
void mmc_wait_done(struct mmc_request *mrq)
180 {
181 complete(mrq->done_data);
182 }
181行确是complete,而且complete就是wait_for_completion的那个对象,好了大胆想象吧,N年后的某个地方我们肯定会与mmc_wait_done在聚首。不信就走着瞧....

5. mmc_wait_for_cmd

是时候进入mmc_wait_for_cmd了,不过遗憾的是这个函数也确实没有太多吸引人眼球的地方,还是贴出他的源码来吧。
[mmc/core/core.c]
217 int
mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries)
218 {
219 struct
mmc_request mrq;
220
221 WARN_ON(!host->claimed);
222
223 memset(&mrq,
0, sizeof(struct mmc_request));
224
225 memset(cmd->resp,
0, sizeof(cmd->resp));
226 cmd->retries
= retries;
227
228 mrq.cmd
= cmd;
229 cmd->data
= NULL;
230
231 mmc_wait_for_req(host,
&mrq);
232
233 return
cmd->error;
234 }
命令的提交形式与数据请求的有点区别,至少没能构建一个完整的struct
mmc_request结构。但是最终却都是调用了mmc_wait_for_req。至于这些个struct
mmc_command 、
struct mmc_reques现在确实不方便也没办法说清楚,说到真正作用在硬件上的传输过程之时,也将是揭开他神秘面纱之日。
故事发展到这里我们断了线索,但是有一点是肯定的core为我们所干的远不止这些,至少到目前为止我们只分析到了呈上所做的工作,至于启下会干那些工作等我们看完host再来给他画上圆满的句号,我想只有这样才能表达我们对这个吃苦耐劳的core最崇高的敬意。
http://m.blog.csdn.net/blog/rain0993/8476755

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多