基于S3C2440的嵌入式Linux驅(qū)動——MMC/SD子系統(tǒng)解讀(一)
本文的內(nèi)容基于如下硬件和軟件平臺:
目標平臺:TQ2440
CPU:s3c2440
內(nèi)核版本:3.12.5
基于SD規(guī)范4.10,即《SD Specifications Part 1 Physical Layer Simplified Specification Version 4.10》。
一、MMC子系統(tǒng)構(gòu)架待寫。。。
待寫。。。
首先看看子系統(tǒng)是如何初始化的,完成哪些工作。
代碼位于linux/drivers/mmc/core/core.c。
staticint__initmmc_init(void)
{
intret;
/*創(chuàng)建一個工作隊列*/
workqueue=alloc_ordered_workqueue("kmmcd",0);
if(!workqueue)
return-ENOMEM;
/*注冊mmc總線,總線提供probe方法
并直接在內(nèi)部調(diào)用驅(qū)動probe方法*/
ret=mmc_register_bus();
if(ret)
gotodestroy_workqueue;
/*注冊名為mmc_host的類*/
ret=mmc_register_host_class();
if(ret)
gotounregister_bus;
/*注冊sdio總線,總線提供probe方法
并直接在內(nèi)部調(diào)用驅(qū)動probe方法*/
ret=sdio_register_bus();
if(ret)
gotounregister_host_class;
return0;
unregister_host_class:
mmc_unregister_host_class();
unregister_bus:
mmc_unregister_bus();
destroy_workqueue:
destroy_workqueue(workqueue);
returnret;
}
代碼首先注冊了一個工作隊列,這個工作隊列將用于掃描sd卡設(shè)備。我們會在后面進行說明。
工作對類已內(nèi)核線程的形式運行,可以用ps命令看到名為[kmmcd]的內(nèi)核線程。
接著注冊了兩條名為mmc和sdio的總線,以及一個名為mmc_host的類。具體代碼如下:
staticstructbus_typemmc_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,
.shutdown=mmc_bus_shutdown,
.pm=&mmc_bus_pm_ops,
};
intmmc_register_bus(void)
{
returnbus_register(&mmc_bus_type);
}
staticstructclassmmc_host_class={
.name="mmc_host",
.dev_release=mmc_host_classdev_release,
};
intmmc_register_host_class(void)
{
returnclass_register(&mmc_host_class);
}
staticstructbus_typesdio_bus_type={
.name="sdio",
.dev_attrs=sdio_dev_attrs,
.match=sdio_bus_match,
.uevent=sdio_bus_uevent,
.probe=sdio_bus_probe,
.remove=sdio_bus_remove,
.pm=SDIO_PM_OPS_PTR,
};
intsdio_register_bus(void)
{
returnbus_register(&sdio_bus_type);
}
staticstructclassmmc_host_class={
.name="mmc_host",
.dev_release=mmc_host_classdev_release,
};
intmmc_register_host_class(void)
{
returnclass_register(&mmc_host_class);
}
熟悉Linux的設(shè)備驅(qū)動模型的同學(xué)對這些肯定非常熟悉??偩€和類的注冊只是調(diào)用了相應(yīng)的接口,這些就不再贅述了。
其次,sdio總線不是我們關(guān)心的。我們只關(guān)心mmc總線。首先來看看mmc總線的match方法:
代碼位于linux/drivers/mmc/core/bus.c。
/*
*ThiscurrentlymatchesanyMMCdrivertoanyMMCcard-drivers
*themselvesmakethedecisionwhethertodrivethiscardintheir
*probemethod.
*/
staticintmmc_bus_match(structdevice*dev,structdevice_driver*drv)
{
return1;
}
match返回居然直接返回了1。這表示任意的驅(qū)動都能和mmc卡設(shè)備成功匹配。
從注釋中我們也能看出,驅(qū)動的probe方法將會決定驅(qū)動是否能真正的匹配這個mmc卡設(shè)備。
熟悉設(shè)備驅(qū)動模型的可能知道,隨著match返回1表示匹配成功后,將會調(diào)用總線提供的probe方法。接著我們來看下mmc總線的probe方法。
代碼位于linux/drivers/mmc/core/bus.c。
staticintmmc_bus_probe(structdevice*dev)
{
structmmc_driver*drv=to_mmc_driver(dev->driver);
structmmc_card*card=mmc_dev_to_card(dev);
returndrv->probe(card);
}
從這里我們可以看到在mmc的probe方法中直接調(diào)用了驅(qū)動probe方法,這也驗證了剛才注釋中所說的話。
從上面分析可以看出,子系統(tǒng)初始化代碼僅僅注冊了兩條總線和一個類,并建立了一個工作隊列。
MMC核心層要和SD卡設(shè)備進行通信,為了完成這一個工作需要將CMD或者ACMD命令通過MMC/SD控制器發(fā)送給SD卡。
那么MMC核心層如何將通信的數(shù)據(jù)包交給MMC/SD控制器,并讓后者去發(fā)送呢?
MMC通過函數(shù)mmc_wait_for_req完成這個工作,我們來看下這個函數(shù)。
4.1 mmc_wait_for_req函數(shù)下列代碼位于linux/drivers/mmc/core/core.c。
/**
*mmc_wait_for_req-startarequestandwaitforcompletion
*@host:MMChosttostartcommand
*@mrq:MMCrequesttostart
*
*StartanewMMCcustomcommandrequestforahost,andwait
*forthecommandtocomplete.Doesnotattempttoparsethe
*response.
*/
voidmmc_wait_for_req(structmmc_host*host,structmmc_request*mrq)
{
__mmc_start_req(host,mrq);
mmc_wait_for_req_done(host,mrq);
}
EXPORT_SYMBOL(mmc_wait_for_req);
通過注釋可以發(fā)現(xiàn),該函數(shù)會阻塞并等待request的完成。
該函數(shù)分兩步走,第一步調(diào)用__mmc_start_req發(fā)送命令,第二部調(diào)用 mmc_wait_for_req_done等待命令完成。
分別來看下這兩個函數(shù) :
staticint__mmc_start_req(structmmc_host*host,structmmc_request*mrq)
{
/*初始化completion,并設(shè)置done方法*/
init_completion(&mrq->completion);
mrq->done=mmc_wait_done;
/*如果mmc已經(jīng)被拔出,設(shè)置錯誤并返回錯誤*/
if(mmc_card_removed(host->card)){
mrq->cmd->error = -ENOMEDIUM;