Linux内核平台设备已经存在的情况下注册平台驱动的匹配绑定过程分析

amsamsLinux驱动 2024-06-21 16:38:03 2610阅读 举报

 

在《 Linux 内核中设备树中的节点是如何转换为platform_device的》一文中已经介绍了编译进内核的驱动在内核中已经存在,内核启动的时候将设备树节点转换为平台设备,然后与内核中已经存在的驱动去如何进行匹配已经绑定。本文介绍另一个情况,就是驱动没有被编译到内核,内核启动时平台设备没有找到匹配的平台驱动,启动完成后我们动态加载驱动是如何实现匹配与绑定的。

我们以gpio_led_driver为例,分析整个注册过程:

static struct platform_driver gpio_led_driver = { .probe = gpio_led_probe, .shutdown = gpio_led_shutdown, .driver = { .name = "leds-gpio", .of_match_table = of_gpio_leds_match, }, }; module_platform_driver(gpio_led_driver);

展开以后就是:

#define module_platform_driver(__platform_driver) \ module_driver(__platform_driver, platform_driver_register, \ platform_driver_unregister)


module_driver --> platform_driver_register --> __platform_driver_register :

int __platform_driver_register(struct platform_driver *drv, struct module *owner) { drv->driver.owner = owner; drv->driver.bus = &platform_bus_type; drv->driver.probe = platform_drv_probe; drv->driver.remove = platform_drv_remove; drv->driver.shutdown = platform_drv_shutdown; return driver_register(&drv->driver); }


上面注册了相应的probe remove shutdown 等函数后,开始调用driver_register

这里我们需要注意,driver的总线类型(bus_type)被初始化为platform_bus_type

drv->driver.bus = &platform_bus_type;


其中platform_bus_type也在文件. /drivers/base/platform.c 中有具体定义:

struct bus_type platform_bus_type = { .name = "platform", .dev_groups = platform_dev_groups, .match = platform_match, .uevent = platform_uevent, .dma_configure = platform_dma_configure, .pm = &platform_dev_pm_ops, };

我们是已platform为例讲解,所以注册驱动的总线类型是platform的。如果是I2C总线呢?

其实也类似,例如在./drivers/i2c/i2c-core.c中有I2C注册函数i2c_register_driver源码(省略部分无关代码)

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)  
{  
    ……  
    driver->driver.owner = owner;  
    driver->driver.bus = &i2c_bus_type;  
     ……  
} 

所以,如果注册的是i2c驱动,那么总线类型初始化为i2c_bus_type,也可以在文件 ./ drivers/i2c/i2c-core.c 中看到其定义

struct bus_type i2c_bus_type = {  
    .name       = "i2c",  
    .match      = i2c_device_match,  
    .probe      = i2c_device_probe,  
    .remove     = i2c_device_remove,  
    .shutdown   = i2c_device_shutdown,  
    .pm     = &i2c_device_pm_ops,  
}; 
当总线类型和probe、remove、shutdown等函数注册后,就开始调用driver_register注册对应的驱动了。

driver_register源代码在文件./drivers/base/driver.c中

int driver_register(struct device_driver *drv)
{
	int ret;
	struct device_driver *other;

	if (!drv->bus->p) {
		pr_err("Driver '%s' was unable to register with bus_type '%s' because the bus was not initialized.\n",
			   drv->name, drv->bus->name);
		return -EINVAL;
	}

	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);  //注册uevent事件

	return ret;
}

首先来看driver_find,其实就是在总线的私有数据的drivers_kset列表中查找有没有这个驱动的名字:

struct device_driver *driver_find(const char *name, struct bus_type *bus)
{
	struct kobject *k = kset_find_obj(bus->p->drivers_kset, name);
	struct driver_private *priv;

	if (k) {
		/* Drop reference added by kset_find_obj() */
		kobject_put(k);
		priv = to_driver(k);
		return priv->driver;
	}
	return NULL;
}


driver_find找了所谓的驱动会怎样呢?我们观察driver_find的返回值,你会发现,这里返回的是指针,也就是说driver_find是一个指针函数喽。指针的类型是struct device_driver类型的。

struct device_driver 在文件 include/linux/device.h中定义

struct device_driver {  
    const char      *name;  
    struct bus_type     *bus;  
  
    struct module       *owner;  
    const char      *mod_name;  /* used for built-in modules */  
  
    bool suppress_bind_attrs;   /* disables bind/unbind via sysfs */  
  
    const struct of_device_id   *of_match_table;  
    const struct acpi_device_id *acpi_match_table;  
  
    int (*probe) (struct device *dev);  
    int (*remove) (struct device *dev);  
    void (*shutdown) (struct device *dev);  
    int (*suspend) (struct device *dev, pm_message_t state);  
    int (*resume) (struct device *dev);  
    const struct attribute_group **groups;  
  
    const struct dev_pm_ops *pm;  
  
    struct driver_private *p;  
};  

这个结构体里面包含了设备驱动的重要信息,例如名字(name)、总线类型(bus)、所述模块(owner)和一些用于回调的函数指针(probe,remove,suspend...)。

总结driver_find过程如下:

1. driver_find,拿到了drv->name和drv->bus开始找驱动

2. kset_find_obj 通过driver_find传递的bus->p->drivers_kset,利用list_for_each_entry遍历kset循环链表。(kset结构体中有循环链表指针next和prev)

3. 遍历循环链表中每一个kobj中的成员变量name

4. 通过strcmp(kobject_name(k), name)比较drv->name 和kobj中的name,如果有相同则表示查找成功

5. return :如果找到,则返回device_driver的指针,如果没有找到则返回了NULL。

为了能更好的说明driver_find,我用下面的图示意一下。

通过判断driver_find的返回值other,如果 if(other) 条件成立,说明other 不是NULL ,也就是说driver_find查找成功。但driver_register是注册驱动程序,如果驱动已经注册过,就不需要再次注册了。如果已经注册,那么直接 return -EBUSY; 后面的操作就不需要进行了。

看完driver_find,再来看bus_add_driver(drv):

int bus_add_driver(struct device_driver *drv)
{
	struct bus_type *bus;
	struct driver_private *priv;
	int error = 0;

	bus = bus_get(drv->bus); //获取驱动所属的总线,也就是platform总线
	if (!bus)
		return -EINVAL;

	pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);

	priv = kzalloc(sizeof(*priv), GFP_KERNEL); //分配一个priv结构体的空间
	if (!priv) {
		error = -ENOMEM;
		goto out_put_bus;
	}
	klist_init(&priv->klist_devices, NULL, NULL);//初始化priv下的klist_devices列表,就是挂这个驱动所属的设备
priv->driver = drv; //priv下的驱动指向驱动 drv->p = priv; //驱动的p指向priv,也就是这里分配的priv是driver下的priv priv->kobj.kset = bus->p->drivers_kset; //driver下的priv的kobj.kset指向总线的私有数据的drivers_kset列表
error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL, "%s", drv->name); //kset, kobject节点初始化,koject插入kset链表(尾插) if (error) goto out_unregister; klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); 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_groups(drv, bus->drv_groups); if (error) { /* How the hell do we get out of this pickle? Give up */ printk(KERN_ERR "%s: driver_create_groups(%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); /* drv->p is freed in driver_release() */ drv->p = NULL; out_put_bus: bus_put(bus); return error; }

我们看下klist_init(&priv->klist_devices, NULL, NULL),第一个参数是本驱动的私有数据下的设备列表,后面两个参数传入的是NULL:

void klist_init(struct klist *k, void (*get)(struct klist_node *),
		void (*put)(struct klist_node *))
{
	INIT_LIST_HEAD(&k->k_list);
	spin_lock_init(&k->k_lock);
	k->get = get;
	k->put = put;
}

在文件./include/linux/list.h中有INIT_LIST_HEAD定义

static inline void INIT_LIST_HEAD(struct list_head *list)  
{  
    list->next = list;  
    list->prev = list;  
}  

很明显,这是一个 内联函数(有inline)。实现的功能也很简单,list是链表,我们在实现循环链表时总会定义两个指针 next 和 prev 。next指向下一个节点的地址,prev指向上一个节点的地址。所以INIT_LIST_HEAD其实就是使next和prev都指向自己的地址,我们判断链表是否为空的时候不就是看看next和prev指向的地址是否相同吗,相同表示为空。

下面是INIT_LIST_HEAD的一个简单示意图

因为驱动新创建所以挂在本驱动的设备驱动列表是空的,也就是建立一个空列表,初始化列表的头,列表的get和put都指向空。

再来看kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,"%s", drv->name);

int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
			 struct kobject *parent, const char *fmt, ...)
{
	va_list args;
	int retval;

	kobject_init(kobj, ktype); //里边主要对kobj->state_开头的一些变量赋初值,将驱动下的私有数据的kobj->type = driver_ktype

	va_start(args, fmt); //对传入的可变参数drv->name做处理
	retval = kobject_add_varg(kobj, parent, fmt, args);
	va_end(args);

	return retval;
}

kobject_init源码也在文件./lib/kobject.c  中:

void kobject_init(struct kobject *kobj, struct kobj_type *ktype)  
{  
    char *err_str;  
  
    if (!kobj) {//检查kobj指针是否指向有效的kobject  
        err_str = "invalid kobject pointer!";  
        goto error;  
    }  
    if (!ktype) {//检查ktype是否指向有效的kobj_type  
        err_str = "must have a ktype to be initialized properly!\n";  
        goto error;  
    }  
    if (kobj->state_initialized) {//检查kobj的初始化状态是否已经被初始化  
        /* do not error out as sometimes we can recover */  
        printk(KERN_ERR "kobject (%p): tried to init an initialized "  
               "object, something is seriously wrong.\n", kobj);  
        dump_stack();  
    }  
  
    kobject_init_internal(kobj);//如果上面检查都通过,那么开始初始化  
    kobj->ktype = ktype;  
    return;  
  
error:  
    printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str);  
    dump_stack();  
}  


static __printf(3, 0) int kobject_add_varg(struct kobject *kobj,
					   struct kobject *parent,
					   const char *fmt, va_list vargs)
{
	int retval;

	retval = kobject_set_name_vargs(kobj, fmt, vargs);
	if (retval) {
		pr_err("kobject: can not set name properly!\n");
		return retval;
	}
	kobj->parent = parent;
	return kobject_add_internal(kobj);
}

kobject_add_varg(kobj, parent, fmt, args)==》kobject_set_name_vargs(kobj, fmt, vargs) :

int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
				  va_list vargs)
{
	const char *s;

	if (kobj->name && !fmt)
		return 0;

	s = kvasprintf_const(GFP_KERNEL, fmt, vargs);
	if (!s)
		return -ENOMEM;

	/*
	 * ewww... some of these buggers have '/' in the name ... If
	 * that's the case, we need to make sure we have an actual
	 * allocated copy to modify, since kvasprintf_const may have
	 * returned something from .rodata.
	 */
	if (strchr(s, '/')) {
		char *t;

		t = kstrdup(s, GFP_KERNEL);
		kfree_const(s);
		if (!t)
			return -ENOMEM;
		strreplace(t, '/', '!');
		s = t;
	}
	kfree_const(kobj->name);
	kobj->name = s;

	return 0;
}

其实就是把drv->name做了一些格式化处理后赋值给了drv->priv_>kobj->name。

return kobject_add_internal(kobj);过kobject_add_internal将准备好的kobject添加到kset的循环列表中,并且在sys/目录下创建kobject的目录。

static int kobject_add_internal(struct kobject *kobj)
{
	int error = 0;
	struct kobject *parent;

	if (!kobj)
		return -ENOENT;

	if (!kobj->name || !kobj->name[0]) {
		WARN(1,
		     "kobject: (%p): attempted to be registered with empty name!\n",
		     kobj);
		return -EINVAL;
	}

	parent = kobject_get(kobj->parent); 读取kobject的parent指针

	/* join kset if set, use it as parent if we do not already have one */
	if (kobj->kset) { //判断kobject的kset是否为空
		if (!parent) //如果kobj->parent为空,则赋值为kobject的kset自带的kobject
	           parent = kobject_get(&kobj->kset->kobj);
		kobj_kset_join(kobj);//把kobject挂到他的kset的列表kobj->kset->list上
		kobj->parent = parent;
	}

	pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",
		 kobject_name(kobj), kobj, __func__,
		 parent ? kobject_name(parent) : "<NULL>",
		 kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");

	error = create_dir(kobj); //创建kobject的相关目录
	if (error) {
		kobj_kset_leave(kobj);
		kobject_put(parent);
		kobj->parent = NULL;

		/* be noisy on error issues */
		if (error == -EEXIST)
			pr_err("%s failed for %s with -EEXIST, don't try to register things with the same name in the same directory.\n",
			       __func__, kobject_name(kobj));
		else
			pr_err("%s failed for %s (error: %d parent: %s)\n",
			       __func__, kobject_name(kobj), error,
			       parent ? kobject_name(parent) : "'none'");
	} else
		kobj->state_in_sysfs = 1; //更改kobject的sys文件系统状态标志

	return error;
}

代码里面if (kobj->kset)对kobject的kset进行的检查,那么我们这里的kset什么值呢?这就要回到bus_add_driver函数,其中有这么一句话priv->kobj.kset = bus->p->drivers_kset;所以此时的kobject->kset不为空。

if (!parent)的作用是判断kobject是否有父类,对于kset、kobject、parent和list,在权威书籍LDD3-chapter14(linux设备驱动)中有一个很经典的图

sysfs创建目录的代码就不详细说明了,记得在前面分析 kobject_init_and_add 时,有这样的一句注释 initialize a kobject structure and add it to the kobject hierarchy

单词hierarchy是 层级,等级的意思。注释的大致意思是将kobject添加到kobject等级中。这里的“等级”体现最明显的就是目录结构。

目录创建完成之后,通过populate_dir()在该目录下创建属性:

static int populate_dir(struct kobject *kobj)
{
    struct kobj_type *t = get_ktype(kobj);
    struct attribute *attr;
    int error = 0;
    int i;
    if (t && t->default_attrs) {
        for (i = 0; (attr = t->default_attrs[i]) != NULL; i++) {
            error = sysfs_create_file(kobj, attr);
            if (error)
                break;
        }
    }
    return error;
}

这个函数遍历kobj_type的default_attrs的每一项,通过sysfs_create_file()创建属性文件。echo目录下的属性文件就可以写数据了。这里不展开,后面我们找个具体的例子来分析。

说到目录,我们会很快联想到子目录或者上一级目录。要在sysfs里面创建kobject相关的目录,也需要遵守目录的等级制度啦。按照kobject的parent(也是kobject类)就是上一级目录的规则去创建,目录名是kobject->name。为了能让读者更加清楚创建的规则,我就以目前手中的平台为例:

static struct platform_driver gpio_led_driver = {
	.probe		= gpio_led_probe,
	.shutdown	= gpio_led_shutdown,
	.driver		= {
		.name	= "leds-gpio",
		.of_match_table = of_gpio_leds_match,
	},
};

可以看到driver->name 是“leds-gpio”。通过platform_driver_register(&gpio_led_driver);注册platform架构驱动。当代码执行到上述过程,肯定会在sysfs下创建相关的目录,并且以 kobject->name 命名。

我通过终端,访问sys目录,可以观察到/sys/bus/platform/drivers/leds-gpio 的目录结构刚好符合了我们分析代码的顺序。


klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); 就是把drv->priv->knode_bus挂在了platform总线的私有数据的驱动列表里。


接着就到了最重要的driver_attach(drv):

int driver_attach(struct device_driver *drv)
{
	return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}

展开以后:

int bus_for_each_dev(struct bus_type *bus, struct device *start,
		     void *data, int (*fn)(struct device *, void *))
{
	struct klist_iter i;
	struct device *dev;
	int error = 0;

	if (!bus || !bus->p)
		return -EINVAL;

	klist_iter_init_node(&bus->p->klist_devices, &i,
			     (start ? &start->p->knode_bus : NULL));
	while (!error && (dev = next_device(&i)))
		error = fn(dev, data);
	klist_iter_exit(&i);
	return error;
}

其实就是对平台总线的私有数据中的设备列表中挂的每一个设备与本驱动传入到__driver_attach执行一遍,也就是进行匹配。

static int __driver_attach(struct device *dev, void *data)
{
	struct device_driver *drv = data;
	int ret;

	/*
	 * Lock device and try to bind to it. We drop the error
	 * here and always return 0, because we need to keep trying
	 * to bind to devices and some drivers will return an error
	 * simply if it didn't support the device.
	 *
	 * driver_probe_device() will spit a warning if there
	 * is an error.
	 */

	ret = driver_match_device(drv, dev);
	if (ret == 0) {
		/* no match */
		return 0;
	} else if (ret == -EPROBE_DEFER) {
		dev_dbg(dev, "Device match requests probe deferral\n");
		driver_deferred_probe_add(dev);
	} else if (ret < 0) {
		dev_dbg(dev, "Bus failed to match device: %d", ret);
		return ret;
	} /* ret > 0 means positive match */

	if (driver_allows_async_probing(drv)) {
		/*
		 * Instead of probing the device synchronously we will
		 * probe it asynchronously to allow for more parallelism.
		 *
		 * We only take the device lock here in order to guarantee
		 * that the dev->driver and async_driver fields are protected
		 */
		dev_dbg(dev, "probing driver %s asynchronously\n", drv->name);
		device_lock(dev);
		if (!dev->driver) {
			get_device(dev);
			dev->p->async_driver = drv;
			async_schedule(__driver_attach_async_helper, dev);
		}
		device_unlock(dev);
		return 0;
	}

	device_driver_attach(drv, dev);

	return 0;
}

然后就是进行匹配,匹配成功调用本驱动的probe函数,这些在《 Linux 内核中设备树中的节点是如何转换为platform_device的》一文中已经介绍过,此处不再重复。


版权声明:
作者:amsams
链接:https://www.dianziwang.net/p/125574e0584dbd.html
来源:Linux驱动
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以点击 “举报”


登录 后发表评论
0条评论
还没有人评论过~