i2c_driver
AP3216C是红外、光强、距离三合一的传感器,以读出光强、距离值为例,步骤如下:
复位:往寄存器0写入0x4
使能:往寄存器0写入0x3
读红外:读寄存器0xA、0xB得到2字节的红外数据
读光强:读寄存器0xC、0xD得到2字节的光强
读距离:读寄存器0xE、0xF得到2字节的距离值
AP3216C的设备地址是0x1E。本章以AP3216C为基础,介绍i2c驱动的写法。
基本框架
构建自己的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匹配*/ };
注册自己的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); }
在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;
}
open函数里面执行硬件操作,比如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 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; }
出口函数,反着来
static void __exit i2c_driver_ap3216c_exit(void) { i2c_del_driver(&i2c_ap3216c_driver); }
完整的【a3216c_drv.c】代碼如下:
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/mod_devicetable.h>
#include <linux/bitops.h>
#include <linux/jiffies.h>
#include <linux/property.h>
#include <linux/acpi.h>
#include <linux/i2c.h>
#include <linux/nvmem-provider.h>
#include <linux/regmap.h>
#include <linux/pm_runtime.h>
#include <linux/gpio/consumer.h>
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");