一、Linux I2C 驱动框架简介
Linux 内核将 I2C 驱动分为两部分:
- **I2C 总线驱动:**SOC 的I2C 控制器驱动,也叫做I2C 适配器驱动。一旦编写完成就不再需要再做修改,其他的I2C 设备直接调用主机驱动提供的API 函数完成读写操作即可。
- **I2C 设备驱动:**I2C 设备驱动就是针对具体的I2C 设备而编写的驱动。
二、I2C 总线驱动
总线驱动用到两个重要的数据结构:
- i2c_adapter
- i2c_algorithm
2.1 i2c_adapter
i2c_adapter 结构体定义在 include/linux/i2c.h 文件中,结构体内容如下:
struct i2c_adapter {
struct module *owner;
unsigned int class; /* classes to allow probing for */
const struct i2c_algorithm *algo; /* the algorithm to access the bus */
void *algo_data;
/* data fields that are valid for all devices */
struct rt_mutex bus_lock;
int timeout; /* in jiffies */
int retries;
struct device dev; /* the adapter device */
int nr;
char name[48];
struct completion dev_released;
struct mutex userspace_clients_lock;
struct list_head userspace_clients;
struct i2c_bus_recovery_info *bus_recovery_info;
const struct i2c_adapter_quirks *quirks;
};
2.2 i2c_algorithm
i2c_algorithm 就是I2C 适配器与I2C 设备进行通信的方法。
结构体定义如下:
struct i2c_algorithm {
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num);
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data);
u32 (*functionality) (struct i2c_adapter *);
};
- master_xfer : 就是 I2C 适配器的传输函数,可以通过此函数来完成与 IIC 设备 之间的通信。
- smbus_xfer : 就是 SMBUS 总线的传输函数。
2.3 I2C 适配器驱动编写步骤
-
初始化 i2c_adapter 结构体变量。
-
设置i2c_algorithm 中的master_xfer 函数 。
-
向系统注册设置好的i2c_adapter 。注册函数原型如下:
int i2c_add_adapter(struct i2c_adapter* adapter) int i2c_add_numberred_adapter(struct i2c_adapter* adap)
这两个函数的区别在于i2c_add_adapter 使用动态总线号,而i2c_add_numbered_adapter 使用静态总线号。
-
删除I2C 适配器
void i2c_del_adapter(struct i2c_adapter* adap)
一般SOC 的I2C 总线驱动都是由半导体厂商编写的,我们只需要专注于I2C 设备驱动即可。
三、I2C 设备驱动
I2C 设备驱动重点关注两个数据结构体:
- i2c_client
- i2c_driver
根据总线、设备、和驱动模型,i2c_client 就是描述设备信息的,i2c_driver 描述设备驱动,类似于platform_driver 。
3.1 i2c_client 结构体
i2c_client 结构体定义在 include/linux/i2c.h 中,内容如下:
struct i2c_client
{
unsigned short flags; /* div., see below */
unsigned short addr; /* chip address - NOTE: 7bit addresses are stored in the _LOWER_ 7 bits */
char name[I2C_NAME_SIZE];
struct i2c_adapter *adapter; /* the adapter we sit on */
struct device dev; /* the device structure */
int irq; /* irq issued by device */
struct list_head detected;
};
一个设备对应一个i2c_client ,每检测到一个I2C 设备就会给这个I2C 设备分配一个i2c_client 。
3.2 i2c_driver 结构体
i2c_driver 类似 platform_driver ,是我们编写I2C 设备驱动重点要处理的内容。
struct i2c_driver
{
unsigned int class;
int ( *attach_adapter )( struct i2c_adapter * ) __deprecated;
int ( *probe )( struct i2c_client *, const struct i2c_device_id * );
int ( *remove )( struct i2c_client * );
void ( *shutdown )( struct i2c_client * );
void ( *alert )( struct i2c_client *, unsigned int data );
int ( *command )( struct i2c_client *client, unsigned int cmd, void *arg );
struct device_driver driver;
const struct i2c_device_id *id_table;
int ( *detect )( struct i2c_client *, struct i2c_board_info * );
const unsigned short *address_list;
struct list_head clients;
};
- probe : 当i2c 设备和驱动匹配成功以后,probe 函数就会执行,和platform 驱动一样。
- device_driver : 驱动结构体,如果使用设备树的话,需要设置device_driver 的of_match_table 成员变量,也就是驱动的兼容(compatible) 属性。
对于我们 I2C 设备驱动编写人来说,重点工作就是构建 i2c_driver ,构建完成以后需要向Linux 内核注册这个i2c_driver 。
注册i2c_driver
#define i2c_add_driver (driver) \ i2c_register_driver(THIS_MODULE,driver)
注销 I2C 设备驱动
void i2c_del_driver( struct i2c_driver * driver)
3.3 I2C 设备和驱动匹配过程
I2C 设备和驱动的匹配过程是由I2C 核心来完成的, driver/i2c/i2c-core.c 就是I2C 的核心部分,I2C 核心提供了一些与具体硬件无关的API 函数,比如前面讲过的:
i2c_adapter 注册、注销函数
int i2c_add_adapter(struct i2c_adapter* adapter)
int i2c_add_numbered_adapter(struct i2c_adapter* adap)
void i2c_del_adapter(struct i2c_adapter* adap)
i2c_driver 注册、注销函数
int i2c_register_driver(struct module* owner,struct i2c_driver* driver)
int i2c_add_driver(struct i2c_driver* driver)
int i2c_del_driver(struct i2c_driver* driver)
匹配过程
设备和驱动的匹配过程,具体是由 I2C 总线完成,I2C 总线的数据结构为 i2c_bus_type , 定义在 driver/i2c/i2c-core.c 文件,i2c_bus_type 内容如下:
struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
};
.match 就是I2C 总线的设备和驱动匹配函数,在这里就是i2c_device_match 这个函数。