目录
[I2C 总线驱动](#I2C 总线驱动)
[i2c_algorithm 结构体](#i2c_algorithm 结构体)
[I2C 设备驱动](#I2C 设备驱动)
[i2c_client 结构体](#i2c_client 结构体)
[i2c_driver 结构体](#i2c_driver 结构体)
[i2c_del_driver 函数](#i2c_del_driver 函数)
[I2C 设备和驱动匹配过程](#I2C 设备和驱动匹配过程)
Linux内核将 I2C 驱动分为两部分:
- I2C 总线驱动, I2C 总线驱动就是 SOC 的 I2C 控制器驱动,也叫做 I2C 适配器驱动。
- I2C 设备驱动, I2C 设备驱动就是针对具体的 I2C 设备而编写的驱动。
I2C 总线驱动
I2C 总线驱动重点是 I2C 适配器驱动,这里要用到两个重要的数据结构: i2c_adapter 和 i2c_algorithm。
i2c_adapter结构体
Linux 内核将 SOC 的 I2C 适配器(控制器)抽象成 i2c_adapter, i2c_adapter 结构体定义在 include/linux/i2c.h 文件中。
i2c_adapter结构体内容如下:
cpp
struct i2c_adapter {
struct module *owner; // 拥有该适配器的模块,用于引用计数
unsigned int class; // 允许探测的设备类
const struct i2c_algorithm *algo; // 总线访问算法(关键操作函数指针)
void *algo_data; // 算法私有数据
/* 所有设备通用的数据字段 */
struct rt_mutex bus_lock; // 总线互斥锁(实时互斥锁,防止并发访问)
int timeout; // 超时时间(以jiffies为单位)
int retries; // 操作重试次数
struct device dev; // 适配器对应的设备结构体
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; // 适配器特殊行为/变通方案
};
其中,i2c_algorithm 类型的指针变量 algo,对于一个 I2C 适配器,肯定要对外提供读写 API 函数,设备驱动程序可以使用这些 API 函数来完成读写操作。
i2c_algorithm 就是 I2C 适配器与 IIC 设备进行通信的方法。
i2c_algorithm 结构体
i2c_algorithm 结构体定义在 include/linux/i2c.h 文件中,内容如下(删除条件编译):
cpp
struct i2c_algorithm {
......
/* I2C 传输函数(核心操作) */
int (*master_xfer)(struct i2c_adapter *adap, // 关联的适配器
struct i2c_msg *msgs, // 消息数组(包含读写操作)
int num); // 消息数量
/* SMBus 传输函数 */
int (*smbus_xfer)(struct i2c_adapter *adap, // 关联的适配器
u16 addr, // 设备地址(7位或10位)
unsigned short flags, // 标志位(如I2C_M_TEN表示10位地址)
char read_write, // 读写方向(I2C_SMBUS_READ/WRITE)
u8 command, // SMBus命令字节
int size, // 数据大小(决定SMBus协议类型)
union i2c_smbus_data *data); // 传输数据(读写缓冲区)
/* 查询适配器支持的功能 */
u32 (*functionality)(struct i2c_adapter *); // 返回I2C_FUNC_*标志位组合
// 例如:是否支持SMBus、10位地址等
......
};
- master_xfer 就是 I2C 适配器的传输函数,可以通过此函数来完成与 IIC 设备之间的通信。
- smbus_xfer 就是 SMBUS 总线的传输函数。
I2C 总线驱动,主要工作就是初始化 i2c_adapter 结构体变量,然后设置 i2c_algorithm 中的 master_xfer 函数。
注册函数
通过 i2c_add_numbered_adapter或 i2c_add_adapter这两个函数向系统注册设置好的 i2c_adapter,这两个函数的原型如下:
cpp
int i2c_add_adapter(struct i2c_adapter *adapter)
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
i2c_add_adapter 使用动态的总线号,而 i2c_add_numbered_adapter使用静态总线号。
这两个函数的对比如下:

删除函数
如果要删除 I2C 适配器的话使用i2c_del_adapter 函数即可,函数原型如下:
cpp
void i2c_del_adapter(struct i2c_adapter * adap)
- adap:要删除的 I2C 适配器。
一般 SOC 的 I2C 总线驱动都是由半导体厂商编写的,比如 I.MX6U 的 I2C 适配器驱动 NXP 已经编写好了,不需要用户去编写。
I2C 设备驱动
I2C 设备驱动主要是两个数据结构: i2c_client 和 i2c_driver,i2c_client 就是描述设备信息的, i2c_driver 描述驱动内容,类似于 platform_driver。
i2c_client 结构体
i2c_client 结构体定义在 include/linux/i2c.h文件中,内容如下:
cpp
struct i2c_client {
unsigned short flags; /* 标志位(如I2C_CLIENT_TEN表示10位地址)*/
unsigned short addr; /* 设备地址(7位,存储于低7位)*/
......
char name[I2C_NAME_SIZE]; /* 设备名称(匹配驱动用,如"lm75")*/
struct i2c_adapter *adapter; /* 所属的I2C适配器(指向父控制器)*/
struct device dev; /* 内嵌的设备结构体(用于设备模型)*/
int irq; /* 设备使用的中断号(若无则为0)*/
struct list_head detected; /* 链表节点(用于管理已探测到的设备)*/
......
};
一个设备对应一个 i2c_client,每检测到一个 I2C 设备就会给这个 I2C 设备分配一个i2c_client。
i2c_driver 结构体
i2c_driver 结构体定义在include/linux/i2c.h 文件中,内容如下:
cpp
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 *); /* 系统关机时调用的设备关闭回调 */
/* 警报回调(例如SMBus警报协议) */
void (*alert)(struct i2c_client *, unsigned int data);
/* 参数说明:
- data: 协议相关数据(SMBus警报协议中为低比特位"event flag")
*/
/* 类ioctl命令,用于执行设备特定功能 */
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
/* 参数说明:
- cmd: 自定义命令号
- arg: 命令参数指针
*/
struct device_driver driver; /* 内嵌的标准设备驱动结构体(包含.name/.owner等) */
const struct i2c_device_id *id_table; /* 支持的设备ID表(用于驱动匹配) */
/* 自动设备创建的检测回调 */
int (*detect)(struct i2c_client *, struct i2c_board_info *);
/* 功能:
- 检测设备并填充board_info(用于动态设备注册)
*/
const unsigned short *address_list; /* 探测时的设备地址列表(用于detect回调) */
struct list_head clients; /* 该驱动管理的客户端设备链表 */
};
其中,有一个device_driver 驱动结构体,如果使用设备树的话,需要设置 device_driver 的of_match_table 成员变量,也就是驱动的兼容(compatible)属性。
cpp
struct device_driver driver;
构建 i2c_driver完成以后,需要向Linux 内核注册这个 i2c_driver。
i2c_register_driver函数
i2c_driver 注册函数为 i2c_register_driver,此函数原型如下:
cpp
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
- owner: 一般为 THIS_MODULE。
- driver:要注册的 i2c_driver。
- 返回值: 0,成功;负值,失败。
i2c_add_driver宏
i2c_add_driver 也常常用于注册 i2c_driver, i2c_add_driver 是一个宏,定义如下:
cpp
#define i2c_add_driver(driver) \
i2c_register_driver(THIS_MODULE, driver)
i2c_add_driver 就是对 i2c_register_driver 做了一个简单的封装,只有一个参数,就是要注册的 i2c_driver。
i2c_del_driver 函数
注销 I2C 设备驱动的时候,需要将前面注册的 i2c_driver 从 Linux 内核中注销掉,需要用到i2c_del_driver 函数,此函数原型如下:
cpp
void i2c_del_driver(struct i2c_driver *driver)
- driver:要注销的 i2c_driver。
i2c_driver 的注册示例代码如下:
cpp
/* i2c 驱动的 probe 函数 */
static int xxx_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
/* 函数具体程序 */
return 0;
}
/* i2c 驱动的 remove 函数 */
static int xxx_remove(struct i2c_client *client)
{
/* 函数具体程序 */
return 0;
}
/* 传统匹配方式 ID 列表 */
static const struct i2c_device_id xxx_id[] = {
{"xxx", 0},
{}
};
/* 设备树匹配列表 */
static const struct of_device_id xxx_of_match[] = {
{ .compatible = "xxx" },
{ /* Sentinel */ }
};
/* i2c 驱动结构体 */
static struct i2c_driver xxx_driver = {
.probe = xxx_probe,
.remove = xxx_remove,
.driver = {
.owner = THIS_MODULE,
.name = "xxx",
.of_match_table = xxx_of_match,
},
.id_table = xxx_id,
};
/* 驱动入口函数 */
static int __init xxx_init(void)
{
int ret = 0;
ret = i2c_add_driver(&xxx_driver);
return ret;
}
/* 驱动出口函数 */
static void __exit xxx_exit(void)
{
i2c_del_driver(&xxx_driver);
}
module_init(xxx_init);
module_exit(xxx_exit);
I2C 设备和驱动匹配过程
I2C 设备和驱动的匹配过程是由 I2C 核心来完成的, drivers/i2c/i2c-core.c 就是 I2C 的核心部分文件。
I2C 核心提供了一些与具体硬件无关的 API 函数:
cpp
//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)
void i2c_del_driver(struct i2c_driver *driver)
设备和驱动的匹配过程也是由 I2C 总线完成的, I2C 总线的数据结构为 i2c_bus_type,
i2c_bus_type结构体
i2c_bus_type结构体定义在 drivers/i2c/i2c-core.c文件, 内容如下:
cpp
struct bus_type i2c_bus_type = {
.name = "i2c", // 总线名称(出现在/sys/bus/i2c)
/* 核心回调函数 */
.match = i2c_device_match, // 设备与驱动匹配的核心逻辑
.probe = i2c_device_probe, // 设备探测入口(最终调用驱动的probe)
.remove = i2c_device_remove, // 设备移除入口(最终调用驱动的remove)
.shutdown = i2c_device_shutdown,// 系统关机时调用的设备关闭处理
};
.match 就是 I2C 总线的设备和驱动匹配函数,在这里就是 i2c_device_match 这个函数。
i2c_device_match函数
i2c_device_match内容如下:
cpp
/**
* i2c_device_match - 匹配I2C设备与驱动的核心函数
* @dev: 待匹配的设备(struct device指针)
* @drv: 待匹配的驱动(struct device_driver指针)
*
* 返回值:匹配成功返回1,失败返回0
*/
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
/* 验证设备是否为有效的I2C客户端 */
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver;
/* 如果不是I2C客户端设备立即返回不匹配 */
if (!client)
return 0;
/* 第一阶段:设备树(OF)风格匹配(最高优先级) */
if (of_driver_match_device(dev, drv))
return 1;
/* 第二阶段:ACPI风格匹配 */
if (acpi_driver_match_device(dev, drv))
return 1;
/* 获取I2C驱动结构体 */
driver = to_i2c_driver(drv);
/* 第三阶段:ID表匹配(传统匹配方式) */
if (driver->id_table)
return i2c_match_id(driver->id_table, client) != NULL;
/* 所有匹配方式都失败 */
return 0;
}
- of_driver_match_device 函数,用于完成设备树设备和驱动匹配。比较 I2C 设备节点的 compatible 属性和 of_device_id 中的 compatible 属性是否相等,如果相当的话就表示 I2C设备和驱动匹配。
- **acpi_driver_match_device 函数,**用于 ACPI 形式的匹配。
- **i2c_match_id 函数,**用于传统的、无设备树的 I2C 设备和驱动匹配过程。比较 I2C设备名字和 i2c_device_id 的 name 字段是否相等,相等的话就说明 I2C 设备和驱动匹配。