1.驱动框架
Linux内核也将 I2C 驱动框架分为三部分,符合 Linux 的驱动分离与分层的思想:
- ①、I2C 总线驱动,I2C 总线驱动就是 SOC 的 I2C 控制器驱动,也叫做 I2C 适配器驱动。
- ②、I2C设备驱动,I2C 设备驱动就是针对具体的 I2C 设备而编写的驱动。
- ②、I2C 核心,提供了IIC总线驱动与IIC设备驱动的注册、注销方法和与具体IIC控制器无关的代码,该部分用来管理IIC总线驱动与IIC设备驱动。
2.数据结构
(1)i2c_bus_type:
I2C总线对应着/bus下的一条总线,这个i2c总线结构体管理着i2c设备与I2C驱动的匹配,删除等操作,I2C总线会调用i2c_device_match函数看I2C设备和I2C驱动是否匹配,如果匹配就调用i2c_device_probe函数,进而调用I2C驱动的probe函数。
注意:i2c_device_match会管理I2C设备和I2C总线匹配规则,这将和如何编写I2C驱动程序息息相关。
(2)i2c_adapter:
描述一个适配器(IIC控制器),用于向IIC核心注册。设备数据结构i2c_client中的一个对象指向i2c_adapter,这样就可以通过其中的方法(i2c_adapter内部的i2c_algorithm对象)以及i2c物理控制器来和一个i2c总线的物理设备进行交互。
(3)i2c_algorithm:
描述一个适配器的通信方法,用于产生I 2 C访问周期需要的信号,该类的对象algo是i2c_adapter的一个域,其中的master_xfer()注册的函数最终被设备驱动端的i2c_transfer()回调。在构建i2c_adapter数据结构时要填充这个数据结构,否则i2c_adapter什么也做不了。
(4)i2c_client:
描述一个挂接在硬件i2c总线上的设备的设备信息,即i2c设备的设备对象,与i2c_driver对象匹配成功后通过detected和i2c_driver相连。一般通过i2c_new_device()创建。
(5)i2c_driver:
描述一个挂接在硬件i2c总线上的设备的驱动方法,即i2c设备的驱动对象,通过i2c_bus_type和设备信息i2c_client匹配,匹配成功后通过clients和i2c_client对象相连。
(6)i2c_msg:
描述一个在设备端和主机端之间进行流动的数据,在设备驱动中打包并通过i2c_transfer()发送。
2.1 I2C总线驱动中数据结构:i2c_adapter 和 i2c_algorithm
I2C 总线驱动重点是 I2C 适配器(也就是 SOC 的 I2C 接口控制器)驱动,这里要用到两个重要的数据结构:i2c_adapter 和 i2c_algorithm,Linux 内核将 SOC 的 I2C 适配器(控制器)抽象成 i2c_adapter,i2c_adapter 结构体定义在 include/linux/i2c.h 文件中,结构体内容如下:
498 struct i2c_adapter {
499 struct module *owner; //所有者
500 unsigned int class; /*适配器支持的从设备类型 */
501 const struct i2c_algorithm *algo; /* 适配器的通信方法,也就是上面的i2c_algorithm */
502 void *algo_data;
503
504 /* data fields that are valid for all devices */
505 struct rt_mutex bus_lock;
506
507 int timeout; /* 超时时间 */
508 int retries;
509 struct device dev; /* 表明这是一个设备,挂载在I2C总线上 */
510
511 int nr; //适配器(总线)的编号
512 char name[48]; //适配器name
513 struct completion dev_released;
514
515 struct mutex userspace_clients_lock;
516 struct list_head userspace_clients;
517
518 struct i2c_bus_recovery_info *bus_recovery_info;
519 const struct i2c_adapter_quirks *quirks;
520 };
第 501 行,i2c_algorithm 类型的指针变量 algo,对于一个 I2C 适配器,肯定要对外提供读写 API 函数,设备驱动程序可以使用这些 API 函数来完成读写操作。i2c_algorithm 就是 I2C 适配器与 IIC 设备进行通信的方法。
i2c_algorithm 结构体定义在 include/linux/i2c.h 文件中,内容如下(删除条件编译):
391 struct i2c_algorithm {
......
398 int (*master_xfer)(struct i2c_adapter *adap,
struct i2c_msg *msgs,
399 int num);
400 int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
401 unsigned short flags, char read_write,
402 u8 command, int size, union i2c_smbus_data *data);
403
404 /* To determine what the adapter supports */
405 u32 (*functionality) (struct i2c_adapter *);
......
411 };
第 398 行,master_xfer 就是 I2C 适配器的传输函数,可以通过此函数来完成与 IIC 设备之间的通信。
第 400 行,smbus_xfer 就是 SMBUS 总线的传输函数。
综上所述,I2C 总线驱动,或者说 I2C 适配器驱动的主要工作就是初始化 i2c_adapter 结构体变量,然后设置 i2c_algorithm 中的 master_xfer 函数。完成以后通过 i2c_add_numbered_adapter或 i2c_add_adapter 这两个函数向系统注册设置好的 i2c_adapter.
2.2 I2C设备驱动中数据结构:i2c_client 和 i2c_driver
I2C 设备驱动重点关注两个数据结构:i2c_client 和 i2c_driver,根据总线、设备和驱动模型,i2c_client 就是描述设备信息的,i2c_driver 描述驱动内容,类似于 platform_driver。
1 、i2c_client结构体
i2c_client 结构体定义在 include/linux/i2c.h 文件中,内容如下:
217 struct i2c_client {
218 unsigned short flags; //I2C_CLIENT_TEN表示设备使用10bit从地址,I2C_CLIENT_PEC表示设备使用SMBus检错
219 unsigned short addr; /* 芯片地址,7 位,存在低 7 位 */
......
222 char name[I2C_NAME_SIZE]; //iic设备名字,用于匹配iic驱动
223 struct i2c_adapter *adapter; /*iic设备是挂在哪个适配器(总线)上 */
224 struct device dev; /* 设备结构体 */
225 int irq; /* 中断 */
226 struct list_head detected; //是i2c_adapter结构体或i2c_driver结构体中链表的节点
......
230 };
一个设备对应一个 i2c_client,每检测到一个 I2C 设备就会给这个 I2C 设备分配一个i2c_client。
struct i2c_board_info {
char type[I2C_NAME_SIZE]; //设备名,最长20个字符,最终安装到client的name上
unsigned short flags; //最终安装到client.flags
unsigned short addr; //设备从地址slave address,最终安装到client.addr上
void *platform_data; //设备数据,最终存储到i2c_client.dev.platform_data上
struct dev_archdata *archdata;
struct device_node *of_node; //OpenFirmware设备节点指针
struct acpi_dev_node acpi_node;
int irq; //设备采用的中断号,最终存储到i2c_client.irq上
};
2 、i2c_driver结构体
i2c_driver 类似 platform_driver,是我们编写 I2C 设备驱动重点要处理的内容,i2c_driver 结构体定义在 include/linux/i2c.h 文件中,内容如下:
161 struct i2c_driver {
162 unsigned int class;
163
164 /* Notifies the driver that a new bus has appeared. You should
165 * avoid using this, it will be removed in a near future.
166 */
167 int (*attach_adapter)(struct i2c_adapter *) __deprecated; //老的匹配函数
168
169 /* Standard driver model interfaces */
170 int (*probe)(struct i2c_client *, const struct i2c_device_id *);//当前匹配成功后执行函数,一般是申请资源,注册字符驱动
171 int (*remove)(struct i2c_client *);//当前移除设备或驱动时执行的移除函数,一般是释放资源
172
173 /* driver model interfaces that don't relate to enumeration */
174 void (*shutdown)(struct i2c_client *);
175
176 /* Alert callback, for example for the SMBus alert protocol.
177 * The format and meaning of the data value depends on the
178 * protocol.For the SMBus alert protocol, there is a single bit
179 * of data passed as the alert response's low bit ("event
180 flag"). */
181 void (*alert)(struct i2c_client *, unsigned int data);
182
183 /* a ioctl like command that can be used to perform specific
184 * functions with the device.
185 */
186 int (*command)(struct i2c_client *client, unsigned int cmd,
void *arg);
187
188 struct device_driver driver; //表明这是一个驱动,驱动模型用来挂在I2C总线上
189 const struct i2c_device_id *id_table; /设备匹配列表,非常重要,IIC设备的名字与这个列表匹配
190
191 /* Device detection callback for automatic device creation */
192 int (*detect)(struct i2c_client *, struct i2c_board_info *);
193 const unsigned short *address_list; //该驱动所支持的所有(次设备)的地址数组,用于注册驱动时自动去匹配
194 struct list_head clients; //用来挂接与该i2c_driver匹配成功的i2c_client (次设备)的一个链表头
195 };
参考文档:
【linux iic子系统】linux下i2c框架(二)_linux下应用层实现i2c协议-CSDN博客
正点原子-linux驱动开发指南