在这篇文章中,我们来分析一下linux下的sd/emmc驱动的总线函数。在linux源码的\drivers\mmc\core下的bus.c文件中。
看一下总线变量的定义:
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_dev_groups函数的实现,我全局搜索都没找到,那就先不管它了。
一、mmc_bus_match/*
* This currently matches any MMC driver to any MMC card - drivers
* themselves make the decision whether to drive this card in their
* probe method.
*/
static int mmc_bus_match(struct device *dev, struct device_driver *drv)
{
return 1;
}
这个函数也真是懒,直接就返回了,表示匹配成功。注释也说了,真正的驱动和设备是否匹配的函数是放在了驱动的probe函数中,而该probe函数我们在前面的文章中
emmc/sd 区块层解析已经分析过。
二、mmc_bus_uevent
关于uevent,可以参考:
Linux--内核Uevent事件机制 与 Input子系统
看下源码:
static int mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct mmc_card *card = mmc_dev_to_card(dev);
const char *type;
int retval = 0;
switch (card->type) {
case MMC_TYPE_MMC:
type = "MMC";
break;
case MMC_TYPE_SD:
type = "SD";
break;
case MMC_TYPE_SDIO:
type = "SDIO";
break;
case MMC_TYPE_SD_COMBO:
type = "SDcombo";
break;
default:
type = NULL;
}
if (type) {
retval = add_uevent_var(env, "MMC_TYPE=%s", type);
if (retval)
return retval;
}
retval = add_uevent_var(env, "MMC_NAME=%s", mmc_card_name(card));
if (retval)
return retval;
/*
* Request the mmc_block device. Note: that this is a direct request
* for the module it carries no information as to what is inserted.
*/
retval = add_uevent_var(env, "MODALIAS=mmc:block");
return retval;
}
我的总结就是,当卡拔插等事件发生的时候,内核向用户空间发送事件,使得用户空间可以动态加载驱动模块。
三、mmc_bus_probe
static int mmc_bus_probe(struct device *dev)
{
struct mmc_driver *drv = to_mmc_driver(dev->driver);
struct mmc_card *card = mmc_dev_to_card(dev);
return drv->probe(card);
}
这里调用了驱动的probe函数,该函数我们在
sd/mmc卡驱动函数指针解析中解析过。
四、mmc_bus_remove
static int mmc_bus_remove(struct device *dev)
{
struct mmc_driver *drv = to_mmc_driver(dev->driver);
struct mmc_card *card = mmc_dev_to_card(dev);
drv->remove(card);
return 0;
}
该函数调用了驱动的remove函数指针指向的函数,在
sd/mmc卡驱动函数指针解析中解析过。
五、mmc_bus_shutdown
static void mmc_bus_shutdown(struct device *dev)
{
struct mmc_driver *drv = to_mmc_driver(dev->driver);
struct mmc_card *card = mmc_dev_to_card(dev);
struct mmc_host *host = card->host;
int ret;
if (dev->driver && drv->shutdown)
drv->shutdown(card);
if (host->bus_ops->shutdown) {
ret = host->bus_ops->shutdown(host);
if (ret)
pr_warn("%s: error %d during shutdown\n",
mmc_hostname(host), ret);
}
}
第9行:调用驱动的shutdown函数,该函数在sd/mmc卡驱动函数指针解析的2.5节中描述。
第12行:调用bus_ops的shutdown函数,关于bus_ops,会在下一篇文章中分析。
六、mmc_bus_pm_ops
mmc_bus_pm_ops是一个dev_pm_ops类型的变量,而dev_pm_ops是定义在linux源码的\include\linux\pm.h文件中的结构,如下:
struct dev_pm_ops {
int (*prepare)(struct device *dev);
void (*complete)(struct device *dev);
int (*suspend)(struct device *dev);
int (*resume)(struct device *dev);
int (*freeze)(struct device *dev);
int (*thaw)(struct device *dev);
int (*poweroff)(struct device *dev);
int (*restore)(struct device *dev);
int (*suspend_late)(struct device *dev);
int (*resume_early)(struct device *dev);
int (*freeze_late)(struct device *dev);
int (*thaw_early)(struct device *dev);
int (*poweroff_late)(struct device *dev);
int (*restore_early)(struct device *dev);
int (*suspend_noirq)(struct device *dev);
int (*resume_noirq)(struct device *dev);
int (*freeze_noirq)(struct device *dev);
int (*thaw_noirq)(struct device *dev);
int (*poweroff_noirq)(struct device *dev);
int (*restore_noirq)(struct device *dev);
int (*runtime_suspend)(struct device *dev);
int (*runtime_resume)(struct device *dev);
int (*runtime_idle)(struct device *dev);
};
这是一个跟电源相关的结构,这里我们来分析一下该结构的变量mmc_bus_pm_ops:
static const struct dev_pm_ops mmc_bus_pm_ops = {
SET_RUNTIME_PM_OPS(mmc_runtime_suspend, mmc_runtime_resume,
mmc_runtime_idle)
SET_SYSTEM_SLEEP_PM_OPS(mmc_bus_suspend, mmc_bus_resume)
};
里面有两个宏(这个变量及宏在
linux reboot函数各分支对比分析一文的第六节展开过),通过代码我们知道,这些宏就是将函数指针指向了宏的参数。
下面来分析下这些函数:
6.1、mmc_runtime_suspend(bus.c)
static int mmc_runtime_suspend(struct device *dev)
{
struct mmc_card *card = mmc_dev_to_card(dev);
struct mmc_host *host = card->host;
int ret = 0;
if (host->bus_ops->runtime_suspend)
ret = host->bus_ops->runtime_suspend(host);
return ret;
}
第7行:判断当前host的ops的runtime_suspend指针是否为空,不为空执行总线ops的runtime_suspend函数指针指向的函数,在
mmc ops结构解析一文的第五节中,我们知道runtime_suspend指针指向mmc_runtime_suspend(mmc.c)函数。
6.2、mmc_runtime_resume(bus.c)
static int mmc_runtime_resume(struct device *dev)
{
struct mmc_card *card = mmc_dev_to_card(dev);
struct mmc_host *host = card->host;
int ret = 0;
if (host->bus_ops->runtime_resume)
ret = host->bus_ops->runtime_resume(host);
return ret;
}
这个函数的结构与mmc_runtime_suspend的结构几乎一样,不解释。
6.3、mmc_runtime_idle
static int mmc_runtime_idle(struct device *dev)
{
return 0;
}
啥没干。
6.4、mmc_bus_suspend
static int mmc_bus_suspend(struct device *dev)
{
struct mmc_driver *drv = to_mmc_driver(dev->driver);
struct mmc_card *card = mmc_dev_to_card(dev);
struct mmc_host *host = card->host;
int ret;
if (dev->driver && drv->suspend) {
ret = drv->suspend(card);
if (ret)
return ret;
}
ret = host->bus_ops->suspend(host);
return ret;
}
第8行:判断驱动是否为空,判断驱动的suspend函数是否为空,如果不为空则执行驱动的suspend函数;
第9行:通过sd/mmc卡驱动函数指针解析可以知道,驱动的suspend函数指针指向mmc_blk_suspend函数,可以在该文的第2.3节了解它的实现。
第14行:执行总线ops的suspend函数,查看mmc ops结构解析的第五节。
6.4、mmc_bus_resume
static int mmc_bus_resume(struct device *dev)
{
struct mmc_driver *drv = to_mmc_driver(dev->driver);
struct mmc_card *card = mmc_dev_to_card(dev);
struct mmc_host *host = card->host;
int ret;
ret = host->bus_ops->resume(host);
if (ret)
pr_warn("%s: error %d during resume (card was removed?)\n",
mmc_hostname(host), ret);
if (dev->driver && drv->resume)
ret = drv->resume(card);
return ret;
}
这个函数的结构与mmc_bus_suspend不一样的是,先调用总线ops的resume函数,再调用驱动的resume函数。
第8行:执行总线ops的resume函数,参考mmc ops结构解析的第四节。
第14行:执行驱动的resume函数,参考sd/mmc卡驱动函数指针解析的2.4节。
那么总线函数就分析完了 。