这里调用了i2c_driver->attach_adapter(adapter),我们看看里面是不是通过发送IIC设备地址,等待ACK响应来匹配的
8、以struct i2c_driver eeporm_driver 为例子,进入i2c_driver->eeprom_attach_adapter()函数
如下图所示,里面调用了i2c_probe(adapter, &addr_data, eeprom_detect)函数
上图的第一个参数就是i2c_adapter适配器,第二个参数addr_data变量,里面存放了IIC设备地址信息,第3个参数eeprom_detect就是具体的设备探测回调函数i2c_probe()函数,会通过adapter适配器发送IIC设备地址addr_data,如果收到ACK信号,就会调用eeprom_detect()回调函数来注册i2c_client结构体,该结构体对应真实的物理从设备,而i2c_driver对应的是设备驱动,也就是说,只有当适配器支持这个设备驱动,才会注册i2c_client从设备,后面会讲这个回调函数如何注册i2c_client
而在i2c_client->detach_client()中,则注销i2c_client结构体
其中addr_data变量是struct i2c_client_address_data结构体,它的成员如下所示:
struct i2c_client_address_data {
unsigned short *normal_i2c; //存放正常的设备高7位地址数据
unsigned short *probe; //存放不受*ignore影响的高7位设备地址数据
unsigned short *ignore; //存放*ignore的高7位设备地址数据
unsigned short **forces; //forces表示适配器匹配不了该设备,也要将其放入适配器中
};
当上面结构体的数组成员以I2C_CLIENT_END结尾,则表示地址已结束,比如at24c02为例,看这个结构体如何定义的
#define AT24C02_ADDR (0xA0>>1) //AT24C02地址
static unsigned short ignore[] = { I2C_CLIENT_END };
static unsigned short normal_addr[] = { AT24C02_ADDR, I2C_CLIENT_END };
static unsigned short force_addr[] = {ANY_I2C_BUS, AT24C02_ADDR ,2C_CLIENT_END};
static unsigned short * forces[] = {force_addr, NULL};
//ANY_I2C_BUS:表示支持所有适配器总线,若填指定的适配器总线ID,则表示该设备只支持指定的那个适配器
static struct i2c_client_address_data addr_data = {
.normal_i2c = normal_addr, //存放at24c02地址
.probe = ignore, //表示无地址
.ignore = ignore, //表示无地址
. forces = forces, //存放强制的at24c02地址,表示强制支持
};
一般而言,都不会设置.forces成员,这里只是打个比方
8.1 接下来继续进入i2c_probe()函数继续分析,如下所示:
int i2c_probe(struct i2c_adapter *adapter, struct i2c_client_address_data *address_data, int (*found_proc) (struct i2c_adapter *, int, int))
{
i2c_probe_address(adapter, forces[kind][i + 1],kind, found_proc);
}
里面调用了i2c_probe_address()函数,从名称上来看,显然它就是用来发送起始信号+设备地址,来探测IIC设备地址用的
8.2 进入i2c_probe_address()函数:
static int i2c_probe_address(struct i2c_adapter *adapter, int addr, int kind,
int (*found_proc) (struct i2c_adapter *, int, int))
{
/* 判断设备地址是否有效,addr里存放的是设备地址前7位,比如AT24C02=0XA0,那么addr=0x50 */
if (addr < 0x03 || addr > 0x77) {
dev_warn(&adapter->dev, "Invalid probe address 0x%02xn",
addr);//打印地址无效,并退出
return -EINVAL;
}
/* 查找链表中其他IIC设备的设备地址,若这个设备地址已经被使用,则return */
if (i2c_check_addr(adapter, addr))
return 0;
/* Make sure there is something at this address, unless forced */
if (kind < 0) {
if (i2c_smbus_xfer(adapter, addr, 0, 0, 0,
I2C_SMBUS_QUICK, NULL) < 0)
//进入I2C传输函数
return 0;
... ...
}
8.3 其中i2c_smbus_xfer()传输函数如下:
s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags,
char read_write, u8 command, int size,
union i2c_smbus_data * data)
{
s32 res;
flags &= I2C_M_TEN | I2C_CLIENT_PEC;
if (adapter->algo->smbus_xfer) {//如果adapter适配器有smbus_xfer这个函数
mutex_lock(&adapter->bus_lock);//加互斥锁
res = adapter->algo->smbus_xfer(adapter,addr,flags,read_write,
command,size,data);
//调用adapter适配器里的传输函数
mutex_unlock(&adapter->bus_lock);解互斥锁
} else //否则使用默认函数传输设备地址
res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write,
command,size,data);
return res;
}
看了上面代码后,显然我们的s3c2410-i2c适配器没有algo->smbus_xfer函数,而是使用i2c_smbus_xfer_emulated()函数,如下图所示:
PS:通常适配器都是不支持的,使用默认的i2c_smbus_xfer_emulated()函数
8.4 接下来看i2c_smbus_xfer_emulated()函数如何传输的:
static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,unsigned short flags,char read_write, u8 command, int size, union i2c_smbus_data * data)
{
unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3]; //属于 msg[0]的buf成员
unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2]; //属于 msg[1]的buf成员
int num = read_write == I2C_SMBUS_READ?2:1; //如果为读命令,就等于2,表示要执行两次数据传输
struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 },
{ addr, flags | I2C_M_RD, 0, msgbuf1 }}; //定义两个i2c_msg结构体,
msgbuf0[0] = command; //IIC设备地址最低位为读写命令
... ...
if (i2c_transfer(adapter, msg, num) < 0)
return -1;
/*设置i2c_msg结构体成员*/
if (read_write == I2C_SMBUS_READ)
switch(size) {
... ...
case I2C_SMBUS_BYTE_DATA: //如果是读字节
if (read_write == I2C_SMBUS_READ)
msg[1].len = 1;
else {
msg[0].len = 2;
msgbuf0[1] = data->byte;
}
break;
... ...
}
... ...
if (i2c_transfer(adapter, msg, num) < 0) //将 i2c_msg结构体的内容发送给I2C设备
return -1;
... ...
}
其中i2c_msg结构体的结构,如下所示:
struct i2c_msg {
__u16 addr;/* slave address I2C从机的设备地址
__u16 flags; //当flags=0表示写,flags=I2C_M_RD表示读
__u16 len;//传输的数据长度,等于buf数组里的字节数
__u8 *buf;//存放数据的数组
};
上面代码中之所以读操作需要两个i2c_msg,写操作需要一个i2c_msg,是因为读IIC设备是两个流程
在上一节里就已经分析到了https://blog.csdn.net/xiaodingqq/article/details/81808875
只要发送一个S起始信号则就是一个i2c_msg,如下两个读写操作图所示:
而在i2c_transfer()函数中,最终又是调用了之前分析的i2c_adapter->algo->master_xfer()发送函数,如下图所示:
其中i2c_transfer()的参数*adap表示通过那个适配器传输出去,msgs表示I2C消息,num表示msgs的数目
内核每发送一个Msg都会先发出S开始信号和设备地址,直到所有Msg传输完毕,最后发出P停止信号。
当i2c_transfer()返回值为正数,表示已经传输整数个数据,当返回负数,说明I2C传输出错
8.5 所以在i2c_driver->attach_adapter(adapter)函数里主要执行以下几步:
1)调用i2c_probe(adap,i2c_client_address_data设备地址结构体,回调函数)
2)将要发的设备地址结构体打包成i2c_msg
3)然后执行i2c_transfer()来调用i2c_adapter->algo->master_xfer()将i2c_msg发出去
4)若收到ACK回应,便进入回调函数,注册i2c_client从设备,使该设备与适配器联系在一起
所以适配器和i2c设备驱动最终注册框架图如下所示:
9、接下来便来分析回调函数如何注册i2c_client从设备的
struct i2c_client {
unsigned short flags;//标志
unsigned short addr; //该i2c从设备的设备地址,存放地址高7位
char name[I2C_NAME_SIZE]; //设备名字
struct i2c_adapter *adapter;//依附的i2c_adapter,表示该IIC设备支持哪个适配器
struct i2c_driver *driver;//依附的i2c_driver ,表示该IIC从设备的驱动是哪个
struct device dev;//设备结构体
int irq;//设备所使用的结构体
struct list_head detected;//链表头
};
还是以driver/i2c/chips/eeprom.c为例,如下图所示:
9.1 这里的回调函数是eeprom_detect()函数,代码如下所示:
static int eeprom_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client; //定义一个i2c_client结构体局部变量
new_client =kzalloc(sizeof(struct i2c_client), GFP_KERNEL); //分配i2c_client结构体为全局变量
/*设置i2c_client结构体*/
new_client->addr = address; //设置设备地址
new_client->adapter = adapter; //设置依附的i2c_adapter
new_client->driver = &eeprom_driver; //设置依附的i2c_driver
new_client->flags = 0; //设置标志位为初始值
strlcpy(new_client->name, "eeprom", I2C_NAME_SIZE); //设置名字
/*注册i2c_client*/
if ((err = i2c_attach_client(new_client)))
goto exit_kfree; //注册失败,便释放i2c_client这个全局变量
... ...
exit_kfree:
kfree(new_client);
exit:
return err;
}
当注册了i2c_client从设备后,便可以使用i2c_transfer()来实现与设备传输数据了
10、接下来,我们便参考driver/i2c/chips/eeprom.c驱动,来写出AT24C02驱动以及测试程序
驱动代码步骤如下:
1、定义file_operations结构体,设置字符设备的读写函数(实现对AT24C02的读写操作)
//构造i2c_msg结构体,使用i2c_transfer()来实现与设备传输数据
2、定义i2c_client_address_data结构体,里面保存AT24C02的设备地址
3、定义一个i2c_driver驱动结构体
3.1 设置i2c_driver -> attach_adapter
//里面直接调用i2c_probe(adap, i2c_client_address_data结构体,回调函数);
3.2 设置i2c_driver->detach_client
[1] [2] [3]