Linux驱动 device 的probe函数是怎么被调用的

2024-01-25 16:48

本文主要是介绍Linux驱动 device 的probe函数是怎么被调用的,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

今天正好有空,研究了一下platformdevice的probe函数时如何被调用的。我觉得这个过程应该可以推广到一般设备的探测函数的调用。

以mini2440中的watchdog为例。

先看配置文件中对watchdog的设置:

[cpp]  view plain copy
print ?
  1. static struct resource s3c_wdt_resource[] = {  
  2.     [0] = {  
  3.         .start = S3C24XX_PA_WATCHDOG,  
  4.         .end   = S3C24XX_PA_WATCHDOG + S3C24XX_SZ_WATCHDOG - 1,  
  5.         .flags = IORESOURCE_MEM,  
  6.     },  
  7.     [1] = {  
  8.         .start = IRQ_WDT,  
  9.         .end   = IRQ_WDT,  
  10.         .flags = IORESOURCE_IRQ,  
  11.     }  
  12.   
  13. };  
  14.   
  15. struct platform_device s3c_device_wdt = {  
  16.     .name         = "s3c2410-wdt",  
  17.     .id       = -1,  
  18.     .num_resources    = ARRAY_SIZE(s3c_wdt_resource),  
  19.     .resource     = s3c_wdt_resource,  
  20. };  


 

在系统启动时 mini2440_machine_init调用了 platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices)); 其中 mini2440_devices 中就包括了s3c_deice_wdt。

[cpp]  view plain copy
print ?
  1. int platform_add_devices(struct platform_device **devs, int num)  
  2. {  
  3.     int i, ret = 0;  
  4.   
  5.     for (i = 0; i < num; i++) {  
  6.         ret = platform_device_register(devs[i]);  
  7.         if (ret) {  
  8.             while (--i >= 0)  
  9.                 platform_device_unregister(devs[i]);  
  10.             break;  
  11.         }  
  12.     }  
  13.   
  14.     return ret;  
  15. }  
[cpp]  view plain copy
print ?
  1. int platform_device_register(struct platform_device *pdev)  
  2. {  
  3.     device_initialize(&pdev->dev);  
  4.     return platform_device_add(pdev);  
  5. }  
[cpp]  view plain copy
print ?
  1. int platform_device_add(struct platform_device *pdev)  
  2. {  
  3.     int i, ret = 0;  
  4.   
  5.     if (!pdev)  
  6.         return -EINVAL;  
  7.   
  8.     if (!pdev->dev.parent)  
  9.         pdev->dev.parent = &platform_bus;  
  10.   
  11.     pdev->dev.bus = &platform_bus_type;  
  12.   
  13.     if (pdev->id != -1)  
  14.         dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);  
  15.     else  
  16.         dev_set_name(&pdev->dev, "%s", pdev->name);  
  17.   
  18.     for (i = 0; i < pdev->num_resources; i++) {  
  19.         struct resource *p, *r = &pdev->resource[i];  
  20.   
  21.         if (r->name == NULL)  
  22.             r->name = dev_name(&pdev->dev);  
  23.   
  24.         p = r->parent;  
  25.         if (!p) {  
  26.             if (resource_type(r) == IORESOURCE_MEM)  
  27.                 p = &iomem_resource;  
  28.             else if (resource_type(r) == IORESOURCE_IO)  
  29.                 p = &ioport_resource;  
  30.         }  
  31.   
  32.         if (p && insert_resource(p, r)) {  
  33.             printk(KERN_ERR  
  34.                    "%s: failed to claim resource %d\n",  
  35.                    dev_name(&pdev->dev), i);  
  36.             ret = -EBUSY;  
  37.             goto failed;  
  38.         }  
  39.     }  
  40.   
  41.     pr_debug("Registering platform device '%s'. Parent at %s\n",  
  42.          dev_name(&pdev->dev), dev_name(pdev->dev.parent));  
  43.   
  44.     <span style="color:#ff0000">ret = device_add(&pdev->dev);  
  45. </span> if (ret == 0)  
  46.         return ret;  
  47.   
  48.  failed:  
  49.     while (--i >= 0) {  
  50.         struct resource *r = &pdev->resource[i];  
  51.         unsigned long type = resource_type(r);  
  52.   
  53.         if (type == IORESOURCE_MEM || type == IORESOURCE_IO)  
  54.             release_resource(r);  
  55.     }  
  56.   
  57.     return ret;  
  58. }  



内核把设备挂在虚拟的platform bus下面。platformbus 虽说在物理上不存在这样的bus,但是在内核的代码中,platform bus是有真是的代码的,和其他的bus并没有区别。上面的device_add就是将设备加入到系统中。这个函数时非常重要的,这里不列出代码了。

    到现在为止,仅仅是设备加入到了系统中,设备还没有与驱动联系到一起。下面分析驱动的加载过程,就可以看到驱动是怎么样和设备关联到一起的。

    驱动部分:

[cpp]  view plain copy
print ?
  1. static struct platform_driver s3c2410wdt_driver = {  
  2.     .probe      = s3c2410wdt_probe,  
  3.     .remove     = __devexit_p(s3c2410wdt_remove),  
  4.     .shutdown   = s3c2410wdt_shutdown,  
  5.     .suspend    = s3c2410wdt_suspend,  
  6.     .resume     = s3c2410wdt_resume,  
  7.     .driver     = {  
  8.         .owner  = THIS_MODULE,  
  9.         .name   = "s3c2410-wdt",  
  10.     },  
  11. };  
  12.   
  13.   
  14. static char banner[] __initdata =  
  15.     KERN_INFO "S3C2410 Watchdog Timer, (c) 2004 Simtec Electronics\n";  
  16.   
  17. static int __init watchdog_init(void)  
  18. {  
  19.     printk(banner);  
  20.     return platform_driver_register(&s3c2410wdt_driver);  
  21. }  
  22.   
  23. static void __exit watchdog_exit(void)  
  24. {  
  25.     platform_driver_unregister(&s3c2410wdt_driver);  
  26. }  

 

[cpp]  view plain copy
print ?
  1. int platform_driver_register(struct platform_driver *drv)  
  2. {  
  3.     drv->driver.bus = &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.   
  11.     return<span style="color:#cc0000"> driver_register(&drv->driver);  
  12. </span>}  

上面这个过程有点像是C++中的虚函数。看下面的这两个结构体:

[cpp]  view plain copy
print ?
  1. struct <span style="color:#ff6600">platform_driver</span> {  
  2.     int (*probe)(struct platform_device *);  
  3.     int (*remove)(struct platform_device *);  
  4.     void (*shutdown)(struct platform_device *);  
  5.     int (*suspend)(struct platform_device *, pm_message_t state);  
  6.     int (*resume)(struct platform_device *);  
  7.     struct device_driver driver;  
  8.     struct platform_device_id *id_table;  
  9. };  
[cpp]  view plain copy
print ?
  1. struct <span style="color:#ff6600">device_driver</span> {  
  2.     const char      *name;  
  3.     struct bus_type     *bus;  
  4.   
  5.     struct module       *owner;  
  6.     const char      *mod_name;  /* used for built-in modules */  
  7.   
  8.     bool suppress_bind_attrs;   /* disables bind/unbind via sysfs */  
  9.   
  10.     int (*probe) (struct device *dev);  
  11.     int (*remove) (struct device *dev);  
  12.     void (*shutdown) (struct device *dev);  
  13.     int (*suspend) (struct device *dev, pm_message_t state);  
  14.     int (*resume) (struct device *dev);  
  15.     const struct attribute_group **groups;  
  16.   
  17.     const struct dev_pm_ops *pm;  
  18.   
  19.     struct driver_private *p;  
  20. };  

在platform_device 和 device_driver 中都包含了 函数指针: probe  remove  shutdown  suspend resume 等。device_driver是基类,platform_driver是派生类。这里具体的面向对象的思想我还没有琢磨明白。到现在这些函数指针都已经进行了赋值。下面看一下 device_driver的probe函数:

[cpp]  view plain copy
print ?
  1. static int platform_drv_probe(struct device *_dev)  
  2. {  
  3.     struct platform_driver *drv = to_platform_driver(_dev->driver);  
  4.     struct platform_device *dev = to_platform_device(_dev);  
  5.   
  6.     return drv->probe(dev);  
  7. }  

这里相当于是用手动的方法 去调用了派生类的probe函数,相当于是手动实现了C++中的虚函数。这样就会调用到s3c2410wdt_driver的probe函数。

好,接下来我们看platform_drv_probe函数是如何实现调用的。

[cpp]  view plain copy
print ?
  1. 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.         put_driver(other);  
  17.         printk(KERN_ERR "Error: Driver '%s' is already registered, "  
  18.             "aborting...\n", drv->name);  
  19.         return -EBUSY;  
  20.     }  
  21.   
  22.     ret = <span style="color:#cc0000">bus_add_driver</span>(drv);  
  23.     if (ret)  
  24.         return ret;  
  25.     ret = driver_add_groups(drv, drv->groups);  
  26.     if (ret)  
  27.         bus_remove_driver(drv);  
  28.     return ret;  
  29. }  
[cpp]  view plain copy
print ?
  1. int bus_add_driver(struct device_driver *drv)  
  2. {  
  3.     struct bus_type *bus;  
  4.     struct driver_private *priv;  
  5.     int error = 0;  
  6.   
  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.     priv->driver = drv;  
  20.     drv->p = priv;  
  21.     priv->kobj.kset = bus->p->drivers_kset;  
  22.     error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,  
  23.                      "%s", drv->name);  
  24.     if (error)  
  25.         goto out_unregister;  
  26.   
  27.     if (drv->bus->p->drivers_autoprobe) {  
  28.         <span style="color:#ff0000">error = driver_attach(drv);</span>  
  29.         if (error)  
  30.             goto out_unregister;  
  31.     }  
  32.     klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);  
  33.     module_add_driver(drv->owner, drv);  
  34.   
  35.     error = driver_create_file(drv, &driver_attr_uevent);  
  36.     .....  
  37.     error = driver_add_attrs(bus, drv);  
  38.       
  39.     return error;  
  40. }  


 

[cpp]  view plain copy
print ?
  1. int driver_attach(struct device_driver *drv)  
  2. {  
  3.     return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);  
  4. }  


其中bus_for_each_dev就是对已经添加到drv->bus上面的每个设备,这行_driver_attach函数。

[cpp]  view plain copy
print ?
  1. static int __driver_attach(struct device *dev, void *data)  
  2. {  
  3.     struct device_driver *drv = data;  
  4.   
  5.     if (!<span style="color:#cc0000">driver_match_device</span>(drv, dev))  
  6.         return 0;  
  7.   
  8.     if (dev->parent) /* Needed for USB */  
  9.         down(&dev->parent->sem);  
  10.     down(&dev->sem);  
  11.     if (!dev->driver)  
  12.         <span style="color:#cc0000">driver_probe_device</span>(drv, dev);  
  13.     up(&dev->sem);  
  14.     if (dev->parent)  
  15.         up(&dev->parent->sem);  
  16.   
  17.     return 0;  
  18. }  

上面就是我们要看的地方,其中driver_match_device是检测设备和驱动是否匹配,drvier_probe_device 就是执行探测函数。下面分别看这两个函数。

[cpp]  view plain copy
print ?
  1. 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;  
  5. }  
[cpp]  view plain copy
print ?
  1. 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. }  
[cpp]  view plain copy
print ?
  1. struct bus_type platform_bus_type = {  
  2.     .name       = "platform",  
  3.     .dev_attrs  = platform_dev_attrs,  
  4.     .match      = platform_match,  
  5.     .uevent     = platform_uevent,  
  6.     .pm     = &platform_dev_pm_ops,  
  7. };  

 

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

在driver_probe_device中又调用了really_probe,我们直接看这个函数:

[cpp]  view plain copy
print ?
  1. static int really_probe(struct device *dev, struct device_driver *drv)  
  2. {  
  3.     int ret = 0;  
  4.   
  5.     atomic_inc(&probe_count);  
  6.     dev->driver = drv;  
  7.     if (driver_sysfs_add(dev)) {  
  8.         printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",  
  9.             __func__, dev_name(dev));  
  10.         goto probe_failed;  
  11.     }  
  12.   
  13.     if (dev->bus->probe) {  
  14.         ret = <span style="color:#000000">dev->bus->probe(dev);</span>  
  15.         if (ret)  
  16.             goto probe_failed;  
  17.     } else if (drv->probe) {  
  18.         ret = <span style="color:#cc0000">drv->probe(dev);</span>  
  19.         if (ret)  
  20.             goto probe_failed;  
  21.     }  
  22.   
  23.     driver_bound(dev);  
  24.     ret = 1;  
  25.     pr_debug("bus: '%s': %s: bound device %s to driver %s\n",  
  26.          drv->bus->name, __func__, dev_name(dev), drv->name);  
  27.     goto done;  
[cpp]  view plain copy
print ?
  1. }  


就是在这个函数中,通过drv->probe从而调用了platform_drv_probe函数。其实上面的函数中 dev->bus->probe 和 drv->probe应该指向的是相同的函数。

到这里,我们的脉络已经清晰了。

这篇关于Linux驱动 device 的probe函数是怎么被调用的的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/643924

相关文章

qt5cored.dll报错怎么解决? 电脑qt5cored.dll文件丢失修复技巧

《qt5cored.dll报错怎么解决?电脑qt5cored.dll文件丢失修复技巧》在进行软件安装或运行程序时,有时会遇到由于找不到qt5core.dll,无法继续执行代码,这个问题可能是由于该文... 遇到qt5cored.dll文件错误时,可能会导致基于 Qt 开发的应用程序无法正常运行或启动。这种错

电脑提示xlstat4.dll丢失怎么修复? xlstat4.dll文件丢失处理办法

《电脑提示xlstat4.dll丢失怎么修复?xlstat4.dll文件丢失处理办法》长时间使用电脑,大家多少都会遇到类似dll文件丢失的情况,不过,解决这一问题其实并不复杂,下面我们就来看看xls... 在Windows操作系统中,xlstat4.dll是一个重要的动态链接库文件,通常用于支持各种应用程序

Python中help()和dir()函数的使用

《Python中help()和dir()函数的使用》我们经常需要查看某个对象(如模块、类、函数等)的属性和方法,Python提供了两个内置函数help()和dir(),它们可以帮助我们快速了解代... 目录1. 引言2. help() 函数2.1 作用2.2 使用方法2.3 示例(1) 查看内置函数的帮助(

Linux脚本(shell)的使用方式

《Linux脚本(shell)的使用方式》:本文主要介绍Linux脚本(shell)的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录概述语法详解数学运算表达式Shell变量变量分类环境变量Shell内部变量自定义变量:定义、赋值自定义变量:引用、修改、删

C++ 函数 strftime 和时间格式示例详解

《C++函数strftime和时间格式示例详解》strftime是C/C++标准库中用于格式化日期和时间的函数,定义在ctime头文件中,它将tm结构体中的时间信息转换为指定格式的字符串,是处理... 目录C++ 函数 strftipythonme 详解一、函数原型二、功能描述三、格式字符串说明四、返回值五

Linux链表操作方式

《Linux链表操作方式》:本文主要介绍Linux链表操作方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、链表基础概念与内核链表优势二、内核链表结构与宏解析三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势六、典型应用场景七、调试技巧与

详解Linux中常见环境变量的特点与设置

《详解Linux中常见环境变量的特点与设置》环境变量是操作系统和用户设置的一些动态键值对,为运行的程序提供配置信息,理解环境变量对于系统管理、软件开发都很重要,下面小编就为大家详细介绍一下吧... 目录前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变

Linux系统中的firewall-offline-cmd详解(收藏版)

《Linux系统中的firewall-offline-cmd详解(收藏版)》firewall-offline-cmd是firewalld的一个命令行工具,专门设计用于在没有运行firewalld服务的... 目录主要用途基本语法选项1. 状态管理2. 区域管理3. 服务管理4. 端口管理5. ICMP 阻断

Linux实现线程同步的多种方式汇总

《Linux实现线程同步的多种方式汇总》本文详细介绍了Linux下线程同步的多种方法,包括互斥锁、自旋锁、信号量以及它们的使用示例,通过这些同步机制,可以解决线程安全问题,防止资源竞争导致的错误,示例... 目录什么是线程同步?一、互斥锁(单人洗手间规则)适用场景:特点:二、条件变量(咖啡厅取餐系统)工作流

Linux中修改Apache HTTP Server(httpd)默认端口的完整指南

《Linux中修改ApacheHTTPServer(httpd)默认端口的完整指南》ApacheHTTPServer(简称httpd)是Linux系统中最常用的Web服务器之一,本文将详细介绍如何... 目录一、修改 httpd 默认端口的步骤1. 查找 httpd 配置文件路径2. 编辑配置文件3. 保存