我是靠谱客的博主 负责店员,最近开发中收集的这篇文章主要介绍一文带你深入理解probe函数,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

驱动注册的probe函数

probe函数在设备驱动注册最后收尾工作,当设备的device 和其对应的driver 在总线上完成配对之后,系统就调用 platform设备的probe函数完成驱动注册最后工作。资源、 中断调用函数以及其他相关工作。下面是probe被调用的一些程序流程。

从driver_register看起:


1int driver_register(struct device_driver * drv)

2{

3klist_init(&drv->klist_devices, klist_devices_get, klist_devices_put);

4init_completion(&drv->unloaded);

5return bus_add_driver(drv);

6}

klist_init与init_completion没去管它,可能是2.6的这个设备模型要做的一些工作。直觉告诉我要去bus_add_driver。

bus_add_driver中:

都是些Kobject 与 klist 、attr等。还是与设备模型有关的。但是其中有一句:

driver_attach(drv);

单听名字就很像:


1void driver_attach(struct device_driver * drv)

2{

3 bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);

4}

这个熟悉,遍历总线上的设备并设用__driver_attach。

在__driver_attach中又主要是这样:

driver_probe_device(drv, dev);

跑到driver_probe_device中去看看:

有一段很重要:

if (drv->bus->match && !drv->bus->match(dev, drv))

goto Done;

明显,是调用的驱动的总线上的match函数。如果返回1,则可以继续,否则就Done了。

继承执行的话:


1if (drv->probe)

2   ret = drv->probe(dev);

3if (ret) {

4    dev->driver = NULL;

5 goto ProbeFailed;

6}

只要probe存在则调用之。至此就完成了probe的调用。

这个过程链的关键还是在drv->bus->match ,因为其余的地方出错的话就是注册失败,而只要注册不失败且match返回1,那么就铁定会调用驱程的probe了。你可以注册一个总线类型和总线,并在 match中总是返回 1, 会发现,只要struct device_driver中的bus类型正确时,probe函数总是被调用.

 内核资料直通车:Linux内核源码技术学习路线+视频教程代码资料https://link.zhihu.com/?target=https%3A//docs.qq.com/doc/DUGZVQk1qWVBHTEl3

学习直通车:Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈-学习视频教程-腾讯课堂是不是学完操作系统原理后觉得纸上谈兵不过瘾?是不是面对浩若烟海的Linux内核源代码迷失在代码的海洋里不知所措?这门课可以带您用理论结合实践的方法一步一步抓住Linux内核最核心的部分代码,理解Linux操作系统运行的基本过程及涉及的核心机制。https://ke.qq.com/course/4032547?flowToken=1044374

有两个重要的链表挂在bus上,一个是设备device链表,一个是驱动driver链表。

每当我们向一根bus注册一个驱动driver时,套路是这样的:

driver_register(struct device_driver * drv) -> bus_add_driver() -> driver_attach() ->

bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);

bus_for_each_dev遍历该总线上所有的device,执行一次__driver_attach(),看能不能将驱动关联(attach)到某个设备上去。

__driver_attach()

->driver_probe_device()

->drv->bus->match(dev, drv), // 调用bus的match函数,看device和driver匹不匹配。如果匹配上,

继续执行really_probe()。

->really_probe()

->driver->probe()。(如果bus->probe非空,则调用bus->probe)

而每当我们向一根bus添加一个硬件时时,套路是这样的:

device_add()

\ device_add 中有很多操作kobject,注册sysfs,形成硬件hiberarchy结构的代码。

如果您忘记了,先回头去参考参考"我是sysfs"

->bus_attach_device() -> device_attach() ->bus_for_each_drv()

bus_for_each_drv与bus_for_each_dev类似,遍历该总线上所有的driver,执行一次__device_attach(),看能不能将设备关联(attach)到某个已登记的驱动上去。

__device_attach()

->driver_probe_device() //后面与上面一样

总结一些,一句话,注册一个某个bus的驱动就是先把驱动自己链入到bus驱动链表中去,在从bus的设备链表中一一寻找,看有没有自己可以关联上的设备。找到就probe,再把二者bind起来。反之,添加设备道理也是一样的。

论坛讨论帖:

1. 在看platform驱动是,发现一个很低能的问题,static int __devinitdm9000_probe(struct platform_device *pdev) 中的struct platform_device *pdev是从那里来的device,跟踪platform_driver_register(&dm9000_driver);一直没有发现platform_device的出现,但是现在probe函数中突然出现了这个变量,那么这个变量是从和而来?

2. platform驱动分为platform_driver和platform_device两个结构体表示,前者表示平台驱动,后者表示平台设备,这个struct platform_device *pdev就是在平台相关文件里注册的。平台下会定义一个platform_device,然后platform_device_register(&platform_device),所以你注册的平台驱动,当在platform_bus上探测到有个设备的时候,这个你之前在平台文件里注册的platform_device就会传过来。

3. "platform_bus上探测到有个设备的时候"此句怎么理解,是怎么探测到的呢?

4. 驱动肯定是自己注册了。至于设备的注册,有些设备所使用的协议有枚举过程,从这处过程中可以得到设备信息,进而生成相应设备结构体,然后注册在相应总线上。

在总线上注册设备时,会遍历该总线上已注册的驱动,用总线的match方法判断是否有匹配的驱动,如果有,则调用驱动的probe函数;在总线上注册驱动时,会遍历该总线上已注册的设备,用总线的match方法判断是否有匹配的设备,如果有,则调用驱动的probe函数。即,不管是先注册设备还是先注册驱动,总线的match方法会作用于所有组合,如果匹配了,则调用驱动的probe方法,这样就探测到了。

platform_bus是虚拟的总线,没有具体的协议。上面所讲设备的结构体是在枚举时动态生成的,设备的注册也是在枚举时触发的,而platform_device的定义与注册是在平台初始化文件里手动做的。后面的情况就是一样的了。其它一些没有类似枚举之类过程的总线,如I2C,也是这种流程。

5. 内核启动的时候, platform_device 是优先于 platform_driver 注册的。 比如 platform_device A , 是在arch/arm/mach-XXXX/mach-XXXX.c 文件里注册的, 而这个文件的代码是 优先于 platform_driver_register 执行的。
所以在你platform_driver_register执行的时候, platform_device A已经被挂在platform_bus总线上了, 而platform_driver_register()有个功能是到platform_bus上去挨个找寻,找寻挂在上面的platform_bus上的platform_device。找到了就执行probe()。

6. 可不可以这样,我没有试过。
但是因为驱动中有 开发者实现的 probe 函数。 如果先注册驱动, 驱动就找不到设备, 从而不执行probe函数,而驱动中最重要的就是probe函数,硬件的初始化,寄存器的配置,时钟的使能都在probe函数里完成。从这一点来说,驱动先于设备注册,应该不可行。

7. 你好,关于I2C驱动,目前有三个问题想请教下:
1,请问用i2c_add_driver注册I2C的client端的驱动的时候,这个client是怎么和哪个adapter attach的(在此之前,系统中注册了四个bit-style的adapter)?注意,我的i2c_driver中,并没有赋值attach_adapter,但是赋值了probe
2,注册adapter是用的 i2c_add_adapter,跟了下这个函数,发现它call的是->i2c_register_adapter->
adap->dev.bus = &i2c_bus_type;
adap->dev.type = &i2c_adapter_type;
res = device_register(&adap->dev);
这里是注册的是一个device,那么根据device-driver的模型,那adapter的driver是怎么被赋值的呢?
3,i2c_add_driver是向哪跟总线上注册的,是i2c_bus_type吗?i2c_register_adapter又是向哪跟总线注册的呢? 也是i2c_bus_type吗?
谢谢

8. i2c_driver并不是要跟adapter绑定,而是要和i2c_client绑定 。注册一个i2c_driver时并不知道它支持的设备在哪条总线上。例如一个系统有两个i2c控制器,每个控制器上有一个相同型号的EEPROM,它们只需一个i2c_driver。关键在于如何发现哪条总线上有i2c_driver所支持设备。

老I2C框架里,由i2c_driver负责发现设备,每注册一个adapter遍历已注册i2c_driver,每注册一个i2c_driver遍历已注册的i2c_adapter,不让他们错过任何一次组合,让i2c_driver在adapter上探测一下是否有它支持的设备,如果有,则生成一个i2c_client实例。当然由于i2c协议很弱,这种探测很不可靠。

现在(尤其在SOC系统中)倾向于静态定义这些信息:如果我知道哪条总线上有哪个设备,我就犯不着让i2c_driver去找它们了。于是在平台初始化函数里注册i2c_board_info,这个结构体就是一个i2c_client模板。i2c_board_info里有个bus_num作为匹配的凭证,如果bus_num与i2c_adapter的bus_num相同,那就匹配上了。所有注册的i2c_board_info会放在一个链表里,每注册一个i2c_adapter就去扫描这个链表,如果匹配就生成i2c_client。

在老框架,i2c_client肯定是在i2c_driver时调用attach_adapter时出现的;新框架里则可以在没有i2c_driver的情况下根据静态信息生成i2c_client。另外让i2c_driver探测设备时,顶多能知道设备地址,而欲查询设备其它属性,i2c协议里没有通用的方法。而i2c_board_info静态了提供了这些属性。

而对于旧框架,新框架一方面在接口上保持兼容,另一方面在功能上也有更新。新框架里通过bus_num来区别是扫描静态的i2c_board_info链表。如果不是,那就又归到老式的方法:让i2c_driver去找设备。这个过程跟上面讲的老框架的过程一样,添加adapter或者driver时相互遍历已注册的对方。新方法里有个i2c_detect,看起来是把以前散落在各个i2c_driver->attach_adapter中的代码抽象出来了,最后再调用i2c_driver->detect,这个的功能应该和i2c_driver->attach_adapter是一样的。只不过它只负责是否检测到驱动支持的设备,而i2c_client的生成则放在外面框架代码里了。最后再调用i2c_driver->attach_adapter,兼容旧驱动。注释里说这个方法以后可能会消失。其实在SOC系统里,一般到静态扫描就完了,也不会去调用detect方法。

不管通过什么方法生成i2c_client,注册它后,下面就是经典设备模型里的match/probe了。

i2c_driver与i2c_adapter都是注册在i2c_bus_type上,至于i2c_adapter的driver,我以前看代码时也没找着。现在看来,它根本没有另一半。其实看i2c_bus_type->match方法,它是通过i2c_verify_client把device转成i2c_client的,这里只验证device->type是不是i2c_client_type,也就是说,这条总线根本没准备为i2c_adapter匹配任何东西。

最后

以上就是负责店员为你收集整理的一文带你深入理解probe函数的全部内容,希望文章能够帮你解决一文带你深入理解probe函数所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(52)

评论列表共有 0 条评论

立即
投稿
返回
顶部