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适配器。

 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\n", 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结构体,该结构体对应真实的物理从设备。

函数的调用顺序为:

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

其中,master_xfer(adap,msgs,num);的参数*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()来实现与设备进行后续的传输数据了。 

猜你喜欢

转载自www.cnblogs.com/lxl-lennie/p/10220598.html
9.1