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生产者,消费者问题

pthread_cond_wait() :用于阻塞当前线程,等待别的线程使用pthread_cond_signal()或pthread_cond_broadcast来唤醒它。 pthread_cond_wait() 必须与pthread_mutex 配套使用。pthread_cond_wait()函数一进入wait状态就会自动release mutex。当其他线程通过pthread

[职场] 护理专业简历怎么写 #经验分享#微信

护理专业简历怎么写   很多想成为一名护理方面的从业者,但是又不知道应该怎么制作一份简历,现在这里分享了一份护理方面的简历模板供大家参考。   蓝山山   年龄:24   号码:12345678910   地址:上海市 邮箱:jianli@jianli.com   教育背景   时间:2011-09到2015-06   学校:蓝山大学   专业:护理学   学历:本科

Linux 安装、配置Tomcat 的HTTPS

Linux 安装 、配置Tomcat的HTTPS 安装Tomcat 这里选择的是 tomcat 10.X ,需要Java 11及更高版本 Binary Distributions ->Core->选择 tar.gz包 下载、上传到内网服务器 /opt 目录tar -xzf 解压将解压的根目录改名为 tomat-10 并移动到 /opt 下, 形成个人习惯的路径 /opt/tomcat-10

RedHat运维-Linux文本操作基础-AWK进阶

你不用整理,跟着敲一遍,有个印象,然后把它保存到本地,以后要用再去看,如果有了新东西,你自个再添加。这是我参考牛客上的shell编程专项题,只不过换成了问答的方式而已。不用背,就算是我自己亲自敲,我现在好多也记不住。 1. 输出nowcoder.txt文件第5行的内容 2. 输出nowcoder.txt文件第6行的内容 3. 输出nowcoder.txt文件第7行的内容 4. 输出nowcode

【Linux进阶】UNIX体系结构分解——操作系统,内核,shell

1.什么是操作系统? 从严格意义上说,可将操作系统定义为一种软件,它控制计算机硬件资源,提供程序运行环境。我们通常将这种软件称为内核(kerel),因为它相对较小,而且位于环境的核心。  从广义上说,操作系统包括了内核和一些其他软件,这些软件使得计算机能够发挥作用,并使计算机具有自己的特生。这里所说的其他软件包括系统实用程序(system utility)、应用程序、shell以及公用函数库等

Java面试八股之怎么通过Java程序判断JVM是32位还是64位

怎么通过Java程序判断JVM是32位还是64位 可以通过Java程序内部检查系统属性来判断当前运行的JVM是32位还是64位。以下是一个简单的方法: public class JvmBitCheck {public static void main(String[] args) {String arch = System.getProperty("os.arch");String dataM

电脑不小心删除的文件怎么恢复?4个必备恢复方法!

“刚刚在对电脑里的某些垃圾文件进行清理时,我一不小心误删了比较重要的数据。这些误删的数据还有机会恢复吗?希望大家帮帮我,非常感谢!” 在这个数字化飞速发展的时代,电脑早已成为我们日常生活和工作中不可或缺的一部分。然而,就像生活中的小插曲一样,有时我们可能会在不经意间犯下一些小错误,比如不小心删除了重要的文件。 当那份文件消失在眼前,仿佛被时间吞噬,我们不禁会心生焦虑。但别担心,就像每个问题

【操作系统】信号Signal超详解|捕捉函数

🔥博客主页: 我要成为C++领域大神🎥系列专栏:【C++核心编程】 【计算机网络】 【Linux编程】 【操作系统】 ❤️感谢大家点赞👍收藏⭐评论✍️ 本博客致力于知识分享,与更多的人进行学习交流 ​ 如何触发信号 信号是Linux下的经典技术,一般操作系统利用信号杀死违规进程,典型进程干预手段,信号除了杀死进程外也可以挂起进程 kill -l 查看系统支持的信号

Windows/macOS/Linux 安装 Redis 和 Redis Desktop Manager 可视化工具

本文所有安装都在macOS High Sierra 10.13.4进行,Windows安装相对容易些,Linux安装与macOS类似,文中会做区分讲解 1. Redis安装 1.下载Redis https://redis.io/download 把下载的源码更名为redis-4.0.9-source,我喜欢跟maven、Tomcat放在一起,就放到/Users/zhan/Documents

ABAP怎么把传入的参数刷新到内表里面呢?

1.在执行相关的功能操作之前,优先执行这一段代码,把输入的数据更新入内表里面 DATA: lo_guid TYPE REF TO cl_gui_alv_grid.CALL FUNCTION 'GET_GLOBALS_FROM_SLVC_FULLSCR'IMPORTINGe_grid = lo_guid.CALL METHOD lo_guid->check_changed_data.CALL M