# i2c_driver AP3216C是红外、光强、距离三合一的传感器,以读出光强、距离值为例,步骤如下: * 复位:往寄存器0写入0x4 * 使能:往寄存器0写入0x3 * 读红外:读寄存器0xA、0xB得到2字节的红外数据 * 读光强:读寄存器0xC、0xD得到2字节的光强 * 读距离:读寄存器0xE、0xF得到2字节的距离值 AP3216C的设备地址是0x1E。本章以AP3216C为基础,介绍i2c驱动的写法。 ## 基本框架 1. 构建自己的i2c_driver结构体,里面有匹配规则 和 probe 函数 ```c static struct i2c_driver i2c_ap3216c_driver = { .driver = { .name = "ap3216c", .of_match_table = of_match_ids_ap3216c, /* a:设备树匹配 b:i2c_client.name匹配 */ }, .probe_new = ap3216c_probe, .remove = ap3216c_remove, .id_table = ap3216c_ids, /* c:i2c_client.name匹配*/ }; ``` 2. 注册自己的i2c_driver 到内核的i2c总线,然后到内核上的client总线上,按照a、b和c的匹配规则如果匹配到自己的client,就调用 .probe_new函数 ```c static int __init i2c_driver_ap3216c_init(void) { printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); return i2c_add_driver(&i2c_ap3216c_driver); } ``` 3. 在probe函数里面注册自己的字符设备驱动 * a:register_chrdev,注册file_operations 在里面有自己的read open函数 * b:class_create创建类 * c: device_create在类型创建设备节点/dev/ap3216c ```c static int ap3216c_probe(struct i2c_client *client) { printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); ap3216c_client = client; /* register_chrdev */ major = register_chrdev(0, "ap3216c", &ap3216c_ops); ap3216c_class = class_create(THIS_MODULE, "ap3216c_class"); device_create(ap3216c_class, NULL, MKDEV(major, 0), NULL, "ap3216c"); /* /dev/ap3216c */ return 0; } ``` 4. open函数里面执行硬件操作,比如ap3216c复位 ```c static int ap3216c_open (struct inode *node, struct file *file) { /* * 复位操作,ap3216c要求上电复位的 */ i2c_smbus_write_byte_data(ap3216c_client, 0, 0x4); /* delay for reset */ mdelay(20); i2c_smbus_write_byte_data(ap3216c_client, 0, 0x3); return 0; } ``` 5. 读寄存器数据 ```c static ssize_t ap3216c_read (struct file *file, char __user *buf, size_t size, loff_t *offset) { /* * 读寄存器数据 */ int err; char kernel_buf[6]; int val; if (size != 6) return -EINVAL; val = i2c_smbus_read_word_data(ap3216c_client, 0xA); /* read IR */ kernel_buf[0] = val & 0xff; kernel_buf[1] = (val>>8) & 0xff; val = i2c_smbus_read_word_data(ap3216c_client, 0xC); /* read 光强 */ kernel_buf[2] = val & 0xff; kernel_buf[3] = (val>>8) & 0xff; val = i2c_smbus_read_word_data(ap3216c_client, 0xE); /* read 距离 */ kernel_buf[4] = val & 0xff; kernel_buf[5] = (val>>8) & 0xff; err = copy_to_user(buf, kernel_buf, size); return size; } ``` 6. 出口函数,反着来 ```c static void __exit i2c_driver_ap3216c_exit(void) { i2c_del_driver(&i2c_ap3216c_driver); } ``` 完整的【a3216c_drv.c】代碼如下: ```c #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int major = 0; static struct class *ap3216c_class; static struct i2c_client *ap3216c_client; /* * 5:读寄存器数据 */ static ssize_t ap3216c_read (struct file *file, char __user *buf, size_t size, loff_t *offset) { /* * 读寄存器数据 */ int err; char kernel_buf[6]; int val; if (size != 6) return -EINVAL; val = i2c_smbus_read_word_data(ap3216c_client, 0xA); /* read IR */ kernel_buf[0] = val & 0xff; kernel_buf[1] = (val>>8) & 0xff; val = i2c_smbus_read_word_data(ap3216c_client, 0xC); /* read 光强 */ kernel_buf[2] = val & 0xff; kernel_buf[3] = (val>>8) & 0xff; val = i2c_smbus_read_word_data(ap3216c_client, 0xE); /* read 距离 */ kernel_buf[4] = val & 0xff; kernel_buf[5] = (val>>8) & 0xff; err = copy_to_user(buf, kernel_buf, size); return size; } /* * 4:复位操作,ap3216c要求上电复位的 */ static int ap3216c_open (struct inode *node, struct file *file) { /* * 复位操作,ap3216c要求上电复位的 */ i2c_smbus_write_byte_data(ap3216c_client, 0, 0x4); /* delay for reset */ mdelay(20); i2c_smbus_write_byte_data(ap3216c_client, 0, 0x3); return 0; } static struct file_operations ap3216c_ops = { .owner = THIS_MODULE, .open = ap3216c_open, .read = ap3216c_read, }; static const struct of_device_id of_match_ids_ap3216c[] = { { .compatible = "com_name,chip_name", .data = NULL }, { /* END OF LIST */ }, }; static const struct i2c_device_id ap3216c_ids[] = { { "chip_name", (kernel_ulong_t)NULL }, { /* END OF LIST */ } }; /* * 3:在probe函数里面注册自己的字符设备驱动 * a:register_chrdev,注册file_operations 在里面有自己的read open函数 * b:class_create创建类 * c: device_create在类型创建设备节点/dev/ap3216c */ static int ap3216c_probe(struct i2c_client *client) { printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); ap3216c_client = client; /* register_chrdev */ major = register_chrdev(0, "ap3216c", &ap3216c_ops); ap3216c_class = class_create(THIS_MODULE, "ap3216c_class"); device_create(ap3216c_class, NULL, MKDEV(major, 0), NULL, "ap3216c"); /* /dev/ap3216c */ return 0; } static int ap3216c_remove(struct i2c_client *client) { printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); device_destroy(ap3216c_class, MKDEV(major, 0)); class_destroy(ap3216c_class); /* unregister_chrdev */ unregister_chrdev(major, "ap3216c"); return 0; } /* * 1:构建自己的i2c_driver结构体,里面有匹配规则 和 probe 函数 */ static struct i2c_driver i2c_ap3216c_driver = { .driver = { .name = "ap3216c", .of_match_table = of_match_ids_ap3216c, /* a:设备树匹配 b:i2c_client.name匹配 */ }, .probe_new = ap3216c_probe, .remove = ap3216c_remove, .id_table = ap3216c_ids, /* c:i2c_client.name匹配*/ }; /* * 2:注册自己的i2c_driver 到内核的i2c总线,然后到内核上的client总线上,按照a、b和c的匹配规则 * 如果匹配到自己的client,就调用 .probe_new函数, */ static int __init i2c_driver_ap3216c_init(void) { printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); return i2c_add_driver(&i2c_ap3216c_driver); } module_init(i2c_driver_ap3216c_init); /* * 6:出口函数,反着来 */ static void __exit i2c_driver_ap3216c_exit(void) { i2c_del_driver(&i2c_ap3216c_driver); } module_exit(i2c_driver_ap3216c_exit); MODULE_AUTHOR("www.100ask.net"); MODULE_LICENSE("GPL"); ```