前言:著名的Linus Torvalds曾说:Read the fucking source code,对,就是酱紫,读懂源码,眼见为实。
上一篇,主要弄清楚了为什么系统一开始就存在platform总线这个过程!这篇我们来探究一下匹配过程,弄清楚为什么匹配成功后就会自动调用probe()函数【俗称:探测函数】,还是一样,不关注细枝末节,不要想着弄懂每一行代码的意思,还是走通这个流程。主要解决以下三个问题:
1.总线注册完成后会生成platform节点,代码哪里可以看出来
2.怎么匹配的
3.为什么匹配过后会自动调用probe()函数
一、platform_bus_init浅析
路径:1.kernel-3.18/drivers/base/platform.c
上一篇中,我们最终走到了platform_bus_init()这个函数,先来看这两个最主要的函数
1.device_register()
2.bus_register()
路径:2.kernel-3.18/drivers/base/core.c
这个函数写的很简洁,但是并不简单,具体的代码就不详细分析!
第一步初始化设备,设置dev结构中的各个变量等以及对kobj的相关操作
第二步添加设备是device_register的主要工作。
路径:3.kernel-3.18/drivers/base/platform.c
在bus_register(&platform_bus_type),可以看到传入的参数是platform_bus_type这个结构体,
在这个结构体中我们重点关注match这个函数!下文会分析匹配过程。
在这之前,我们先解答问题1
路径:4.kernel-3.18/drivers/base/bus.c
问题1.总线注册完成后会生成platform节点,代码哪里可以看出来
可以看出当调用bus_register()会生成名字为platform的总线,那么问题1就解决了
我们通过连接手机来查看一下上面的分析是否正确
注册完成后,会在/sys/bus/下生成platform节点
通过adb命令,从上图中可以看到,/sys/bus/下确实生成了platform,除了platform总线
还有i2c总线,usb总线等等。
另外,在platform总线下还生成了两个节点1.devices 2.drivers,到这里可以证明我们的
分析是正确的,
当然,我们还可以修改一下代码,在使用adb命令看看是否改变
重新编译刷机后,可以看到改变了
[ps:乱改名字会开不了机,卡在开机的某个流程,具体原因请思考吧]
到此我们的问题1就走通了!
二、匹配过程
问题2.怎么匹配的
问题3.为什么匹配成功后会自动调用probe()函数
首先以一个LCM的驱动来说,驱动注册的时候都会去调用platform_driver_register()这个函数去注册!
有图有真相!【以下是LCM驱动的初始化函数】
因此,匹配的过程就从platform_driver_register()这个函数下手!
路径:kernel-3.18/drivers/misc/mediatek/lcm/jd9365_hd720_dsi/jd9365_hd720_dsi.c
先总体看一下匹配过程!
路径:kernel-3.18/include/linux/platform_device.h
可以看到platform_driver_register()其实是一个宏,实际上调用的是
__platform_driver_register(drv, THIS_MODULE)这个函数
路径:kernel-3.18/drivers/base/platform.c
可以看到代码中drv->driver.bus = &platform_bus_type;
这个platform_bus_type很重要,因为最终会调用该结构体下的match函数进行匹配,我们来看一下
【ps:下文中会用到这里的分析,请注意】
永远记住【总线】的match函数才是最终的设备与驱动的匹配函数,成功后会调用驱动的
probe()函数
路径:kernel-3.18/drivers/base/driver.c
路径:kernel-3.18/drivers/base/bus.c
kernel-3.18/drivers/base/dd.c
接下来回去调用__driver_attach()函数
上面这段英文说的是:锁定设备并尝试着去绑定它【因为platform总线下会挂在很多设备,然后系统就会全部都匹配一次,看看能不能匹配上,匹配上了驱动和设备就绑定了】我们会在此抛出错误,并且总是返回0,因为我们需要不停的去尝试[让驱动]绑定设备,有一些驱动会返回错误,简单来说,就是不支持该设备【没法玩】
走到这个函数,我们的问题就明朗了,答案就在这段源码里!
可以看到首先会调用driver_match_device()去匹配,如果匹配失败了,就返回0,如果匹配成功,接着往下走,然后就会调用到driver_probe_device()函数,该函数实际上最终会调用驱动自身的probe()函数!
走到这里,我们已经解决了一个问题:为什么匹配成功后就会自动调用probe()函数!
可以分两路去跟源码,一路是继续跟踪driver_match_device()匹配过程,另一路是跟踪driver_probe_device()去看看最终是否调用自身的probe函数!
兵分两路之一 driver_match_device()
路径:kernel-3.18/drivers/base/base.h
可以看到这个函数非常简单,
如果drv->bus->match存在【不为null】,就返回drv->bus->match(dev, drv),否则返回1!
我们在上文分析中,有这样一段代码赋值:drv->driver.bus = &platform_bus_type;
而且platform_bus_type结构体里会把platform_match赋值match这个成员变量!
因此drv->bus->match是不为空的,所以会调用drv->bus->match(dev, drv)
也就是driver指向的bus总线的match函数,其实就是platform_match()这个函数!
路径:kernel-3.18/drivers/base/platform.c
/*通过驱动里定义了of_device_id项,则通过这一项来比对;*
if (of_driver_match_device(dev, drv))
return 1;
/*如果在平台驱动中定义了id_table项,则通过对比id_table来判断*/
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
/*通过对比平台设备名字和平台驱动名字来判断*/
return (strcmp(pdev->name, drv->name) == 0);
到此我们的匹配过程分析完毕!
兵分两路之二 driver_probe_device()
接下来继续看probe()的调用
到此,我们可以看到,在really_probe()函数中,先判断总线的probe()函数是否存在,如果存在【不为NULL】的话,直接调用总线的probe函数,否则才去调用驱动自身的probe()函数!
到此整个流程就分析完毕了,分析了驱动的匹配流程,其实还有设备的匹配流程没有分析,但是和驱动的流程是相似的!
Stay hungry,Stay foolish!
荆轲刺秦王