linux 设备树解析及probe调用流程

以platform设备为例

设备树解析

很好的一张总结图,转自:设备树解析过程及platform设备注册

设备树解析过程

probe函数调用

转自:device 的probe函数是怎么被调用的

在驱动程序中,我们通常会定义一个platform_driver的结构体,其中包含了各种操作函数。

static struct platform_driver xxx_driver = {
	.probe		= xxx_probe,
	.remove		= __devexit_p(xxx_remove),
	.shutdown        = xxx_shutdown,
	.suspend        = xxx_suspend,
	.resume		= xxx_resume,
	.of_match_table = icm40607_of_match,
        .driver		= {
		.owner	= THIS_MODULE,
		.name	= "xxx_name",
                .id_table   = xxx_id,
	},
};

其中,of_match_table和id_table都是用来在设备树中和设备匹配,且都是通过compatible,前者优先级更高,后者则会在前者未匹配的情况下去掉compatible的供应商信息后再匹配,即id_table只匹配sensor名字。

而这个结构体的定义之中会找到许多函数指针。

struct platform_driver {
	int (*probe)(struct platform_device *);
	int (*remove)(struct platform_device *);
	void (*shutdown)(struct platform_device *);
	int (*suspend)(struct platform_device *, pm_message_t state);
	int (*resume)(struct platform_device *);
	struct device_driver driver;
	struct platform_device_id *id_table;
};
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 */
 
	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;
};

之后我们初始化时会调用函数platform_driver_register进行注册,把platform的函数指针传入了driver结构体中。这样device_driver和platform_driver都有了具体的值,但是好像还是不知道如何执行到我们定义的probe函数。

int platform_driver_register(struct platform_driver *drv)
{
	drv->driver.bus = &platform_bus_type;
	if (drv->probe)
		drv->driver.probe = platform_drv_probe;//platform_drv_probe是函数名
	if (drv->remove)
		drv->driver.remove = platform_drv_remove;
	if (drv->shutdown)
		drv->driver.shutdown = platform_drv_shutdown;
 
	return driver_register(&drv->driver);
}

继续找platform_drv_probe的定义:

static int platform_drv_probe(struct device *_dev)
{
	struct platform_driver *drv = to_platform_driver(_dev->driver);
	struct platform_device *dev = to_platform_device(_dev);
        .
        .
        .
	return drv->probe(dev);
}

找到to_platform_driver的定义后,发现调用了一个熟悉的函数container_of。

#define to_platform_driver(drv)    \
(container_of((drv), struct platform_driver, driver))
#define container_of(ptr, type, member) ({              \
const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
(type *)( (char *)__mptr - offsetof(type,member) );})

container_of这个函数第一次遇见是再看宋宝华的书的时候,当时在写platform总线下的字符设备驱动程序。但是,实现open函数时不知道怎么让file的私有数据指针private_data指向我在probe中定义地设备结构体lxy_device的指针(局部变量)。
container_of(ptr,type,member),这里面有ptr,type,member分别代表传入的具体成员指针、结构体类型、结构体中ptr对应的具体成员,ptr类型应该是typeof(member) *。
简单说,这个函数可以根据结构体某个成员的地址获取该结构体的指针(地址),最好能仔细理解这个函数,第一次见真是感觉很巧妙。

int driver_register(struct device_driver *drv)
{
	...
	ret = bus_add_driver(drv);
        ...
}

int bus_add_driver(struct device_driver *drv)
{
        ...
	if (drv->bus->p->drivers_autoprobe) {
		error = driver_attach(drv);
		if (error)
			goto out_unregister;
	}
        ...
}

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

//bus_for_each_dev就是对已经添加到drv->bus上面的每个设备操作。

int bus_for_each_dev(struct bus_type *bus, struct device *start, \
                    void *data, int (*fn)(struct device *, void *))
{
    data = drv;
    klist_iter_init_node(&bus->p->klist_devices, &i,         \
                        (start ? &start->p->knode_bus : NULL));
    while ((dev = next_device(&i)) && !error)
        error = fn(dev, data);
}
static int __driver_attach(struct device *dev, void *data)
{
	struct device_driver *drv = data;
 
	if (!driver_match_device(drv, dev))    // here !
		return 0;
 
	if (dev->parent)	/* Needed for USB */
		down(&dev->parent->sem);
	down(&dev->sem);
	if (!dev->driver)
		driver_probe_device(drv, dev);    //& here !
	up(&dev->sem);
	if (dev->parent)
		up(&dev->parent->sem);
 
	return 0;
}

driver_match_device 是检测设备和驱动是否匹配;
drvier_probe_device 是执行probe函数。

//match 函数
static inline int driver_match_device(struct device_driver *drv,struct device *dev)
{
 return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}

static int platform_match(struct device *dev, struct device_driver *drv)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct platform_driver *pdrv = to_platform_driver(drv);
 
	/* match against the id table first */
	if (pdrv->id_table)
		return platform_match_id(pdrv->id_table, pdev) != NULL;
 
	/* fall-back to driver name match */
	return (strcmp(pdev->name, drv->name) == 0);
}

struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_attrs	= platform_dev_attrs,
	.match		= platform_match,
	.uevent		= platform_uevent,
	.pm		= &platform_dev_pm_ops,
};

其实,bus_type中的match函数也就是platform_match函数,才是真正的将驱动和设备进行匹配的函数。如果成功就返回0. 看到了其中比较了 pdev->name  和 drv->name 。具体到watchdog的驱动中, 这两个对应的就是字符串:"xxx_name"。

//probe 函数
在driver_probe_device中又调用了really_probe:

static int really_probe(struct device *dev, struct device_driver *drv)
{
	int ret = 0;
 
	atomic_inc(&probe_count);
	dev->driver = drv;
	if (driver_sysfs_add(dev)) {
		printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
			__func__, dev_name(dev));
		goto probe_failed;
	}
 
	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;
	}
 
	driver_bound(dev);
	ret = 1;
	pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
		 drv->bus->name, __func__, dev_name(dev), drv->name);
	goto done;

参数dev来自:

__driver_attach中的device *dev
又来自于 next_device(&i)
i来自于klist_iter_init_node,应该是由设备树匹配而来。

那么到了这一步,也就全部联系起来了。
1、platform_driver_register注册时把 platform_drv_probe赋值给drv->driver.probe。
2、然后driver_register调用了driver_probe_device,里面又调用了really_probe,里面通过drv->probe调用了drv->driver.probe,也就是调用了platform_drv_probe。其中传入的参数是device *dev,dev_prv = to_device_private_bus,dev = dev_prv->device。猜测dev由设备树匹配过来。
3、platform_drv_probe中又获取了xxx_probe的指针,最后调用到我们编写的xxx_probe。

posted @ 2021-12-23 15:28  月的光景  阅读(6387)  评论(0编辑  收藏  举报