# i2c adapter ## i2c 适配器框架分析 i2c适配器驱动走的是平台总线驱动那一套东西,过程如下: 1. 设备树在根节点提供i2c控制器的设备节点如下: ```shell / { …… i2c-bus-virtual { compatible = "flyrobot,i2c-bus-virtual"; }; …… }; ``` 2. 编写平台总线驱动 - 提供自己的平台驱动结构体,里面有probe和remove函数 ```c static const struct of_device_id i2c_bus_virtual_dt_ids[] = { { .compatible = "flyrobot,i2c-bus-virtual", },// 和设备树匹配 { /* sentinel */ } }; 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_bus_virtual_driver`注册平台驱动总线上,当和设备树匹配成功后,会调用probe函数 ``` 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; } ``` - probe函数 - 分配适配器 ```c g_adapter = kzalloc(sizeof(*g_adapter), GFP_KERNEL); ``` - 设置适配器 ```c g_adapter->owner = THIS_MODULE; g_adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; g_adapter->nr = -1; g_adapter->algo = &i2c_bus_virtual_algo; // 算法结构体,很重要 ``` 注意:`i2c_bus_virtual_algo`和重要下面会分析。 - 注册适配器 ```c ret = i2c_add_adapter(adap); //不管adap->nr是什么,都动态设置adap->nr ret = i2c_add_numbered_adapter(adap); //如果adap->nr == -1 则动态分配nr; 否则使用该nr ``` 整个probe函数如下: ```c static int i2c_bus_virtual_probe(struct platform_device *pdev) { /* get info from device tree, to set i2c_adapter/hardware */ /* alloc, set, register i2c_adapter */ g_adapter = kzalloc(sizeof(*g_adapter), GFP_KERNEL); g_adapter->owner = THIS_MODULE; g_adapter->class = I2C_CLASS_HWMON | I2C_CLASS_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_add_numbered_adapter(g_adapter); return 0; } ``` - remove函数如下 ```c static int i2c_bus_virtual_remove(struct platform_device *pdev) { i2c_del_adapter(g_adapter); // 反注册适配器 return 0; } ``` - 出口函数,unregister平台驱动, ```c static void __exit i2c_bus_virtual_exit(void) { platform_driver_unregister(&i2c_bus_virtual_driver); } ``` 3. 算法结构体分析: 上面介绍了在probe函数中分配设置i2c_adapter结构体,该结构体内如如下: ![](media/012_i2c_adapter.png) 从图中可以看到里面有个`i2c_algorithm`结构体。内容如下: ![image-20210303121043020](media/058_i2c_algorithm.png) * **master_xfer**:这是最重要的函数,它实现了一般的I2C传输,用来传输一个或多个i2c_msg * master_xfer_atomic: 可选的函数,功能跟master_xfer一样,在`atomic context`环境下使用,比如在关机之前、所有中断都关闭的情况下,用来访问电源管理芯片 * smbus_xfer:实现SMBus传输,如果不提供这个函数,SMBus传输会使用master_xfer来模拟 * smbus_xfer_atomic: 可选的函数,功能跟smbus_xfer一样,在`atomic context`环境下使用,比如在关机之前、所有中断都关闭的情况下,用来访问电源管理芯片 * **functionality**:返回所支持的flags:各类I2C_FUNC_* * reg_slave/unreg_slave: 有些I2C Adapter也可工作与Slave模式,用来实现或模拟一个I2C设备 有上面的分析可知我们构建我们自己的算法结构体`i2c_bus_virtual_algo`即可,如下: ```c 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; } const struct i2c_algorithm i2c_bus_virtual_algo = { .master_xfer = i2c_bus_virtual_master_xfer, // i2c 硬件实现读写数据 .functionality = i2c_bus_virtual_func, // 提供该i2c驱动支持的功能 }; ``` 下面就是重要的`i2c_bus_virtual_master_xfer`函数,里面如果是模拟的i2c,就会通过io拉时序,如果是硬件i2c,就会操作i2c寄存器,然后写或者读数据。大致的操作如下: ```c static int i2c_bus_virtual_master_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num) { for (i = 0; i < num; i++) { struct i2c_msg *msg = msgs[i]; { // 1. 設置控制寄存器 发出S信号: 设置寄存器发出S信号 CTLREG = S; // 2. 根据Flag发出设备地址和R/W位: 把这8位数据写入某个DATAREG即可发出信号 // 判断是否有ACK if (!ACK) return ERROR; else { // 3. read / write if (read) { STATUS = XXX; // 这决定读到一个数据后是否发出ACK给对方 val = DATAREG; // 这会发起I2C读操作 } else if(write) { DATAREG = val; // 这会发起I2C写操作 val = STATUS; // 判断是否收到ACK if (!ACK) return ERROR; } } // 4. 設置控制寄存器 发出P信号 CTLREG = P; } } return i; } ``` 整个驱动框架代碼如下: ```c #include #include #include #include //#include #include #include #include #include #include //#include #include #include static struct i2c_adapter *g_adapter; static int i2c_bus_virtual_master_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) { // 這裏面和硬件有关系,具体的硬件自己添加 实现通过i2c控制器读写功能 } 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; } const struct i2c_algorithm i2c_bus_virtual_algo = { .master_xfer = i2c_bus_virtual_master_xfer, .functionality = i2c_bus_virtual_func, }; static int i2c_bus_virtual_probe(struct platform_device *pdev) { /* get info from device tree, to set i2c_adapter/hardware */ /* alloc, set, register i2c_adapter */ g_adapter = kzalloc(sizeof(*g_adapter), GFP_KERNEL); g_adapter->owner = THIS_MODULE; g_adapter->class = I2C_CLASS_HWMON | I2C_CLASS_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_add_numbered_adapter(g_adapter); return 0; } static int i2c_bus_virtual_remove(struct platform_device *pdev) { i2c_del_adapter(g_adapter); return 0; } static const struct of_device_id i2c_bus_virtual_dt_ids[] = { { .compatible = "100ask,i2c-bus-virtual", }, { /* sentinel */ } }; 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, }; static int __init i2c_bus_virtual_init(void) { int ret; printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); 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) { printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); platform_driver_unregister(&i2c_bus_virtual_driver); } module_exit(i2c_bus_virtual_exit); MODULE_AUTHOR("www.100ask.net"); MODULE_LICENSE("GPL"); ```