1. 程式人生 > >new style I2C裝置驅動分析,probe,struct i2c_device_id

new style I2C裝置驅動分析,probe,struct i2c_device_id

本文來自:http://hi.baidu.com/fcni_cn/item/561f6f3f0c391abc124b146a

最近看了韋東山驅動視訊的i2c部分,總結出相關知識,分享給大家  .在核心中有兩種方式的i2c裝置驅動的編寫方法,一種legacy方式,一種是newstyle方式。韋東山視訊和宋寶華書籍裡講解的都是legacy方式,但是在新版本核心中,legacy方式的i2c裝置驅動已經編譯不過去了,因為幾個主要的核心函式都已經不存在了,即核心不再支援legacy方式的i2c裝置驅動。下面講解下newstyle方式的i2c裝置驅動。



<一>定義並填充i2c_driver:

  1. staticconst struct i2c_device_id at24c08b_id[] = { 
  2.     {"at24c08b", 0 }, //該i2c_driver所支援的i2c_client
  3.     {} 
  4. }; 
  5. MODULE_DEVICE_TABLE(i2c,at24c08b_id); 
  6. /*定義並填充i2c_driver:
  7. *probe裝置探測函式,i2c_add_driver()時會被呼叫 
  8. *remove裝置解除安裝函式; 
  9. */
  10. staticstruct i2c_driver at24c08b_driver = { 
  11.     .driver= { 
  12.         .name= "at24c08b", 
  13.        .owner = THIS_MODULE, 
  14.     },
  15.     .probe= at24c08b_probe, 
  16.         .remove= __devexit_p(at24c08b_remove), 
  17.         .id_table= at24c08b_id, 
  18. };



<二>模組初始化函式
  1. staticint __init at24c08b_init(void) 
  2. {  
  3.     returni2c_add_driver(&at24c08b_driver); 
  4. }


分析i2c_add_driver():
  1.     i2c_register_driver()
  2.         driver->driver.bus= &i2c_bus_type;//設定i2c_driver的匯流排型別
  3.         driver_register()//這個函式結束後就會呼叫probe()函式                                   
  4.           i2c_for_each_dev(driver,__process_new_driver);//對每一個存在的i2c_adapter,呼叫__process_new_driver()函式                                    
  5.               i2c_do_add_adapter()
  6.                 i2c_detect(adap,driver);
  7.                     //我們的i2c_driver沒設定address_list和detect()函式,所以到這裡就返回了。
  8.                     address_list= driver->address_list;
  9.                     if(!driver->detect || !address_list)
  10.                         return0;

分析driver_register():
  1.     driver_find()//i2c_driver是否已經被註冊
  2.     bus_add_driver()//將i2c_driver掛接到i2c匯流排i2c_bus_type上
  3.         driver_attach()
  4.             //對i2c總線上的每一個i2c裝置i2c_client都會呼叫__driver_attach,這裡的dev即i2c_client,drv即i2c_driver
  5.             bus_for_each_dev(drv->bus,NULL, drv, __driver_attach);
  6.                 driver_match_device(drv,dev)
  7.                     //呼叫i2c匯流排i2c_bus_type的match函式
  8.                     returndrv->bus->match ? drv->bus->match(dev, drv) : 1;
  9.                         i2c_device_match()
  10.                             //若i2c_client的名字和i2c_device_id的中名字相同,則匹配成功,才會呼叫後面的probe()
  11.                             i2c_match_id(driver->id_table,client)
  12.                 driver_probe_device()
  13.                     really_probe()
  14.                         //呼叫i2c匯流排i2c_bus_type的probe函式
  15.                         dev->bus->probe(dev);
  16.                             i2c_device_probe()
  17.                                 //呼叫到i2c_driver的probe()函式
  18.                                 driver->probe(client, i2c_match_id(driver->id_table, client))     


i2c匯流排i2c_bus_type的定義如下:
  1. structbus_type i2c_bus_type = {
  2.     .name        ="i2c",
  3.     .match        =i2c_device_match,
  4.     .probe        =i2c_device_probe,
  5.     .remove        =i2c_device_remove,
  6.     .shutdown    =i2c_device_shutdown,
  7.     ...
  8. };


<三>註冊i2c裝置相關資訊
對於newstyle方式,需要通過i2c_register_board_info()函式註冊i2c_board_info,向核心提供i2c裝置的相關資訊。
在arch/arm/mach-s3c2440/mach-mini2440.c新增如下程式碼:

  1. /*I2C裝置at24c08b的相關資訊*/
  2. staticstruct i2c_board_info i2c_devices[] __initdata = { 
  3.     {I2C_BOARD_INFO("at24c08b", 0x50), }, //0x50是at24c08b的裝置地址 
  4. };
  5. staticvoid __init mini2440_machine_init(void)
  6. {
  7.     …
  8.     i2c_register_board_info(0,i2c_devices,ARRAY_SIZE(i2c_devices));
  9. }


分析i2c_register_board_info():
  1.     structi2c_devinfo    *devinfo;//定義一個i2c_devinfo
  2.         devinfo->board_info= *info;//儲存i2c_board_info
  3.             //將i2c_devinfo掛在連結串列__i2c_board_list上
  4.             list_add_tail(&devinfo->list,&__i2c_board_list);


搜尋__i2c_board_list可知:
  1. i2c_add_numbered_adapter()//i2c-s3c2410.c中呼叫該函式來註冊一個i2c_adapter
  2.     i2c_add_adapter()
  3.         i2c_register_adapter()
  4.             i2c_scan_static_board_info()
  5.                 list_for_each_entry(devinfo,&__i2c_board_list, list) 
  6.                     //利用i2c_adapter和i2c_board_info構造i2c_client
  7.                     if(devinfo->busnum == adapter->nr
  8.                     &&!i2c_new_device(adapter,&devinfo->board_info))
  9.                         structi2c_client *client;
  10.                         client->adapter= adap;//設定i2c_client的adapter
  11.                         client->addr= info->addr;//設定裝置地址
  12.                         …//繼續設定i2c_client
  13.                         device_register()//將i2c裝置i2c_client掛接到i2c總線上


分析device_register():
  1.     device_add()
  2.         bus_add_device()
  3.             //將裝置掛接在總線上,對於i2c而言,即把i2c_client掛接到i2c_bus_type
  4.             klist_add_tail(&dev->p->knode_bus,&bus->p->klist_devices);  

ps:上述這一套分析適用於所有符合匯流排裝置驅動模型的驅動,如usb匯流排,平臺匯流排,pci匯流排,i2c匯流排等


<四>i2c_driver的probe()函式
正常的註冊字元裝置即可,即:
(1)分配裝置號:alloc_chrdev_region()
(2)構造file_operations
(3)分配設定註冊cdev:cdev_init(&cdev,&file_operations); cdev_add()


<五>file_operations的read()和write()函式            
(1)read:

  1. {
  2. /* 檢查該i2c_adapter是否支援讀位元組的功能 */
  3. i2c_check_functionality(I2C_FUNC_SMBUS_READ_BYTE_DATA)
  4. i2c_smbus_read_byte_data()//從eeprom讀一個位元組的資料
  5. copy_to_user()//拷貝至使用者空間
  6. }

不是所有的I2C或者SMBus介面卡實現了I2C規範上的所有功能,因此當訪問I2C介面卡時,
並不能完全假定介面卡提供了你所需的功能。需要有一種檢測介面卡是否提供
了所需功能的方法。
對於不斷更新的I2C介面卡功能常量列表,參考<linux/i2c.h>
I2C_FUNC_I2C    無格式i2c-level命令
I2C_FUNC_10BIT_ADDR    處理10-bit地址的擴充套件
I2C_FUNC_SMBUS_READ_BYTE 處理SMBus read_byte命令
I2C_FUNC_SMBUS_WRITE_BYTE    處理SMBus write_byte命令
I2C_FUNC_SMBUS_READ_BYTE_DATA    處理SMBus read_byte_data命令
I2C_FUNC_SMBUS_WRITE_BYTE_DATA處理SMBus write_byte_data命令
I2C_FUNC_SMBUS_READ_WORD_DATA處理SMBus read_word_data命令
I2C_FUNC_SMBUS_WRITE_WORD_DATA處理SMBus write_word_data命令
...


分析i2c_smbus_read_byte_data(I2C_SMBUS_BYTE_DATA):


  1. i2c_smbus_xfer(I2C_SMBUS_BYTE_DATA)
  2.        i2c_smbus_xfer_emulated(I2C_SMBUS_BYTE_DATA)
  3.                msg[1].len = 1;
  4.                ...//設定讀資料時的i2c_msg
  5.                i2c_transfer()
  6.                //最終呼叫到i2c-s3c2410.c中設定的i2c_adapter     //的master_xfer()函式
  7.                        adap->algo->master_xfer()


(2)write:
  1. {
  2. /* 檢查該i2c_adapter是否支援讀位元組的功能 */
  3. i2c_check_functionality()
  4. copy_from_user(); //獲得使用者空間的資料
  5. i2c_smbus_write_byte_data()//寫資料到eeprom
  6. }

分析i2c_smbus_write_byte_data(I2C_SMBUS_BYTE_DATA):

  1. i2c_smbus_xfer(I2C_SMBUS_BYTE_DATA)
  2. i2c_smbus_xfer_emulated(I2C_SMBUS_BYTE_DATA)
  3.          msg[1].len = 2;
  4.          ...//設定寫資料時的i2c_msg
  5.          i2c_transfer()
  6.                 //最終呼叫到i2c-s3c2410.c中設定的i2c_adapter     //的master_xfer()函式
  7.                 adap->algo->master_xfer()