1.驱动注册
以i2c驱动注册为例分析驱动注册的代码,弄清楚两点东西
(1)都知道对应的驱动和设备匹配上了会跑它们总线bus_type 的match函数,但这是什么个逻辑呢?
(2)驱动和设备匹配上了跑驱动的probe()函数,传进去的形参是怎么来的?
分析前先了解一下内核里的一个链表数据结构
(1)struct klist; //链表,里面包括一个自旋锁,一个链表,两个设置链表节点的回调
(2)struct klist_node; //链表节点,里面包括一个链表用来连接前后节点; 一个void 变量用于指向struct klist,这个从后面代码可知; 而struct kref 结构体里面其实就是个原子量,充当标志位;
(3)struct klist_iter; //这个结构体是一个链表的包装,里面有个链表struct klist,以及一个链表节点指向当前链表节点;
下面开始分析代码,
--- module_i2c_driver(mt6370_pmu); //带着i2c驱动实例去跑驱动注册函数
关注点,
(1)给i2c_driver里的device_driver 进行初始化,赋值了.owner 和 .bus,其中.bus就是device_driver里的bus_type,也即是i2c总线的bus_type,里面定义了设备与驱动的match函数,通过设备树来匹配的方法就写在这里;
(2)驱动注册,这也是要具体分析的点,这里记一下参数,是i2c_driver的device_driver;
(3)i2c_for_each_dev(driver, __process_new_driver); 和i2c_adapter的注册相关的
下面重点分析一下(2)driver_register(&driver->driver)、(3)i2c_for_each_dev(driver, __process_new_driver)
--- driver_register(&driver→driver);
(1)第一个if ,if (!drv->bus->p),是判断bus_type o不ok,我们看一下这个bus->p 是个什么 bus->p 就是 struct subsys_private 的变量,看到里面有一个设备的链表和驱动的链表,说明bus里面是有设备和驱动的链表的,猜想是不是设备和驱动注册的时候都会挂在这里,以及一个bus_type用来保存bus→p里的这个bus,那得到bus和 p任何一个都可以得到彼此了;
(2)ret = bus_add_driver(drv); 首先注意到前面很多的if,应该是为了排除一些错误的情况,就不想太多的看了,直接分析这个bus_add_driver 函数,函数的参数还是是i2c_driver的device_driver;
分析函数的前面部分,后面是驱动文件系统的创建就不想看了
(1)看到这里有个数据结构 struct driver_private *priv; 看名字是驱动的私有数据结构,那device_driver里面应该是有的,看看里面有什么
里面有个设备的链表和一个链表节点,这个链表节点的名字叫做knode_bus,看起来就是要挂在bus→p里的驱动链表上
(2)bus = bus_get(drv→bus); //获得bus_type
(3)priv = kzalloc(sizeof(*priv), GFP_KERNEL);
...
priv->driver = drv;
drv->p = priv;
priv->kobj.kset = bus->p→drivers_kset;
分配空间,和device_driver 互相保存;
(4)klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); 这个函数就是把priv里的链表节点加到bus→p里去
(5)if (drv->bus->p->drivers_autoprobe) {
...
error = driver_attach(drv);
if的前半部分什么异步probe,就直接看一下 driver_attach(drv); 这个关键函数吧,函数的参数还是是i2c_driver的device_driver;
--- driver_attach
跑bus_for_each_dev 函数,参数依次是bus_type,NULL,device_driver以及一个回调函数;
--- bus_for_each_dev
这个函数分析一下,
(1)struct klist_iter i; 定义了一个链表的包装,里面有个链表指针 *i_klist 和一个链表节点指针 *i_cur;
(2)klist_iter_init_node(&bus->p->klist_devices, &i,(start ? &start->p->knode_bus : NULL)); 这个函数是用来初始化klist_iter 的,也就是给它的*i_klist 和 *i_cur 赋值;
注意它的参数,第一个是bus→p里的设备链表 klist_devices,第二个就是要初始化的klist_iter *i,第三个是链表的开始节点,这里很明显是start = NULL,所以i→i_cur =NULL;
(3)while (!error && (dev = next_device(&i)))
error = fn(dev, data);
while循环的第一个条件必然为true,主要看他第二个条件,第二个条件是用来得到dev的,这个dev会是传递到驱动probe里的吗,他又是哪里来的呢;
代码逻辑是从klist_iter里的设备链表去得到一个链表节点,然后通过这个设备节点去得到设备的私有数据,而设备的私有数据和驱动的私有数据是一样的,可以知其一而得到另外一个,这里主要还是得看看链表节点怎么得到的;
--- klist_next前面这些就是定义了最近的节点last和下一个节点next,要是最近一个节点不为NULL,next就指向last的下一个节点,要是最近的节点为NULL,next就指向链表第一个节点然后进入while循环;
while循环遍历整个链表至链表头,直到找到对应的节点后退出循环同时用i→i_cur指向它;
这样我们就可以回到bus_for_each_dev 函数,next_device 函数就是得到了bus→p里面设备链表中合适的设备节点,我们看看得到了dev后做了什么
拿着得到的dev和data(也就是device_driver)跑了fn回调函数(就是前面的__driver_attach函数),那这个函数应该就是要绑定驱动和设备了,绑定就是要把设备dev作为驱动probe函数的参数然后跑probe;
这里看一下klist_iter_exit(&i);对之前选中的设备节点执行设备链表的put函数,也就是删除这个设备的,因为它匹配了,当然可能不是物理删除,只是改了标志位让他不再参与匹配;
---__driver_attach
(1)ret = driver_match_device(drv, dev); 这个就是跑对应总线的bus_type里的match函数;
(2)然后根据(1)的匹配返回值来执行“重新匹配”(返回-EPROBE_DEFER)、匹配失败(小于0)、匹配成功(大于0)的操作;
(3)if (!dev->p->dead && !dev→driver)
driver_probe_device(drv, dev);
匹配成功后就跑driver_probe_device(drv, dev); 函数,条件是device 里的device_driver为空;
----driver_probe_device 参数是device_driver以及找到的dev
这里主要看一下really_repo 函数,参数是device_driver以及找到的dev
----really_probe这个函数很长,截取关键的部分,可以看到对应总线的bus_type 里有probe函数就会跑总线里的,没有就跑驱动的probe的;
要是一般的platform设备驱动到这里就好了,因为一般platform驱动的probe如下,
传个dev就o了;
但是我们看看i2c设备的probe函数,
这很显然是跑了i2c总线bus_type里的probe导致的,猜测i2c总线bus_type里的probe 里会去调驱动的probe,分析一下i2c_bus_type
看看i2c_device_probe 函数
---- i2c_device_probe 参数是找到合适的设备链表里的dev
这里都是用contain_of,通过dev得到i2c_client,通过dev→driver得到i2c_driver;
代码比较多,挑关键的看一下
看到会调用驱动的probe 函数,第一个参数是i2c_client,第二个参数是i2c驱动具体匹配的设备id,可以看一下这个函数
从这个驱动的匹配表(一个数组)里去根据名字来找到对应的匹配项,然后返回;
至此,第一部分驱动注册分析结束;