MMC hot plug --- Linux Kernel 实现赏析

总体架构

这里写图片描述

static int __init mmc_init(void)
{
    int ret;
    /*
        创建工作队列
        作者功夫堪称玄妙,一上来就放大招了.

        看 第二个参数, WQ_UNBOUND ,__WQ_ORDERED
        该 workqueue 中的 worker 还不和具体的CPU 绑定,

        #define alloc_ordered_workqueue(fmt, flags, args...)            \
            alloc_workqueue(fmt, WQ_UNBOUND | __WQ_ORDERED | (flags), 1, ##args)

         mmc为什么自己要创建且创建这样类型的workqueue, ?

         根据上图,你大概能猜测到 mmc是怎么处理hot-plug的了把,
         有类比的是USB的hot-plug, 不过这里使用的和usb的那些套路有点不同

         至于cd-gpios ,usb/mmc hot-plug之间的差别等 放到后面在说 
    */
    workqueue = alloc_ordered_workqueue("kmmcd", 0);
    if (!workqueue)
        return -ENOMEM;
    /*
        注册mmc 总线

        它有probe,  只是做了一下转换 转手就把任务扔给了 driver 的probe 了 ,
        一些总线的probe函数都是这样做的把,最终还是找 driver来处理

        static struct bus_type mmc_bus_type = {
            .name       = "mmc",
            .dev_groups = mmc_dev_groups,
            .match      = mmc_bus_match,
            .uevent     = mmc_bus_uevent,
            {
                转换一下, 
                底层的设备驱动模型不应该拿到具体的device mmc_card, 只能在这里救场了 
                设备驱动模型抽象的很纯粹 , 哈哈.

                struct mmc_driver *drv = to_mmc_driver(dev->driver);
                struct mmc_card *card = mmc_dev_to_card(dev);

                控制交到了driver, driver 接手继续努力

                return drv->probe(card);
            }
            .remove     = mmc_bus_remove,
            .shutdown   = mmc_bus_shutdown,
            .pm         = &mmc_bus_pm_ops,
        };
    */
    ret = mmc_register_bus();
    if (ret)
        goto destroy_workqueue;

    /*
        注册类吧,  看下面的 Figure  - 1

        底层调用class_register()来注册, 

        整个mmc系统中只有在这里调用到了这个函数,

        那么在这里为什么不直接使用原生的class_register(), 
        mmc_host_class是在host中定义 , Kernel中 分层的思想在这里显现出来一点,隔离的干净,   
    */
    ret = mmc_register_host_class();
    if (ret)
        goto unregister_bus;

    /*
        又来一条总线的注册,
        同理, 该总线的 probe函数拿到 device之后 做了一些处理之后 就交给了 driver
        的probe(), 
    */
    ret = sdio_register_bus();
    if (ret)
        goto unregister_host_class;

    return 0;

unregister_host_class:
    mmc_unregister_host_class();
unregister_bus:
    mmc_unregister_bus();
destroy_workqueue:
    destroy_workqueue(workqueue);

    return ret;
}

Figure - 1
这里写图片描述

中断

/**
    轮询和中断这两种方式 都是通过底层的mmc_rescan() 来实现的, 

    什么时候使用中断,什么时候使用轮询呢 ? 这里和网卡的那套还不一样
    将优先使用中断

    依据 :

    mmc_gpiod_request_cd_irq
    [
        ....
        irq = gpiod_to_irq(ctx->cd_gpio);   gpio 子系统 这儿暂不分析
        ....
        if (irq >= 0) {
             注册 irq 
            ret = devm_request_threaded_irq(&host->class_dev, irq,
             mmc_gpio_cd_irqt, ....);
        }
        ....

        #define MMC_CAP_NEEDS_POLL (1 << 5) 
        作者说 Needs polling for card-detection 

        if (irq < 0)
            host->caps |= MMC_CAP_NEEDS_POLL;
    }
*/
static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id)
{
    /*
        被中断的进程把 mmc 主机控制器传入了进来,
        其目的是为了使用 mmc_rescan()来发现设备
    */
    struct mmc_host *host = dev_id;

    host->trigger_card_event = true;
    /*
        200ms 定时防抖, 鸦片战争也没有这么长吧.哈哈.. !
        其底层还是 调用 queue_delayed_work()

        请移步 
        https://blog.csdn.net/leesagacious/article/details/50491819

        再看为什么要传入一个 host
        struct mmc_host *mmc_alloc_host()
        {
            再是 mmc 主机控制器 也是 一块堆内存而已
            struct mmc_host *host;    
            host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);
            .....

            看这里 
            工作任务的处理函数 :  (_work)->func = mmc_rescan();
            INIT_DELAYED_WORK(&host->detect, mmc_rescan);
        }
    */
    mmc_detect_change(host, msecs_to_jiffies(200));

    return IRQ_HANDLED;
}

轮询

power on / off.

猜你喜欢

转载自blog.csdn.net/leesagacious/article/details/79858485