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