关于platform_driver 是如何匹配 platform_device的和如何调用到platform_driver中的probe函数的研究
来源:互联网 发布:神魔诛天变身进阶数据 编辑:程序博客网 时间:2024/05/22 07:04
在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、
- <SPAN style="FONT-SIZE: 16px">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);
- }</SPAN>
int platform_driver_register(struct platform_driver *drv){drv->driver.bus = &platform_bus_type;//驱动的总线类型指向platform_bus_typeif (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、
- <SPAN style="FONT-SIZE: 16px">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;
- }</SPAN>
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、
- <SPAN style="FONT-SIZE: 16px">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;
- }</SPAN>
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、驱动的匹配关键是上面函数中的
- <SPAN style="FONT-SIZE: 16px"> if (drv->bus->p->drivers_autoprobe) {//drivers_autoprobe在初始化的时候定义为1,系统则会调用下面的driver_attach函数进行驱动与设备的匹配
- error = driver_attach(drv);
- if (error)
- goto out_unregister;
- }</SPAN>
if (drv->bus->p->drivers_autoprobe) {//drivers_autoprobe在初始化的时候定义为1,系统则会调用下面的driver_attach函数进行驱动与设备的匹配error = driver_attach(drv);if (error)goto out_unregister;}
- <SPAN style="FONT-SIZE: 16px">int driver_attach(struct device_driver *drv)
- {
- return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
- }</SPAN>
int driver_attach(struct device_driver *drv){return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);}
- <SPAN style="FONT-SIZE: 16px"> //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;
- }</SPAN>
//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;}
//寻找到下一个设备的节点
- <SPAN style="FONT-SIZE: 16px">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;
- }</SPAN>
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指针,这样就实现驱动和设备的匹配
- <SPAN style="FONT-SIZE: 16px">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;
- }
- </SPAN>
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),函数原型如下:
- <SPAN style="FONT-SIZE: 16px">static inline int driver_match_device(struct device_driver *drv,
- struct device *dev)
- {
- return drv->bus->match ? drv->bus->match(dev, drv) : 1;//无论设备与驱动是否匹配成功都会返回1
- }</SPAN>
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函数,然后传进去的是该驱动的指针和设备的指针
- <SPAN style="FONT-SIZE: 16px">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);
- }</SPAN>
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函数
- <SPAN style="FONT-SIZE: 16px">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;
- }</SPAN>
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;}
- <SPAN style="FONT-SIZE: 16px">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;
- }</SPAN>
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中的driversprintk(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函数
- 关于platform_driver 是如何匹配 platform_device的和如何调用到platform_driver中的probe函数的研究
- 关于platform_driver 是如何匹配 platform_device的和如何调用到platform_driver中的probe函数的研究
- 关于platform_driver 是如何匹配 platform_device的和如何调用到platform_driver中的probe函数的研究
- 关于platform_driver 是如何匹配 platform_device的和如何调用到platform_driver中的probe函数的研究
- 关于platform_driver 是如何匹配 platform_device的和如何调用到platform_driver中的probe函数的研究
- 关于platform_driver 是如何匹配 platform_device的和如何调用到platform_driver中的probe函数的研究
- 关于platform_driver 是如何匹配 platform_device的和如何调用到platform_driver中的probe函数的研究
- 关于platform_device和platform_driver的匹配
- 关于platform_device和platform_driver的匹配
- 关于platform_device和platform_driver的匹配
- 关于platform_device和platform_driver的匹配
- platform_device和platform_driver的匹配
- platform_device和platform_driver的注册过程,及probe函数何时调用的分析
- platform_device和platform_driver的注册过程,及probe函数何时调用的分析
- platform_device和platform_driver的注册过程,及probe函数何时调用的分析
- platform_device和platform_driver的注册过程,及probe函数何时调用的分析
- platform_device和platform_driver的注册过程,及probe函数何时调用的分析
- Ubuntu 12.10截图快捷键
- hdoj3635_Dragon Balls
- hdu 1217 Arbitrage
- android判断网络是否连接
- 角谷定理:正整数,偶数除以2,基数乘以3加1,问:10000中最长的角谷数
- 关于platform_driver 是如何匹配 platform_device的和如何调用到platform_driver中的probe函数的研究
- servlet中service doGet doPost 的关系
- QT下QTableView显示乱码
- java-jsoup解析html页面的内容
- 我的博客~~
- eclipse查看android源码
- mysql高可用探究(五)Heartbeat+DRBD+mysql高可用方案
- uCGUI使用
- ios新浪微博分享