eMMC驱动架构分析doc.docx
- 文档编号:28558139
- 上传时间:2023-07-19
- 格式:DOCX
- 页数:19
- 大小:27.58KB
eMMC驱动架构分析doc.docx
《eMMC驱动架构分析doc.docx》由会员分享,可在线阅读,更多相关《eMMC驱动架构分析doc.docx(19页珍藏版)》请在冰豆网上搜索。
eMMC驱动架构分析doc
eMMc驱动分析
块设备是Linux杂的设备之.一,但是作为固执「知其然的Geek,我们总会把代码翻个遍,把道理弄个透。
当然了,快速地学习一种新的东两,方法是最眞耍的,个人觉得:
内核卅中MMC/SD卡驱动程序构架是学习EMMC驱动程序的朋点,只冇理解了它才能真正理解该块设备驱动程序,同时才能真正理解LINUX块设备驱动程序。
一.需要的基础知识:
1.LINUX设备驱动的基本结构。
2.块设备驰动程序的基本构架(相信研究过LDD3当中的sbull的人应该都不成问题,如呆只是走马观花的话,那可得好好再补补了)
3.LINUX设备驱动模型。
4.EMMC的原理,S:
NandFlash的基础上加上一个负责:
ECC、负戯均衡和坏块管理功能的controlefo
一.・驱动程序分析
首先,说明一卜EMMC驱动涉及到的文件。
另外,我们匝点是分析驱动程序的基本构架,所以不同内核版本的差异并不是很大。
MMC/SD卡驱动程序位J-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/SDE控制器为例,其它类熨的控制器类似。
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的卡控制器了,而H•体是s3c24xx的卡控制器还是atmel的卡控制器,core层是不用知道的。
对这几个1:
1录有一个人概认识以后,我们來看几个重耍的数据结构:
structmmc_host用來描述卡控制器
structmmc_card用來描述卡
structmmc_driver用來描述mmc忙驱动
structmmc_host_ops用來描述K控制器操W用「•从主机控制器口向core加上册操作函数,从而将core层与JI•体的主机控制器隔离。
也就是说core要操作主机控制器,就用这个ops当屮给的曲数指针操作,不能直接调用人体主控制器的函数。
第一阶段:
从s3cmci_init开始往I、•看
staticint_inits3cmci_init(void)platform_driver_register(&s3cmci_drive「_2410);
}
有platform_driver_register函数,根据设备模型的知识,我们知道那•定会有对应的platform_device_register函数的,町是在哪里呢?
没仃看到,那足不是这个s3cmci_driver_2410当中给的probe函数就不执行屮?
?
当然不是,mci接11•般都是破件做好的(我认为是这样),所以在系统启动时一定会白调用platform_device_register对板匕的资源进行注册,如果没有这个硬件资源,那我们这个驱动也就没有用了•女A我们就假定是冇mci接「I的,ifn且也有与s3cmci_driver_2410对应的腴件资源注册了,那门己就会去跑probe函数。
来看一下s3cmci_driver_2410:
staticstructplatform_drivers3cmci_driver_2410={
driver.name=*'s3c2410-sdr,
probe
=s3cmci_probe_2410,
.remove
二s3cmci_removet
.suspend
二s3cmci_suspend,
resume
=s3cmciresume,
我们到s3cmci_probe_2410函数屮看.还是干脆自•接看s3cmci_probe算门
staticints3cmci_probe(structplatform_deviceapdev,intis2440)〃來『I/host/s3cmci.c
structmmc_host*mmc;
structs3cmci_hostwhost;
intret;
mmc二mmc_alloc_host(sizeof(structs3cmci_host)f&pdev->dev);
if(!
mmc){
ret=-ENOMEM;
gotoprobe_out;
mmc->ops=&s3cmci_ops;
ret=mmc_add_host(mmc);
if(ret){
dev_err(&pdev->dev,"failedtoaddmmchost./n");gotofree_dmabuf;
}
platform_set_drvdata(pdev,mmc);
return0;
}
这个函数很长,做的事件也很多,但我们关心的整个驱动的构架/流程,所以过滤掉-些细节的东西,只看2个最重耍的函数:
mmcallochost、mmcaddhost。
函数命塔已经很形象了,询者是屮请一个mmc_host,而后者是添加一个mmc_host。
中间还有一个操作,就是给mmc的ops成员赋上了s3cmci_ops这个值。
申请mmc_host当然很简单,就是申请一个结构体(我们暂忖.这样认为,因为他里面还做的比它爭情,后面会看到),而添加又是添加到哪电去呢?
看mmc_add_host函数:
intmmc_add_host(structmmc_host"host)〃来自core/host.c
{
interr;
err=device_add(&host->class_dev);
if(err)
returnerr;
mmc_start_host(host);
return0;
}
很简单,就是增加了一个device,然后就调用mmc_start_host了,那就先跳过
device_add这个动作•來看mmc_start_host:
voidmmc_start_host(structmmc_hostFost)//來『I/host/core.c
{
mmc_power_off(host);//掉电一下
mmc_detect_change(host,0);//?
?
?
}
看上去只有两行代码,不过浓缩才是精华,mmc_powe「_off(host)光看名子都知道是在干
什么,先跳过,来看mmc_detect_change,那么它到底干了些什么呢?
看_下就知道了:
voidmmc_detect_change(structmmc_host*host,unsignedlongdelay)//core/core.c
{
mmc_schedule_delayed_work(&host・>detect,delay);
}staticintmmc_schedule_delayed_work(structdelayed_work*work,unsignedlongdelay)
returnqueue_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里面,那估计是在刚才那个中请的地方设直的那个西数,冋过头來看一下mmcallochost:
structmmc_hostammc_alloc_host(intextra,structdevice*dev)//來core/host.c
structmmc_host*host;
host=kzalloc⑸zeof(st「uctmmc_host)+extra,GFP_KERNEL);
if(Jhost)
returnNULL;
INIT_DELAYED_WORK(&host->detect,mmc_rescan);
returnhost;
如果你看了queue_delayed_work这个函数功能介绍,相信对INIT_DELAYED_WORK也不会陌生了吧。
不废话了.来看mmcrescan:
//来自core/host.c
voidmmc_rescan(structwork_struct*work)IIII來hcore/host.c
{
structmmc_host*host=containe「_of(wo「k,structmmc^host,detect.work);
u32ocr;
interr;
/*detectanewlyinsertedcardw/
*FirstwesearchforSDIO...
•/
err=mmc_send_io_op_cond(host,0t&oc「);if(Ierr){
if(mmc_attach_sdio(hoslocr))mmc_power_off(host);
gotoout;
}
/*
...thennormalSD…err=mmc_send_app_op_cond(host,0,&oc「);if(!
err){
if(mmc_attach_sd(host,ocr))mmc_power_off(host);
gotoout;
}
*...andfinallyMMC.
7
err=mmc_send_op_cond(host,0,&ocr);
if(!
err){
if(mmc_attach_mmc(host,ocr))
mmc_power_off(host);
gotoout;
mmc_release_host(host);
mmc_power_off(host);
out:
if(host->caps&MMC_CAP_NEEDS_POLL)mmc_schedule_delayed_work(&host->detect.HZ);
}
浏览一个这个惭数,看看两数X,再看看注释,知道什么了吗?
它是在检测是不是有卡插入了卡控制器,如果有卡挺入就耍采取相应的行动了。
这里耍明白一点,我们平时用的SD/MMCR就足-个£,如采耍操作它得用SD/MMCK控制器才行,所以可以看到仃structmmc_card.structmmc_host的区分。
到这里了,來回忆一下s3cmci_probe这个函数做的事情,人概就是准备一个mmc_host结构,然后添加一•个主控制器设备到内核,最后乂调用了一下mmc_rescan來检测是不是有卡插入了。
如采有卡插入了还好,可以去操作E了,那如果没有卡插入呢?
mmc_rescan不是口调用了一次吗?
是啊,的确是白调用了一次•可是卡插入时为什么PC还是能检测到呢?
看来卡检测的动作不光是在probe的最后一步做了一次,其它地方也有做。
卡插入一般都是人为地随时去插入的,像这种情况一般都是会用中断机制去提供系统有外來侵入,然后再去采取行动。
SD/MMC卡也的确是这样做的,找來找去,发现在s3cmci_probe里面注册了•个屮断函数s3cmd_irq_cd(函数名的意思应该是irqcarddetect),就足迖个了,看看这个函数先:
staticirqreturn_ts3cmci_irq_cd(intirq,void*dev_id)//host/s3cmci.c
structs3cmci_host*host=(structs3cmci_host*)dev_id;
mmc_detect_change(host->mmc,msecs_tojiffies(500));
returnIRQ_HANDLED;
}
看到这个甫数想都不用想,直接跳到mmcjescan里面去看就行了。
前面已经知道了mmc_rescan里面就是在检测卡是不是插入了,既然卡随时插入我们都能检测到了,那就來看卡插入后都做了些什么动作吧。
第二阶段:
mmc_rescan里而既耍检测sd又耍检测mmcR的,我们就陳着一个往下走,假泄有个人插入了MMC卡,那就应该走下面这儿行:
err=mmc_send_op_cond(host,0,&ocr);
if(!
err){
if(mmc_attach_mmc(host,ocr))
mmc_power_off(host);
gotoout;
}
mmc_send_op_cond这个换数据说是读了一I、卡的什么值,这个值是什么意义我也不消楚,这就像检测FLASH时读FLASH的ID一样,网卡也是这样的,不用管这个值的意义了,只要知道它能标识是一个MMC卡插入就行了。
如果取这个值没有错谋的话就得进mmc_attach_mmc了:
r
*StartingpointforMMCcardinit.
*/intmmc_attach_mmc(structmmc_host*host,u32ocr)//core/mmc.c
interr;
mmc_attach_bus_ops(host);//这个与总线的电源管理仃关,暂时跳过
*Detectandinitthecard
T
err=mmc_init_card(host,host->ocr,NULL);if(err)
gotoerr;
mmc_release_host(host);
err=mmc_add_card(host->card);if(err)
gotoremove_card;
return0;
remove_card:
err:
returnerr;
还是找儿个关键函数來看mmc_init_ca「d从函数名来看就是初始化一个card,这个card
就用structmmc_card结构来描述,然后乂调用mmc_add_ca「d将卡设备添加到了内核,先來看mmcjnit_card都做了些什么事情:
staticintmmcjnit_card(structmmc_host*host,u32ocrt
structmmc_ca「d*oldcard)
structmmc_card*card;
interr;
u32cid[4];
unsignedintmax_dtr;
r
wAllocatecardstructure.
7
card=mmc_alloc_card(host,&mmc_type);
if(IS_ERR(card)){
err=PTR_ERR(card);
gotoerr;
}
card->type=MMC_TYPE_MMC;
card->rca=1;
memcpy(card->raw_cid,dd9sizeof(card->raw_cid));
host->card=card;return0;
freeboard:
returnerr;
}
将与破件操作相关的全部删抻"最后対我们有用的也就这儿行了mmc_alloc_card申请了一个structmmc_card结构,然后给card->type赋上MMC_TYPE_MMC,最后将card又赋给了host->card,这和貝•体硬件还是挺像的,因为一个主控制器一般就插一个卡,有卡时host->card有值,没有卡时host->card自己就是NULL了。
钻进mmcalloccard里而來看看:
•AllocateandinitialiseanewMMCcardstructure
7
structmmc_cardwmmc_alloc_card(structmmc_hostwhost,structdevice_typewtype)
{
structmmc_card*card;
card=kzalloc⑸zeof(st「uctmmc_card),GFP_KERNEL);
if(Icard)
returnERR_PTR(-ENOMEM);
card->host=host;
device_initialize(&card->dev);
card->dev.parent=mmc_classdev(host);
card->dev.bus=&mmc_bus_type;
card->dev.release=mmcreleasecard;
card->dev.type二type;
returncard;
}
Structmmc_card结构里面包含了一个structdevice结构,mmc_alloc_card不但申请了内存,而忖.还填允了structdevice中的儿个成员,尤比card->dev.bus=&mmc_bus_type;这一句耍直点对待。
申请一个mmc_ca「d结构,并简单初始化后,mmc_init_ca「d的使命就完成J‘,然后再调用mmc_add_card将这个card设备添加到内核。
mmc_add_card实实很简单,就是调用device_add将card->dev添加到内核当中去。
知道总线模空这个东西的人都明白,理到device.add甲.面总线就应该有动作了,H体是哪个总线呢?
那就得看你调用device_add时送的那个dev里而指定的是哪个总线T,我们送的card->dev,那么card->dev.bus具体指向什么呢?
很明现是那个
mmc_bus_type:
staticstructbus_typemmc_bus_type={
name
="mmc",
dev_attrs
二mmcdevattrs,
.match
=mmcbusmatch,
uevent
=mmc_bus_uevent,
probe
=mmc_bus_probe,
.remove
=mmcbusremove,
MB9
.suspend
=mmc_bus_suspend,
resume
=mmcbusresume,
};
在device_add里面,设备対应的总线会拿看你这个设备和挂在这个总线上的所有驰动程序去匹配(match),此时会调用match两数,如果匹配到了就会调用总线的probe函数或驱动的probe函数,那我们看一下这里的mmc_bus_match是如何进行匹配的:
staticintmmc_bus_match(structdevicewdev,structdevice_driver*drv)
return1;
}
看來match永远都能成功,那就去执行probe吧:
staticintmmc_bus_probe(structdevice*dev)
{
structmmc_driver*drv=to_mmc_driver(dev->dnver);
structmmc_card*card=dev_to_mmc_card(dev);
returndrv->probe(card);
}
这里就有点麻烦了,在这个函数里而又调用了一下drv->probe(),那这个drv是什么呢?
上而冇:
structmmcdriverwdrv=tommcdriver(dev->driver);
match换数总是返冋1,那看來只耍是挂在这条总线上的driver都有可能跑到这里來了,事实的确也是这样的,不过好在挂在这条总线上的driver只有一个,它是这样定义的:
staticstructmmc_drivermmc_driver={
drv={
.name="mmcblk:
},
probe=mmc_blk_probe,
.remove二mmcblkremove,
.suspend=mmc_blk_suspend,
resume=mmc_blk_resumet};
看到这里时,card/core/host儿个l!
经全部被扯进來了,边看mmc_driver中的儿个函数,他们儿个如何联系起來也就慢慢明白了。
那我们继续吧。
第三阶段:
前面已经看到了,在总线的probe里面调用了drv->p「obe,而这个函数就对的是mmc_blk_probe,具体这个mmc_driver是怎么挂到mmc_bus上的,门己去看mmc_blk_init(),就几彳『代码,应该不难。
staticintmmc_blk_probe(structmmc_card*card)//來flcard/block.c
{
structmmcblkdata*md;
interr;
md二mmc_blk_alloc(card);
if(IS_ERR(
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- eMMC 驱动 架构 分析 doc
![提示](https://static.bdocx.com/images/bang_tan.gif)