linux內核模型---總線,設備,驅動在展訊平臺上I2C設備的實例解析

        前段時間一直在閱讀CSDN大牛fudan_abc寫的文章,對linux內核中的總線,設備,驅動加深了了解,在此對fudan_abc深表感謝。感謝像fudan_abc這樣的大牛無私的傳道受業解惑,在學習linux內核模型知識時,對照着展訊7715平臺上的I2C設備進行了簡單的實例分析,記錄了下述文字。其中原理性描述,來自於fudan_abc,請讀者知悉。



 
        Linux 設備模型中三個很重要的概念就是總線,設備,驅動.即 bus,device,driver,而實際上內核中也定義了這麼一些數據結構,他們是 struct bus_type,struct device,struct device_driver,這三個重要的數據結構都來自一個地方,include/linux/device.h.我們知道總線有很多種,pci總線,scsi 總線,usb 總線,所以我們會看到 Linux 內核代碼中出現pci_bus_type,scsi_bus_type,usb_bus_type,他們都是 struct bus_type 類型的變量.而struct bus_type 結構中兩個非常重要的成員就是 struct kset drivers 和 struct kset devices。kset 和另一個叫做 kobject 正是 Linux Kernel 2.6 中設備模型的基本元素。


        這裏我們只需要知道,drivers 和 devices 的存在,讓struct bus_type 與兩個鏈表聯繫了起來,一個是 devices 的鏈表,一個是 drivers 的鏈表,也就是說,知道一條總線所對應的數據結構,就可以找到這條總線所關聯的設備有哪些,又有哪些支持這類設備的驅動程序.
 

         而要實現這些,就要求每次出現一個設備就要向總線彙報,或者說註冊,每次出現一個驅動,也要向總線彙報,或者說註冊.比如系統初始化的時候,會掃描連接了哪些設備,併爲每一個設備建立起一個 struct device 的變量,每一次有一個驅動程序,就要準備一個 struct device_driver 結構的變量.把這些變量統統加入相應的鏈表,device 插入 devices 鏈表,driver 插入 drivers 鏈表. 這樣通過總線就能找到每一個設備,每一個驅動。


        struct bus_type 中爲 devices 和 drivers 準備了兩個鏈表,而代表 device 的結構體 struct device 中又有兩個成員,struct bus_type *bus 和 struct device_driver *driver。同樣,代表driver 的結構體 struct device_driver 同樣有兩個成員,struct bus_type *bus 和 struct list_head devices。struct device 和 struct device_driver 的定義和 struct bus_type 一樣,在 include/linux/device.h 中。憑一種男人的直覺,可以知曉,struct device 中的 bus 記錄的是這個設備連在哪條總線上,driver 記錄的是這個設備用的是哪個驅動,反過來,struct device_driver 中的 bus 代表的也是這個驅動屬於哪條總線,devices 記錄的是這個驅動支持的那些設備,沒錯,是 devices(複數),而不是 device(單數),因爲一個驅動程序可以支持一個或多個設備,反過來一個設備則只會綁定給一個驅動程序。




上面是理論知識,下面我們具體以展訊平臺的I2C設備重力加速度傳感器MC3XXX驅動爲例進行實例分析:


1. 首先在Board-sp7715ga.c文件中通過sc8810_add_i2c_devices函數(其實是i2c_register_board_info函數)將指定的I2C設備(         MC3XXX_ACC_I2C_NAME, MC3XXX_ACC_I2C_ADDR---包括設備名和設備地址)申請一個 I2C struct device 結構,並且掛入I2C總線中的 devices 鏈表中來。
 

注意:該device掛入devices鏈表的操作會先於I2C設備驅動加載的操作被執行。i2c_register_board_info函數對此進行了特別說明:

/* Systems using the Linux I2C driver stack can declare tables of board info while they initialize.  This should be done in board-specific init code near arch_initcall() time, or equivalent, before any I2C adapter driver is registered.  For example, mainboard init code could define several devices, as could the init code for each daughtercard in a board stack.

 *

 * The I2C devices will be created later, after the adapter for the relevant bus has been registered.  After that moment, standard driver model tools are used to bind "new style" I2C drivers to the devices.  The bus number for any device declared using this routine is not available for dynamic allocation.

 */
 

2.具體的驅動程序在Mc3xxx.c文件中。module_init(mc3xxx_i2c_init)被稱爲驅動程序的初始化入口(driver initialization entry point)。當我們使用 insmod 這個命令去安裝的時候(內核配置該模塊爲 M)或者程序啓動運行到該模塊的時候(內核配置爲Y,該模塊編譯入內核),module_init()註冊的函數mc3xxx_i2c_init將會被執行。

 

3. mc3xxx_i2c_init函數調用了i2c_add_driver(&mc3xxx_driver)命令,開始註冊其 struct device_driver 結構,然後它去I2C總線的 devices 鏈表(注意:該鏈表在步驟1中已被更新入mc3xxx設備)中去尋找(遍歷),去尋找每一個還沒有綁定 driver 的設備,即 struct device 中的 struct device_driver 指針仍爲空的設備,然後它會去觀察這種設備的特徵(mc3xxx_driver 驅動中的id_table表,第4點對此進行專門的說明),看是否是他所支持的設備,如果是,那麼調用一個叫做 device_bind_driver 的函數,將設備與驅動進行綁定.換句話說,把 struct device 中的 struct device_driver driver 指向這個mc3xxx_driver,而 struct device_driver mc3xxx_driver 把 struct device 加入他的那張 struct list_head devices 鏈表中來.就這樣,bus,device,和 driver,這三者之間或者說他們中的兩兩之間,就給聯繫上了.

 

4. 一個 driver 可以支持多個 device,一個linux系統裏也有不同的device和driver。那麼當發現一個 device 的時候,如何知道哪個 driver纔是她的 Mr.Right 呢?這就是 id_table 的用處,讓 struct mc3xxx_driver 準備一張表,裏邊註明該 driver 支持哪些設備,這總可以了吧.如果你這個設備屬於這張表裏的,那麼 ok,綁定吧。
 

實際上id_table這個結構體對每一個設備來說,就相當於是她的身份證,記錄了她的一些基本信息,通常我們的身份證上會記錄我們的姓名,性別,出生年月,戶口地址等等,而 linux各種 設備她也有她需要記錄的信息,以區分她和別的 linux 設備,比如 Vendor-廠家,Product-產品,以及其他一些比如產品編號,產品的類別,遵循的協議等。
 

於是我們知道,一個 I2C driver 會把它的這張 id 表去和每一個 I2C 設備的實際情況進行比較,如果該設備的實際情況和這張表裏的某一個 id 相同,準確地說,只有這許多特徵都吻合,才能夠把一個 I2C device 和這個 I2C driver 進行綁定,這些特徵哪怕差一點也不行。

.
 

附:mc3xxx驅動結構體

static struct i2c_driver mc3xxx_driver = {

         .driver = {

                   .name = MC3XXX_DEV_NAME,

                   .owner = THIS_MODULE,

         },

         .probe    = mc3xxx_i2c_probe,

         .remove   =mc3xxx_i2c_remove,

         .suspend  = mc3xxx_i2c_suspend,

         .resume   = mc3xxx_i2c_resume,

         .id_table = mc3xxx_id,

};

在模塊被正常加載後,使用lsmod命令時能查看到一個模塊名爲MC3XXX_DEV_NAME的模塊(注意:模塊名並不等於設備名)。owner 這玩藝是用來給模塊計數的,每個模塊都這麼用,賦值總是THIS_MODULE。因爲一個模塊可以被多個設備共用,纔會有模塊計數這麼一個說法.
 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章