(基于4.14内核版本)
为了梳理清楚linux内核中的i2c实现框架,从本文开始,博主将分几个章节分别解析i2c总线在linux内核中的形成过程、匹配过程、以及设备驱动程序源码实现。
在介绍linux内核中i2c框架之前,我们最好是知道怎么使用它,实现一个相应的i2c设备驱动程序demo,然后从使用去深挖背后的实现原理,先知道怎么用,然后再知道为什么可以这么用。
I2C的基本知识扫盲
回到本文的重点——I2C,做过裸板开发或者是单片机开发的朋友肯定对I2C不陌生,I2C是主从结构,主器件使用从机地址进行寻址,它的拓扑结构是这样的:
基本的流程是这样的:
- 主机发送从机地址
- 从机监听总线,检测到接收地址与自身地址地址匹配,回复。
- 主机启动收发数据
- 从机接收数据,响应
- 数据收发完毕,主机释放总线。
完整的I2C操作其实是比较复杂的,这里就不再展开讲解,博主将会在随后的章节中进行详解。
I2C设备驱动程序框架
I2C设备驱动程序框架分为两个部分:driver和device。
分别将driver和device加载到内存中,i2c bus在程序加载时会自动调用match函数,根据名称来匹配driver和device,匹配完成时调用probe()
在driver中,定义probe()函数,在probe函数中创建设备节点,针对不同的设备实现不同的功能。
在device中,设置设备I2C地址,选择I2C适配器。
I2C适配器:I2C的底层传输功能,一般指硬件I2C控制器。
I2C设备驱动程序
driver端示例
直接来看下面的示例代码:
i2c_bus_driver.c:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
/* 结构体数组 结构体第一个参数为名称,第二个参数为private数据*/
static const struct i2c_device_id downey_drv_id_table[] = {
{"downey_i2c",0},
{},
};
static int major;
static struct class *i2c_test_cls;
static struct device *i2c_test_dev;
static const char* CLASS_NAME = "I2C_TEST_CLASS";
static const char* DEVICE_NAME = "I2C_TEST_DEVICE";
static struct i2c_client *downey_client;
static int i2c_test_open(struct inode *node, struct file *file)
{
printk(KERN_ALERT "i2c init \n");
return 0;
}
static ssize_t i2c_test_read(struct file *file,char *buf, size_t len,loff_t *offset)
{
int cnt = 0;
uint8_t reg = 0;
uint8_t val = 0;
copy_from_user(®,buf,1);
/*i2c读byte,通过这个函数可以从设备中指定地址读取数据*/
val = i2c_smbus_read_byte_data(downey_client,reg);
cnt = copy_to_user(&buf[1],&val,1);
return 1;
}
static ssize_t i2c_test_write(struct file *file,const char *buf,size_t len,loff_t *offset)
{
uint8_t recv_msg[255] = {0};
uint8_t reg = 0;
int cnt = 0;
cnt = copy_from_user(recv_msg,buf,len);
reg = recv_msg[0];
printk(KERN_INFO "recv data = %x.%x\n",recv_msg[0],recv_msg[1]);
/*i2c写byte,通过这个函数可以往设备中指定地址写数据*/
if(i2c_smbus_write_byte_data(downey_client,reg,recv_msg[1]) < 0){
printk(KERN_ALERT " write failed!!!\n");
return -EIO;
}
return len;
}
static int i2c_te