13_Linux2.6 IIC驱动
文章目录
1、i2c驱动框架
i2c驱动框架模型:
其中步骤四④为:在i2c_add_adapter和i2c_add_driver的第③步时若发现设备,则构造一个i2c_client结构体来配对。
分析i2c注册i2c_driver的过程:
i2c_add_driver
i2c_register_driver
driver->driver.bus = &i2c_bus_type;
driver_register(&driver->driver);
list_for_each_entry(adapter, &adapters, list) {
driver->attach_adapter(adapter);
i2c_probe(adapter, &addr_data, eeprom_detect);
i2c_probe_address // 发出Start信号,发出设备地址(来自addr_data)
i2c_smbus_xfer
i2c_smbus_xfer_emulated
i2c_transfer
adap->algo->master_xfer // s3c24xx_i2c_xfer
2、编写驱动程序
怎么写I2C设备驱动程序?
① 分配一个i2c_driver结构体
② 设置
attach_adapter // 它直接调用 i2c_probe(adap, 设备地址, 发现这个设备后要调用的函数);
detach_client // 卸载这个驱动后,如果之前发现能够支持的设备,则调用它来清理
③ 注册:i2c_add_driver
2.7、i2c实验的读写函数
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;
/* address = buf[0]
* data = buf[1]
*/
if(size != 1)
return -EINVAL;
copy_from_user(&address, buf, 1);
/* 数据传输三要素:源、目的、长度 */
/* 读at24cxx时,先把要读的存储空间的地址告诉它 */
msg[0].addr = at24cxx_client->addr; /* 目的 */
msg[0].buf = &address; /* 源 */
msg[0].len = 1; /* 地址 = 1 byte */
msg[0].flags = 0; /* 0表示写 */
/* 然后启动读操作 */
msg[1].addr = at24cxx_client->addr; /* 源 */
msg[1].buf = &data; /* 目的 */
msg[1].len = 1; /* 数据 = 1 byte */
msg[1].flags = I2C_M_RD; /* 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;
/* address = buf[0]
* data = buf[1]
*/
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; /* 地址+数据 = 2 byte */
msg[0].flags = 0; /* 0表示写 */
ret = i2c_transfer(at24cxx_client->adapter, msg, 1);
if(ret == 1)
return 2;
else
return -EIO;
}
2.6、在file_operations结构体at24cxx_fops中:
添加用于i2c实验的读写函数
static struct file_operations at24cxx_fops = {
.owner = THIS_MODULE,
.read = at24cxx_read,
.write = at24cxx_write,
};
2.5、发现新的adapter或者driver之后调用函数at24cxx_detect:
① 在这里构造一个i2c_client结构体: 以后收发数据时会用到它;
② 构造主设备号,添加file_operations结构体at24cxx_fops;
static int at24cxx_detect(struct i2c_adapter *adapter, int address, int kind)
{
printk("at24cxx_detect\n");
/* 构造一个i2c_client结构体: 以后收发数据时会用到 它 */
at24cxx_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
at24cxx_client->addr = address;
at24cxx_client->adapter = adapter;
at24cxx_client->driver = &at24cxx_driver;
strcpy(at24cxx_client->name, "at24cxx");
i2c_attach_client(at24cxx_client); /* Tell the I2C layer a new client has arrived */
major = register_chrdev(0, "at24cxx", &at24cxx_fops);
cls = class_create(THIS_MODULE, "at24cxx");
class_device_create(cls, NULL, MKDEV(major, 0), NULL, "at24cxx"); /* /dev/at24cxx */
return 0;
}
2.4、at24cxx_attach和at24cxx_detach函数:
在这个函数中要用到识别设备所要发出的地址数据addr_data
static unsigned short ignore[] ={I2C_CLIENT_END};
/* i2c设备地址值是前面7位 1 0 1 0 0 0 0 A2 A1 A0
* 改为0x60的话,由于不存在设备地址为0x60的设备,
* 所以at24cxx_detect不被调用
*/
static unsigned short normal_addr[] ={0x50, I2C_CLIENT_END};
/* 参数1:总线,表示在哪条总线上查找设备
* 参数2:设备地址
* 参数3:查找设备时用于退出的标记
*/
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, /* 要发出S信号和设备地址并得到ACK信号,才能确定存在这个设备 */
.probe = ignore,
.ignore = ignore,
//.forces = forces, /* 强制认为存在这个设备 */
};
static int at24cxx_attach(struct i2c_adapter *adapter)
{
/* 参数3:发现新的adapter或者driver之后调用这个函数 */
return i2c_probe(adapter, &addr_data, at24cxx_detect);
}
static int at24cxx_detach(struct i2c_client *client)
{
printk("at24cxx_detach\n");
class_device_destroy(cls, MKDEV(major, 0));
class_destroy(cls);
unregister_chrdev(major, "at24cxx");
i2c_detach_client(client);
kfree(i2c_get_clientdata(client));
return 0;
}
2.3、在i2c_driver结构体at24cxx_driver中:
/* 1. 分配/定义一个i2c_driver结构体 */
/* 2. 设置i2c_driver结构体 */
static struct i2c_driver at24cxx_driver = {
.driver = {
.name = "at24cxx",
},
.attach_adapter = at24cxx_attach,
.detach_client = at24cxx_detach,
};
2.1、在入口函数at24cxx_init中:
注册i2c_driver:
i2c_add_driver(&at24cxx_driver);
2.2、在出口函数at24cxx_exit中:
卸载i2c_driver:
i2c_del_driver(&at24cxx_driver);
3、测试
1th:
- insmod at24cxx.ko
观察输出信息
- 修改normal_addr里的0x50为0x60
编译加载,观察输出信息,没有设备不会调用函数at24cxx_detect
2th:
没有真正的i2c设备,使用forces强制指定设备,也可以成功调用at24cxx_detect函数
3th:
在装载时没有构造i2c_client结构体,所以在卸载时不会调用at24cxx_detach函数,需要加上;
在at24cxx_detect里面添加。
4th:
完成基本框架,现在来填写i2c的读写函数
5th:
完整的i2c驱动程序,可以测试读写操作
总结:
构造i2c_driver结构体,里面有一个函数:.attach_adapter = at24cxx_attach,当注册驱动时,会从总线的adapter链表中拿出来,调用其.attach_adapter = at24cxx_attach函数,会调用i2c_probe,probe函数会使用总线里的传输函数发出信号查看是否有那些IIC设备;当发现有这些设备之后注册i2c_client结构体,对其注册、设置等操作;之后就是注册字符设备,实现IIC的读写函数等等。
以前我们得数据都是读取那些寄存器,现在都是使用IIC总线驱动程序提供的哪些读写函数,到IIC总线上将数据读写进来。