i2c设备与驱动注册

i2c设备与驱动

图片不好上传,可以跟着源码去看,是mt6735平台下的

也可以去看有道云链接:点击打开链接

前言:i2c-core.c为具体的i2c外设提供了统一的设备注册接口和驱动注册接口,它分离了设备驱动device driver和硬件控制的实现细节;设备与驱动的对应关系是多对一的;即如果设备类型是一样的,会共用同一套驱动。

设备注册:
将i2c控制器设备注册为platform设备,为每一个控制器定义一个struct platform _device数据结构,然后调用platform _device_register()将设备注册到platform bus上。
驱动注册:
步骤和设备注册的步骤类似,也是为驱动定义了一个struct platform_driver数据结构,然后调用platform _driver _r e gister()将驱动注册到platform bus上。

i2c设备驱动
驱动注册:
首先调用i2c_register_driver()函数
路径:kernel-3.18/drivers/i2c/i2c-core.c

调用driver_register函数:调用了bus_add_driver函数,将driver添加到总线上 路径:kernel-3.18/drivers/base/driver.c

在bus_add_driver函数里面调用了driver_attach()函数,
路径:kernel-3.18/drivers/base/dd.c


从上图中可以看出__driver_attach()函数中调用了driver_match_device()函数和driver_probe_device()函数,可以分为两路去看这两个函数,先看driver_match_device()
路径:kernel-3.18/drivers/base/base.h

很简单,如果driver->bus->match不为null,就执行driver->bus->match函数,这里的match函数执行的是i2c_device_match函数
路径:kernel-3.18/drivers/i2c/i2c-core.c
driver->driver.bus = &i2c_bus_type;

通过i2c_match_id函数:

只匹配id的名字和client的名字,跟驱动的名字没有关系,注意这里的client是设备转换过来,而不是设备的本身!!!


再回过头来看driver_probe_device函数
路径:kernel-3.18/drivers/base/dd.c

really_probe函数:
从图中可以看出会优先调用i2c_device_probe函数
在i2c_device_probe函数中最终会调用驱动中的probe函数:
路径:kernel-3.18/drivers/i2c/i2c-core.c
这里印证了当设备和驱动函数匹配之后会调用probe函数,并且加载到驱动程序里面的probe函数






设备注册:
i2c在添加注册设备的时候有两种方式,一种是静态注册,还有一种是动态注册;
静态注册 如图中所示:
采用i2c_register_board_info()函数,将所需要的I2C从设备加入一个名为__i2c_board_list双向循环链表;系统在成功加载I2C主设备adapt后,这张链表里所有I2C从设备逐一地完成 i2c_client的注册:
i2c_register_adapter() -> i2c_scan_static_board_info() -> i2c_new_device() -> device_register()

先看i2c_register_board_info()函数,如下图,__i2c_frist_dynamic_bus_num表示当前系统允许的动态总线最大值;使用kzalloc函数给设备结构分配一片空间;
总的来说,在系统初始化的过程中,我们可以通过 i2c_register_board_info,将所需要的I2C从设备加入一个名为__i2c_board_list双向循环链表,系统在成功加载I2C主设备adapt后,就会对这张链表里所有I2C从设备逐一地完成 i2c_client的注册。

系统初始化的时候,会根据板级(board)i2c设备配置信息,创建i2c客户端设备(i2c_client),添加到i2c子系统中
路径:kernel-3.18/drivers/i2c/i2c-core.c
list_for_each_entry函数会去比遍历I2C从设备组成的双向循环链表(即__i2c_board_list双向循环链表),并调用i2c_new_device函数完成所有I2C从设备的i2c_client的注册。



动态注册
i2c_register_driver() -> __process_new_driver() -> i2c_do_add_adapter() - > i2c_detect() -> i2c_new_device() -> device_register()
对遍历得到的每一个device执行__process_new_driver() ,通过 i2c_detect() 检测是否有合适的设备挂在总线上,若有,则调用i2c_new_device()注册设备。

i2c_register_driver()函数在上面注册驱动的时候已经提到过了,但是在这里我们要从另外一边去分析

i2c_for_each_dev()函数遍历i2c总线上的klist_devices链表,对得到的每一个device,执行__process_new_driver

i2c_do_add_adapter()函数调用i2c_detect()函数

i2c_detect实现了i2c设备发现:在注册驱动后,通过i2c_detect检测是否有适合的设备连接在总线上
i2c_detect实现如下:
在每一个adapter上遍历驱动给出的地址列表(address_list),由i2c_detect_address函数完成;最终会调用driver->detect(即设备驱动提供的设备发现函数);如果发现满足条件的设备,执行i2c_new_device,为设备建立i2c_client;

通过上面两种方式都可以执行到i2c_new_device,那么我们接着i2c_new_device往下分析:
在此处,重点关注红色标注处

先看device_register函数:

在device_add函数中:
路径:kernel-3.18/drivers/base/bus.c
bus_add_device函数很关键,这个函数是driver_register中核心函数,真正的功能实现都在这个函数里面,蓝色字体为函数功能说明

bus_probe_device函数主要功能是为新设备探测即匹配驱动,主要执行device_attach函数,如下:

路径:kernel-3.18/drivers/base/dd.c
在device_attach函数中,遍历bus上所有的driver,并且调用__device_attach函数,如下:

在__device_attach函数中,如果driver和device匹配(match)成功,就会调用driver_probe_device函数,如下:

好了,到这里后面就不用分析了,__device_attach()函数和__driver_attach()函数加载了两个相同的函数,流程和驱动的注册是一样的。

猜你喜欢

转载自blog.csdn.net/qq_40658985/article/details/80527009