Linux MMC 框架源码分析

版权声明:本博内容均由 https://blog.csdn.net/Bruno_Mars 原创,转载请注明出处,谢谢! https://blog.csdn.net/Bruno_Mars/article/details/88771190

linux mmc 框架源码分析

文章基于

内核版本
linux-3.2.0x
CPU
芯唐 的 NUC972
源码路径 linux-3.10.x\drivers\mmc

文章仅分析 sd卡这类设备在 Linux mmc 框架中的源码
本文基于个人的理解与思考, 如有错误的地方欢迎指正.

linux mmc 框架代码目录结构

源码路径 kernel-3.2\drivers\mmc

mmc/
|-- card
|   |-- block.c
|   |-- block.o
|   |-- queue.c
|   |-- queue.h
|   `-- sdio_uart.c
|-- core
|   |-- bus.c
|   |-- bus.h
|   |-- core.c
|   |-- core.h
|   |-- host.c
|   |-- host.h
|   |-- quirks.c
|   |-- sd.c
|   |-- sd.h
|   |-- sd_ops.c
|   `-- sd_ops.h
`-- host
    |-- nuc970_emmc.c
    `-- nuc970_sd.c
card
mmc card 层代码, 实现块设备的各种功能接口
core
mmc 核心层代码, 实现检测外接存储(如sd卡)和生成对应设备的一整个流程
host
mmc host 层代码,对应不同的cpu有不同的 host 驱动,通过对 cpu mmc 相关寄存器控制实现与外接sd卡的命令/数据收发
目录下的 nuc970_sd.c 就是 NUC972 芯片的针对 sd 类的设备的适配器驱动
而 nuc970_emmc.c 就是 NUC972 芯片的针对 emmc 类的设备的适配器驱动

linux mmc 框架

在这里插入图片描述

linux mmc 分为三层,分别是:

  • mmc card 层
  • mmc 核心层
  • mmc host 层

mmc 核心层

mmc 核心层提供一下功能:

  • 提供 mmc host 注册接口: int mmc_add_host(struct mmc_host *host)
  • 提供 mmc card 驱动注册接口: int mmc_register_driver(struct mmc_driver *drv)
  • 负责外接sd卡的检测和检测到sd卡接入后生成对应总线设备的一整个过程

mmc host 层

  • 调用核心层接口 mmc_add_host() 注册一个 mmc host
  • 提供最外接sd卡的一系列操作函数, 即 struct mmc_host_ops 结构体的一系列函数, 如 .get_cd 函数实现检测sd卡是否在位
  • 实现检测sd卡插入/拔出的机制(如中断)

struct mmc_host_ops 结构体包含了一系列对外接sd卡的操作函数, 结构体中比较常用的回调有以下几个:

struct mmc_host_ops {
    ...
	void	(*request)(struct mmc_host *host, struct mmc_request *req);
	void	(*set_ios)(struct mmc_host *host, struct mmc_ios *ios);
	int	(*get_ro)(struct mmc_host *host);
	int	(*get_cd)(struct mmc_host *host);
	void	(*enable_sdio_irq)(struct mmc_host *host, int enable);
	...
};

其中(与sd卡的数据交互包括两种, 一种是命令 CMD, 一种是数据 DATA)
request 函数
: 进行sd卡数据读写时的回调, 需要控制 mmc 寄存器实现数据发送接收

set_ios 函数
进行sd卡命令收发时的回调, 需要控制 mmc 寄存器实现命令发送接收
get_ro 函数
检测sd卡是否为只读模式的回调
get_cd 函数
检测sd卡接入/拔出状态的的回调
enable_sdio_irq 函数
使能/关闭 cpu mmc 中断功能的回调

mmc card 层

  • 调用核心层接口 mmc_register_driver() 注册一个 mmc crad 层的平台驱动, 用来与平台设备匹配之后生成sd卡对应的块设备
  • 实现块设备的分区管理和读写管理

mmc host层

host层平台设备

源码: linux-3.10.x\arch\arm\mach-nuc970\dev.c

添加平台设备

void __init nuc970_platform_init(struct platform_device **device, int size)
	platform_add_devices(nuc970_public_dev, ARRAY_SIZE(nuc970_public_dev)); /*添加平台设备*/
static struct platform_device *nuc970_public_dev[] __initdata = {
    ...
    &nuc970_device_sdh,
    ...
}
struct platform_device nuc970_device_sdh = {
        .name		  = "nuc970-sdh",
        .id		  = -1,
        .num_resources	  = ARRAY_SIZE(nuc970_sdh_resource),
        .resource	  = nuc970_sdh_resource,
	    .dev              = {
		.dma_mask = &nuc970_device_sdh_dmamask,
		.coherent_dma_mask = 0xffffffffUL
	}
};
static struct resource nuc970_sdh_resource[] = {
        [0] = {
                .start = NUC970_PA_SDH,
                .end   = NUC970_PA_SDH + NUC970_SZ_SDH - 1,
                .flags = IORESOURCE_MEM,
        },
        [1] = {
                .start = IRQ_SDH,
                .end   = IRQ_SDH,
                .flags = IORESOURCE_IRQ,
        }
};

host层平台驱动

源码: linux-3.10.x\drivers\mmc\host\nuc970_sd.c

注册平台驱动

static struct platform_driver nuc970_sd_driver = {
        .probe      = nuc970_sd_probe,
        ...
        .driver     = {
                .name   = "nuc970-sdh",
                .owner  = THIS_MODULE,
        },
};
module_platform_driver(nuc970_sd_driver);

注册平台驱动和设备后
平台总线根据 name 为 “nuc970-sdh” 进行平台驱动/设备的匹配
匹配后回调进入 .probe = nuc970_sd_probe

static int nuc970_sd_probe(struct platform_device *pdev)
    /*获取平台资源*/
    platform_get_resource(pdev, IORESOURCE_MEM, 0);
    request_mem_region(res->start, res->end - res->start + 1, DRIVER_NAME)

    sema_init(&sdh_fmi_sem, 1); /*信号量初始化*/
    devm_pinctrl_get_select(&pdev->dev, "sd0"); /*引脚初始化*/

    /*时钟初始化*/
    clk_prepare(clk_get(NULL, "sdh_hclk"));
    ...

    /*
    * 申请 mmc_host 结构体,并申请一段私有数据段存放 struct nuc970_sd_host 结构体
    * mmc_host 结构体描述了 mmc 的工作方式
    * nuc970_sd_host 结构体是根据 芯唐nuc970平台 host 层驱动逻辑需求多定义的一个结构体
    */
    mmc = mmc_alloc_host(sizeof(struct nuc970_sd_host), &pdev->dev);
        ->进入 mmc 核心层,进入 struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
        host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL); /*申请一段内存存放 struct mmc_host*/
        host->rescan_disable = 1; /*这个变量在 mmc 扫描是否外接sd卡时用到, 由于mmc host 还没初始化完成,暂时停止扫描*/
        ...
        dev_set_name(&host->class_dev, "mmc%d", host->index); /*设置设备名*/
        ...
        host->class_dev.class = &mmc_host_class; /*设置设备 class 为 mmc_host_class*/
        device_initialize(&host->class_dev); /*初始化设备*/

        /*设置 host 的时钟门控
        * 通过动态调整 mmc 工作时钟,节省功耗
        * 例如sd卡空闲时,关闭 mmc 的时钟
        * 这部分功能逻辑这里不作分析
        */
        mmc_host_clk_init(host);

        /*一些锁的初始化*/
        ...

        init_waitqueue_head(&host->wq); /*初始化一个等待队列 host->wq*/
        INIT_DELAYED_WORK(&host->detect, mmc_rescan); /*初始化一个延时任务 mmc_rescan, mmc_rescan 用于检测是否有外接设备, 如sd卡*/

        /*初始化主机控制器 host 结构体, 填充好默认参数*/
        host->max_segs = 1;
        host->max_seg_size = PAGE_CACHE_SIZE;
        host->max_req_size = PAGE_CACHE_SIZE;
        host->max_blk_size = 512;
        host->max_blk_count = PAGE_CACHE_SIZE / 512;

    /*根据cpu的sd硬件接口,填充 mmc_host 结构体
    * struct mmc_host 结构体是 mmc 核心层处理相关逻辑时使用到的
    */
    mmc->ops = &nuc970_sd_ops; /*注册 mmc_host 的功能接口,由核心层回调*/
        ->nuc970_sd_ops 结构体定义
        static const struct mmc_host_ops nuc970_sd_ops = {
            .request    = nuc970_sd_request,              /*处理 mmc 请求, 进行外接 mmc 设备的读写, 如sd卡的读写*/
            .set_ios    = nuc970_sd_set_ios,              /*配置 mmc 硬件接口, 根据传进的 ios 信息, 配置 mmc 的开关/mmc 宽度等信息*/
            .get_ro     = nuc970_sd_get_ro,               /*检测外接 mmc 设备是否处于写保护模式*/
            .get_cd     = nuc970_sd_card_detect,          /*通过sd卡检测脚判断是否有sd卡接入*/
            .enable_sdio_irq = nuc900_sd_enable_sdio_irq, /*使能/关闭 mmc 的中断寄存器*/
        };
    mmc->f_min = 300000;   /*mmc 控制器支持的最小频率*/
    mmc->f_max = 50000000; /*mmc 控制器支持的最大频率*/
    mmc->ocr_avail = MMC_VDD_27_28|MMC_VDD_28_29|MMC_VDD_29_30|MMC_VDD_30_31|MMC_VDD_31_32|MMC_VDD_32_33 | MMC_VDD_33_34; /*mmc 控制器支持的有效电压范围*/
    mmc->caps |= (MMC_CAP_4_BIT_DATA|MMC_CAP_SDIO_IRQ|MMC_CAP_SD_HIGHSPEED|MMC_CAP_MMC_HIGHSPEED); /*mmc 控制器支持的功能,如4位数据收发等*/
    mmc->max_blk_size  = MCI_MAXBLKSIZE;
    mmc->max_blk_count = MCI_BLKATONCE;
    mmc->max_req_size  = MCI_BUFSIZE;
    mmc->max_segs      = MCI_BLKATONCE;
    mmc->max_seg_size  = MCI_BUFSIZE;

    /*填充 struct nuc970_sd_host *host 结构体
    * struct nuc970_sd_host 结构体是 mmc 控制器层驱动内部进行逻辑处理中使用到的
    */
    host = mmc_priv(mmc); /*前面申请了一段私有数据段存放 struct nuc970_sd_host 结构体, 这里取出来*/
    ...
    host->buffer = dma_alloc_coherent(&pdev->dev, MCI_BUFSIZE, &host->physical_address, GFP_KERNEL); /*申请一段 dma 内存地址*/
    ...

    /*关闭后使能 cpu 的 sd 接口,属于硬件寄存器操作*/
    nuc970_sd_disable(host);
    nuc970_sd_enable(host);

    host->irq = platform_get_irq(pdev, 0);
    ret = request_irq(host->irq, nuc970_sd_irq, IRQF_SHARED, mmc_hostname(mmc), host); /*mmc 的中断申请*/
    ...
    nuc970_sd_write(REG_SDIER, nuc970_sd_read(REG_SDIER) | SDIER_CD0_IE | SDIER_CD0SRC); /*使能 mmc 和中断*/

    mmc_add_host(mmc);
        ->进入 mmc 核心层, 添加一个 mmc host, 代码分析参考 mmc 核心层相关的分析

mmc 核心层

源码: linux-3.10.x\drivers\mmc\core\core.c

核心层初始化

  • 创建工作队列管理 mmc 相关工作队列, 管理 mmc 核心层用到的一些 work
  • 注册 mmc 总线和 sdio 总线, mmc card 层的平台设备和驱动就是注册到对应的总线中
static int __init mmc_init(void)
	workqueue = alloc_ordered_workqueue("kmmcd", 0);
	ret = mmc_register_bus(); /*注册一条总线, 名为 mmc*/
        ->进入 int mmc_register_bus(void)
        return bus_register(&mmc_bus_type);
            -> static struct bus_type mmc_bus_type 定义
            static struct bus_type mmc_bus_type = {
                .name		= "mmc",
                .dev_attrs	= mmc_dev_attrs,
                .match		= mmc_bus_match,
                .probe		= mmc_bus_probe,
                ...
            };
	ret = mmc_register_host_class();
	ret = sdio_register_bus(); /*注册一条总线, 名为 sdio*/
        ->进入 int sdio_register_bus(void)
        return bus_register(&sdio_bus_type);
            -> static struct bus_type sdio_bus_type 定义
            static struct bus_type sdio_bus_type = {
                .name		= "sdio",
                .dev_attrs	= sdio_dev_attrs,
                .match		= sdio_bus_match,
                .probe		= sdio_bus_probe,
                ...
            };

mmc_add_host

注册一个 mmc host, 注册完成后会调用一次扫描 mmc_rescan, 检测外部是否接入了sd卡

int mmc_add_host(struct mmc_host *host)
    ->进入 int mmc_add_host(struct mmc_host *host)
    device_add(&host->class_dev); /*将主机控制器设备添加到系统设备表里面*/

    /*led灯的相关控制,例如在读写sd卡时闪烁led灯
    * 这部分功能代码不作分析
    */
    led_trigger_register_simple(dev_name(&host->class_dev), &host->led);
    ...
    mmc_start_host(host); /*开始启用 mmc 主机控制器*/
        ->进入 void mmc_start_host(struct mmc_host *host)
        host->f_init = max(freqs[0], host->f_min);
        host->rescan_disable = 0; /*mmc host 已经初始化完成,允许开始扫描*/
        mmc_power_up(host); /*控制 mmc 硬件 power up*/
        mmc_detect_change(host, 0); /*检测mmc 主机控制器有没有外接设备,如 sd卡*/
            ->进入 void mmc_detect_change(struct mmc_host *host, unsigned long delay)
            ...
            host->detect_change = 1;
            mmc_schedule_delayed_work(&host->detect, delay);
                ->进入 static int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay)
                queue_delayed_work(workqueue, work, delay); /*将延时任务加入到任务队列中,延时结束后将调度执行该任务,即 mmc_rescan*/
    register_pm_notifier(&host->pm_notify); /*跟电源管理有关,这部分代码不作分析*/

mmc_register_driver

注册一个 mmc card 层总线驱动

int mmc_register_driver(struct mmc_driver *drv)
    ->进入 int mmc_register_driver(struct mmc_driver *drv)
    drv->drv.bus = &mmc_bus_type; /*设置驱动的总线类型为 mmc 总线, 也就是该驱动归属于 mmc 总线*/
        -> static struct bus_type mmc_bus_type 定义
        static struct bus_type mmc_bus_type = {
            .name		= "mmc",
            .dev_attrs	= mmc_dev_attrs,
            .match		= mmc_bus_match,
            .probe		= mmc_bus_probe,
            ...
        };
    return driver_register(&drv->drv); /*注册一个驱动到对应总线, 即 mmc 总线*/

mmc_rescan

mmc 核心层扫描函数, 检测外部有没有接sd卡
针对 sd卡在两种情况下会进行扫描:

  • mmc 主控制器驱动加载时会进行一次扫描, 判断sd卡是否在位
  • sd卡插入是会触发中断,中断中进行一次扫描, 判断sd卡是否在位

检测过程中会以不同的频率检测三种有可能的外接设备接口: SDIO, SD, MMC, sd卡用到的接口是 sd
如果检测到外接了sd卡, 则调用 mmc_add_card 创建一个 mmc card层的总线设备

void mmc_rescan(struct work_struct *work)
    /*若 host->rescan_disable 为 1, 则说明主机控制器还没初始化好,直接返回
    * 避免主机控制器还没初始化好时因为中断进入这个扫描函数时出错
    */
	if (host->rescan_disable)
		return;

    /*如果是不可移除的外接设备,如emmc,则只进行一次 mmc 扫描*/
	if ((host->caps & MMC_CAP_NONREMOVABLE) && host->rescan_entered)
		return;
	host->rescan_entered = 1;

    ...
	host->detect_change = 0;
    ...

    /*回调 mmc 控制器层注册好的 get_cd 函数,判断是否能检测到设备接入,如 sd卡 接入,没有则直接退出*/
	if (host->ops->get_cd && host->ops->get_cd(host) == 0) {
        ...
		goto out;
	}

	mmc_claim_host(host); /*声明占用 mmc 总线,如果已被占用则进入睡眠,等待别人释放*/
        ->进入 int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
	    __mmc_claim_host(host, NULL);
            ->进入 int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
            DECLARE_WAITQUEUE(wait, current); /*初始化一个等待队列*/
            ...
            add_wait_queue(&host->wq, &wait); /*将等待队列加到等待队列头*/
            ...
            while (1) {
                set_current_state(TASK_UNINTERRUPTIBLE); /*设置进程状态为不可中断状态*/
                /*符合三个条件其中一个则停止进程睡眠, 否则进入休眠状态, 等待别人释放总线
                * 传入的 abort 添加符合
                * host->claimed 为0, 即 mmc 总线未被占用
                * host->claimer == current, 即占用 mmc 总线的是进程本身
                */
                stop = abort ? atomic_read(abort) : 0;
                if (stop || !host->claimed || host->claimer == current)
                    break;

                /*不符合上面三个添加的话则进行调度,进程进入休眠*/
                schedule();
            }
            set_current_state(TASK_RUNNING); /*设置进程状态为运行状态*/
            if (!stop) {
                /*由当前进程占用 mmc 总线*/
                host->claimed = 1;
                host->claimer = current;
                host->claim_cnt += 1;
            } else
                wake_up(&host->wq);
            remove_wait_queue(&host->wq, &wait); /*删除等待队列*/

            /*回调 mmc 控制器层的 enable 函数*/
            if (host->ops->enable && !stop && host->claim_cnt == 1)
                host->ops->enable(host);

    /*这里以从大到小不同频率去驱动 mmc 控制器外接的设备,如sd卡
    * 默认的频率为 unsigned freqs[] = { 400000, 300000, 200000, 100000 };
    */
	for (i = 0; i < ARRAY_SIZE(freqs); i++) {
		mmc_rescan_try_freq(host, max(freqs[i], host->f_min));
            ->进入 static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
            host->f_init = freq;
            mmc_power_up(host); /*初始化 mmc 总线,配置好总线频率等*/
                ->进入 static void mmc_power_up(struct mmc_host *host)
                    if (host->ios.power_mode == MMC_POWER_ON) /*如果已处于 MMC_POWER_ON 状态, 直接返回*/
                        return;
                    ...
                    /*设置 sd卡 需要的电压
                    * 如果 ocr 已经配置,则直接使用,否则从 ocr_avail 中选择合适的
                    */
                    if (host->ocr)
                        bit = ffs(host->ocr) - 1;
                    else
                        bit = fls(host->ocr_avail) - 1;
                    host->ios.vdd = bit;

                    host->ios.chip_select = MMC_CS_DONTCARE; /*这里只分析sd卡,不关心片选设置*/
                    host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; /*总线模式*/
                    host->ios.power_mode = MMC_POWER_UP; /*上电状态设置为已上电*/
                    host->ios.bus_width = MMC_BUS_WIDTH_1; /*总线宽度设置为1*/
                    host->ios.timing = MMC_TIMING_LEGACY; /*总线时序,默认*/
                    mmc_set_ios(host); /*设置到 mmc 主控制器中*/
                        ->static inline void mmc_set_ios(struct mmc_host *host)
                        ...
                        host->ops->set_ios(host, ios); /*回调到 mmc 控制器层的 set_ios 函数,按配置设置好相关寄存器*/

                    __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330); /*设置信号线的电压为3.3伏,也是回调 mmc 控制器层的函数*/
                    mmc_delay(10); /*配置了电压后延时一会,等待电压变化*/

                    /*设置 mmc 总线的频率*/
                    host->ios.clock = host->f_init;
                    host->ios.power_mode = MMC_POWER_ON;
                    mmc_set_ios(host);
                    ...

            mmc_hw_reset_for_init(host);
                ->进入 static void mmc_hw_reset_for_init(struct mmc_host *host)
	            host->ops->hw_reset(host); /*回调 mmc 控制器层注册好的 hw_reset 函数,对外接设备进行一次复位*/

            sdio_reset(host); /*发送复位命令, sd卡忽略此操作*/
            mmc_go_idle(host); /*发送 CMD0 命令, 进入 GO_IDLE_STATE 状态*/
            mmc_send_if_cond(host, host->ocr_avail); /*发送 CMD8 命令,返回成功说明支持 SD 2.0 协议,否则支持 SD 1.0*/

            /*这里按顺序分别探测 SDIO/SD/MMC 三种接口
            * 如果检测到对应接口的设备,则返回,否则更换别的总线频率进行检测
            * 只分析sd卡的探测流程
            */
            if (!mmc_attach_sd(host))...
                ->进入 int mmc_attach_sd(struct mmc_host *host)
                err = mmc_send_app_op_cond(host, 0, &ocr); /*发送 CMD41 获取外接sd卡支持的工作电压*/
                mmc_sd_attach_bus_ops(host); /*设置总线对应的操作函数集*/
                    ->进入 mmc_sd_attach_bus_ops(struct mmc_host *host)
                    /*根据 mmc 外接设备可不可以移除, 设置不同操作函数集*/
                    bus_ops = &mmc_sd_ops;
                        -> struct mmc_bus_ops mmc_sd_ops 定义, 操作函数集包含了 sd卡的各种动作对应的回调, 如检测到sd卡插入/sd卡拔出
                        static const struct mmc_bus_ops mmc_sd_ops = {
                            .remove = mmc_sd_remove,
                            .detect = mmc_sd_detect,
                            .suspend = NULL,
                            .resume = NULL,
                            .power_restore = mmc_sd_power_restore,
                            .alive = mmc_sd_alive,
                        };
                    mmc_attach_bus(host, bus_ops); /*这里将操作函数集注册到 mmc 控制器*/

                /*这里排除掉一些过低的不合适的电压*/
                ...
                host->ocr = mmc_select_voltage(host, ocr); /*选择cpu支持的和sd卡支持的合适范围内的sd卡电压*/
                    ->进入 mmc_select_voltage(struct mmc_host *host, u32 ocr)
                    ocr &= host->ocr_avail; /*选择cpu支持输入的和sd卡同时支持的电压*/
                    bit = ffs(ocr);
                    ...
                    host->ios.vdd = bit;
                    mmc_set_ios(host); /*将支持的电压配置下去, cpu的mmc接口将输出合适的电压*/
                    ...
                ...
                err = mmc_sd_init_card(host, host->ocr, NULL); /*初始化一个 mmc 总线设备*/
                    ->进入 mmc_sd_init_card(struct mmc_host *host, u32 ocr, struct mmc_card *oldcard)
                    err = mmc_sd_get_cid(host, ocr, cid, &rocr); /*读取sd卡 CID 寄存器信息*/
                    ...
                    card = mmc_alloc_card(host, &sd_type); /*申请一个总线设备, 归属于 mmc 总线*/
                        ->进入 struct mmc_card *mmc_alloc_card(struct mmc_host *host, struct device_type *type)
                        card = kzalloc(sizeof(struct mmc_card), GFP_KERNEL); /*申请内存存放 struct mmc_card*/
                        card->host = host;
                        device_initialize(&card->dev);
                        card->dev.parent = mmc_classdev(host);
                        card->dev.bus = &mmc_bus_type; /*配置改设备归属于 mmc 总线设备*/
                            -> static struct bus_type mmc_bus_type 定义
                            static struct bus_type mmc_bus_type = {
                                .name		= "mmc",          /*总线名称*/
                                ...
                            };
                        card->dev.release = mmc_release_card;
                        card->dev.type = type; /*配置 mmc card 设备类型*/

                    card->type = MMC_TYPE_SD; /*配置 mmc card 的类型为sd卡*/
                    ...
                    err = mmc_send_relative_addr(host, &card->rca); /*发送 CMD3 读取 RCA 寄存器信息*/
                    err = mmc_sd_get_csd(host, card); /*发送 CMD9 读取 CSD 寄存器信息*/
                    err = mmc_select_card(card); /*发送 CMD7 选中外接的sd卡*/
                    err = mmc_sd_setup_card(host, card, oldcard != NULL); /*初始化外接sd卡的基本配置*/
                    ...
                    /*如果支持高速,则配置外接sd卡和cpu mmc接口为高速模式*/
                    err = mmc_sd_switch_hs(card);
                    mmc_sd_go_highspeed(card);
                    mmc_set_clock(host, mmc_sd_get_max_clock(card));

                    /*如果支持4线传输,则配置外接sd卡和cpu mmc接口 数据传输宽度为4*/
                    err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
                    mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
                    host->card = card;

                mmc_release_host(host); /*释放总线*/
                err = mmc_add_card(host->card); /*将 MMC 总线设备 注册进内核设备列表*/
                    ->进入 int mmc_add_card(struct mmc_card *card)
                    ...
                    dev_set_name(&card->dev, "%s:%04x", mmc_hostname(card->host), card->rca); /*设置 mmc 总线设备名称*/
                    ...
                    mmc_init_context_info(card->host);
                    ret = device_add(&card->dev); /*添加 mmc 总线设备进内核*/
                    mmc_card_set_present(card);

                mmc_claim_host(host); /*占用总线*/
		if (freqs[i] <= host->f_min) /*各种 mmc 总线频率都尝试完了就退出*/
			break;
	}
	mmc_release_host(host); /*释放总线*/
}

mmc card层

mmc card层总线设备

mmc card层总线设备在核心层进行 mmc 总线扫描时生成
在核心层的 mmc_rescan 函数中, 会去检测外接的 mmc 设备, 如sd卡, 如果检测到外接了sd卡, 就会创建一个 mmc 总线设备

mmc card层总线驱动

源码: linux-3.10.x\drivers\mmc\card\block.c

mmc card层初始化

  • 注册 mmc card层驱动到 mmc 总线上
static int __init mmc_blk_init(void)
    ...
	res = register_blkdev(MMC_BLOCK_MAJOR, "mmc"); /*注册一个新的块设备, 名为 mmc*/
	res = mmc_register_driver(&mmc_driver); /*注册一个 mmc card层总线驱动*/
static struct mmc_driver mmc_driver = {
    .drv		= {
        .name	= "mmcblk",
    },
    .probe		= mmc_blk_probe,
    .remove		= mmc_blk_remove,
    .suspend	= mmc_blk_suspend,
    .resume		= mmc_blk_resume,
};

当 mmc card设备与 mmc card驱动匹配时, 回调进入 .probe = mmc_bus_probe
进行 sd卡块设备分区的管理

static int mmc_blk_probe(struct mmc_card *card)
    ...
	md = mmc_blk_alloc(card); /*申请一个 mmc 块设备*/
        ->进入 static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
        md = mmc_blk_alloc_req(card, &card->dev, size, false, NULL, MMC_BLK_DATA_AREA_MAIN);
            ->进入 static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, struct device *parent, sector_t size, ...)
            md = kzalloc(sizeof(struct mmc_blk_data), GFP_KERNEL); /*申请一段内存存放 struct mmc_blk_data 结构体*/

            /*初始化 struct mmc_blk_data 结构体*/
            ...
            md->disk = alloc_disk(perdev_minors); /*申请一个块设备*/
            ...

            /*初始化给 mmc 块设备用的请求队列, 创建一个内核线程处理 mmc 块设备请求*/
            ret = mmc_init_queue(&md->queue, card, &md->lock, subname);
                ->进入 int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock, const char *subname)
                mq->card = card;
                mq->queue = blk_init_queue(mmc_request_fn, lock); /*初始化一个块设备请求队列, 当应用进行块设备读写时, 内核将组成一个请求, 然后回调到 mmc_request_fn*/
                ...
                blk_queue_prep_rq(mq->queue, mmc_prep_request); /*初始化一个 blk_queue_prep_rq, 在处理块设备请求队列之前调用, 初始化一些标志位*/
                ...
                mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd/%d%s", host->index, subname ? subname : ""); /*创建一个内核线程, 处理 mmc 块设备请求*/

            md->queue.issue_fn = mmc_blk_issue_rq; /*初始化一个回调, 在内核线程里面调用的, 用来处理请求*/

            /*初始化申请到的块设备*/
            md->disk->major	= MMC_BLOCK_MAJOR;
            md->disk->fops = &mmc_bdops;      /*配置块设备文件操作接口*/
            md->disk->queue = md->queue.queue; /*将请求队列与块设备对应起来*/
            ...
            snprintf(md->disk->disk_name, sizeof(md->disk->disk_name), "mmcblk%d%s", md->name_idx, subname ? subname : ""); /*初始化块设备名称*/
            ...

    /*
    * 为卡的每个分区申请对应的块设备
    * 这里针对的是 mmc 卡, 对于sd卡进入后直接返回了
    */
	if (mmc_blk_alloc_parts(card, md))
		goto out;
    ...
	mmc_add_disk(md) /*将申请好的块设备注册进系统*/
        ->进入 static int mmc_add_disk(struct mmc_blk_data *md)
        add_disk(md->disk); /*将块设备添加到内核列表中*/

        /*注册对应的 sysfs 文件系统接口*/
        ...
        sysfs_attr_init(&md->force_ro.attr);
        ...
        ret = device_create_file(disk_to_dev(md->disk), &md->force_ro);

    /*
    * 将申请好的每个分区对应的块设备注册进系统
    * 这里针对的是 mmc 卡, 对于sd卡不会进入
    */
	list_for_each_entry(part_md, &md->part, part) {
		if (mmc_add_disk(part_md))
			goto out;
	}

当应用进行块设备读写时, 如拷贝数据到sd卡, 内核将组成一个请求, 然后回调到 mmc_request_fn
函数里面唤醒了 mq->thread 内核线程

static void mmc_request_fn(struct request_queue *q)
    /*
    * 新的块设备请求进来时
    * 如果还有块设备请求还没处理完, 则等待处理完
    * 如果之前的块设备请求已经处理完, 则唤醒内核线程处理新的块设备请求
    */
	if (!mq->mqrq_cur->req && mq->mqrq_prev->req) {
        ...
		wake_up_interruptible(&cntx->wait);
	} else if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)
		wake_up_process(mq->thread);

mmc card层驱动创建了一个内核线程 mq->thread, 负责处理块设备的 request
没有 request 需要处理的时候计入休眠
当有 request 过来时则被 mmc_request_fn 唤醒, 进行 request 的处理

static int mmc_queue_thread(void *d)
	do {
		set_current_state(TASK_INTERRUPTIBLE); /*设置该内核线程为可中断唤醒状态*/
		req = blk_fetch_request(q); /*取出一个块设备请求*/
		mq->mqrq_cur->req = req;
		if (req || mq->mqrq_prev->req) { /*如果请求不为空, 则进入进行块设备请求处理*/
			set_current_state(TASK_RUNNING); /*设置该内核线程为运行状态*/
			mq->issue_fn(mq, req); /*回调 .issue_fn = mmc_blk_issue_rq 函数发送请求*/
            ...
		} else { /*如果块设备请求为空, 则进入休眠等待唤醒*/
            ...
			schedule(); /*调度, 该内核线程进入休眠, 等待有新的块设备请求时唤醒*/
            ...
		}
	} while (1);
static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
    /*处理第一个块设备请求时, 占用总线*/
	if (req && !mq->mqrq_prev->req)
		mmc_claim_host(card->host);
    ...

	ret = mmc_blk_issue_rw_rq(mq, req); /*处理块设备请求*/
        ->进入 static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
        ...
        /*循环处理全部请求*/
        do {
            ...
            areq = mmc_start_req(card->host, areq, (int *) &status); /*开始处理块设备请求*/
                ->进入核心层, 进入 struct mmc_async_req *mmc_start_req(struct mmc_host *host, struct mmc_async_req *areq, int *error)
                mmc_pre_req(host, areq->mrq, !host->areq); /*块设备请求发送前准备*/
                    ->进入 static void mmc_pre_req(struct mmc_host *host, struct mmc_request *mrq, bool is_first_req)
                    host->ops->pre_req(host, mrq, is_first_req); /*回调进入 mmc host层 .pre_req 函数, 该回调接口一般可不实现*/
                ...
                start_err = __mmc_start_data_req(host, areq->mrq); /*块设备请求发送*/
                    ->进入 static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq)
                    mmc_start_request(host, mrq);
                        ->进入 mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
                        host->ops->request(host, mrq); /*回调进入 mmc host层 .request 函数, 将请求发送出去*/
                ...
                mmc_post_req(host, host->areq->mrq, 0); /*块设备请求发送后处理*/
                    ->进入 static void mmc_post_req(struct mmc_host *host, struct mmc_request *mrq, int err)
                    host->ops->post_req(host, mrq, err); /*回调进入 mmc host层 .post_req 函数, 该回调接口一般可不实现*/

            /*处理块设备请求成功/失败的一些处理*/
            switch (status) {
            case MMC_BLK_SUCCESS:
            case MMC_BLK_PARTIAL:
                ...
            case MMC_BLK_CMD_ERR:
                ...
            case MMC_BLK_RETRY:
                ...
            case MMC_BLK_ABORT:
                ...
            case MMC_BLK_DATA_ERR:
                ...
            case MMC_BLK_ECC_ERR:
                ...
            case MMC_BLK_NOMEDIUM:
                ...
            default:
                ...
            }
            ...
        } while (ret);

    /*处理完全部请求/discard/flush 后释放总线*/
	if ((!req && !(mq->flags & MMC_QUEUE_NEW_REQUEST)) || (cmd_flags & MMC_REQ_SPECIAL_MASK))
		mmc_release_host(card->host);

应用读写sd卡流程

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Bruno_Mars/article/details/88771190