spi_register_driver注册流程分析

发布时间 2023-07-13 14:44:47作者: 可乐klelee

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 。这里可能比较不好理解?大概如此:

image-20230629162733269

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函数中提供了三种匹配设备和驱动的方式,我们一次来看一下:

  1. of_driver_match_device 这个函数最终调用到__of_match_node 函数,在这个函数里,通过把device_driverof_match_tableof_device_id结构体的数组)和device里的of_nodedevice_node结构体)进行匹配,匹配方式是分别比较两者的name、type、和compatible字符串,三者要同时相同(一般name、和type为空,只比较compatible字符串,比较compatible时,是比较整个字符串,不管字符串里的逗号的,直接compare整个字符串。仔细回忆我们的驱动代码,老师是不是说过这个compatible 很重要?
  2. acpi_driver_match_device acpi系统的匹配方式,主要查询dev的acpi id进行匹配
  3. 直接比较设备的别名和驱动的名字进行匹配。

总之对于上述的三种匹配方式,不管哪一种匹配方式成功,都返回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是谁吗?再来看这张图

image-20230629162733269

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_deviceto_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驱动的注册过程,我也是查资料过来的。所以可能会有一些错误,还请毫不客气的指出。谢谢!