根据提供的文件信息,本文将对I2C驱动各层进行深入解析,并重点介绍与I2C相关的几个核心函数以及它们在Linux内核中的作用。I2C(Inter-Integrated Circuit)是一种广泛应用于微控制器和外围设备之间的双向二线制串行总线标准。它通过两根线(SDA和SCL)实现数据传输,可以连接多个具有I2C接口的设备。在Linux系统中,I2C驱动架构主要包括以下几层:适配器层、设备层和驱动层。 ### 1. I2C驱动层 #### `i2c_add_driver` 和 `i2c_del_driver` 这两个函数分别用于添加和删除一个I2C驱动。其原型分别为: ```c int i2c_add_driver(struct i2c_driver *driver); int i2c_del_driver(struct i2c_driver *driver); ``` - `i2c_add_driver`: 此函数接收一个指向`struct i2c_driver`类型的指针参数`driver`,并将其注册到I2C子系统中。该操作通常发生在模块初始化阶段。 - `i2c_del_driver`: 当模块卸载时调用此函数来注销`struct i2c_driver`类型对象,释放相应的资源。 ### 2. I2C设备层 #### `i2c_new_device` 和 `i2c_unregister_device` 这些函数用于创建和注销I2C设备。 ```c struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info); void i2c_unregister_device(struct i2c_client *client); ``` - `i2c_new_device`: 创建一个新的I2C设备。参数`adap`表示I2C适配器,`info`是包含设备信息的结构体,该函数返回指向新创建的`struct i2c_client`类型的指针。 - `i2c_unregister_device`: 卸载一个I2C设备,参数`client`是指向`struct i2c_client`类型的指针。 ### 3. 数据传输 #### `i2c_master_send` 和 `i2c_master_recv` 这两个函数实现了基本的数据发送和接收功能。 ```c int i2c_master_send(const struct i2c_client *client, const char *buf, int count); int i2c_master_recv(const struct i2c_client *client, char *buf, int count); ``` - `i2c_master_send`: 向I2C设备发送数据。参数`client`是I2C客户端结构体,`buf`是指向待发送数据缓冲区的指针,`count`表示要发送的字节数。 - `i2c_master_recv`: 从I2C设备接收数据。参数含义与`i2c_master_send`相同,但`buf`此时为接收数据的缓冲区。 ### 4. 适配器层 #### `i2c_add_adapter` 和 `i2c_del_adapter` 这些函数用于管理I2C适配器。 ```c int i2c_add_adapter(struct i2c_adapter *adapter); int i2c_del_adapter(struct i2c_adapter *adapter); ``` - `i2c_add_adapter`: 注册一个I2C适配器。这个函数通常在硬件初始化过程中调用。 - `i2c_del_adapter`: 注销一个I2C适配器,在硬件卸载时调用。 #### `i2c_transfer` 这个函数用于执行一组I2C消息的传输操作。 ```c int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); ``` - `adap`: 指向`struct i2c_adapter`类型的指针。 - `msgs`: 指向`struct i2c_msg`类型的指针数组,表示要发送的消息序列。 - `num`: 消息的数量。 ### 示例代码分析 接下来,我们通过一个具体的示例代码片段来进一步了解I2C驱动的构建过程。 #### 1. 创建I2C设备实例 在实际应用中,例如在`mach-smdkv210.c`文件中定义了I2C设备数组`smdkv210_i2c_devs0`: ```c static struct i2c_board_info smdkv210_i2c_devs0[] __initdata = { {I2C_BOARD_INFO("24c02", 0x50)}, /* Samsung S524AD0XD1 */ {I2C_BOARD_INFO("wm8580", 0x1b)}, }; ``` 这里通过`I2C_BOARD_INFO`宏创建了两个I2C设备实例,并通过`i2c_register_board_info`函数将它们注册到I2C子系统中: ```c i2c_register_board_info(0, smdkv210_i2c_devs0, ARRAY_SIZE(smdkv210_i2c_devs0)); ``` #### 2. 注册I2C驱动 定义一个`struct i2c_driver`类型的变量,并通过`i2c_add_driver`函数将其注册到I2C子系统中。此外,还需要定义一个`struct i2c_device_id`类型的数组`xx_idtable`,用于匹配特定的I2C设备ID。 ```c struct i2c_device_id xx_idtable[] = { {"24c02", 0x456}, {"24c08", 0x457}, }; static struct i2c_driver my_i2c_driver = { .driver = { .name = "my_i2c_driver", }, .probe = my_i2c_probe, .remove = my_i2c_remove, .id_table = xx_idtable, }; static int __init my_i2c_init(void) { return i2c_add_driver(&my_i2c_driver); } static void __exit my_i2c_exit(void) { i2c_del_driver(&my_i2c_driver); } module_init(my_i2c_init); module_exit(my_i2c_exit); ``` 在`probe`函数中,完成对I2C设备的初始化工作,如注册字符设备号、创建设备节点等: ```c static int my_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { int ret; // 注册字符设备号 ret = register_chrdev(MAJOR_NR, "my_i2c", &my_i2c_fops); if (ret < 0) { printk(KERN_ERR "Failed to register character device\n"); return ret; } // 创建设备节点 my_i2c_class = class_create(THIS_MODULE, "my_i2c_class"); if (IS_ERR(my_i2c_class)) { unregister_chrdev(MAJOR_NR, "my_i2c"); return PTR_ERR(my_i2c_class); } my_i2c_dev = device_create(my_i2c_class, NULL, MKDEV(MAJOR_NR, 0), NULL, "my_i2c_dev"); if (IS_ERR(my_i2c_dev)) { class_destroy(my_i2c_class); unregister_chrdev(MAJOR_NR, "my_i2c"); return PTR_ERR(my_i2c_dev); } // 完成其他初始化工作... return 0; } ``` #### 3. 设备操作 在文件操作结构体`fops`中定义读写操作函数,这些函数内部会调用`i2c_master_send`和`i2c_master_recv`等函数来实现真正的数据交换: ```c static const struct file_operations my_i2c_fops = { .owner = THIS_MODULE, .read = my_i2c_read, .write = my_i2c_write, }; static ssize_t my_i2c_read(struct file *filp, char *buf, size_t count, loff_t *ppos) { struct i2c_client *client = filp->private_data; char *buffer; int ret; buffer = kmalloc(count, GFP_KERNEL); if (!buffer) return -ENOMEM; ret = i2c_master_recv(client, buffer, count); if (ret < 0) { kfree(buffer); return ret; } copy_to_user(buf, buffer, count); kfree(buffer); return count; } static ssize_t my_i2c_write(struct file *filp, const char *buf, size_t count, loff_t *ppos) { struct i2c_client *client = filp->private_data; char *buffer; int ret; buffer = kmalloc(count, GFP_KERNEL); if (!buffer) return -ENOMEM; copy_from_user(buffer, buf, count); ret = i2c_master_send(client, buffer, count); if (ret < 0) { kfree(buffer); return ret; } kfree(buffer); return count; } ``` 通过上述步骤,我们可以完成一个完整的I2C驱动程序的设计与实现。这一过程涉及到了I2C驱动的各个层面,包括设备层、驱动层以及适配器层等多个方面,充分展示了Linux内核I2C子系统的强大功能及其灵活性。
/*注册一个i2c驱动*/
int i2c_add_driver(struct i2c_driver *driver)
/*注销一个i2c驱动*/
int i2c_del_driver(struct i2c_driver *driver)
/*创建一个新的i2c设备*/
struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
参数1: client对应的adapter
参数2: 从设备的描述信息--用于i2c的私有数据
void i2c_unregister_device(struct i2c_client *client)
/*发送i2c数据*/ ----> 在通用驱动/私有特定驱动中用
int i2c_master_send(const struct i2c_client *client, const char *buf, int count)
参数1: 驱动对象对应的client对象
参数2: 发送的数据
参数3: 个数
/*接收i2c数据*/
int i2c_master_recv(const struct i2c_client *client, char *buf, int count)
/*传输一个i2c数据包*/--->能够完成发送和接收
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
参数1: i2c的适配器
参数2: 数据包
参数3: 数据包的个数
/*注册一个i2c适配器*/
int i2c_add_adapter(struct i2c_adapter *adapter)
// 这个函数比较重要, 会去构建client
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
int i2c_del_adapter(struct i2c_adapter *adapter)
/*注册板级信息*/---> 将 i2c_board_info对象注册到__i2c_board_list
int __init i2c_register_board_info(int busnum,struct i2c_board_info const *info, unsigned len)
参数1: i2c总线号码
参数2: 需要被添加的 i2c_board_info对象
参数3: i2c_board_info对象个数
-------------------------------------------------------------------------------
编写特定i2c从设备驱动的方法:
1, i2c client设备驱动----> 构建一个{ I2C_BOARD_INFO("24c02", 0x50), },
a, mach-smdkv210.c
static struct i2c_board_info smdkv210_i2c_devs0[] __initdata = {
{ I2C_BOARD_INFO("24c02", 0x50), }, /* Samsung S524AD0XD1 */
{ I2C_BOARD_INFO("wm8580", 0x1b), },
};
// 将I2C_BOARD_INFO添加到一个链表中__i2c_board_list
//adpater会扫描链表__i2c_board_list, 构建一个client,将client注册到i2c总线
i2c_register_board_info(0, smdkv210_i2c_devs0,
ARRAY_SIZE(smdkv210_i2c_devs0));
2, i2c driver 驱动
1, 构建一个struct i2c_driver
2, 构建一个id列表const struct i2c_device_id *id_table;
struct i2c_device_id xx_idtable[] = {
{"24c02", 0x456},
{"24c08", 0x457},
}
3, 将i2c_driver注册到i2c总线
剩余10页未读,继续阅读
- 粉丝: 0
- 资源: 1
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
最新资源
- Python项目之淘宝模拟登录.zip
- 课程设计项目:python+QT实现的小型编译器.zip
- (源码)基于AVR ATmega644的智能卡AES解密系统.zip
- (源码)基于C++插件框架的计算与打印系统.zip
- (源码)基于Spring Boot和Vue的苍穹外卖管理系统.zip
- (源码)基于wxWidgets库的QMiniIDE游戏开发环境管理系统.zip
- 通过C++实现原型模式(Prototype Pattern).rar
- 学习记录111111111111111111111111
- 通过java实现原型模式(Prototype Pattern).rar
- 通过python实现原型模式(Prototype Pattern).rar