1. 程式人生 > >9.1 IIC驅動原始碼分析

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()來實現與裝置進行後續的傳輸資料了。