关于platform_driver 是如何匹配 platform_device的和如何调用到platform_driver中的probe函数的研究
2012年02月24日 10:16:24
阅读数:7184
在linux中platform平台驱动又三大部分组成,第一是bus、第二是驱动、第三是设备。
第一总线也就是platform_bus,总线也是一种特殊的device,到底层下面还是要调用device_register来注册该总线设备,然后是用来注册总线的属性结构体 bus_type(platform_bus_type),至此platform平台的总线已经准备好。具体介绍可以看我另一篇博客。
第二是设备,platform_device,它的注册流程是:platform_device_register(struct platform_device *pdev)->device_initialize(&pdev->dev)->platform_device_add(pdev)->pdev->dev.bus = &platform_bus_type->device_add(&pdev->dev)->把设备挂在虚拟的platform bus下。
第三是驱动,现在总线、设备都准备好了,然后等着驱动来匹配设备进行驱动,具体的驱动匹配设备流程如下:
1、
-
int platform_driver_register(struct platform_driver *drv) -
{
-
drv->driver.bus = &platform_bus_type;//驱动的总线类型指向platform_bus_type -
if (drv->probe) -
drv->driver.probe = platform_drv_probe; -
if (drv->remove) -
drv->driver.remove = platform_drv_remove; -
if (drv->shutdown) -
drv->driver.shutdown = platform_drv_shutdown; -
if (drv->suspend) -
drv->driver.suspend = platform_drv_suspend; -
if (drv->resume) -
drv->driver.resume = platform_drv_resume; -
return driver_register(&drv->driver); -
}
2、
-
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) {
-
//如果已经被注册,则返回提示错误并且减少引用计数。 -
put_driver(other); -
printk(KERN_ERR "Error: Driver '%s' is already registered, " -
"aborting...\n", drv->name); -
return -EEXIST; -
} -
//若还没有注册,则在总线上注册该驱动 -
ret = bus_add_driver(drv); -
if (ret) -
return ret; -
ret = driver_add_groups(drv, drv->groups); -
if (ret) -
bus_remove_driver(drv); -
return ret; -
}
3、
-
int bus_add_driver(struct device_driver *drv) -
{
-
struct bus_type *bus; -
struct driver_private *priv; -
int error = 0; -
//用于增加该bus所属的顶层bus的kobject的引用计数,返回的是其所属的顶层bus的指针。 -
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; -
//指向顶层的bus的p->drivers_kset -
//设置私有数据的父容器,在这一步中,设置了kset为platform下的drivers_kset结构,也就是drivers呢个目录 -
priv->kobj.kset = bus->p->drivers_kset; -
//初始化kobj对象,设置容器操作集并建立相应的目录,这里由于没有提供parent,所以会使用父容器中的kobj为父对象 -
error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL, -
"%s", drv->name); -
if (error) -
goto out_unregister; -
//检测所属总线的drivers_autoprobe属性是否为真 -
//为真则进行与设备的匹配,到这里,就会与我们之前注册的test_device连接上了, -
//至于如何连接,进行了什么操作,将在别的文章中详细描述 -
if (drv->bus->p->drivers_autoprobe) {
-
error = driver_attach(drv); -
if (error) -
goto out_unregister; -
} -
//挂载到所属总线驱动链表上 -
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); -
module_add_driver(drv->owner, drv); -
//建立uevent属性文件 -
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); -
} -
error = add_bind_files(drv); -
if (error) {
-
/* Ditto */ -
printk(KERN_ERR "%s: add_bind_files(%s) failed\n", -
__func__, drv->name); -
} -
kobject_uevent(&priv->kobj, KOBJ_ADD); -
return 0; -
out_unregister: -
kfree(drv->p); -
drv->p = NULL; -
kobject_put(&priv->kobj); -
out_put_bus: -
bus_put(bus); -
return error; -
}
4、驱动的匹配关键是上面函数中的
-
if (drv->bus->p->drivers_autoprobe) {//drivers_autoprobe在初始化的时候定义为1,系统则会调用下面的driver_attach函数进行驱动与设备的匹配 -
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, 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 {
-
struct klist *i_klist; -
struct klist_node *i_cur; -
}; -
*/ -
struct klist_iter i; -
struct device *dev; -
int error = 0; -
if (!bus) -
return -EINVAL; -
//如果第三个参数不为空就增加引用计数 -
klist_iter_init_node(&bus->p->klist_devices, &i, -
(start ? &start->p->knode_bus : NULL));//一直是NULL -
//经过上面的宏之后,i实际上变成了i->i_klist=&bus->p->klist_devices,bus->p->klist_devices指向的是挂接在它上面的所有的设备的指针 -
while ((dev = next_device(&i)) && !error) -
//fu函数传入的是device的指针和device_driver的指针, -
error = fn(dev, data); -
//如果klist_iter_init_node第三个参数不为空则减少引用计数 -
klist_iter_exit(&i); -
return error; -
}
//寻找到下一个设备的节点
-
static struct device *next_device(struct klist_iter *i) -
{
-
struct klist_node *n = klist_next(i); -
struct device *dev = NULL; -
struct device_private *dev_prv; -
if (n) {
-
dev_prv = to_device_private_bus(n); -
dev = dev_prv->device; -
} -
return dev; -
}
函数error = fn(dev, data)的原型如下:
传进来的参数第一个参数为不断遍历到的设备节点的指针,第二个参数为固定的一个驱动所对应的struct device_driver *drv指针,这样就实现驱动和设备的匹配
-
static int __driver_attach(struct device *dev, void *data) -
{
-
struct device_driver *drv = data; -
/* -
* 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. -
*/ -
//当设备和驱动的名字不匹配的时候返回的是0,然后就会调用下面的return 0; -
if (!driver_match_device(drv, dev)) -
return 0; -
if (dev->parent) /* Needed for USB */ -
down(&dev->parent->sem); -
down(&dev->sem); -
if (!dev->driver) -
driver_probe_device(drv, dev);//调用探测函数进行探测,并且调用platform_driver中的probe函数 -
up(&dev->sem); -
if (dev->parent) -
up(&dev->parent->sem); -
return 0; -
}
在上面有两个比较关键的函数driver_match_device(dre,dev),函数原型如下:
-
static inline int driver_match_device(struct device_driver *drv, -
struct device *dev) -
{
-
return drv->bus->match ? drv->bus->match(dev, drv) : 1;//无论设备与驱动是否匹配成功都会返回1 -
}
当驱动的指针不为空的时候,这个drv->bus所指向的的这个驱动所属的的总线的bus_type中的match函数,然后传进去的是该驱动的指针和设备的指针
-
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); -
}
最后调用驱动的probe函数进行设备的探测
driver_probe_device(drv, dev);//调用探测函数进行探测,并且调用platform_driver中的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); -
ret = really_probe(dev, drv); -
return ret; -
}
-
static int really_probe(struct device *dev, struct device_driver *drv) -
{
-
int ret = 0; -
atomic_inc(&probe_count); -
pr_debug("bus: '%s': %s: probing driver %s with device %s\n", -
drv->bus->name, __func__, drv->name, dev_name(dev)); -
WARN_ON(!list_empty(&dev->devres_head)); -
//找到了设备的驱动,并且将dev->driver指针指向自己的这个驱动 -
dev->driver = drv; -
if (driver_sysfs_add(dev)) {//在sys目录下建立连接指向自己的在sys中的drivers -
printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n", -
__func__, dev_name(dev)); -
goto probe_failed; -
} -
//在bus_type platform_bus_type中并没有设置probe函数,所以下面函数并不会被调用 -
if (dev->bus->probe) {
-
ret = dev->bus->probe(dev); -
if (ret) -
goto probe_failed; -
} else if (drv->probe) {//上面总线没有probe函数,所以直接调用驱动当中的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; -
probe_failed: -
devres_release_all(dev); -
driver_sysfs_remove(dev); -
dev->driver = NULL; -
if (ret != -ENODEV && ret != -ENXIO) {
-
/* driver matched but the probe failed */ -
printk(KERN_WARNING -
"%s: probe of %s failed with error %d\n", -
drv->name, dev_name(dev), ret); -
} -
/* -
* Ignore errors returned by ->probe so that the next driver can try -
* its luck. -
*/ -
ret = 0; -
done: -
atomic_dec(&probe_count); -
wake_up(&probe_waitqueue); -
return ret; -
}
至此platform_driver的probe函数实现了调用并且匹配了platform_device
今天的文章关于platform_driver 是如何匹配 platform_device的和如何调用到platform_driver中的probe函数的研究分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/26119.html