9.1 IIC驱动源码分析

学习目标:分析linux内核源码下的i2c总线驱动 drivers/i2c/busses/i2c-s3c2410.c 和 driver/i2c/chips/eeprom.c 设备驱动;


一、i2c驱动框架

在drivers/i2c/目录下查看文件结构可看到:

其中,

1)Busses: I2C总线驱动相关的文件。例如i2c-s3c2410.c.

2)Chips: I2C设备驱动相关文件。例如:eeprom.c、m41t00.c.

3)Algos:i2c通信相关,使能中断、启动传输、i2c寄存器操作等。

4)i2c-core.c:实现了I2C核心的功能,例如:I2C总线的初始化、注册和适配器添加和注销等相关工作以及/proc/bus/i2c*接口。
5)i2c-dev.c:提供了通用的read、 write和ioctl等接口,实现了I2C适配器设备文件的功能。

二、源码分析

简单的结构图:

 第一部分: i2c总线驱动程序 drivers/i2c/busses/i2c-s3c2410.c

 1 static struct platform_driver s3c2440_i2c_driver = {
 2     .probe        = s3c24xx_i2c_probe,
 3     .remove        = s3c24xx_i2c_remove,
 4     .resume        = s3c24xx_i2c_resume,
 5     .driver        = {
 6         .owner    = THIS_MODULE,
 7         .name    = "s3c2440-i2c",
 8     },
 9 };
10 
11 static int __init i2c_adap_s3c_init(void)
12 {
13     int ret;
15     ret = platform_driver_register(&s3c2410_i2c_driver);//注册平台platform_driver
16     if (ret == 0) {
17         ret = platform_driver_register(&s3c2440_i2c_driver);
18         if (ret)
19             platform_driver_unregister(&s3c2410_i2c_driver);
20     }
22     return ret;
23 }

进入probe函数可以看到:

 (1)*设置i2c_adapter适配器结构体,i2c_adapter适配器指向s3c24xx_i2c;

    i2c->adap.algo_data = i2c;   
    i2c->adap.dev.parent = &pdev->dev;

其中,s3c24xx_i2c结构体为:

这里.master_xfer  = s3c24xx_i2c_xfe函数:与i2c通信相关,使能中断、启动传输、i2c寄存器操作等功能。==》发送i2c信号函数。

(2)i2c_add_adapter(&i2c->adap);注册adapter适配器。

进入其中的i2c_register_adapter函数:

 static int i2c_register_adapter(struct i2c_adapter *adap)
{ .......
list_add_tail(&adap->list, &adapters);//添加adap到链表中
/* let legacy drivers scan this bus for matching devices */ list_for_each(item,&drivers) { driver = list_entry(item, struct i2c_driver, list); if (driver->attach_adapter) /* We ignore the return code; if it fails, too bad */ driver->attach_adapter(adap); }
}

1)首先将adapter放入i2c_bus_type的adapter链表中

2)然后将所有的i2c设备调出来,执行i2c_driver设备的attach_adapter函数进行匹配;

第二部分:i2c设备驱动,以driver/i2c/chips/eeprom.c 设备驱动为例

利用i2c_add_driver分配eeprom_driver结构体:

进入i2c_add_driver函数,可看到调用的int i2c_register_driver函数:

 1  int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
 2 {  .....................
 3    list_add_tail(&driver->list,&drivers);
 4    pr_debug("i2c-core: driver [%s] registered
", driver->driver.name);
 5    /* legacy drivers scan i2c busses directly */
 6    if (driver->attach_adapter) {
 7    struct i2c_adapter *adapter;
 8    list_for_each_entry(adapter, &adapters, list) {
 9     driver->attach_adapter(adapter);
10  }

1)首先添加driver到driver链表中

2)然后取出adapters链表中所有的i2c_adapter, 然后调用了i2c_driver->attach_adapter()函数,进行匹配。

接下来会进入eeprom_driver结构体的eeprom_attach_adapter函数:

其中,第1个参数就是i2c_adapter适配器, 参数addr_data存放了I2C设备地址的信息, 调用i2c_probe_address 发出S信号,发出设备地址(来自addr_data),最终会调用到adap->algo->master_xfer函数发信号,如果如果收到ACK信号,就调用eeprom_detect()回调函数来注册i2c_client结构体,该结构体对应真实的物理从设备。

这里的addr_data为 i2c_client_address_data结构体:

1 struct i2c_client_address_data {
2        unsigned short *normal_i2c;     //存放正常的设备高7位地址数据
3        unsigned short *probe;          //存放不受*ignore影响的高7位设备地址数据
4        unsigned short *ignore;         //存放*ignore的高7位设备地址数据
5        unsigned short **forces;        //forces表示适配器匹配不了该设备,也要将其放入适配器中
6 
7 };

例如:DS1374.c设备驱动程序中:

接下来的i2c_probe函数的调用顺序为:

i2c_probe(adapter, &addr_data, eeprom_detect);
       -->i2c_probe_address // 发出S信号,发出设备地址(来自addr_data)
          -->i2c_smbus_xfer
            --> i2c_smbus_xfer_emulated
               --> i2c_transfer
                   -->adap->algo->master_xfer(adap,msgs,num);// s3c24xx_i2c_xfer

其中,在i2c_smbus_xfer_emulated函数中,

 1 static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
 2                                    unsigned short flags,
 3                                    char read_write, u8 command, int size,
 4                                    union i2c_smbus_data * data)
 5 {
10    unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3];  
11    unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2];
12    int num = read_write == I2C_SMBUS_READ?2:1;
13    struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 },    ///定义2个i2c_msg结构体,
14                              { addr, flags | I2C_M_RD, 0, msgbuf1 }
15                            };
16    int i;
17    u8 partial_pec = 0;
18    msgbuf0[0] = command;
19    switch(size) {
20    case I2C_SMBUS_QUICK:
21    msg[0].len = 0;
22    /* Special case: The read/write field is used as data */
23     msg[0].flags = flags | (read_write==I2C_SMBUS_READ)?I2C_M_RD:0;
24     num = 1;
25     break;
26     case I2C_SMBUS_BYTE:
27     if (read_write == I2C_SMBUS_READ) {
28      /* Special case: only a read! */
29     msg[0].flags = I2C_M_RD | flags;
30     num = 1;
31     }
32     break;
33    ....
34    if (i2c_transfer(adapter, msg, num) < 0)   //将 i2c_msg结构体的内容发送给I2C设备
35    return -1;
36    /* Check PEC if last message is a read */
37    if (i && (msg[num-1].flags & I2C_M_RD)) {
38     if (i2c_smbus_check_pec(partial_pec, &msg[num-1]) < 0)
39     return -1;
40    }
41   .....
42 }

其中,其中i2c_msg结构体为:

1 struct i2c_msg {
2        __u16 addr;          //I2C从机的设备地址
3        __u16 flags;           //当flags=0表示写, flags= I2C_M_RD表示读
4        __u16 len;              //传输的数据长度,等于buf数组里的字节数
5        __u8 *buf;              //存放数据的数组
6 };

进入i2c_transfer函数的master_xfer(adap,msgs,num);即总线驱动适配器指向结构体的s3c24xx_i2c_xfer函数;其中,参数*adap表示通过哪个适配器传输出去,msgs表示I2C消息,num表示msgs的数目。

总而言之,根据以上分析,i2c_driver ->attach_adapter(adapter)函数里主要执行以下几步:

1) 调用 i2c_probe(adap, addr_data 设备地址结构体, 回调函数);

2) 将要发的设备地址结构体打包成i2c_msg,

3) 然后执行i2c_transfer()来调用i2c_adapter->algo->master_xfer()将i2c_msg发出去

4) 若收到ACK回应,便进入回调函数,注册i2c_client从设备,使该设备与适配器联系在一起。

第三部分:通过回调函数eeprom_detect注册和设置i2c_client从设备(即结构体),使用i2c_transfer()来实现与设备进行后续的传输数据了。 

eeprom_detect函数的代码如下:

 1 static int eeprom_detect(struct i2c_adapter *adapter, int address, int kind)
 2 {
 3    struct i2c_client *new_client;  //分配i2c_client结构体
 4    struct eeprom_data *data;
 5    int err = 0;
     ....
6   new_client = &data->client; //设置i2c_client结构体 7   memset(data->data, 0xff, EEPROM_SIZE); 8    i2c_set_clientdata(new_client, data); 9    new_client->addr = address; 10    new_client->adapter = adapter; 11    new_client->driver = &eeprom_driver; 12    new_client->flags = 0; 13    ..... 14     /* Tell the I2C layer a new client has arrived */ 15   if ((err = i2c_attach_client(new_client))) //注册i2c_client结构体 16   goto exit_kfree; 17    .....
   exit_detach:
     i2c_detach_client(new_client);
   exit_kfree:
     kfree(data);
18 }

在函数中主要做的以下工作:1)分配i2c_client结构体;2)设置i2c_client结构体;3)注册i2c_client结构体。之后就可以用i2c_transfer()来传输数据了。

原文地址:https://www.cnblogs.com/lxl-lennie/p/10220598.html