总体架构
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;
}