Linux設備驅動模型底層架構及組織方式


1、什么是設備驅動模型?

設備驅動模型,說實話這個概念真的不好解釋,他是一個比較抽象的概念,我在網上也是沒有找到關於設備驅動模型的一個定義,那么今天就我所學、所了解

到的,我對設備驅動模型的一個理解:設備驅動模型其實是Linux內核為了管理硬件上的設備和對應的驅動制定的一套軟件體系。那么其實設備驅動模型是一個比較

抽象、比較廣的一個概念,一兩句話是很難說清楚的,類(class)、總線(bus)、設備(device)、驅動(driver)、mdev(自動創建設備節點和設備類)

sysfs等都屬於設備驅動模型的范疇。

 

2、為什么需要有設備驅動模型?

早期內核(2.4之前)沒有的設備驅動模型的概念,但照樣可以用;而到了2.6版本正式引入了設備驅動模型,因為實際的硬件設備越來越多,很多設備都有功耗要求等的

一些新特點,這就導致內核必須有一套更加優秀、更加易用的驅動體系來對設備進行分工管理,這就引入了我們這里所說的設備驅動模型。

設備驅動模型負責統一實現和維護一些特性,諸如:電源管理、熱插拔、對象生命周期、用戶空間和驅動空間的交互等基礎設施(sysfs),設備驅動模型目的是簡化驅動

程序編寫、管理設備和驅動,但是客觀上設備驅動模型本身設計和實現很復雜。其實這也就印證了一句話:越好用的東西,其實實現起來就越復雜。

 

3、設備驅動模型的底層架構

(1)kobject結構體 (include\linux\kobject.h)

kobject結構體是設備驅動模型底層的一個結構體,這個結構體是設備驅動模型下的所有對象的一個基本單元,他是對設備驅動模型下所有對象抽象出來的共有的部分;

kobject結構體提供了一些公共型的服務:對象引用計數、維護對象鏈表、對象上鎖、對用戶空間的表示。

設備驅動模型中的各種對象其內部都會包含一個kobject,地位相當於面向對象思想中的總基類。

(2)kobject結構體元素分析

 1 struct kobject {
 2     const char        *name;                   //   對象的名字
 3     struct list_head    entry;                 //   用來指向平行關系中的下一個kobject結構體對象(可以理解為同一個目錄下的多個文件或者文件夾給他們鏈接起來)
 4     struct kobject        *parent;             //   用來指向父類對象(也就是他的上一層文件夾所對應的對象)
 5     struct kset        *kset;                  //   用來指向父類對象的kset
 6     struct kobj_type    *ktype;                //   指向一個kobj_type對象
 7     struct sysfs_dirent    *sd;
 8     struct kref        kref;                   //   kobject的引用計數
 9     unsigned int state_initialized:1;          //   該對象是否被初始化了  的狀態標志位
10     unsigned int state_in_sysfs:1;
11     unsigned int state_add_uevent_sent:1;
12     unsigned int state_remove_uevent_sent:1;
13     unsigned int uevent_suppress:1;
14 };

 

(3)kset結構體元素分析  (include\linux\kobject.h)

 

1 struct kset {
2     struct list_head list;                    //   用來鏈接該目錄下的所有kobject對象
3     spinlock_t list_lock;                    //   自旋鎖
4     struct kobject kobj;                    //   這個kobject就是本目錄對應的對象結構體
5     const struct kset_uevent_ops *uevent_ops;
6 };

 

從kset結構體可以看出來,kobject其實是被kset包含了,kset的主要作用是做頂層kobject的容器類,kset的主要目的是將各個kobject

(代表着各個對象)組織出目錄層次架構,可以認為kset就是為了在sysfs中弄出目錄,從而讓設備驅動模型中的多個對象能夠有層次有邏輯性的組織在一起。

可以這樣理解只要是sysfs中的一個文件夾形式的對象必須是被kset包含進來的。

(4)kset和kobject之間的關系

kset與kobject的關系:kset是kobject的一個容器,可以認為kset是一個目錄,而kobject是這個目錄下的各個文件,如果這個kset目錄下還有子目錄,那么這個

目錄下就會存在子kset。

         

kset與kobject、kobject與kobject、kset與kset之間的連接關系:

     (1)平行關系下的連接關系:用kobject對象A、kset對象B進行說明

               (1)kobject與kobject的連接關系:A通過list_head結構體中的next指針指向下一個kobject對象的list_head結構體(如果A是一個鏈表尾,則next

                   指向他上層的kset對象中的list_head結構體),通過list_head結構體中的prev指針指向上一個kobject對象的list_head結構體(如果A是一個鏈

                   表頭,則prev指向他上層的kset對象中的list_head結構體)。

               (2)kset與kobject的連接關系:B通過嵌入內部的kobject結構體中的list_head結構體中的next指針指向下一個kobject對象的list_head結構體(如果

                   B是一個鏈表尾,則next指向他的上層的kset中的list_head結構體),通過list_head結構體中的prev指針指向上一個kobject對象的list_head

                   結構體(如果B是一個鏈表頭,則prev指向他上層的kset中的list_head結構體)。

               (3)kset與kset之間的連接關系:B通過嵌入內部的kobject結構體中的list_head結構體中的next指針指向下一個kset中的kobject結構體中的list_head

                   結構體(如果B是一個鏈表尾,則next指向他的上層的kset中的list_head結構體),通過list_head結構體中的prev指針指向上一個kset中的

                   kobject結構體中的list_head結構體(如果B是一個鏈表頭,則prev指向他上層的kset中的list_head結構體)。

     所以從上面可以看出來,平行關系的連接是通過kobject下的list_head結構體進行連接的,父類kset是通過kset下的list_head連接他下面的所有對象組成的鏈表頭和鏈表尾。

     (2)上下級關系下的連接關系:屬於kset容器下的所有kobject對象通過parent指針指向父類kset中的kobject結構體,通過kset指針指向父類kset。

          屬於kset容器下的所有kset子容器通過他下面的kobject結構體中parent指針指向父類kset中的kobject結構體,通過kobject結構體中的kset指針指向父類kset容器。

(5)kobj_type結構體

很多書中簡稱為ktype,每一個kobject都需要綁定一個ktype來提供相應功能

關鍵點1:sysfs_ops,提供該對象在sysfs中的操作方法(show和store)
關鍵點2:attribute,提供在sysfs中以文件形式存在的屬性,其實就是應用接口

(6)kset、kobject、kobj_type3者之間的關系

 

4、設備驅動模型下的總線式設備驅動的組織方式

(1)總線

總線其實是物理上本來就有的,他的英文名bus(公共汽車),總線的作用是將總線兩端的設備連接起來,使他們能夠通過總線進行通信(數據交互);那么驅動模型中

引入總線其實是借用了總線式的設計優點,與物理總線形成一個對應關系,能夠更加方便的管理硬件設備。

(1.1)bus_type結構體 (include\linux\device.h)

bus_type結構體是總線中用來創建一類總線時需要到的結構體,實際的總線根據自身的特點和要求對結構體進行填充形成具體的總線。

 1 struct bus_type {
 2     const char        *name;                         //  總線名字
 3     struct bus_attribute    *bus_attrs;                   //  該總線的屬性
 4     struct device_attribute    *dev_attrs;           //  該總線下設備的屬性
 5     struct driver_attribute    *drv_attrs;           //  該總線下設備驅動的屬性
 6 
 7     int (*match)(struct device *dev, struct device_driver *drv);            //  該總線下設備與設備驅動的匹配函數
 8     int (*uevent)(struct device *dev, struct kobj_uevent_env *env);      //   事件函數    熱撥插
 9     int (*probe)(struct device *dev);                                                      //   總線下的  探針函數
10     int (*remove)(struct device *dev);
11     void (*shutdown)(struct device *dev);
12 
13     int (*suspend)(struct device *dev, pm_message_t state);
14     int (*resume)(struct device *dev);
15 
16     const struct dev_pm_ops *pm;     //  電源管理相關的
17 
18     struct bus_type_private *p;           //  總線的私有數據  p->subsys.kobj 表示該總線在驅動模型中對應的對象
19 };

例如內核中ac97總線的定義:                                                            usb總線的定義  

  

(2)設備

(2.1)設備驅動模型中的總線式設計中會包含設備和驅動兩個,例如對於usb總線:usb_device和usb_driver ;對於platform總線:platform_device和platform_driver

他們中會包含相應的基類設備和基類驅動:struct device和struct device_driver,就好比和上面的驅動模型中所有對象都包含了基類kobject,所以對於所有具體總線

中的設備結構體中包含了基類struct device,驅動結構體中包含了基類struct device_driver。

而本段談論的是struct device結構體(include\linux\device.h),也就是設備。

(2.2)struct device是硬件設備在內核驅動框架中的抽象

(2.3)通常device不會單獨使用,而是被包含在一個具體設備結構體中,如struct usb_device。就是上面說的被包含到一個具體的總線下的設備結構體中

 1 struct device {
 2     struct device *parent;
 3     struct device_private *p;
 4     struct kobject kobj;         /* 包含一個kobject結構體,因為屬於設備驅動模型中的一個對象 */
 5     const char *init_name; 
 6     struct device_type *type;
 7     struct mutex mutex;    
 8     struct bus_type    *bus;        
 9     struct device_driver *driver;    
10     void *platform_data;        /* 這個是一個void類型的指針,用來指向這個設備特有的數據結構,數據結構類型由自己的定義,后面的led驅動就是使用了這個自定義結構體來存放設備的信息:GPIO等,而沒有使用總線下的資源結構體 */
11     struct dev_pm_info power;
12 
13 #ifdef CONFIG_NUMA
14     int        numa_node;    
15 #endif
16 
17     u64    *dma_mask;    
18     u64    coherent_dma_mask;
19     struct device_dma_parameters *dma_parms;
20     struct list_head dma_pools;    
21     struct dma_coherent_mem    *dma_mem; 
22     struct dev_archdata    archdata;
23     
24 #ifdef CONFIG_OF
25     struct device_node *of_node;
26 #endif
27 
28     dev_t devt;    
29     spinlock_t devres_lock;
30     struct list_head devres_head;
31     struct klist_node knode_class;
32     struct class *class;
33     void  (*release)(struct device *dev);  /* 當我們卸載設備時調用時調用的函數 */
34 };

(3)驅動(struct device_driver)(include\linux\device.h)

跟上面一樣,struct device_driver是驅動程序在內核驅動模型中的抽象,結構體如下:重點是關注name和probe

(4)類(class)

class的真正意義在於作為同屬於一個class的多個設備的容器。也就是說,class是一種人造概念,目的就是為了對各種設備進行分類管理。當然,class在分類的

同時還對每個類貼上了一些“標簽”,這也是設備驅動模型為我們寫驅動提供的基礎設施。所以一個設備可以從類這個角度講他是屬於哪個類也可以從總線的角度講

他是掛在哪個總線下的

mdev(嵌入式中用於自動創建設備節點和設備類的工具)的使用離不開class。

(4.1)相關結構體:struct class (include\linux\device.h)和 struct class_device。

 

5、設備驅動模型和設備驅動框架的關系

剛開始學設備驅動模型的時候,很容易搞混,我看老師有時候會把這兩個概念搞在一起,我覺得有必要對這兩個概念做一下區別。(以下純屬於我自己的理解)

(1)適用性范圍:設備驅動框架其實是針對某一類具體的硬件設備而言的,不同的硬件設備都有針對他們本身特點的驅動框架,例如led就會存在led的驅動框架,flash就會有flash的

驅動框架,你就不能把led的驅動框架用在flash硬件設備上。而設備驅動模型之前說了它是用來管理硬件設備、使得驅動編寫更加簡單的一種軟件體系,本身這套體系是很復雜的

,這套體系只有一套,能夠對所有的硬件設備都是可行的。

(2)作用:設備驅動框架是內核中維護驅動相關的工程師們針對不同的硬件設備寫的標准接口,他們已經將硬件設備相同的屬性都封裝起來了,留出來一些接口給我們具體的驅動開發工程師

來使用,其實這些標准接口也是使用最原始的內核提供的編寫驅動相關的接口函數封裝而來的,僅僅是為了使得驅動開發變得簡單。而設備驅動模型是內核中構建實現的一套復雜的軟件體系,

他的作用並不僅僅限於用在驅動的開發上,更多的是用來設備和設備驅動的管理上,也是為了應對現在越來越多的新特性。

(3)需要注意的是:我們是可以不基於驅動模型和不使用驅動框架來編寫一個驅動程序,其實剛開始學習驅動的時候寫的驅動就直接使用內核提供的最原始的接口函數來編寫驅動的,

這一點是沒有問題的,驅動也是能夠正常運行的,當然對於復雜的設備可能比較困難。

 

好了,今天就寫這么多了,從早上寫到吃晚飯,感覺都快虛脫了。

參考:《朱友鵬嵌入式Linux開發\5.Linux驅動開發\5.5.linux設備驅動模型》

        http://blog.chinaunix.net/uid-24227137-id-3266449.html

        http://www.cnblogs.com/hello2mhb/archive/2013/10/23/3365204.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM