mmc驱动代码分析(未完待续)

mmc驱动代码分析(未完待续)

参考资料

先附上参考资料,也方便自己查询。

原理性的知识就不做分析了,参考资料里的大佬分析得都很好。

蜗窝科技网站的mmcemmc相关的文章,Linux Kernel Internals

Hacker_Albert博客mmc相关的文章。

Linux设备驱动开发详解-基于最新的Linux 4.0内核》–宋宝华 编著

mmc驱动代码分析(未完待续)

本文章的分析基于linux-4.9.259版本内核,可通过163镜像站下载linux-4.9.259.tar.xz

MMC/SD存储卡的驱动位于内核源码的目录drivers/mmc下,下面又分为card、core、host3个子目录。card层实际上跟Linux的块设备子系统对接,实现块设备驱动以及完成请求,但是具体的协议经过core层的接口,最终通过host完成传输,因此整个MMC子系统的框架如下图所示。card目录除实现标准的MMC/SD存储卡以外,还包含一些SDIO外设和驱动。core目录除了给card提供接口外,也定义好了host驱动的框架。

mmc驱动代码分析(未完待续)

上图MMC子系统的框架是通过Fedora33系统的LibreOffice Draw软件画的,谁说Linux只有字符界面,画图,视频,什么都行。

card/block.c 文件 的mmc_blk_init初始化块设备,通过

res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");

注册块设备。再通过

res = mmc_register_driver(&mmc_driver);

向core层注册mmc驱动。

其中mmc_driver结构体有2个重要成员函数,mmc_blk_probe函数是在检测到mmc卡插入时执行,mmc_blk_remove函数是在mmc卡移除时执行。

static struct mmc_driver mmc_driver = {
    .drv        = {  
        .name   = "mmcblk",
        .pm = &mmc_blk_pm_ops,
    },   
    .probe      = mmc_blk_probe,
    .remove     = mmc_blk_remove,
    .shutdown   = mmc_blk_shutdown,
};

core/bus.c文件下的mmc_bus_probe调用mmc_blk_probe

static struct bus_type mmc_bus_type = {
    .name       = "mmc",
    .dev_groups = mmc_dev_groups,
    .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,
}; 

mmc_bus_type的分析 TODO

函数调用顺序为:mmc_blk_probe-> mmc_blk_alloc-> mmc_blk_alloc_req-> mmc_init_queue

mmc_blk_alloc_req函数中,通过

md->disk->fops = &mmc_bdops;

指定块设备操作的函数,当应用层执行open、close、ioctl等操作时调用相应的接口:

static const struct block_device_operations mmc_bdops = {
    .open           = mmc_blk_open,
    .release        = mmc_blk_release,
    .getgeo         = mmc_blk_getgeo,
    .owner          = THIS_MODULE,
    .ioctl          = mmc_blk_ioctl,
#ifdef CONFIG_COMPAT
    .compat_ioctl       = mmc_blk_compat_ioctl,
#endif
};

mmc_init_queue中通过

mq->queue = blk_init_queue(mmc_request_fn, lock);

绑定了请求处理函数mmc_request_fn。再通过

mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd/%d%s", host->index, subname ? subname : "");

运行mmc对应的内核线程mmc_queue_thread

当应用层调用read,write等接口时,mmc_request_fn函数会唤醒mmc对应的内核线程mmc_queue_thread来处理请求。mmc对应的内核线程mmc_queue_thread执行mmc_blk_issue_rq函数,mmc_blk_issue_rq函数再调用mmc_blk_issue_rw_rqmmc_blk_issue_rw_rq函数最终会调用core层的mmc_start_req函数。

mmc_start_req函数里调用host驱动的mmc_host_ops成员函数(位于文件host/sdhci.c):

static const struct mmc_host_ops sdhci_ops = {
    .request    = sdhci_request,
    .post_req   = sdhci_post_req,
    .pre_req    = sdhci_pre_req,
    .set_ios    = sdhci_set_ios,
    .get_cd     = sdhci_get_cd,
    .get_ro     = sdhci_get_ro,
    .hw_reset   = sdhci_hw_reset,
    .enable_sdio_irq = sdhci_enable_sdio_irq,
    .start_signal_voltage_switch    = sdhci_start_signal_voltage_switch,
    .prepare_hs400_tuning       = sdhci_prepare_hs400_tuning,
    .execute_tuning         = sdhci_execute_tuning,
    .select_drive_strength      = sdhci_select_drive_strength,
    .card_event         = sdhci_card_event,
    .card_busy  = sdhci_card_busy,
};

接下来以Samsung SoC平台为例子分析host driver(文件host/sdhci-s3c.c):

注册platfom驱动:

static struct platform_driver sdhci_s3c_driver = { 
    .probe      = sdhci_s3c_probe,
    .remove     = sdhci_s3c_remove,
    .id_table   = sdhci_s3c_driver_ids,
    .driver     = { 
        .name   = "s3c-sdhci",
        .of_match_table = of_match_ptr(sdhci_s3c_dt_match),
        .pm = &sdhci_s3c_pmops,
    },  
};

module_platform_driver(sdhci_s3c_driver);

当设备和驱动匹配时调用sdhci_s3c_driver中的成员函数sdhci_s3c_probe

sdhci_s3c_probe函数先初始化参数,其中sdhci_s3c_opssdhci_ops类型,是host driver要实现的核心内容,由于各个host的硬件有所差异,所以实际和硬件交互的驱动部分还是在host driver中实现:

static struct sdhci_ops sdhci_s3c_ops = {
    .get_max_clock      = sdhci_s3c_get_max_clk,
    .set_clock      = sdhci_s3c_set_clock,
    .get_min_clock      = sdhci_s3c_get_min_clock,
    .set_bus_width      = sdhci_s3c_set_bus_width,
    .reset          = sdhci_reset,
    .set_uhs_signaling  = sdhci_set_uhs_signaling,
};

sdhci_s3c_probe函数最后调用sdhci_add_host注册sdhci_host,在注册之前已经设置的信息有:sdhci的寄存器的映射过后的基地址、quirks、quirks2、中断号、提供给sdhci core用来操作硬件的操作函数 等。

上面讲到的mmc_start_req函数调用Samsung SoC平台的sdhci_s3c_ops成员函数(通过struct mmc_host_ops sdhci_ops里的成员函数调用)。

上一篇:大数据之-Hadoop之HDFS_读数据流程_原理篇---大数据之hadoop工作笔记0069


下一篇:画图详解HDFS文件上传流程