linux下IIC驱动程序
IIC总线驱动共3个部分 :IIC核心,IIC总线驱动,IIC设备驱动。
1> IIC核心:提供总线驱动和设备驱动的注册和注销方法
2> IIC总线驱动:是对适配器端的实现
3> IIC设备驱动:是对设备端的实现
框架图:
IIC总线驱动用于识别IIC设备,提供读写函数,提供如何收发数据,但是不知道数据的具体含义。在内核源代码的drivers/i2c/busses/目录中有很多IIC总线驱动,例如S3C2440,对应i2c-s3c2410.c。
i2c_add_adapter 作用
<a> 将adapter放入链表
<b>调用driver中的attach_adapter函数
<a> 将adapter放入链表
<b>调用driver中的attach_adapter函数
<c>在attach_adapte函数中调用i2c_probe函数
<d>用adapter的master_xfer函数发信号,确定有没有该设备,如果有,调用i2c_probe中的定义的发现这个设备后要调用的函数。
i2c设备驱动程序功能(以drivers/i2c/chip/eeprom.c为例)。
<a>分配构造一个i2c_driver
static struct i2c_driver eeprom_driver = {
.driver = {
.name = "eeprom",
},
.id = I2C_DRIVERID_EEPROM,
.attach_adapter = eeprom_attach_adapter,
.detach_client = eeprom_detach_client,
};
<b>使用i2c_add_driver函数将i2c_driver放入链表
<c>从adapter链表取出适配器调用driver的attach_adapter函数, 在attach_adapter中调用i2c_probe函数,用adapter的master_xfer发信号,确定有没有该设备,如果有,调用i2c_probe中定义的发现这个设备后要调用的函数。
代码清单如下:
1.定义变量并设置地址
static unsigned short ignore[] = {I2C_CLIENT_END}; static unsigned short normal_addr = {0x50, I2C_CLIENT_END}; static unsigned short force_addr[] = {ANY_I2C_BUS, 0x60, I2C_CLIENT_END}; static unsigned short *forces[] = {force_addr, NULL}; static struct i2c_client_address_data addr_data = { .normal_i2c = normal_addr, .probe = ignore, .ignore = ignore, //.forces = forces, }; static struct i2c_driver at24cxx_driver; static int major; static struct class *cls; struct i2c_client *at24cxx_client;
2.入口函数和出口函数
static int at24cxx_init(void) { i2c_add_driver(&at24cxx_driver); return 0; } static void at24cxx_exit(void) { i2c_del_driver(&at24cxx_driver); }
3.分配构造i2c_driver结构体
static struct i2c_driver at24cxx_driver = { .driver = { .name = "at24cxx", }, .attach_adapter = at24cxx_attach, .detach_adapter = at24cxx_detach, }; static int at24cxx_attach(struct i2c_adapter *adapter) { return i2c_probe(adapter, &addr_data, at24cxx_detect); } static int at24cxx_detect(struct i2c_adapter *adapter, int address, int kind) { at24cxx_client = kzmalloc(sizeof(struct i2c_client), GFP_KERNEL); at24cxx_client->addr = address; at24cxx_client->adapter = adapter; at24cxx_client->driver = &at24cxx_driver; strcpy(at24cxx_driver->name, "at24cxx"); i2c_attach_client(at24cxx_client); major = register_chrdev(0, "at24cxx", &at24cxx_fops); cls = class_create(THIS_MODULE, "at24cxx"); device_create(cls, NULL, MKDEV(major, 0), NULL, "at24cxx"); return 0; } static int at24cxx_detach(struct i2c_client *client) { device_destroy(cls, MKDEV(major, 0)); class_destroy(cls); unregister_chrdev(major, "at24cxx"); i2c_detach_client(client); kfree(i2c_get_clientdata(client)); }
4.构造并初始化file_operations结构体
static struct file_operations at24cxx_fops = { .owner = THIS_MODULE, .read = at24cxx_read, .write = at24cxx_write, }; static ssize_t at24cxx_read(struct file *file, char __user *buf, size_t size, loff_t *offset) { unsigned char address; unsigned char data; struct i2c_msg msg[2]; int ret; if (size != 1) return -EINVAL; copy_from_user(&address, buf, 1); msg[0].addr = at24cxx_client->addr; msg[0].buf = &address; msg[0].len = 1; msg[0].flags = 0; msg[1].addr = at24cxx_client->addr; msg[1].buf = &data; msg[1].len = 1; msg[1].flags = I2C_M_RD; ret = i2c_transfer(at24cxx_client->adapter, msg, 2); if (ret == 2) { copy_to_user(buf, &data, 1); return 1; } else { return -EIO; } } static ssize_t at24cxx_write(struct file *file, const char __user *buf, size_t size, loff_t *offset) { unsigned char val[2]; struct i2c_msg msg[1]; int ret; if (size != 2) { return -EINVAL; } copy_from_user(val, buf, 2); msg[0].addr = at24cxx_client->addr; msg[0].buf = val; msg[0].len = 2; msg[0].flags = 0; ret = i2c_transfer(at24cxx_client->adapter, msg, 1); if (ret == 1) { return 2; } else { return -EIO; } }