I2C 设备驱动(Client Driver)是运行在 Linux 内核中,用于控制挂接在 I2C 总线上的具体外设(如传感器、EEPROM、触摸屏)的程序。它通过 I2C 核心层提供的 API 与设备通信,并向应用层提供标准接口。
1. 驱动模型
I2C 设备驱动基于 Linux 设备驱动模型(Device Driver Model)。
- Bus:
i2c_bus_type - Device:
struct i2c_client(代表从设备) - Driver:
struct i2c_driver(代表驱动程序)
当 i2c_client(通常由设备树解析生成)与 i2c_driver(由 id_table 或 compatible 匹配)匹配成功时,probe 函数被调用。
2. 关键结构体
2.1 struct i2c_driver
这是驱动开发者必须定义的结构体。
c
static struct i2c_driver my_i2c_driver = {
.driver = {
.name = "my-sensor",
.of_match_table = my_of_match, // 设备树匹配表
},
.probe = my_probe, // 匹配成功调用
.remove = my_remove, // 卸载时调用
.id_table = my_id_table, // 传统 ID 匹配表
};1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
2.2 struct i2c_client
这是 probe 函数的入参,代表一个真实的 I2C 设备。
c
struct i2c_client {
unsigned short flags; // 标志 (I2C_CLIENT_TEN, I2C_CLIENT_PEC)
unsigned short addr; // 7位设备地址
char name[I2C_NAME_SIZE];
struct i2c_adapter *adapter; // 挂接的总线适配器
struct device dev; // 继承的 device 结构
int irq; // 中断号
// ...
};1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
3. 驱动编写步骤
3.1 定义匹配表
c
static const struct of_device_id my_of_match[] = {
{ .compatible = "vendor,my-sensor", },
{ }
};
MODULE_DEVICE_TABLE(of, my_of_match);1
2
3
4
5
2
3
4
5
3.2 实现 Probe 函数
probe 是驱动的入口,主要工作是初始化设备和注册接口。
c
static int my_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
// 1. 检查适配器能力 (可选但推荐)
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
return -ENODEV;
// 2. 初始化设备 (复位、配置寄存器)
// 使用 i2c_master_send / i2c_master_recv 或 i2c_smbus_...
// 3. 注册用户接口
// 例如:注册字符设备、Input 设备、IIO 设备、Misc 设备等
// 这里以注册 misc 设备为例
dev_info(&client->dev, "My Sensor Probed at 0x%02x\n", client->addr);
return 0;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
3.3 实现 Remove 函数
c
static int my_remove(struct i2c_client *client)
{
// 注销接口,清理资源
return 0;
}1
2
3
4
5
2
3
4
5
3.4 注册驱动
c
module_i2c_driver(my_i2c_driver); // 宏,等价于 module_init 和 module_exit1
4. 数据传输 API
在驱动中,不要直接操作寄存器,而是使用 I2C Core 提供的 API。
4.1 标准 I2C 传输
i2c_master_send(client, buf, count): 发送数据。i2c_master_recv(client, buf, count): 接收数据。i2c_transfer(adapter, msgs, num): 执行复杂的组合传输(最底层)。
4.2 SMBus 接口 (推荐)
SMBus (System Management Bus) 是 I2C 的子集,定义了标准的命令格式。如果设备兼容 SMBus,优先使用这些 API,因为它们在某些仅支持 SMBus 的控制器上也能工作,且语义更清晰。
i2c_smbus_read_byte_data(client, command): 读指定寄存器(8位)。i2c_smbus_write_byte_data(client, command, value): 写指定寄存器(8位)。i2c_smbus_read_i2c_block_data(...): 读块数据。- // ... 更多详见
<linux/i2c.h>
5. 示例:读取 Chip ID
c
u8 reg = 0x00; // 假设 ID 寄存器地址为 0x00
u8 val;
struct i2c_msg msgs[2];
// 方法 1: 使用 i2c_transfer
msgs[0].addr = client->addr;
msgs[0].flags = 0; // Write
msgs[0].len = 1;
msgs[0].buf = ®
msgs[1].addr = client->addr;
msgs[1].flags = I2C_M_RD; // Read
msgs[1].len = 1;
msgs[1].buf = &val;
if (i2c_transfer(client->adapter, msgs, 2) != 2)
dev_err(&client->dev, "Read failed\n");
else
dev_info(&client->dev, "Chip ID: 0x%02x\n", val);
// 方法 2: 使用 SMBus (更简洁)
s32 ret = i2c_smbus_read_byte_data(client, 0x00);
if (ret < 0)
dev_err(&client->dev, "Read failed\n");
else
dev_info(&client->dev, "Chip ID: 0x%02x\n", ret);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
6. 设备树节点
在 dts 中引用该驱动:
dts
&i2c1 {
status = "okay";
my_sensor@50 {
compatible = "vendor,my-sensor";
reg = <0x50>;
};
};1
2
3
4
5
6
7
8
2
3
4
5
6
7
8