sd卡驱动分析之core

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_initsubsys_initcall永远是linux内核中最“忠实”的奸臣。当然也还有其他的乱臣贼子,一张口就道出内核入口的,这里就不在一一列出了。前面说到card目录的时候,入口显然是module_init,记性稍微好点的哥们可能还记得当时是因为mmc_bus_type这条总线才让我们card的故事得以延续的。换句话来说,如果这条总线都还尚未注册,那么请问card目录又将如何利用总线mmc_bus_typeprobe方法最终走向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 cardBluetooth cardRadio/TV cardSDIO的应用将是未来嵌入式系统最重要的接口技术之一,并且也会取代目前 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行初始化一个等待节点,后面我们将看到他的作用。

457might_sleep宏其实就是检查是否需要重新调度,如果是,则进行调度。一般都用在可能引发睡眠的上下文中,完成任务的抢占。

459行将新的等待节点加入到主机的host->wq等待对列中,主要这里只是说加入进来

464行满足以下条件之一该线程将不会睡眠直接跳出while循环,分别是设置了终止、host空闲可用或者拥有该host的是本线程。当符合以上三条中的任何一种情况,程序会跳转到470行,重新设置线程状态为运行态。下面我们在abort=0的条件下分两种情况来讨论上面的整段代码的执行情况:

l  情况一:如果此时host不是空闲状态,host-> claimed=1host->claimer != current此时按常理来说本线程将等待459行,462行以及467行这三行代码使得线程睡眠在host->wq等待队列上,什么时候唤醒后面说到。

l  情况二:此时host于空闲状态host-> claimed=0,或者host拥有者就是当前这个线程,这时程序直接跳转到470行,重新将进程状态设为运行态,当然这时的471-475行也就得以执行了。这三个变量的重新赋值意味着本线程得到了Host的控制权,当然478行的等待队列中的元素也就该删除了,480host开始工作,具体实现后面再分析。

最后,来看看这段代码的一个最特殊的情况abort=0时,这种情况本身就是个bug,既要claime然后又让人家abort,这一点我是看不穿想不透了,不过476行的代码倒是有几分的人性化,如果像前面所说的有线程睡眠在了host->wq上了,那么这个时候wake_up(&host->wq);一旦发出就回唤醒那些等待host的线程重新申请资源,毕竟这个host较少,资源还是相当紧张的。当然这是这种变态社会中的一种极其变态的情况,真正正常的wake_up可能还是要等到mmc_release_host的时候,在这里只是先提一下。

最后的最后要说的是current这个东西,这个指的不是别人,正是前面一节我们谈到的那个块请求处理的内核线程,当然早在cardprob中有个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_hostmmc_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,前面说过一个变态的,这里出现的这个可是他家的正统血脉哦,一个主机控制器的releasewake_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 }    

我汗,这啥意思,咋整个代码比潘长江还短。罢了不管他,研究下代码。

200mmc_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...

202wait_for_completion(&complete);典型的complete机制,不会吧没听说过,那只能说明你out了。内核同步机制中complete可是也占了部分江山的呀,wait_for_completion以后的结果就是调用线程你可以进入冬眠了,什么时候我这边做完了会利用complete来解脱你的。好了话都说到这份上了,您自己说这个等待的过程是不是很漫长,何况也没有个期限,即使一万年也得等啊。

197-198mmc_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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值