源码分析mmc体系结构

版权声明:本文为博主原创文章,转载请注明出处:https://blog.csdn.net/huang_165/article/details/86678814

      由上一篇博文《简单分析mmc体系结构》知道,mmc core会注册一个mmc bus并等待card device、card driver的加入。
也简单地介绍了mmc framework三层大概功能、用到那些内核组件。这篇博文是上一篇博文的源码分析版。


内核版本:

#uname -a
Linux rk3399 4.4.154 #62 SMP Sun Jan 27 17:09:48 PST 2019 aarch64 GNU/Linux


sd 3.0协议文档:https://pan.baidu.com/s/1uQextx64UkZmvbIrYSvLvg

我的源码版本:https://github.com/Mr-jinfa/rk3399-project
2019.1.28号版本的

博文解决两个问题:
一:探究mmc framework如何支持sd卡的冷启动、热插拔的?
二:探究上层文件系统-->块io调度层下来的请求是如何作用到sd卡上的(即从上层应用到硬件层的动作)。

解决这两个问题的方法是参看源码调用关系,建议读者结合博主的《简单分析mmc体系结构》来看。


问题一:探究mmc framework如何支持sd卡的冷启动、热插拔的。

card 冷启动时检查卡并加入到系统中的执行路径
dw_mci_rockchip_probe	初始化mmc主机控制器
	|->dw_mci_pltfm_register
	|	|->dw_mci_probe
	|	|	|->dw_mci_init_slot  初始化插槽
	|	|	|	|->mmc_alloc_host  申请一个插槽
	|	|	|	|	|->INIT_DELAYED_WORK(&host->detect, mmc_rescan);  host->detect挂一个服务函数--mmc_rescan
	|	|	|	|->mmc->ops = &dw_mci_ops;  填充host操作集
	|	|	|            (1)|->dw_mci_get_cd  检查下cd gpio看是否有卡插入,card冷启动会检测到,热插拔的话就等mmc_gpio_cd_irqt执行。
	|	|	|	|->mmc_add_host  注册一个host驱动--用来和sd卡通信
	|	|	|		|->device_add(&host->class_dev)
	|	|	|		|->mmc_start_host  启动host包括检测卡是否有效
	|	|	|			|->mmc_claim_host(host)  占用host,也就是占用主机控制器
	|	|	|			|->mmc_gpiod_request_cd_irq  为cd_gpio挂一个中断isr,方便以后有卡插入能第一时间探测到.
	|	|	|				|->devm_request_threaded_irq:mmc_gpio_cd_irqt中断服务函数
	|	|	|			|->_mmc_detect_change  主动检测下卡有无插入,
	|	|	|			|->mmc_schedule_delayed_work:host->detect  切换上下文,去执行host->detect
							请看detect切换点
card 热插拔时检查卡并加入到系统中的执行路径
由上面的冷启动路径可知,在(1)这个位置作为冷启动、热插拔卡的分路,即卡是热插拔的话就等mmc_gpio_cd_irqt,那么我们分析mmc_gpio_cd_irqt。
mmc_gpio_cd_irqt
	|->mmc_detect_change
	|	|->_mmc_detect_change
	|	|	|->if (device_can_wakeup(mmc_dev(host)) sd卡cd gpio具有唤醒cpu功能?
	|	|	|	|->pm_wakeup_event
	|	|	|->mmc_schedule_delayed_work:host->detect  切换出去,执行host->detect
				请看detect切换点
detect切换点:
host->detect:mmc_rescan
	|->host->ops->get_cd(host)  先检查下cd gpio确定是否位卡插入电平
	|->mmc_rescan_try_freq  最终调用这个函数来确认sd卡的插入与否
		|->mmc_attach_mmc  探测mmc卡
			|->mmc_attach_bus
			|->mmc_select_voltage  适配电压(mmc协议图示)
			|->mmc_init_card  处理这个检测动作、初始化卡
			|->mmc_add_card  用驱动模型注册一个新的MMC卡。
			|->设置卡的名字、类型
			|->mmc_init_context_info  初始化同步上下文,这个上下文将用来同步card driver和host 层
				|->init_waitqueue_head(&host->context_info.wait)
			|->mmc_of_find_child_device  找到合适的device并增加到mmc bus中。
			|->device_add  这样一个card device就生成了,然后mmc bus match、mmc bus probe就开始动作了。

问题二:探究上层文件系统-->块io调度层下来的请求是如何作用到sd卡上的

      先找到分析入口:根据博主的博文《Linux下编写一个ramdisk块设备驱动...》提供的源码可知,编写一个块设备驱动需要填充一个请求处理函数。blk_init_queue(sbull_full_request, &dev->lock);sbull_full_request为请求处理函数。
      那么在mmc framework中我们在card driver层找到这种请求处理函数就好了,从mmc/card/block.c的probe函数出发。

mmc_blk_probe
	|->mmc_blk_alloc 填充一个disk设备
	|	|->mmc_blk_alloc_req
	|	|	|->md = kzalloc(sizeof(struct mmc_blk_data), GFP_KERNEL)  申请一个块设备用到的数据
	|	|	|->mmc_init_queue:md->queue
	|	|	|	|->sema_init(&mq->thread_sem, 1)  线程运行标记-信号量
	|	|	|	|->kthread_run:mmc_queue_thread  设置一个线程
	|	|	|	|->blk_init_queue:mmc_request_fn  为上层文件系统挂一个请求处理函数
	|	|	|->md->queue.issue_fn = mmc_blk_issue_rq
	|	|	|->md->queue.data = md;
	|	|	|->md->disk->fops = &mmc_bdops;
	|->mmc_blk_alloc_parts  设置分区个数
	|->mmc_add_disk  分配分区
mmc_request_fn:这个函数是处理请求事件的入口函数-信标
	|->wake_up_process:mq->thread  唤醒线程来处理请求
mq->thread:mmc_queue_thread
	|->down(&mq->thread_sem)
	|->blk_fetch_request  取出一个请求
	|->mq->issue_fn(mq, req)  调用mmc_blk_issue_rq-信标
mmc_blk_issue_rq
	->mmc_blk_part_switch  选择对应的分区
	根据cmd_flags的不同做相应处理:假如是一个普通的读写cmd
	->mmc_blk_issue_rw_rq
		|->mmc_blk_rw_rq_prep  做些准备工作
		|->mmc_start_req  启动传输并从参数返回status
		|	|->mmc_wait_for_data_req_done  等待sd卡一个respond
		|	|	|->wait_event_interruptible:context_info->wait  在”detect切换点“有初始化这个队列头
		|	|	|->__mmc_start_request  处理请求
		|	|		|->mmc_retune  调整mmc控制器;如时钟速率
		|	|		|->host->ops->request(host, mrq)即dw_mci_request(由dw_mci_init_slot可知)
		|->switch (status) 根据返回的status不同做相应处理:假如成功读写
			|->mmc_blk_reset_success
			|->mmc_blk_simulate_delay
			|->mmc_blk_end_packed_req(只发命令,无数据操作的话)
			|->blk_end_request(有数据操作
dw_mci_request  这个request是由mmc host 层提供的
	|->dw_mci_get_cd先检测下cd gpio看有没有卡
	|->dw_mci_queue_request  
		|->dw_mci_start_request
			|->if(host->state == STATE_IDLE)  如果当前主机空闲的话
			|	@true|->dw_mci_start_request  开始传输
			|		|->提取cmd
			|		|->__dw_mci_start_request
			|		|	|->dw_mci_prepare_command  准备命令
			|		|	|->dw_mci_submit_data  提交数据
			|		|	|	|->dw_mci_submit_data_dma 提交到mmc host dma处
			|		|	|->dw_mci_start_command  启动dma传输
			|	@flase|->list_add_tail(&slot->queue_node, &host->queue);  加到host->缓冲队列中

总结一下:
1.mmc framework初始化时会注册一个cd gpio irq中断服务函数、检测下卡有无插入来实现mmc 框架下的sd卡的冷启动热插拔
2.mmc framework用一个线程来承接上层request,可以看到request下来的数据如果主机忙就缓存起来,不忙就用dma将数据运载出去。


用线程的好处是:
a.异步处理,在主线程畅快运行时可以通过call "req线程",来让"req线程"做数据操作,req线程卡住也不影响主线程的运行,最多主线程做一些回收工作。
b.主线程、"req线程"都可以休眠,特别是"req线程"它可以在sd卡忙时主动将数据缓存在host->queue中。
c.主线程可以在适当时间做大数据批量传输。


    关于mmc framework一些函数说明可以参考:https://blog.csdn.net/zwj0403/article/details/38409061
当然,也可以参考博主github提供的注释。

你可能感兴趣的:(块设备)