关于platform_driver 是如何匹配 platform_device的和如何调用到platform_driver中的probe函数的研究

本文解析了Linux平台驱动模型的三大组成部分:总线、设备和驱动。详细介绍platform_bus的注册过程、platform_device的注册流程以及platform_driver如何通过probe函数完成与设备的匹配。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >



        在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、

  1. <span style="font-size:16px;">int platform_driver_register(struct platform_driver *drv)  
  2. {  
  3.     drv->driver.bus = &platform_bus_type;//驱动的总线类型指向platform_bus_type  
  4.     if (drv->probe)  
  5.         drv->driver.probe = platform_drv_probe;  
  6.     if (drv->remove)  
  7.         drv->driver.remove = platform_drv_remove;  
  8.     if (drv->shutdown)  
  9.         drv->driver.shutdown = platform_drv_shutdown;  
  10.     if (drv->suspend)  
  11.         drv->driver.suspend = platform_drv_suspend;  
  12.     if (drv->resume)  
  13.         drv->driver.resume = platform_drv_resume;  
  14.     return driver_register(&drv->driver);  
  15. }</span>  


 2、

  1. <span style="font-size:16px;">int driver_register(struct device_driver *drv)  
  2. {  
  3.     int ret;  
  4.     struct device_driver *other;  
  5.   
  6.     BUG_ON(!drv->bus->p);  
  7. //检测总线的操作函数和驱动的操作函数是否同时存在,同时存在则提示使用总线提供的操作函数  
  8.     if ((drv->bus->probe && drv->probe) ||  
  9.         (drv->bus->remove && drv->remove) ||  
  10.         (drv->bus->shutdown && drv->shutdown))  
  11.         printk(KERN_WARNING "Driver '%s' needs updating - please use "  
  12.             "bus_type methods\n", drv->name);  
  13.     //查找这个驱动是否已经在总线上注册,并增加引用计数,若已经注册,则返回提示信息。  
  14.     other = driver_find(drv->name, drv->bus);  
  15.     if (other) {  
  16.     //如果已经被注册,则返回提示错误并且减少引用计数。  
  17.         put_driver(other);  
  18.         printk(KERN_ERR "Error: Driver '%s' is already registered, "  
  19.             "aborting...\n", drv->name);  
  20.         return -EEXIST;  
  21.     }  
  22.     //若还没有注册,则在总线上注册该驱动  
  23.     ret = bus_add_driver(drv);  
  24.     if (ret)  
  25.         return ret;  
  26.     ret = driver_add_groups(drv, drv->groups);  
  27.     if (ret)  
  28.         bus_remove_driver(drv);  
  29.     return ret;  
  30. }</span>  


3、

 

  1. <span style="font-size:16px;">int bus_add_driver(struct device_driver *drv)  
  2. {  
  3.     struct bus_type *bus;  
  4.     struct driver_private *priv;  
  5.     int error = 0;  
  6. //用于增加该bus所属的顶层bus的kobject的引用计数,返回的是其所属的顶层bus的指针。  
  7.     bus = bus_get(drv->bus);  
  8.     if (!bus)  
  9.         return -EINVAL;  
  10.   
  11.     pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);  
  12.   
  13.     priv = kzalloc(sizeof(*priv), GFP_KERNEL);  
  14.     if (!priv) {  
  15.         error = -ENOMEM;  
  16.         goto out_put_bus;  
  17.     }  
  18.     klist_init(&priv->klist_devices, NULL, NULL);  
  19.     //将这两个结构体连接起来  
  20.     priv->driver = drv;  
  21.     drv->p = priv;  
  22. //指向顶层的bus的p->drivers_kset  
  23. //设置私有数据的父容器,在这一步中,设置了kset为platform下的drivers_kset结构,也就是drivers呢个目录   
  24.     priv->kobj.kset = bus->p->drivers_kset;  
  25. //初始化kobj对象,设置容器操作集并建立相应的目录,这里由于没有提供parent,所以会使用父容器中的kobj为父对象    
  26.     error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,  
  27.                      "%s", drv->name);  
  28.     if (error)  
  29.         goto out_unregister;  
  30. //检测所属总线的drivers_autoprobe属性是否为真     
  31.   //为真则进行与设备的匹配,到这里,就会与我们之前注册的test_device连接上了,  
  32.   //至于如何连接,进行了什么操作,将在别的文章中详细描述   
  33.     if (drv->bus->p->drivers_autoprobe) {  
  34.         error = driver_attach(drv);  
  35.         if (error)  
  36.             goto out_unregister;  
  37.     }  
  38.     //挂载到所属总线驱动链表上  
  39.     klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);  
  40.     module_add_driver(drv->owner, drv);  
  41. //建立uevent属性文件  
  42.     error = driver_create_file(drv, &driver_attr_uevent);  
  43.     if (error) {  
  44.         printk(KERN_ERR "%s: uevent attr (%s) failed\n",  
  45.             __func__, drv->name);  
  46.     }  
  47.     //建立设备属性文件  
  48.     error = driver_add_attrs(bus, drv);  
  49.     if (error) {  
  50.         /* How the hell do we get out of this pickle? Give up */  
  51.         printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",  
  52.             __func__, drv->name);  
  53.     }  
  54.     error = add_bind_files(drv);  
  55.     if (error) {  
  56.         /* Ditto */  
  57.         printk(KERN_ERR "%s: add_bind_files(%s) failed\n",  
  58.             __func__, drv->name);  
  59.     }  
  60.   
  61.     kobject_uevent(&priv->kobj, KOBJ_ADD);  
  62.     return 0;  
  63. out_unregister:  
  64.     kfree(drv->p);  
  65.     drv->p = NULL;  
  66.     kobject_put(&priv->kobj);  
  67. out_put_bus:  
  68.     bus_put(bus);  
  69.     return error;  
  70. }</span>  


 4、驱动的匹配关键是上面函数中的

  1. <span style="font-size:16px;">  if (drv->bus->p->drivers_autoprobe) {//drivers_autoprobe在初始化的时候定义为1,系统则会调用下面的driver_attach函数进行驱动与设备的匹配  
  2.         error = driver_attach(drv);  
  3.         if (error)  
  4.             goto out_unregister;  
  5.     }</span>  



 

  1. <span style="font-size:16px;">int driver_attach(struct device_driver *drv)  
  2. {  
  3.     return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);  
  4. }</span>  
  1. <span style="font-size:16px;"//bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);  
  2. int bus_for_each_dev(struct bus_type *bus, struct device *start,  
  3.              void *data, int (*fn)(struct device *, void *))  
  4. {/* 
  5.  
  6. struct klist_iter { 
  7.     struct klist        *i_klist; 
  8.     struct klist_node   *i_cur; 
  9. }; 
  10.  
  11. */  
  12.     struct klist_iter i;  
  13.     struct device *dev;  
  14.     int error = 0;  
  15.   
  16.     if (!bus)  
  17.         return -EINVAL;  
  18. //如果第三个参数不为空就增加引用计数  
  19.     klist_iter_init_node(&bus->p->klist_devices, &i,  
  20.                  (start ? &start->p->knode_bus : NULL));//一直是NULL  
  21. //经过上面的宏之后,i实际上变成了i->i_klist=&bus->p->klist_devices,bus->p->klist_devices指向的是挂接在它上面的所有的设备的指针                
  22.     while ((dev = next_device(&i)) && !error)  
  23. //fu函数传入的是device的指针和device_driver的指针,  
  24.         error = fn(dev, data);  
  25. //如果klist_iter_init_node第三个参数不为空则减少引用计数  
  26.     klist_iter_exit(&i);  
  27.     return error;  
  28. }</span>  


 //寻找到下一个设备的节点

  1. <span style="font-size:16px;">static struct device *next_device(struct klist_iter *i)  
  2. {  
  3.     struct klist_node *n = klist_next(i);  
  4.     struct device *dev = NULL;  
  5.     struct device_private *dev_prv;  
  6.   
  7.     if (n) {  
  8.         dev_prv = to_device_private_bus(n);  
  9.         dev = dev_prv->device;  
  10.     }  
  11.     return dev;  
  12. }</span>  


 函数error = fn(dev, data)的原型如下:

传进来的参数第一个参数为不断遍历到的设备节点的指针,第二个参数为固定的一个驱动所对应的struct device_driver *drv指针,这样就实现驱动和设备的匹配

  1. <span style="font-size:16px;">static int __driver_attach(struct device *dev, void *data)  
  2. {  
  3.     struct device_driver *drv = data;  
  4.   
  5.     /* 
  6.      * Lock device and try to bind to it. We drop the error 
  7.      * here and always return 0, because we need to keep trying 
  8.      * to bind to devices and some drivers will return an error 
  9.      * simply if it didn't support the device. 
  10.      * 
  11.      * driver_probe_device() will spit a warning if there 
  12.      * is an error. 
  13.      */  
  14. //当设备和驱动的名字不匹配的时候返回的是0,然后就会调用下面的return 0;  
  15.     if (!driver_match_device(drv, dev))  
  16.         return 0;  
  17.   
  18.     if (dev->parent) /* Needed for USB */  
  19.         down(&dev->parent->sem);  
  20.     down(&dev->sem);  
  21.     if (!dev->driver)  
  22.         driver_probe_device(drv, dev);//调用探测函数进行探测,并且调用platform_driver中的probe函数  
  23.     up(&dev->sem);  
  24.     if (dev->parent)  
  25.         up(&dev->parent->sem);  
  26.   
  27.     return 0;  
  28. }  
  29. </span>  


在上面有两个比较关键的函数driver_match_device(dre,dev),函数原型如下:

  1. <span style="font-size:16px;">static inline int driver_match_device(struct device_driver *drv,  
  2.                       struct device *dev)  
  3. {  
  4.     return drv->bus->match ? drv->bus->match(dev, drv) : 1;//无论设备与驱动是否匹配成功都会返回1  
  5. }</span>  

当驱动的指针不为空的时候,这个drv->bus所指向的的这个驱动所属的的总线的bus_type中的match函数,然后传进去的是该驱动的指针和设备的指针

  1. <span style="font-size:16px;">static int platform_match(struct device *dev, struct device_driver *drv)  
  2. {  
  3.     struct platform_device *pdev = to_platform_device(dev);  
  4.     struct platform_driver *pdrv = to_platform_driver(drv);  
  5.   
  6.     /* match against the id table first */  
  7.     if (pdrv->id_table)//为空,不会被调用  
  8.         return platform_match_id(pdrv->id_table, pdev) != NULL;  
  9.   
  10.     /* fall-back to driver name match */  
  11.     return (strcmp(pdev->name, drv->name) == 0);  
  12. }</span>  


最后调用驱动的probe函数进行设备的探测

driver_probe_device(drv, dev);//调用探测函数进行探测,并且调用platform_driver中的probe函数

  1. <span style="font-size:16px;">int driver_probe_device(struct device_driver *drv, struct device *dev)  
  2. {  
  3.     int ret = 0;  
  4. //再次检查设备有没有在总线上注册,当发现还没有注册的时候,返回一个错误  
  5.     if (!device_is_registered(dev))  
  6.         return -ENODEV;  
  7.   
  8.     pr_debug("bus: '%s': %s: matched device %s with driver %s\n",  
  9.          drv->bus->name, __func__, dev_name(dev), drv->name);  
  10.   
  11.     ret = really_probe(dev, drv);  
  12.   
  13.     return ret;  
  14. }</span>  


 

  1. <span style="font-size:16px;">static int really_probe(struct device *dev, struct device_driver *drv)  
  2. {  
  3.     int ret = 0;  
  4.   
  5.     atomic_inc(&probe_count);  
  6.     pr_debug("bus: '%s': %s: probing driver %s with device %s\n",  
  7.          drv->bus->name, __func__, drv->name, dev_name(dev));  
  8.     WARN_ON(!list_empty(&dev->devres_head));  
  9. //找到了设备的驱动,并且将dev->driver指针指向自己的这个驱动  
  10.     dev->driver = drv;  
  11.     if (driver_sysfs_add(dev)) {//在sys目录下建立连接指向自己的在sys中的drivers  
  12.         printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",  
  13.             __func__, dev_name(dev));  
  14.         goto probe_failed;  
  15.     }  
  16. //在bus_type  platform_bus_type中并没有设置probe函数,所以下面函数并不会被调用  
  17.     if (dev->bus->probe) {  
  18.         ret = dev->bus->probe(dev);  
  19.         if (ret)  
  20.             goto probe_failed;  
  21.     } else if (drv->probe) {//上面总线没有probe函数,所以直接调用驱动当中的probe函数  
  22.         ret = drv->probe(dev);  
  23.         if (ret)  
  24.             goto probe_failed;  
  25.     }  
  26.   
  27.     driver_bound(dev);  
  28.     ret = 1;  
  29.     pr_debug("bus: '%s': %s: bound device %s to driver %s\n",  
  30.          drv->bus->name, __func__, dev_name(dev), drv->name);  
  31.     goto done;  
  32.   
  33. probe_failed:  
  34.     devres_release_all(dev);  
  35.     driver_sysfs_remove(dev);  
  36.     dev->driver = NULL;  
  37.   
  38.     if (ret != -ENODEV && ret != -ENXIO) {  
  39.         /* driver matched but the probe failed */  
  40.         printk(KERN_WARNING  
  41.                "%s: probe of %s failed with error %d\n",  
  42.                drv->name, dev_name(dev), ret);  
  43.     }  
  44.     /* 
  45.      * Ignore errors returned by ->probe so that the next driver can try 
  46.      * its luck. 
  47.      */  
  48.     ret = 0;  
  49. done:  
  50.     atomic_dec(&probe_count);  
  51.     wake_up(&probe_waitqueue);  
  52.     return ret;  
  53. }</span>  

        至此platform_driver的probe函数实现了调用并且匹配了platform_device
 


<think>我们正在讨论Linux设备模型中的三个关键结构体:platform_deviceplatform_driverdevice_driver。 它们之间的关系是: 1. platform_device 代表一个具体的平台设备,通常描述SoC上的外设(如I2C控制器、SPI控制器等),它包含设备的资源(如内存地址、中断号等)。 2. device_driver 是一个通用的设备驱动结构体,包含驱动的基本操作(如probe、remove)设备模型需要的字段。 3. platform_driver 是平台设备驱动,它内嵌了一个device_driver结构体,并扩展了平台设备特有的操作(如平台设备匹配电源管理)。 具体关系如下: - platform_driver 结构体中包含一个 device_driver 结构体成员(通常命名为driver)。 - platform_driver 通过其内部的 device_driver 成员与内核设备模型关联。 - 当注册一个 platform_driver 时,实际上是通过其内部的 device_driver 注册到总线上(platform_bus_type)。 - platform_device platform_driver 通过名称进行匹配(即platform_device的name字段platform_driver的id_table或者driver.name匹配)。 - 匹配成功后,会调用 platform_driver 中的 probe 函数。 结构体定义(简化): ```c struct platform_device { const char *name; // 设备名称,用于匹配驱动 int id; struct device dev; // 内嵌的通用设备结构体 // ... 资源信息等 }; struct device_driver { const char *name; // 驱动名称 struct bus_type *bus; // 所属总线 int (*probe)(struct device *dev); // 探测设备 int (*remove)(struct device *dev); // 移除设备 // ... 其他操作 }; struct platform_driver { int (*probe)(struct platform_device *); // 平台设备的探测函数 int (*remove)(struct platform_device *); // 移除函数 struct device_driver driver; // 内嵌的通用设备驱动 const struct platform_device_id *id_table; // 设备匹配表 }; ``` 工作流程: 1. 系统启动时,平台设备(platform_device)通常通过板级文件或设备树被注册到内核。 2. 驱动程序通过调用`platform_driver_register`注册一个platform_driver。 3. 内核的platform总线(platform_bus_type)会尝试将已注册的设备与驱动进行匹配。 4. 匹配依据:platform_driverdriver.name与platform_device的name匹配,或者platform_driver的id_table中的任一条目与platform_device的name匹配。 5. 匹配成功则调用platform_driverprobe函数(注意:实际调用的是platform_driver->driver.probe,但通常这个函数指针被设置为一个内部函数,该内部函数调用platform_driver->probe,并将device转换为platform_device)。 注意:在platform_driver的初始化中,通常会将device_driverprobe函数设置为一个通用的平台驱动probe函数(例如platform_drv_probe),这个函数调用platform_driver自己的probe函数。 总结: - platform_device 描述硬件设备。 - platform_driver 包含驱动该硬件设备的函数,并内嵌一个device_driver。 - device_driver 是设备驱动的通用部分,由platform_driver中的driver成员实现。 关系图: platform_driver -> 包含 device_driver platform_device -> 包含 device 在总线上,device_driverdevice进行匹配,而实际上匹配的是platform_deviceplatform_driver。 §§相关问题§§ 1. 在内核中,platform_deviceplatform_driver是如何通过名称进行匹配的? 2. 当platform_driver注册时,其内嵌的device_driverprobe函数是如何被设置并最终调用platform_driverprobe函数的? 3. 设备树(Device Tree)机制下,platform_device是如何被创建注册的? 4. platform_device结构体中的struct device dev成员与device_driver结构体有何关联? 5. 在platform驱动模型中,为什么需要将device_driver内嵌在platform_driver中,而不是直接使用device_driver?</think>### Linux设备驱动模型核心结构体关系解析 #### 1. **基础层级关系** ```c struct device_driver { // 通用驱动抽象 const char *name; // 驱动名称 struct bus_type *bus; // 所属总线 int (*probe)(struct device *dev); // 探测设备 ... }; struct platform_driver { // 平台设备驱动 int (*probe)(struct platform_device *); struct device_driver driver; // 内嵌通用驱动结构 const struct platform_device_id *id_table; ... }; struct platform_device { // 平台设备实例 const char *name; // 设备名称 struct device dev; // 内嵌设备基类 struct resource *resource; // 设备资源 ... }; ``` #### 2. **关键交互机制** 1. **注册绑定流程** ```mermaid graph LR A[platform_device_register] --> B[注册到platform_bus] C[platform_driver_register] --> D[注册driverplatform_bus] B & D --> E[总线匹配driver.name与device.name] E --> F[调用platform_driver.probe] ``` 2. **结构体内嵌关系** - `platform_driver` 包含 `device_driver` 成员 - `platform_device` 包含 `device` 成员 - 通过`container_of`宏实现父子结构转换: ```c struct platform_device *pdev = container_of(dev, struct platform_device, dev); struct platform_driver *pdrv = container_of(drv, struct platform_driver, driver); ``` #### 3. **功能对比表** | 结构体 | 作用域 | 关键成员 | 生命周期管理 | |------------------|-------------|----------------------------|-------------------| | `device_driver` | 通用驱动模型 | name, bus, probe | 所有设备类型通用 | | `platform_driver`| 平台设备驱动 | driver, probe, id_table | 封装platform特有操作 | | `platform_device`| 平台设备实例 | name, dev, resource | 描述SoC固定资源 | #### 4. **典型驱动开发流程** ```c // 定义平台驱动 static struct platform_driver my_driver = { .probe = my_probe, .driver = { .name = "my_device", // 匹配设备名称 .owner = THIS_MODULE, }, }; // 设备探测函数 static int my_probe(struct platform_device *pdev) { // 1. 获取设备资源 struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); // 2. 映射寄存器 void __iomem *base = devm_ioremap_resource(&pdev->dev, res); // 3. 注册字符设备等操作 ... } // 注册驱动 module_platform_driver(my_driver); ``` #### 5. **设备树(Device Tree)整合** 现代Linux使用设备树替代硬编码`platform_device`: ```dts my_device: my_device@0x12340000 { compatible = "vendor,my-device"; // 匹配驱动 reg = <0x12340000 0x1000>; // 寄存器空间 interrupts = <0 45 4>; // 中断号 }; ``` 驱动通过`of_match_table`匹配: ```c static const struct of_device_id my_of_ids[] = { { .compatible = "vendor,my-device" }, {} }; struct platform_driver my_driver = { .driver = { .of_match_table = my_of_ids, }, }; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值