SPI 设备驱动注册流程整体流程
先看一下整体流程
driver :: __init
-> spi_register_driver
-> driver_register
-> bus_add_driver
-> driver_attach
-> bus_for_each_dev
-> __driver_attach
-> driver_match_device
-> drv->bus->match
-> spi_match_attach
-> driver_probe_device
-> really_probe
-> drv->probe
-> spi_drv_probe
-> sdrv->probe (wk2xxx_probe)
驱动的入口函数__init
static int __init wk2xxx_init(void)
{
int ret;
printk(KERN_ALERT"%s: " DRIVER_DESC "\n",__func__);
printk(KERN_ALERT "%s: " VERSION_DESC "\n",__func__);
ret = spi_register_driver(&wk2xxx_driver);
if(ret<0){
printk(KERN_ALERT "%s,failed to init wk2xxx spi;ret= :%d\n",__func__,ret);
}
return ret;
}
wk2124驱动注册的时候调用的第一个函数spi_register_driver
,本篇文章来具体的说一下SPI设备的注册流程。发车~
spi_register_driver
直接摁住ctrl
键,鼠标点击spi_register_driver
函数,来到这里:
/**
* spi_register_driver注册一个SPI驱动
*/
int spi_register_driver(struct spi_driver *sdrv)
{
sdrv->driver.bus = &spi_bus_type;
if (sdrv->probe)
sdrv->driver.probe = spi_drv_probe;
if (sdrv->remove)
sdrv->driver.remove = spi_drv_remove;
if (sdrv->shutdown)
sdrv->driver.shutdown = spi_drv_shutdown;
return driver_register(&sdrv->driver);
}
EXPORT_SYMBOL_GPL(spi_register_driver);
该函数接收一个spi_driver
的结构体类型作为参数,这个参数是不是忘记啦?我们一起来回顾一下:
static struct spi_driver wk2xxx_driver = {
.driver = {
.name = "wk2xxx_spi",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(wkmic_spi_dt_match),
},
.probe = wk2xxx_probe,
.remove = wk2xxx_remove,
};
我们根据这个参数去看上面的spi_register_driver
函数,可以看到在spi_register_driver
函数中,对传入的参数的.driver
成员的值进一步扩充,增加了probe
, remove
, shutdown
变量, 然后将.driver
成员传递给了driver_register
。这里可能比较不好理解?大概如此:
driver_register
那么接下来就来到了driver_register
函数:
/**
* driver_register - register driver with bus
* @drv: driver to register
*
* We pass off most of the work to the bus_add_driver() call,
* since most of the things we have to do deal with the bus
* structures.
*/
int driver_register(struct device_driver *drv)
{
int ret;
struct device_driver *other;
BUG_ON(!drv->bus->p);
if ((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown))
printk(KERN_WARNING "Driver '%s' needs updating - please use "
"bus_type methods\n", drv->name);
other = driver_find(drv->name, drv->bus);
if (other) {
printk(KERN_ERR "Error: Driver '%s' is already registered, "
"aborting...\n", drv->name);
return -EBUSY;
}
ret = bus_add_driver(drv);
if (ret)
return ret;
ret = driver_add_groups(drv, drv->groups);
if (ret) {
bus_remove_driver(drv);
return ret;
}
kobject_uevent(&drv->p->kobj, KOBJ_ADD);
return ret;
}
EXPORT_SYMBOL_GPL(driver_register);
driver_register
函数接收一个device_driver
类型的参数drv
。 而在spi_register_driver
函数中,我们调用该函数的时候传入的是spi_driver::driver
到底是不是device_driver
类型的呢?我们一起来瞅一眼,瞅一眼即可:https://elixir.bootlin.com/linux/latest/source/include/linux/spi/spi.h#L329 你,回来了吗? 好的,是的,那我们继续!这个函数相对来说,调用蛮多的,我本想一个一个说清楚的,但是我怕到我嗝屁了,这篇文章还没写完,所以,我决定,不深究,简单陈述!
下面的说明中,有些函数过长或者没有必要进去深究,我都会给出在线代码链接,只需要点击函数的超链接即可在线查看。
driver_find
首先来看第一个调用driver_find
函数 ,这个函数的作用就是去看看当前传进来的这个驱动有没有注册,如果已经注册了那自然不用再次注册,直接返回其已经注册了的地址。否则返回NULL。所以,一般情况下,这个函数不会return,那我们继续……
bus_add_driver
接下来是bus_add_driver
函数 , 程序执行到这里,说明当前驱动没有注册。这里插一嘴,内核的设备和驱动机制大概是,内核有维护两条链表,一条设备链表,一条驱动链表,驱动加载的时候,内核会去遍历设备链表看看有没有对应的设备。当设备插入的时候,内核也会去遍历有没有与之匹配的驱动,简单来说就是如此。
这个函数很长,但是这个函数很重要,所以我决定把它贴出来!!!因为很长,所以这个函数我会在代码内部进行注释说明,后面还会进行总结性说明。发车~
这个函数的目的顾名思义,就是把驱动程序添加到对应的总线上去。
int bus_add_driver(struct device_driver *drv)
{
struct bus_type *bus;
struct driver_private *priv;
int error = 0;
bus = bus_get(drv->bus);
if (!bus)
return -EINVAL;
pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
/*************我在这里: 所以先初始化一点空间去放这个驱动 *****************************************/
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
error = -ENOMEM;
goto out_put_bus;
}
klist_init(&priv->klist_devices, NULL, NULL);
priv->driver = drv;
drv->p = priv;
priv->kobj.kset = bus->p->drivers_kset;
/* 将该驱动添加到kobject驱动模型里面去 */
error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
"%s", drv->name);
if (error)
goto out_unregister;
/* 把驱动添加的驱动总线的尾部 */
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
/* 这里涉及到一个变量drivers_autoproce,是否自动探测设备,这个值默认为1 */
if (drv->bus->p->drivers_autoprobe) {
error = driver_attach(drv); // 上面默认自动探测设备,那么这里就开始去寻找这个设备啦。
if (error)
goto out_unregister;
}
/* 从这里往下的操作,只有上述设备探测得到,才会继续,所以对我们来说,今天我们到这里为止,只需要继续去看上面的函数时如何继续的就可以 */
module_add_driver(drv->owner, drv);
error = driver_create_file(drv, &driver_attr_uevent);
if (error) {
printk(KERN_ERR "%s: uevent attr (%s) failed\n",
__func__, drv->name);
}
error = driver_add_attrs(bus, drv);
if (error) {
/* How the hell do we get out of this pickle? Give up */
printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
__func__, drv->name);
}
if (!drv->suppress_bind_attrs) {
error = add_bind_files(drv);
if (error) {
/* Ditto */
printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
__func__, drv->name);
}
}
return 0;
out_unregister:
kobject_put(&priv->kobj);
kfree(drv->p);
drv->p = NULL;
out_put_bus:
bus_put(bus);
return error;
}
在bus_add_driver
中,将驱动程序添加到了驱动总线的尾部,然后自动开始了设备侦测之路。就是如此简单,我们继续看设备侦测之路是如何进行的。
driver_attach和__driver_attach
static int __driver_attach(struct device *dev, void *data)
{
struct device_driver *drv = data;
if (!driver_match_device(drv, dev))
return 0;
if (dev->parent) /* Needed for USB */
device_lock(dev->parent);
device_lock(dev);
if (!dev->driver)
driver_probe_device(drv, dev);
device_unlock(dev);
if (dev->parent)
device_unlock(dev->parent);
return 0;
}
int driver_attach(struct device_driver *drv)
{
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
EXPORT_SYMBOL_GPL(driver_attach);
先从最下面的driver_attach
函数开始看,在这个函数中调用了bus_for_each_dev
函数,在下面。
int bus_for_each_dev(const struct bus_type *bus, struct device *start,
void *data, int (*fn)(struct device *, void *))
{
struct subsys_private *sp = bus_to_subsys(bus);
struct klist_iter i;
struct device *dev;
int error = 0;
if (!sp)
return -EINVAL;
klist_iter_init_node(&sp->klist_devices, &i,
(start ? &start->p->knode_bus : NULL));
while (!error && (dev = next_device(&i)))
error = fn(dev, data);
klist_iter_exit(&i);
subsys_put(sp);
return error;
}
EXPORT_SYMBOL_GPL(bus_for_each_dev);
这部分代码的目的就是让驱动去匹配设备,其实在bus.c中仔细去看的话,还会发现bus_for_each_drv
函数,这个就是设备去匹配驱动的过程。那么具体的匹配过程是怎样的呢?原来调用bus_for_each_dev
的时候,给他传入了一个函数指针,主要动作都在这个函数指针里面。这里需要着重说明的是,bus_for_each_dev
函数会遍历当前总线上所有的设备,来匹配当前的驱动。需要注意的一条语句是dev = next_device(&i)
这条语句会解决你后面的一个疑问!!
然后我们继续来看在__driver_attach
中又是如何进行设备与驱动的匹配的吧!
在展开说attach函数之前,我想先说一下,一个驱动注册,最主要的目的是要干什么?个人认为,做了那么多流程,最终目的就是希望看到,这个流程走了一圈,最终调回到了驱动的probe函数!
driver_match_device
那么我们从这里开始,就要向着这个方向努力前进了!回到正题,在attach函数中,有这个函数调用driver_match_device
顾名思义,驱动匹配设备,那我们就继续去看这个函数的实现:
居然是个内联函数!!!这个函数定义在drivers/base/base.h
。对哦,上面的函数调用好像没有说明对应的路径,不过没关系,我教你,首先选中函数名,然后按Ctrl+C,然后打开浏览器,打开google.com, 然后Ctrl+V,回车!!! 回归正题!
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
{
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
内核的代码真的是往死里绝,这里直接一个三元运算符完事儿。很牛!如果当前驱动对应的驱动总线上的match函数存在,则执行他,去匹配dev和drv. drv我们知道就是我们当前正在注册的驱动,那么这个dev是什么?不要忘了我们现在正在干嘛,我们现在正在bus_for_each_dev
函数里,遍历当前spi总线上的所有设备,有足够的dev给这个函数传参哦。
match == spi_match_device
那接下来就应该去看这个match函数了。可是这个match函数去那里找呢?冷静,我们分析一波,从上面的语句drv->bus->match ? drv->bus->match(dev, drv) : 1
中我们能看到,match属于bus总线,bus总线是drv的总线,那么一下子就明了了不是?回头去看一眼wk2124驱动中定义的驱动的总线是啥就好了呗!
所以我们再次看到段代码:
static struct spi_driver wk2xxx_driver = {
.driver = {
.name = "wk2xxx_spi",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(wkmic_spi_dt_match),
},
.probe = wk2xxx_probe,
.remove = wk2xxx_remove,
};
soga,是spi_bus_type
,然后这个总线类型的定义是:
struct bus_type spi_bus_type = {
.name = "spi",
.dev_groups = spi_dev_groups,
.match = spi_match_device,
.uevent = spi_uevent,
.probe = spi_probe,
.remove = spi_remove,
.shutdown = spi_shutdown,
};
EXPORT_SYMBOL_GPL(spi_bus_type);
看到了吧,当前总线类型的match函数是spi_match_device
,赶紧来看这个函数:
static int spi_match_device(struct device *dev, struct device_driver *drv)
{
const struct spi_device *spi = to_spi_device(dev);
const struct spi_driver *sdrv = to_spi_driver(drv);
/* Attempt an OF style match */
if (of_driver_match_device(dev, drv))
return 1;
/* Then try ACPI */
if (acpi_driver_match_device(dev, drv))
return 1;
if (sdrv->id_table)
return !!spi_match_id(sdrv->id_table, spi->modalias);
return strcmp(spi->modalias, drv->name) == 0;
}
在此再次提醒能看到这篇文章的小宝贝,当我们调用的函数中开始有dev的时候,我希望你始终要清楚我们现在在干什么,要不然文章看下去会一头雾水。我们现在在遍历spi总线上的所有设备。所以当执行上面的函数的时候,如果设备没有匹配到,会进行下一个设备的匹配,直到匹配到或者spi总线上的设备遍历完了也没有!
我们继续看我们设备的匹配过程,上述spi_match_device
函数中提供了三种匹配设备和驱动的方式,我们一次来看一下:
of_driver_match_device
这个函数最终调用到__of_match_node
函数,在这个函数里,通过把device_driver
的of_match_table
(of_device_id
结构体的数组)和device
里的of_node
(device_node
结构体)进行匹配,匹配方式是分别比较两者的name、type、和compatible字符串,三者要同时相同(一般name、和type为空,只比较compatible字符串,比较compatible时,是比较整个字符串,不管字符串里的逗号的,直接compare整个字符串。仔细回忆我们的驱动代码,老师是不是说过这个compatible
很重要?acpi_driver_match_device
acpi系统的匹配方式,主要查询dev的acpi id进行匹配- 直接比较设备的别名和驱动的名字进行匹配。
总之对于上述的三种匹配方式,不管哪一种匹配方式成功,都返回1。此时说明驱动和设备匹配成功了。那么到此处,就到了一步一步出栈的时候啦!我们再次来明确一下现在在干嘛?对我们在找一个和我们当前驱动匹配的设备,那么现在我们找到,所以我们应该回到开始找设备的那个函数继续执行对不对?
我们是在driver_attach
函数中调用bus_for_each_dev
函数从而开始寻找设备的,在这个函数中,我们不断的调用__driver_attach
函数,进行设备匹配。所以我们便从__driver_attach
继续看。不妨再将它贴出来:
static int __driver_attach(struct device *dev, void *data)
{
struct device_driver *drv = data;
if (!driver_match_device(drv, dev))
return 0;
if (dev->parent) /* Needed for USB */
device_lock(dev->parent);
device_lock(dev);
if (!dev->driver)
driver_probe_device(drv, dev);
device_unlock(dev);
if (dev->parent)
device_unlock(dev->parent);
return 0;
}
在匹配设备的时候我们重点关注的对象是driver_match_device
函数,这个函数只有在匹配到设备的情况下才不会使得__driver_attach
函数返回。就是现在! 所以我们继续往下走,USB那个不管! 先对设备加个类似于互斥锁的东西,防止线程冲突嘛,合情合理!那么接下来重点来了!!!
首先第一个问题: if (!dev->driver)
该语句中dev是谁?还记得我在上面重点强调过bus_for_each_dev
函数中会对所有的SPI设备进行遍历,并在遍历的时候将下一个要匹配的设备赋值给了dev,对,这就是真相。所以dev的driver到底存不存在?我们要知道现在才刚刚匹配完成,你和你相亲对象才刚见面,还没有深入交流呢!所以不存在,后面会有对dev-> driver 赋值的动作,到时候我会再次强调。好的进入这个分支语句!
driver_probe_device
最起码probe关键字已经出现了,说明我们正在不断接近真相。
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
int ret = 0;
if (!device_is_registered(dev)) // 异常处理
return -ENODEV;
pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
pm_runtime_get_suppliers(dev);
if (dev->parent)
pm_runtime_get_sync(dev->parent);
/* 运行时电源管理,这里大概是组织该设备进入低功耗状态吧,没有深入了解 */
pm_runtime_barrier(dev);
if (initcall_debug)
ret = really_probe_debug(dev, drv);
else
ret = really_probe(dev, drv); // 只关注这里的probe函数
pm_request_idle(dev);
if (dev->parent)
pm_runtime_put(dev->parent);
pm_runtime_put_suppliers(dev);
return ret;
}
really_probe
到这里要开始probe了。该函数中最主要的事情是调用了really_probe
这个函数一百多行,完整版请点击前面函数二字查看。
下面的代码我会以注释的形式说明,一起来看:
static int really_probe(struct device *dev, struct device_driver *drv)
{
/* 上面是一些初始化配置和异常处理函数的初始化 */
...
re_probe: // goto语句节点,用于再次probe
dev->driver = drv; // 看到了吧,在这里才对dev的driver进行了赋值
/* If using pinctrl, bind pins now before probing */
ret = pinctrl_bind_pins(dev);
if (ret)
goto pinctrl_bind_failed;
...
/* 终于看到了我们期待的probe函数,这里进行了一个判断,如果bus存在针对该设备类型的probe函数,就调用bus上的probe函数,否则调用driver的probe函数 */
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->probe) {
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
}
drv :: probe
看到这里请冷静,我们需要清楚这个probe是谁?根据上面的代码,有两个probe函数可用,bus的probe和驱动的probe。我们直接来看驱动的probe吧,首选需要明确的是,这个probe函数是哪个?还记得我们刚开始的时候传入的drv是谁吗?再来看这张图
spi_drv_probe
在上面的图中我们明确看到了一直用来传值的driver只是spi_driver接口体的driver成员,而他的probe函数是spi_drv_probe
那么我们继续来看这个函数
static int spi_drv_probe(struct device *dev)
{
const struct spi_driver *sdrv = to_spi_driver(dev->driver);
return sdrv->probe(to_spi_device(dev));
}
从上面开始对于to_spi_device
和to_spi_driver
函数我们都没有深入去看,因为从它的函数名我们就能明确的知道它干了什么,就是将一个设备“格式化”成spi格式的而已。言归正传,回到spi_drv_probe
函数,还记得dev的driver是啥吗?忘了的上去看!!!函数最后调用了当前正在注册的驱动的probe函数,并传入了我们在bus_for_each_dev
匹配到的设备,原来驱动probe中的dev来自这里呀!
到这里就达到了我们最终的目的,进入驱动的probe!
driver_add_groups
然后出栈一直到driver_register
, 继续执行driver_add_groups
函数 ,这个函数的作用是配置该设备文件在文件系统中的属性,举个栗子来说,一个串口设备,在文件系统中都会有一些属性,比如可读可写,属于谁?属于哪个组?等等。
kobject_uevent
最后是kobject_uevent
函数,这个函数的作用是通知用户空间“哎,我又新添加了一个驱动昂,你们要用的赶紧昂!”
总结
还是说,驱动注册最终目的就是调用该驱动的probe函数进行设备配置和使用。上面的一系列流程分析中一直都是冲着这个目标在进行。在分析过程中舍弃了很多比如:为了程序健壮性的添加的判断、为了适配更多的spi设备驱动而添加的兼容性判断等等,我们都是直接掠过了,可能在以后的某一天我们会用到略过的这些代码,但是只要我们清楚整个流程的主干,那以后需要那些枝叶代码,也能很方便的找到的。
写在最后,我也是处于学习的目的去写这么一篇文章,我也是第一次从代码里面去看spi驱动的注册过程,我也是查资料过来的。所以可能会有一些错误,还请毫不客气的指出。谢谢!
标签:bus,register,driver,dev,drv,spi,device From: https://www.cnblogs.com/klelee/p/linux_kernel-spi_register_driver.html