static struct i2c_board_info i2c0_devices[] = { { I2C_BOARD_INFO(\ }, { I2C_BOARD_INFO(\ }, { I2C_BOARD_INFO(\ .irq = intcs_evt2irq(0x3380), /* IRQ28 */ }, { I2C_BOARD_INFO(\ .irq = intcs_evt2irq(0x3340), /* IRQ26 */ }, }; ... i2c_register_board_info(0, i2c0_devices, ARRAY_SIZE(i2c0_devices)); 这样ADXL34X的i2c设备就被注册到了系统中,当名字与i2c_driver中的id_table中的成员匹配时就能够出发probe匹配函数了。
Linux I2C设备驱动编写(二) 在(一)中简述了Linux I2C子系统的三个主要成员i2c_adapter、i2c_driver、i2c_client。三者的关系也在上一节进行了描述。应该已经算是对Linux I2C子系统有了初步的了解。下面再对他们之间的关系进行代码层的深入分析,我认为对他们的关系了解的越好,越有助于I2C设备的驱动开发及调试。
带着问题去分析可能会更有帮助吧,通过对(一)的了解后,可能会产生以下的几点疑问:
? ?
i2c_adapter驱动如何添加?
i2c_client与i2c_board_info究竟是什么关系?
I2C对外API
在解答问题前,不妨先缕顺一下Linux内核的I2C子系统对驱动模块的API有哪些。(来自https://www.kernel.org/doc/htmldocs/device-drivers/i2c.html)
// 对外数据结构 struct i2c_driver — 代表一个I2C设备驱动 struct i2c_client — 代表一个I2C从设备 struct i2c_board_info — 从设备创建的模版 I2C_BOARD_INFO — 创建I2C设备的宏,包含名字和地址 struct i2c_algorithm — 代表I2C传输方法 struct i2c_bus_recovery_info — I2C总线恢复信息?内核新加入的结构,不是很清楚。 //对外函数操作 module_i2c_driver — 注册I2C设备驱动的宏定义 i2c_register_board_info — 静态声明(注册)I2C设备,可多个 i2c_verify_client — 如果设备是i2c_client的dev成员则返回其父指针,否则返回NULL。用来校验设备是否为I2C设备 i2c_lock_adapter — I2C总线持锁操作,会找到最根源的那个i2c_adapter。说明你的模块必须符合GPL协议才可以使用这个接口。后边以GPL代表。 i2c_unlock_adapter — 上一个的反操作,GPL i2c_new_device — 由i2c_board_info信息声明一个i2c设备(client),GPL i2c_unregister_device — 上一个的反操作,GPL。 i2c_new_dummy — 声明一个名为dummy(指定地址)的I2C设备,GPL i2c_verify_adapter — 验证是否是i2c_adapter i2c_add_adapter — 声明I2C适配器,系统动态分配总线号。 i2c_add_numbered_adapter — 同样是声明I2C适配器,但是指定了总线号,GPL i2c_del_adapter — 卸载I2C适配器 i2c_del_driver — 卸载I2C设备驱动 i2c_use_client — i2c_client引用数+1 i2c_release_client — i2c_client引用数-1 __i2c_transfer — 没有自动持锁(adapter lock)的I2C传输接口 i2c_transfer — 自动持锁的I2C传输接口 i2c_master_send — 单条消息发送 i2c_master_recv — 单条消息接收 i2c_smbus_read_byte — SMBus “receive byte” protocol i2c_smbus_write_byte — SMBus “send byte” protocol i2c_smbus_read_byte_data — SMBus “read byte” protocol i2c_smbus_write_byte_data — SMBus “write byte” protocol i2c_smbus_read_word_data — SMBus “read word” protocol i2c_smbus_write_word_data — SMBus “write word” protocol i2c_smbus_read_block_data — SMBus “block read” protocol i2c_smbus_write_block_data — SMBus “block write” protocol i2c_smbus_xfer — execute SMBus protocol operations (一)中对几个基本的结构体和宏定义也有了大概的解释,相信结合I2C的理论基础不难理解。对以上一些I2C的API进行分类:
NAdapter o. Driver Device(client) Transfer module_i2c_d1 i2c_add_adapter river i2c_register_boar__i2c_transfer d_info i2c_add_numbered_2 adapter i2c_del_driver i2c_new_device i2c_transfer 3 i2c_del_adapter i2c_new_dummy i2c_master_send 4 i2c_lock_adapter i2c_verify_client i2c_master_recv i2c_unregister_de5 i2c_unlock_adapter vice i2c_smbus_read_byte i2c_smbus_write_byt6 i2c_verify_adapter i2c_use_client e i2c_smbus_read_byte7 i2c_release_client _data i2c_smbus_write_byt8 e_data 9 i2c_smbus_read_worNAdapter o. Driver Device(client) Transfer d_data i2c_smbus_write_wor10 d_data i2c_smbus_read_bloc11 k_data i2c_smbus_write_blo12 ck_data 13 i2c_smbus_xfer 经过一个表格的整理,不难发现在Linux I2C子系统中,最重要的要数i2c_client,而最多样化的就是数据的传输。
为了更好的理解和衔接,我想也许倒着分析会更有帮助,而这里先暂且不讨论I2C传输过程中的细节。下边的顺序是由client到driver,再到adapter。
I2C client的注册
i2c_client即I2C设备的注册接口有三个:
i2c_register_board_info i2c_new_device i2c_new_dummy 而i2c_new_dummy在内部其实也就是将client的name指定为dummy后依旧执行的是i2c_new_device,所以就只分析前两个就可以了。首先看这两个函数的原型:
i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len) busnum 通过总线号指定这个(些)设备属于哪个总线 info i2c设备的数组集合 i2c_board_info格式 len 数组个数ARRAY_SIZE(info) i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) adap 此设备所依附的I2C适配器指针 info 此设备描述,i2c_board_info格式,bus_num成员是被忽略的 i2c_register_board_info具体实现 int __init i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len) { int status; down_write(&__i2c_board_lock); //i2c设备信息读写锁,锁写操作,其他只读 /* dynamic bus numbers will be assigned after the last static one */ if (busnum >= __i2c_first_dynamic_bus_num) //与动态分配的总线号相关,动态分配的总线号应该是从已经现有最大总线号基础上+1的,这样能够保证动态分配出的总线号与板级总线号不会产生冲突 __i2c_first_dynamic_bus_num = busnum + 1; for (status = 0; len; len--, info++) { //处理info数组中每个成员 struct i2c_devinfo *devinfo; devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL); if (!devinfo) { pr_debug(\ status = -ENOMEM; break; }