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

您所在的位置:网站首页 linux调用驱动 linux 设备树解析及probe调用流程

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

2023-12-16 13:45| 来源: 网络整理| 查看: 265

以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。



【本文地址】


今日新闻


推荐新闻


    CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3