Linux学习笔记--i2c_Adapter驱动框架
这是一个虚拟I2C总线适配器驱动,用于创建软件模拟的I2C总线,不依赖实际硬件。
1. 头文件包含
#include <linux/i2c.h> // I2C核心支持
#include <linux/platform_device.h> // 平台设备驱动
#include <linux/i2c-algo-bit.h> // I2C算法支持
// ... 其他内核头文件
2. 全局变量
static struct i2c_adapter *g_adapter; // 全局I2C适配器指针
3. I2C传输函数
static int i2c_bus_virtual_master_xfer(struct i2c_adapter *i2c_adap,struct i2c_msg msgs[], int num)
{int i;for (i = 0; i < num; i++){// do transfer msgs[i];// 这里需要实现具体的I2C传输逻辑// 对于虚拟总线,可以模拟传输或记录日志}return num; // 返回成功传输的消息数量
}
参数说明:
i2c_adap
:I2C适配器指针msgs[]
:I2C消息数组num
:消息数量I2C消息结构:
struct i2c_msg {__u16 addr; // 设备地址__u16 flags; // 读写标志__u16 len; // 数据长度__u8 *buf; // 数据缓冲区
};
4. 功能支持函数
static u32 i2c_bus_virtual_func(struct i2c_adapter *adap)
{return I2C_FUNC_I2C | I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_EMUL |I2C_FUNC_SMBUS_READ_BLOCK_DATA |I2C_FUNC_SMBUS_BLOCK_PROC_CALL |I2C_FUNC_PROTOCOL_MANGLING;
}
功能标志说明:
I2C_FUNC_I2C
:支持标准I2C协议I2C_FUNC_NOSTART
:支持重复起始条件I2C_FUNC_SMBUS_EMUL
:支持SMBus模拟I2C_FUNC_SMBUS_READ_BLOCK_DATA
:支持读取块数据I2C_FUNC_SMBUS_BLOCK_PROC_CALL
:支持块处理调用I2C_FUNC_PROTOCOL_MANGLING
:支持协议处理
5. I2C算法结构体
const struct i2c_algorithm i2c_bus_virtual_algo = {.master_xfer = i2c_bus_virtual_master_xfer, // 主传输函数.functionality = i2c_bus_virtual_func, // 功能查询函数
};
作用:定义I2C总线的操作方法和能力。
6. 平台驱动探测函数
static int i2c_bus_virtual_probe(struct platform_device *pdev)
{/* 从设备树获取信息,设置i2c_adapter/硬件 *//* 分配、设置、注册i2c_adapter */g_adapter = kzalloc(sizeof(*g_adapter), GFP_KERNEL);// 设置适配器属性g_adapter->owner = THIS_MODULE;g_adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; // 硬件监控和内存SPD类g_adapter->nr = -1; // 动态分配适配器编号snprintf(g_adapter->name, sizeof(g_adapter->name), "i2c-bus-virtual");g_adapter->algo = &i2c_bus_virtual_algo; // 设置算法i2c_add_adapter(g_adapter); // 注册适配器到I2C核心// 也可以使用 i2c_add_numbered_adapter(g_adapter);return 0;
}
适配器属性说明:
owner
:模块所有者class
:设备类(硬件监控 + 内存SPD)nr
:适配器编号(-1表示动态分配)name
:适配器名称algo
:I2C算法
7. 平台驱动移除函数
static int i2c_bus_virtual_remove(struct platform_device *pdev)
{i2c_del_adapter(g_adapter); // 从I2C核心注销适配器kfree(g_adapter); // 释放适配器内存(代码中缺失)return 0;
}
8. 设备树匹配表
static const struct of_device_id i2c_bus_virtual_dt_ids[] = {{ .compatible = "100ask,i2c-bus-virtual", }, // 兼容性字符串{ /* sentinel */ }
};
对应设备树节点:
virtual_i2c_bus: i2c@virtual {compatible = "100ask,i2c-bus-virtual";// 可以添加虚拟总线的参数
};
9. 平台驱动结构体
static struct platform_driver i2c_bus_virtual_driver = {.driver = {.name = "i2c-gpio", // 驱动名称.of_match_table = of_match_ptr(i2c_bus_virtual_dt_ids), // 设备树匹配表},.probe = i2c_bus_virtual_probe, // 探测函数.remove = i2c_bus_virtual_remove, // 移除函数
};
注意:驱动名称是"i2c-gpio",但实际是虚拟I2C总线。
10. 模块初始化和退出
static int __init i2c_bus_virtual_init(void)
{int ret;ret = platform_driver_register(&i2c_bus_virtual_driver);if (ret)printk(KERN_ERR "i2c-gpio: probe failed: %d\n", ret);return ret;
}
module_init(i2c_bus_virtual_init);static void __exit i2c_bus_virtual_exit(void)
{platform_driver_unregister(&i2c_bus_virtual_driver);
}
module_exit(i2c_bus_virtual_exit);
完整执行流程
insmod i2c_bus_virtual.ko↓
i2c_bus_virtual_init()↓
platform_driver_register(&i2c_bus_virtual_driver)↓
内核扫描设备树,查找匹配节点↓
找到 compatible = "100ask,i2c-bus-virtual" 的节点↓
调用 i2c_bus_virtual_probe()↓
分配并设置 i2c_adapter↓
i2c_add_adapter(g_adapter) // 注册虚拟I2C总线
系统状态变化:
注册成功后:
# 查看I2C适配器
$ cat /sys/class/i2c-adapter/i2c-*/name
# 会出现 "i2c-bus-virtual"# 使用i2c-tools查看
$ i2cdetect -l
# 会列出虚拟I2C总线
虚拟I2C总线的用途
1. 测试和开发
// 可以在虚拟总线上测试I2C设备驱动,无需真实硬件
static struct i2c_board_info virtual_devices[] = {{ I2C_BOARD_INFO("ap3216c", 0x1e) },{ I2C_BOARD_INFO("at24c02", 0x50) },
};
2. 模拟硬件
// 在master_xfer中模拟硬件行为
static int i2c_bus_virtual_master_xfer(...)
{for (i = 0; i < num; i++) {if (msgs[i].flags & I2C_M_RD) {// 模拟读取操作memset(msgs[i].buf, 0xAA, msgs[i].len);} else {// 模拟写入操作printk("写入数据: %*ph\n", msgs[i].len, msgs[i].buf);}}return num;
}
3. 调试和日志
// 记录所有I2C传输用于调试
static int i2c_bus_virtual_master_xfer(...)
{int i, j;for (i = 0; i < num; i++) {printk("I2C消息 %d: 地址=0x%02x, 标志=0x%04x, 长度=%d\n",i, msgs[i].addr, msgs[i].flags, msgs[i].len);if (!(msgs[i].flags & I2C_M_RD)) {printk("写入数据: ");for (j = 0; j < msgs[i].len; j++)printk("%02x ", msgs[i].buf[j]);printk("\n");}}return num;
}
设备树配置示例
/ {virtual_i2c_bus: i2c@virtual {compatible = "100ask,i2c-bus-virtual";#address-cells = <1>;#size-cells = <0>;// 在虚拟总线上挂载虚拟设备virtual_sensor@1e {compatible = "virtual,ap3216c";reg = <0x1e>;};virtual_eeprom@50 {compatible = "virtual,at24c02";reg = <0x50>;};};
};