platform驱动之probe函数
驱动注册的probe函数
probe函数在设备驱动注册最后收尾工作,当设备的device 和其对应的driver 在总线上完成配对之后,系统就调用platform设备的probe函数完成驱动注册最后工作。资源、中断调用函数以及其他相关工作。下面是probe被调用的一些程序流程。
从driver_register看起:
plain
- int driver_register(struct device_driver * drv)
- {
- klist_init(&drv->klist_devices, klist_devices_get, klist_devices_put);
- init_completion(&drv->unloaded);
- return bus_add_driver(drv);
- }
klist_init与init_completion没去管它,可能是2.6的这个设备模型要做的一些工作。直觉告诉我要去bus_add_driver。
bus_add_driver中:
都是些Kobject 与 klist 、attr等。还是与设备模型有关的。但是其中有一句:
driver_attach(drv);
单听名字就很像:
plain
- void driver_attach(struct device_driver * drv)
- {
- bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
- }
这个熟悉,遍历总线上的设备并设用__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了。
继承执行的话:
plain
- if (drv->probe)
- ret = drv->probe(dev);
- if (ret) {
- dev->driver = NULL;
- goto ProbeFailed;
- }
只要probe存在则调用之。至此就完成了probe的调用。
这个过程链的关键还是在drv->bus->match ,因为其余的地方出错的话就是注册失败,而只要注册不失败且match返回1,那么就铁定会调用驱程的probe了。你可以注册一个总线类型和总线,并在 match中总是返回 1, 会发现,只要struct device_driver中的bus类型正确时,probe函数总是被调用.
---------------------------------------------------------------------------------------------------------------------------------------------------
有两个重要的链表挂在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匹配任何东西。
platform驱动之probe函数的更多相关文章
- 注册驱动时如何调用probe函数 ?
platform_driver_register driver_register bus_add_driver //把驱动放入总线的驱动链表里 ...
- Linux 驱动框架---platform驱动框架
Linux系统的驱动框架主要就是三个主要部分组成,驱动.总线.设备.现在常见的嵌入式SOC已经不是单纯的CPU的概念了,它们都会在片上集成很多外设电路,这些外设都挂接在SOC内部的总线上,不同与IIC ...
- probe函数何时调用的
转自:http://blog.csdn.net/xiafeng1113/article/details/8030248 Linux中 probe函数何时调用的 所以的驱动教程上都说:只有设备和驱动的名 ...
- linux中 probe函数的何时调用的?
点击打开链接 linux中 probe函数何时调用的 所以的驱动教程上都说:只有设备和驱动的名字匹配,BUS就会调用驱动的probe函数,但是有时我们要看看probe函数里面到底做了什么,还有传递给p ...
- (转)platform_driver_register,什么时候调用PROBE函数 注册后如何找到驱动匹配的设备
platform_driver_register,什么时候调用PROBE函数 注册后如何找到驱动匹配的设备 2011-10-24 19:47:07 分类: LINUX kernel_init中d ...
- Linux Platform驱动模型(一)-设备信息
我在Linux字符设备驱动框架一文中简单介绍了Linux字符设备编程模型,在那个模型中,只要应用程序open()了相应的设备文件,就可以使用ioctl通过驱动程序来控制我们的硬件,这种模型直观,但是从 ...
- Linux Platform驱动模型(二) _驱动方法
在Linux设备树语法详解和Linux Platform驱动模型(一) _设备信息中我们讨论了设备信息的写法,本文主要讨论平台总线中另外一部分-驱动方法,将试图回答下面几个问题: 如何填充platfo ...
- platform驱动分离
目录 platform驱动分离 框架结构 与输入子系统联系 设备描述 驱动算法 注册机制 程序 测试 platform驱动分离 框架结构 与输入子系统联系 设备描述 驱动算法 注册机制 程序 测试 - ...
- Linux Platform驱动模型(一) _设备信息
我在Linux字符设备驱动框架一文中简单介绍了Linux字符设备编程模型,在那个模型中,只要应用程序open()了相应的设备文件,就可以使用ioctl通过驱动程序来控制我们的硬件,这种模型直观,但是从 ...
随机推荐
- d3.js学习笔记(五)——将数据结构化为D3.js可处理的
目标 在这一章,你将会理解如何对数据进行结构化,来更好的使用D3.js. 我们将会回顾我们之前已经学习的,学习D3.js如何使用选集(selections),JavaScript对象基础,以及如何最优 ...
- Django Celery Redis 异步执行任务demo实例
一.windows中安装redis 安装过程见 <在windows x64上部署使用Redis> 二.环境准备 requirements.txt Django==1.10.5 celery ...
- Linux:配置Linux网络和克隆虚拟机并更改配置
Linux学习笔记1:配置Linux网络和克隆虚拟机并更改配置 一.配置Linux网络 在安装Linux的时候,一定要保证你的物理网络的IP是手动设置的,要不然会在Linux设置IP连通网络的时候 ...
- iptables(三)iptables规则管理(增、删、改)
上一篇文章中,我们已经学会了怎样使用iptables命令查看规则,那么这篇文章我们就来总结一下,怎样管理规则. 之前,我们把查看iptables规则的操作比作"增删改查"当中的&q ...
- Python之paramiko
一.基础 paramiko是用python语言写的一个模块,遵循SSH2协议,支持以加密和认证的方式,进行远程服务器的连接.paramiko支持Linux, Solaris, BSD, MacOS X ...
- 【MAF】MAF插件框架简介
引言 Managed Add-In Framework是一个插件式框架.它有两大作用,一个是解耦,插件和宿主由7个管道组成,两边都有适配器Adapter管道,能最大程度地降低插件和宿主的耦合度: ...
- USB转串口WIN8驱动安装
http://jingyan.baidu.com/article/11c17a2c0bb606f446e39da0.html //查看百度经验 http://jingyan.baidu.com/ar ...
- GDB和Core Dump使用笔记
一.GNU C编译器(即GCC) GCC使用 1 .gcc -g hello.c -o hello 编译生成可执行文件 2.gdb hello 启动GDB ...
- RIPng 知识要点
RIPng --------------------------------------------------------- UDP:521 多播地址:FF02::9 -------------- ...
- I.MX6 ethtool 移植
/************************************************************************* * I.MX6 ethtool 移植 * 说明: ...