從這一章開始,我們將詳細的介紹Linux的設備驅動模型。Linux設備驅動模型是一個相當複雜的系統,對於初學者來說真有些無從入手。而且更加困難的是,隨着新的Linux
Kernel的release,Linux的設備驅動模型總會有或大或小的變化,我們將盡量展現 Linux Kernel
的這種變化。
早期的Linux內核(版本2.4之前)並沒有實現一個統一的設備模型,設備節點的創建一般是mknod命令手動創建或利用devfs文件系統創建。早期
的Linux發行版一般會採用手動創建的方式預先把通常用到的節點都創建出來,而嵌入式系統則會採用devfs的方式。起初Linux
2.6
內核還支持devfs,但從2.6.18開始,內核完全移除了devfs系統而採用的udev的方式動態的創建設備節點。因此,新的Linux發行版都採
用udev的方式管理設備節點文件。(關於udev的詳細信息,請參考:http://www.kernel.org/pub/linux/utils/kernel/hotplug/udev.html
)。
Linux2.6設備驅動模型的基本元素是Class、Bus、Device、Driver,下面我們分別介紹各個部分。
Class 和Class
Device
驅動模型最基本的概念是設備及其類別,Linux中使用struct class 和struct
class_device來管理不同類別的設備。由於設備驅動模型是一個複雜的系統,我們還是從一個簡單的例子開始介紹,然後在逐步展開。其實實現設備節點的動態創建是一個很簡單的事情,並不需要太多的代碼。我們修改我們的驅動初始化函數如下:
#include
<linux/device.h> |
重新編譯這個驅動程序,當加載這個驅動到內核中時,系統(一般是hotplug和udev系統)就會自動的創建我們指定的設備名字:/dev/hello,同時,你也可以發現在sysfs系統中添加了新的文件:/sys/class/hello/hello/。
當然並不需要把所有的代碼都貼到這裏,但是這樣做可能更加清楚。我們把添加的代碼顯示成藍色,這樣你可以清楚的看到代碼的簡單程度。這裏主要用到了class_create
和class_device_create函數,它們定義在<linux/device.h>頭文件中。
extern struct class *class_create(struct module
*owner, const char *name); |
Linux是通過設備與設備類來管理設備的,當你調用這些函數向系統註冊設備及其類的時候,內核會自動的在sysfs文件系統中創建對應的文件。如果想了
解更多的關於設備及其類的函數接口,請你閱讀Linux
kernel的源文件(include/device.h頭文件和drivers/base/class.c實現文件)。這裏簡單說明
class_device_create函數,它的最後一個參數是一個變參,類似於printf函數參數,用於指定添加設備的名稱也就是顯示在/dev
/目錄下設備文件名稱。
創建的class_device設備會自動註冊到系統中,這樣對於給定的設備,系統會自動找到匹配的設備驅動。
你可以在設備類或設備目錄下(/sys/class)創建文件,這個文件提供了內核同用戶空間程序的交互接口。內核提供了設備、設備類的內核函數接口,這些接口也定義在<include/device.h>頭文件中。
int class_create_file(struct class *cls, const
struct class_attribute *attr); |
其實,這些函數僅僅是簡單的封裝了sysfs文件系統函數,但它爲設備及設備類提供了統一的函數接口。
Drivers
當一個設備註冊到系統中時,它就向系統表明了哪個驅動匹配這個設備。Linux內核會在註冊的設備驅動中查找匹配的驅動並調用對應的探測函數(probe)來初始化設備。設備驅動接口也定義在<linux/device.h>頭文件中。
struct device_driver { |
其中,比較重要的數據是name,Linux內核就是根據name來匹配設備與驅動的。probe函數用於設備的探測及初始化,remove函數在設備移出系統時被觸發調用。
一般來說,我們的驅動需要實現name、module、probe、remove函數。
Bus
通常我們的驅動並不需要實現Bus接口,也沒有這個必要,因此你完全可以跳過這段,除非你想添加一個新的總線到系統中。
在Linux2.6內核中,struct
bus_type描述了一個bus對象,它定義在<linux/device.h>頭文件中。(你發現沒有,到目前Linux設備模型接口都定義在這個文件中)
struct bus_type { |
在這個結構中,name描述了bus的名字,如它會顯示在/sys/bus/中。match函數用於匹配屬於這個bus的設備和驅動,uevent用於處理Linux
uevent事件。probe和remove類似與driver的函數接口,主要用於設備的Hotplug處理。其他的函數是Power相關的函數接口。
同樣,bus也需要註冊到系統中,並可以在sysfs中創建文件。
sysfs文件系統
sysfs類似於proc文件系統,用於用戶空間程序和內核空間交互數據的接口。但sysfs提供了更多的功能,其中之一就是顯示Linux驅動程序模型的分層結構關係。Ubuntu
804的sysfs文件系統的目錄顯示如下:
當你瀏覽這個文件系統的時候,你會發現裏面有很多鏈接文件,其實正是這些鏈接文件展現了Linux驅動模型各個組成部分之間的關係。
sysfs文件系統中,最重要的就是struct attribute結構,它被用來管理內核sysfs文件的接口(名字,屬性,讀寫函數等)。內核sysfs提供了基本的attribute接口,不同的 設備如bus、device在基本attribute的基礎上定義了自己的讀寫函數,sysfs提供了對應的宏來簡化屬性的操作。請參 考<linux/sysfs.h>頭文件中。
struct attribute {
|
我們看到,sysfs的struct attribute結構本身並不包含讀寫訪問函數,驅動模型的各個部分都會擴展這個結構並定義自己的屬性結構來引入各自的操作函數,如 class:(這個結構定義在<linux/device.h>頭文件中)。
struct class_attribute { |
關於sysfs的更多信息,請參考 Linux內核源代碼樹中的Documentation/filesystems/sysfs.txt文件。
Platform總線
platform總線是Linux內核中的一個虛擬總線,它使得設備的管理更加簡單化。目前大部分的驅動都是用platform總線來寫的。 platform總線模型的各個部分都是繼承自Device模型(姑且這麼說吧),它在系統內實現了個虛擬的總線,即platform_bus,如果你的 設備需要platform總線管理,那麼就需要向系統中註冊platform設備及其驅動程序。就像前面所介紹的那樣,platform總線分爲 platform_bus, platform_device 和platform_driver幾個部分,他們的接口定義在<linux/platform.h>頭文件中。
- platform bus
struct device platform_bus = { |
platform_bus數據結構描述了platform bus設備,platform_bus_type描述了platform bus總線,它提供了platform總線設備和驅動的匹配函數。platform總線是由函數platform_bus_init(void)初始化的。
對於Linux我們一般的設備驅動程序來說,就像前面Bus一段提到的那樣,我們不需要關心platform總線本身,我們只要調用我們的設備和驅動接口就可以了。
- Platform Device
int platform_device_add(struct platform_device
*pdev); |
- Platform Driver
struct platform_driver { |
很顯然,它“繼承”自struct device_driver ,同樣類似於struct device_driver ,一般我們需要實現probe函數,及指定platform_driver能驅動的設備的名字。
- 使用Platform總線
...... |
static struct class *timed_gpio_class; |
Kobject和kset
提到Linux的設備模型,就不得不提kobject和kset這兩個內核對象,他們纔是Linux內核設備模型的最基礎的結構,但講解他們卻是一個枯燥 過程,限於篇幅,這個就不作介紹了,請參考Linux文檔<documentation/kobject.txt>。
後記
在這裏,我們簡單的介紹了Linux的設備模型,包括基本總線、設備、驅動的關係,同時也簡單的介紹了Linux2.6內核的platform總線。這些內容應該足夠讓你瞭解如何使用Linux設備模型來管理設備了。