由 总线(bus_type) + 设备(device) + 驱动(device_driver) 组成,在该模型下,全部的设备经过总线链接起来,即便有些设备没有链接到一根物理总线上,linux为其设置了一个内部的、虚拟的platform总线,用以维持总线、驱动、设备的关系。node
所以,对于实现一个linux下的设备驱动,能够划分为两大步:linux
一、设备注册;算法
二、驱动注册。数据结构
固然,其中还有一些细节问题:架构
一、驱动的probe函数函数
二、驱动和设备是怎么进行绑定的。spa
i2c_adapter:指针
每个i2c_adapter对应一个物理上的i2c控制器,在i2c总线驱动probe函数中动态建立。经过i2c_add_adapter注册到i2c_core。code
i2c_algorithm:orm
i2c_algorithm中的关键函数master_xfer(),以i2c_msg为单位产生i2c访问须要的信号。不一样的平台所对应的master_xfer()是不一样的,须要根据所用平台的硬件特性实现本身的xxx_xfer()方法以填充i2c_algorithm的master_xfer指针;在A31上便是sun6i_i2c_algorithm函数。
i2c_client:
表明一个挂载到i2c总线上的i2c从设备,包含该设备所须要的数据:
该i2c从设备所依附的i2c控制器 struct i2c_adapter *adapter
该i2c从设备的驱动程序struct i2c_driver *driver
该i2c从设备的访问地址addr, name
该i2c从设备的名称name。
从硬件功能上可划分为:i2c控制器和i2c外设(从设备)。每一个i2c控制器总线上均可以挂载多个i2c外设。Linux中对i2c控制器和外设分开管理:经过 i2c-sun6i.c 文件完成了i2c控制器的设备注册和驱动注册;经过i2c-core.c为具体的i2c外设提供了统一的设备注册接口和驱动注册接口,它分离了设备驱动device driver和硬件控制的实现细节(如操做i2c的寄存器)。
该文件是与具体硬件平台相关的,对应于A3x系列芯片。该文件其实是i2c总线驱动的实现,本质上就是向内核注册i2c总线设备、注册总线驱动、实现总线传输的时序控制算法。i2c控制器被注册为Platform设备,以下:
if (twi_used_mask & TWI0_USED_MASK) platform_device_register(&sun6i_twi0_device); if (twi_used_mask & TWI1_USED_MASK) platform_device_register(&sun6i_twi1_device); if (twi_used_mask & TWI2_USED_MASK) platform_device_register(&sun6i_twi2_device); if (twi_used_mask & TWI3_USED_MASK) platform_device_register(&sun6i_twi3_device); if (twi_used_mask) return platform_driver_register(&sun6i_i2c_driver);
须要注意的是:设备与驱动的对应关系是多对一的;即若是设备类型是同样的,会共用同一套驱动,所以上面代码只是注册了一次驱动platform_driver_register(&sun6i_i2c_driver)。
设备注册:
将i2c控制器设备注册为platform设备,为每个控制器定义一个struct platform_device数据结构,而且把.name都设置为"sun6i-i2c"(后面会经过名字进行匹配驱动的),而后是调用platform_device_register()将设备注册到platform bus上。
设备注册完成后其直观的表现就是在文件系统下出现:/sys/bus/platform/devices/sun6i-i2c.0
经过platform_device_register()进行的注册过程,说到底就是对struct platform_device这个数据结构的更改,逐步完成.dev.parent、.dev.kobj、.dev.bus的赋值,而后将.dev.kobj加入到platform_bus->kobj的链表上。
驱动注册:
步骤和设备注册的步骤相似,也是为驱动定义了一个数据结构:
struct platform_driver sun6i_i2c_driver;
由于一个驱动是能够对应多个设备的,而在系统里的3个控制器基本上是一致的(区别就是寄存器的地址不同),因此上面注册的3个设备共享的是同一套驱动。
初始化.probe和.remove函数,而后调用platform_driver_register进行驱动注册。主要函数调用流程:
platform_driver_register --> driver_register --> bus_add_driver --> driver_attach
须要注意的是driver_attach,这个函数遍历了总线上(platform_bus_type)的全部设备,寻找与驱动匹配的设备,并把知足条件的设备结构体上的驱动指针指向驱动,从而完成了驱动和设备的匹配(__driver_attach函数完成)。
若是匹配到设备,这时就须要执行platform_bus_type的probe函数,最终会调用设备驱动的probe函数(sun6i_i2c_probe)。
在sun6i_i2c_probe函数中完成了大量的工做,包括硬件初始化、中断注册、为每一个i2c控制器建立i2c_adapter等。
1268 pdata = pdev->dev.platform_data; 1269 if (pdata == NULL) { 1270 return -ENODEV; 1271 } 1272 1273 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1274 irq = platform_get_irq(pdev, 0); 1275 if (res == NULL || irq < 0) { 1276 return -ENODEV; 1277 } 1278 1279 if (!request_mem_region(res->start, resource_size(res), res->name)) { 1280 return -ENOMEM; 1281 }
1288 1289 strlcpy(i2c->adap.name, "sun6i-i2c", sizeof(i2c->adap.name)); 1290 i2c->adap.owner = THIS_MODULE; 1291 i2c->adap.nr = pdata->bus_num; 1292 i2c->adap.retries = 3; 1293 i2c->adap.timeout = 5*HZ; 1294 i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; 1295 i2c->bus_freq = pdata->frequency; 1296 i2c->irq = irq; 1297 i2c->bus_num = pdata->bus_num; 1298 i2c->status = I2C_XFER_IDLE; 1299 i2c->suspended = 0; 1300 spin_lock_init(&i2c->lock); 1301 init_waitqueue_head(&i2c->wait);
至此,probe函数完成。
i2c控制器的中断服务程序sun6i_i2c_handler调用了sun6i_i2c_core_process,i2c总线的实际传输控制也是在该函数里完成的。
主要流程:
每个i2c控制器设备,在驱动绑定后,都会建立一个i2c_adapter,用以描述该控制器,i2c_adapter的创建与初始化是在驱动probe的时候创建的。每个i2c_adapter包含了一个i2c_algorithm结构体的指针,i2c_algorithm是用来对外提供操做i2c控制器的函数接口的,主要是master_xfer函数,对应于i2c-sun6i.c,实际就是:
static int sun6i_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
该函数的功能是通知i2c_adapter须要对外设进行数据交换,须要交换的信息经过struct i2c_msg *msgs传入。sun6i_i2c_xfer其实是调用了sun6i_i2c_do_xfer进行传输。
由于i2c总线读写速率有限,sun6i_i2c_do_xfer启动i2c传输后,经过wait_event_timeout进入休眠,直到中断唤醒或者超时;中断唤醒是由sun6i_i2c_xfer_complete完成的。
i2c从设备的驱动注册,使用的是i2c-core.c提供的接口:i2c_register_driver;其调用以下:
i2c_register_driver --> driver_register --> bus_add_driver;
对bus_add_driver进行分析:
设备驱动模型是经过kobject对设备驱动进行层次管理的,所以device_driver应该包含kobject成员,linux是将kobject包含在struct driver_private中,再在device_driver中包含struct driver_private;咱们能够理解driver_private是device_driver的私有数据,由内核进行操做。
struct driver_private 是在驱动注册的开始,动态申请,并初始化的。
初始化设备链表,每个与该驱动匹配的device都会添加到该链表下。
指定该驱动所属的kset;
初始化kobject,并将kobject添加到其对应的kset集合中(即bus->p->drivers_kset)。
该函数最终是调用kobject_add_internal将kobject添加到对应的kset中;须要主要的是,若是kobject的parent若是为NULL,在此会将其parent设置为所属kset集合的kobject:
parent = kobject_get(&kobj->kset->kobj);
接下来是为kobject建立文件夹:create_dir(kobj);从而能从/sys/目录下显示。
将遍历总线上的设备链表,查找能够匹配的设备,并绑定。
driver_attach --> bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
将函数指针__driver_attach传入bus_for_each_dev,将每一个查找获得的device进行驱动匹配。
bus_for_each_dev:
遍历总线上的全部设备,由于总线上的设备都是bus->p->klist_devices链表上的一个节点,所以该函数其实就是对链表的遍历,具体能够参考klist。
__driver_attach(源码位置drivers/base/dd.c):
进行设备和驱动匹配,若是匹配成功,尝试进行绑定。
1. 首先进行匹配确认:driver_match_device(drv, dev);
调用关系: --> drv->bus->match --> i2c_device_match
--> of_driver_match_device
i2c_match_id
能够看出,最终有两种方式进行驱动匹配查询:
方法一:经过of_driver_match_device对比of_device_id;
方法二:经过i2c_match_id对比id_table;
方法二实际上就是对比
i2c_driver->id_table->name 和client->name是否一致。
2. 若是匹配确认,进行驱动与设备绑定:driver_probe_device;
调用关系: driver_probe_device --> really_probe
--> dev->bus->probe
driver_bound
在really_probe中,首先将设备的驱动指针指向该驱动:dev->driver = drv。
对应于i2c_bus_type,dev->bus->probe 便是:i2c_device_probe,最终调用驱动的probe函数。
最后是driver_bound,将驱动与设备进行绑定:
其实就是调用klist_add_tail:将设备节点添加到驱动的klist_devices;
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
在sysfs建立drivers目录
方式一:i2c设备动态发现注册
在i2c_register_driver的最后:
INIT_LIST_HEAD(&driver->clients); /* Walk the adapters that are already present */ i2c_for_each_dev(driver, __process_new_driver);
观察i2c_for_each_dev:
int i2c_for_each_dev(void *data, int (*fn)(struct device *, void *)) { int res; mutex_lock(&core_lock); res = bus_for_each_dev(&i2c_bus_type, NULL, data, fn); mutex_unlock(&core_lock); return res; }
其实就是遍历i2c总线上的klist_devices链表,对获得的每个device,执行__process_new_driver。
跟踪 __process_new_driver --> i2c_do_add_adapter --> i2c_detect
i2c_detect实现了i2c设备发现:在注册驱动后,经过i2c_detect检测是否有适合的设备链接在总线上。i2c_detect实现以下:
方式二:i2c设备之静态注册
Linux 3.3 提供了静态定义的方式来注册设备,接口原型:linux-3.3/drivers/i2c/i2c-boardinfo.c
int __init i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len)
核心内容:
扫描__i2c_board_list,建立client
i2c_register_board_info只是把设备描述符加入到了__i2c_board_list,并无建立client,当调用i2c_register_adapter注册adapter时,会扫描__i2c_board_list,建立client;具体调用:
i2c_register_adapter
--> i2c_scan_static_board_info
--> i2c_new_device
--> device_register
在 i2c_new_device完成了client建立,以及设备注册device_register。
PS:
由上面的注册流程可知,i2c_register_board_info应该在i2c_register_adapter以前完成,不然__i2c_board_list中的节点不会被扫描到。
总结:
一、经过i2c_register_board_info,在系统启动之初静态地进行i2c设备注册(axp电源驱动就是这样作的);
二、实现i2c设备驱动的detect函数,在驱动加载的时候动态检测建立设备,aw平台的触摸屏驱动gt82x.ko就是经过这种方式。
一、i2c_add_adapter
二、i2c_new_device/i2c_register_board_info
三、i2c_add_driver
四、调用i2c bus中注册的match函数进行匹配
五、调用platform bus中注册的match函数进行匹配
六、i2cdev_attach_adapter