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

相关文章

Linux使用fdisk进行磁盘的相关操作

《Linux使用fdisk进行磁盘的相关操作》fdisk命令是Linux中用于管理磁盘分区的强大文本实用程序,这篇文章主要为大家详细介绍了如何使用fdisk进行磁盘的相关操作,需要的可以了解下... 目录简介基本语法示例用法列出所有分区查看指定磁盘的区分管理指定的磁盘进入交互式模式创建一个新的分区删除一个存

Linux使用dd命令来复制和转换数据的操作方法

《Linux使用dd命令来复制和转换数据的操作方法》Linux中的dd命令是一个功能强大的数据复制和转换实用程序,它以较低级别运行,通常用于创建可启动的USB驱动器、克隆磁盘和生成随机数据等任务,本文... 目录简介功能和能力语法常用选项示例用法基础用法创建可启动www.chinasem.cn的 USB 驱动

高效管理你的Linux系统: Debian操作系统常用命令指南

《高效管理你的Linux系统:Debian操作系统常用命令指南》在Debian操作系统中,了解和掌握常用命令对于提高工作效率和系统管理至关重要,本文将详细介绍Debian的常用命令,帮助读者更好地使... Debian是一个流行的linux发行版,它以其稳定性、强大的软件包管理和丰富的社区资源而闻名。在使用

怎么关闭Ubuntu无人值守升级? Ubuntu禁止自动更新的技巧

《怎么关闭Ubuntu无人值守升级?Ubuntu禁止自动更新的技巧》UbuntuLinux系统禁止自动更新的时候,提示“无人值守升级在关机期间,请不要关闭计算机进程”,该怎么解决这个问题?详细请看... 本教程教你如何处理无人值守的升级,即 Ubuntu linux 的自动系统更新。来源:https://

Ubuntu系统怎么安装Warp? 新一代AI 终端神器安装使用方法

《Ubuntu系统怎么安装Warp?新一代AI终端神器安装使用方法》Warp是一款使用Rust开发的现代化AI终端工具,该怎么再Ubuntu系统中安装使用呢?下面我们就来看看详细教程... Warp Terminal 是一款使用 Rust 开发的现代化「AI 终端」工具。最初它只支持 MACOS,但在 20

Idea调用WebService的关键步骤和注意事项

《Idea调用WebService的关键步骤和注意事项》:本文主要介绍如何在Idea中调用WebService,包括理解WebService的基本概念、获取WSDL文件、阅读和理解WSDL文件、选... 目录前言一、理解WebService的基本概念二、获取WSDL文件三、阅读和理解WSDL文件四、选择对接

Linux Mint Xia 22.1重磅发布: 重要更新一览

《LinuxMintXia22.1重磅发布:重要更新一览》Beta版LinuxMint“Xia”22.1发布,新版本基于Ubuntu24.04,内核版本为Linux6.8,这... linux Mint 22.1「Xia」正式发布啦!这次更新带来了诸多优化和改进,进一步巩固了 Mint 在 Linux 桌面

LinuxMint怎么安装? Linux Mint22下载安装图文教程

《LinuxMint怎么安装?LinuxMint22下载安装图文教程》LinuxMint22发布以后,有很多新功能,很多朋友想要下载并安装,该怎么操作呢?下面我们就来看看详细安装指南... linux Mint 是一款基于 Ubuntu 的流行发行版,凭借其现代、精致、易于使用的特性,深受小伙伴们所喜爱。对

macOS怎么轻松更换App图标? Mac电脑图标更换指南

《macOS怎么轻松更换App图标?Mac电脑图标更换指南》想要给你的Mac电脑按照自己的喜好来更换App图标?其实非常简单,只需要两步就能搞定,下面我来详细讲解一下... 虽然 MACOS 的个性化定制选项已经「缩水」,不如早期版本那么丰富,www.chinasem.cn但我们仍然可以按照自己的喜好来更换

什么是 Linux Mint? 适合初学者体验的桌面操作系统

《什么是LinuxMint?适合初学者体验的桌面操作系统》今天带你全面了解LinuxMint,包括它的历史、功能、版本以及独特亮点,话不多说,马上开始吧... linux Mint 是一款基于 Ubuntu 和 Debian 的知名发行版,它的用户体验非常友好,深受广大 Linux 爱好者和日常用户的青睐,