Jimmy Chen

A Programmer

(原创)Linux下I2C框架分析

  在上一篇I2C协议的简单介绍后,我们马上结合Linux源码来了解下Linux中的I2C框架是如何的。

1. 基本框架了解

《(原创)Linux下I2C框架分析》

  如上图显示的,要讨论的东西包括driver、client、i2c-dev、i2c-core、Algorithm和adapter。在上层的位置中,Client可以简单的理解为具体的物理设备,而driver就是驱动物理设备的驱动程序,i2c-dev则是为不同的client设备提供统一访问接口的作用。i2c-core在中间的位置,起到承上启下的作用,为上层驱动提供client与driver匹配接口以及上层driver和下层的adapter匹配接口,对下层起到将adapter添加到内核中以及adapter和Algorithm进行匹配的作用。下层中的adapter可以理解为芯片内的I2C驱动器,正是因为不同的芯片拥有不同的架构,而且其数据传输适中要求和芯片引脚不同,所以需要搭配不同的Algorithm来使用。

2. 几个重要的结构体

2.1 i2c_driver

2.2 i2c_client

如上面的英文注解,addr的底七位记录client的地址,adapter记录client依附的adapter,而driver记录client使用的设备驱动i2c_driver。

2.3 i2c_algorithm

2.4 i2c_adapter

2.5 i2c_msg

2.6 总结

  上面给出了5个常用到的结构体,i2c_msg用于I2C通信的消息传递,这里主要总结下i2c_client、i2c_driver、i2c_adapter和i2c_algorithm之间的关系。

i2c_driver和i2c_client的关系

  正如之前所说的,i2c_client代表的是设备上具体的物理设备,例如EEPROM。那么在i2c_client的结构体内存在这i2c_adapter,主要是用于记录client所依附的adapter,当对设备读写时也就能找到对应的adapter以及读写设备的Algorithm了,其次还有一个i2c_driver,主要用于记录该client所使用的i2c_driver。i2c_driver是一个抽象的驱动,不对应任何的物理实体内容。博主目前认为如此设计,主要可能是因为driver和client是一对多的关系,即i2c_client对应的物理设备所使用驱动方法可能是相同的,那么抽象出来就能够服用了。i2c_driver内有一个client链表,就是用来记录以来该抽象i2c_driver的client的。同时i2c_driver提供了client用于注册到和注销相应adapter的方法。

  按照这里的说法,在添加新的设备时,肯定是需要填写对应的i2c_client结构体的,至于i2c_driver结构体,如果没有适合的i2c_driver可以复用的话,那么也是需要工程师进行添加的。

i2c_adapter和i2c_algorithm的关系

  i2c_adapter对应的是物理上的适配器,一般这个适配器会集成在芯片内,而i2c_algorithm对应的是一套通信的方法。i2c_algorithm就是为特定的i2c_adapter提供通信方法的,如果没有为i2c_adapter提供对应的i2c_algorithm的话,那么这个i2c_adapter就什么都做不了。所以我们会发现在i2c_adapter结构体内会存在一个记录i2c_algorithm的指针。而i2c_algorithm中的master_xfer和smbus_xfer指针就是记录对应的通信方法的。在执行I2C通信的时候会调用到。

  那么对于工程师来说,如果如果没有提供对应的适配器方法就需要自己填写i2c_adapter结构体,既然适配器的内容是自行添加的,那么对应的i2c通信方法也需要根据具体的时序和芯片手册标注的寄存器进行编程。这一部分也是需要工程师干预的。

i2c_adapter和i2c_client的关系

  结合上面介绍的内容,很容易就能理解i2c_adapter和i2c_client之间的关系了,无非就是物理适配器和物理设备之间的对应关系。i2c_client要依附于i2c_adapter。由于一个适配器上可以连接多个I2C设备,所以一个i2c_adpater也可以被多个i2c_client依附,i2c_adpater中包括依附于它的i2c_client的链表。

所以,可能需要工程师干预的部分如下:

  • 根据I2C适配器的特性,提供I2C适配器的硬件驱动、探测和初始化适配器(如申请I2C的I/O地址和中断号)、驱动CPU控制的I2C适配器从硬件上产生各种信息以及处理I2C中断
  • 提供与I2C适配器协同运作的Algorithm,用具体适配器的xxx_xfer()方法填充i2c_algorithm的master_xfer指针,并把i2c_algorithm指针赋值给i2c_adapter的alog指针
  • 实现I2C设备的i2c_driver接口,使用具体xxx_attach_adapter()函数指针、xxx_detach_client()函数指针和xxx_command()函数指针赋给i2c_driver的对应指针域
  • 实现具体I2C设备的文件操作接口,如open、write、read接口。

上面说到的前两点和后两点是可以分开进行的。毕竟可以将i2c_adapter可看做是一个驱动来向内核注册。

《(原创)Linux下I2C框架分析》

I2C驱动中个结构体之间的关系

3. i2c-core简单分析

  i2c-core中存在着许多有用的函数,所以这里只列出部分函数的代码,有兴趣的可以自行查看i2c-core.c文件进行查看。

3.1 i2c_device_match

  i2c_device_match就是用来匹配设备device和设备driver的函数,看上面的代码可以知道,匹配过程首先尝试通过设备树的方法,如果设备树的方式匹配失败了在调用i2c_match_id函数进行匹配,i2c_match_id方法是通过名字进行匹配的。所以,我们编写的i2c_client和i2c_driver结构体的时候,其名字字段必须是相同的。

3.2 i2c_add_adapter

  i2c_add_adapter会调用到i2c_register_adapter进行adapter添加。那么i2c_del_adapter是完成的工作理应和上面的相反的,这里就不列出i2c_del_adapter的代码了。

3.3 i2c_register_driver

3.4 i2c_master_send && i2c_master_recv

  这里的i2c_master_xxxx操作都是先填充对应的i2c_msg结构体的内容,然后调用i2c_transfer进行实际的通信操作。同时应该注意到,i2c_master_send和i2c_master_recv方法只能够发送或接收一条i2c_msg信息,如果要一次发送或读取多条i2c_msg信息的话,就需要另寻方法了。

3.5 i2c_transfer

i2c-core.c文件中还有很多函数方法,这里就不一一列出了,毕竟暂时能力有限,理解不对反而祸害了读者,哈哈!

4. i2c-dev.c简单分析

  i2c-dev.c可以看做一个I2C设备驱动。在我看来,i2c-dev是为软件访问I2C设备提供统一的接口。这样,我们的i2c_driver就不用编写过多的read、write方法了。可以通过i2c-dev内的方法,加上i2c_adapter中的algorithm,就能完成I2C的传输。

下面稍微看几个i2c-dev中的函数

4.1 i2cdev_read

  上面可以看出来,i2cdev_read也是会调用到i2c-core中的方法。而且看i2cdev_read中的代码就能感觉到该方法是通用的。

4.2 i2cdev_write

  i2cdev_write方法和read方法基本类似。

当然还有IOCTL方法了,这里就不贴代码了,大家想了解的还是看看源码比较好。之后找个机会和时间实际使用i2c-dev中的方法来编写一个应用来访问I2C好了,那样应该能够更好的理解和使用i2c-dev的作用。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注