我前面基本已经吃透了Platform总线,关于Platform总线的原理和结构,详情见下面三篇博文:
https://blog.csdn.net/wenhao_ir/article/details/145023181
https://blog.csdn.net/wenhao_ir/article/details/145018442
https://blog.csdn.net/wenhao_ir/article/details/145030037
在Platform总线中,三个重点是匹配规则(总线的match函数)、platform_device
结构体、platform_driver
结构体。关于 Platform总线不错的视频总结回顾讲解,请百度网盘搜索“1-3_03_SPI总线设备驱动”,然后从头开始看。
在Linux的I2C总线中,其实重点也是类似的三点,即I2C的match机制、i2c_client
结构体、i2c_driver
结构体,与Platform总线不同的是,在Platform总线中,总线是一个虚拟的总线概念,而在Linux的I2C总线结构中,总线是实实在在的,I2C本来就是一个物理总线嘛,它上面可以外挂很多I2C设备。我们可以利用Linux的I2C总线为这些外挂的I2C设备也提供上相应的驱动,这样上层应用就可以不用再去像博文 https://blog.csdn.net/wenhao_ir/article/details/146361457 中那样,还需要在用户空间去操作I2C控制器去直接操作硬件,而是直接用相应的驱动程序就可以调用相应的I2C设备了。
Linux的I2C总线的结构原理图如下:
关于Linux的I2C总线的结构原理的详细介绍,请百度网盘搜索“1-8_4-8_I2C系统驱动程序模型”,然后从05分45秒开始看。这里就不再赘述了,因为只要吃透了Platform总线,那么理解Linux的I2C总线是很简单的。
i2c_driver
的Demo实现(仅框架)i2c_driver
的Demo实现源码#include
#include
#include
#include
#include
static const struct of_device_id of_match_ids_example[] = {
{ .compatible = "com_name,chip_name" },
{ /* END OF LIST */ }
};
static const struct i2c_device_id example_ids[] = {
{ "chip_name", 0 },
{ /* END OF LIST */ }
};
static int i2c_driver_example_probe(struct i2c_client *client)
{
return 0;
}
static int i2c_driver_example_remove(struct i2c_client *client)
{
return 0;
}
static struct i2c_driver i2c_example_driver = {
.driver = {
.name = "example",
.of_match_table = of_match_ids_example,
},
.probe_new = i2c_driver_example_probe,
.remove = i2c_driver_example_remove,
.id_table = example_ids,
};
static int __init i2c_driver_example_init(void)
{
return i2c_add_driver(&i2c_example_driver);
}
static void __exit i2c_driver_example_exit(void)
{
i2c_del_driver(&i2c_example_driver);
}
module_init(i2c_driver_example_init);
module_exit(i2c_driver_example_exit);
MODULE_AUTHOR("suwenhao");
MODULE_LICENSE("GPL");
i2c_driver
的Demo实现源码分析因为前面基本已经吃透了Platform总线,所以看这段源码是非常轻松的,不过为了将来能快速回顾,还是用chatgpt生成分析吧。
这段代码是一个 Linux I2C 设备驱动的简单示例,它实现了一个 I2C 驱动框架,并注册到了 I2C 总线。下面是对该驱动的详细分析:
代码结构分析
该驱动实现了以下主要功能:
匹配设备:
of_device_id
结构体支持设备树匹配。i2c_device_id
结构体支持传统的 I2C 设备 ID 匹配。驱动核心函数:
probe_new
(即 probe
)函数:当匹配到 I2C 设备时调用,用于设备初始化。remove
函数:当设备被移除时调用,用于释放资源。驱动注册:
i2c_add_driver()
注册驱动。i2c_del_driver()
注销驱动。详细分析
1. 设备树匹配表
static const struct of_device_id of_match_ids_example[] = {
{ .compatible = "com_name,chip_name" },
{ /* END OF LIST */ }
};
compatible
属性匹配时,设备就会与该驱动匹配。"com_name,chip_name"
是设备树中的 compatible
属性,格式一般为 "厂商名,芯片名"
。2. 设备 ID 匹配表
static const struct i2c_device_id example_ids[] = {
{ "chip_name", 0 },
{ /* END OF LIST */ }
};
example_ids
(即 i2c_device_id
结构体数组)用于匹配 i2c_client
结构体中的 name
字段。i2c_client
结构体的定义在 include/linux/i2c.h
中,关键部分如下:struct i2c_client {
char name[I2C_NAME_SIZE]; // 设备名称
const struct i2c_device_id *id;
struct device dev;
...
};
其中,name
是 I2C 设备的名称,用于驱动匹配。"chip_name"
与 i2c_client
结构体中的 name
字段相同,即可匹配成功。3. probe 函数
static int i2c_driver_example_probe(struct i2c_client *client)
{
return 0;
}
probe
在设备匹配成功后被调用,一般在这里进行硬件初始化,比如:
devm_*
API)0
,意味着它什么也没做,仅仅是个框架。4. remove 函数
static int i2c_driver_example_remove(struct i2c_client *client)
{
return 0;
}
remove
被调用,用于释放 probe
里申请的资源。5. I2C 驱动结构体
static struct i2c_driver i2c_example_driver = {
.driver = {
.name = "example",
.of_match_table = of_match_ids_example,
},
.probe_new = i2c_driver_example_probe,
.remove = i2c_driver_example_remove,
.id_table = example_ids,
};
driver.name
:用于 sysfs 和日志中标识该驱动,这个名字也可用来用i2c_client
进行匹配。of_match_table
:绑定设备树匹配表。probe_new
:新的 probe
API,建议使用 probe_new
而不是 probe
。remove
:移除设备时调用。id_table
:传统 I2C 设备 ID 匹配表。6. 驱动初始化和注销
static int __init i2c_driver_example_init(void)
{
return i2c_add_driver(&i2c_example_driver);
}
static void __exit i2c_driver_example_exit(void)
{
i2c_del_driver(&i2c_example_driver);
}
i2c_add_driver()
:将驱动注册到 I2C 子系统。i2c_del_driver()
:从 I2C 子系统移除驱动。7. 模块声明
module_init(i2c_driver_example_init);
module_exit(i2c_driver_example_exit);
MODULE_AUTHOR("suwenhao");
MODULE_LICENSE("GPL");
module_init()
和 module_exit()
设定了驱动的加载和卸载函数。MODULE_LICENSE("GPL")
指明许可证,防止内核报 taint
(污染)警告。i2c_driver
中并没有指定是哪条I2C总线,那么它怎么知道操作哪条I2C总线?答案很简单,因为在i2c_client
中会含有I2C设备挂接于哪条I2C总线的信息啦。你往这篇博文后面看i2c_client
的实现就知道了。
i2c_client
的实现i2c_client
的实现有三种方式,分别为直接使用echo命令创建、利用代码实现、利用设备树生成,详情见 https://blog.csdn.net/wenhao_ir/article/details/146417363 【搜索“i2c_client的实现和生成”】