【亞嵌】手把手教你如何編寫Linux操作系統的設備驅動程序(二)

接上篇

一個極其簡單的字符設備可以說寫好了,文件名就叫test。c吧。下面編譯

 


得到文件test。o就是一個設備驅動程序。如果設備驅動程序有多個文件,把每個文件按上面的命令行編譯,然後:

 


驅動程序已經編譯好了,現在把它安裝到系統中去。

 


如果安裝成功,在/proc/devices文件中就可以看到設備test,並可以看到它的主設備號,要卸載的話,運行

 


下一步要創建設備文件。

 


c 是指字符設備,major是主設備號,就是在/proc/devices裏看到的。用shell命令

 


就可以獲得主設備號,可以把上面的命令行加入你的shell script中去。minor是從設備號,設置成0就可以了。我們現在可以通過設備文件來訪問我們的驅動程序。寫一個小小的測試程序。

 


編譯運行,看看是不是打印出全1 ?

以上只是一個簡單的演示。真正實用的驅動程序要複雜的多,要處理如中斷,DMA,I/O port等問題。這些纔是真正的難點。請看下節,實際情況的處理。

三 設備驅動程序中的一些具體問題

1 I/O Port

和硬件打交道離不開I/O Port,老的ISA設備經常是佔用實際的I/O端口,在linux下,操作系統沒有對I/O口屏蔽,也就是說,任何驅動程序都可以對任意的I/O口操作,這樣就很容易引起混亂。每個驅動程序應該自己避免誤用端口。有兩個重要的kernel函數可以保證驅動程序做到這一點。

1)check_region(int io_port, int off_set)

這個函數察看系統的I/O表,看是否有別的驅動程序佔用某一段I/O口。

參數1:io端口的基地址,

參數2:io端口占用的範圍。

返回值:0 沒有佔用, 非0,已經被佔用。

2)request_region(int io_port, int off_set,char *devname)

如果這段I/O端口沒有被佔用,在我們的驅動程序中就可以使用它。在使用之前,必須向系統登記,以防止被其他程序佔用。登記後,在/proc/ioports文件中可以看到你登記的io口。

參數1:io端口的基地址。

參數2:io端口占用的範圍。

參數3:使用這段io地址的設備名。

在對I/O口登記後,就可以放心地用inb(), outb()之類的函來訪問了。在一些pci設備中,I/O端口被映射到一段內存中去,要訪問這些端口就相當於訪問一段內存。經常性的,我們要獲得一塊內存的物理地址。在dos環境下,(之所以不說是dos操作系統是因爲我認爲DOS根本就不是一個操作系統,它實在是太簡單,太不安全了)只要用段:偏移就可以了。在window95中,95ddk提供了一個vmm 調用 _MapLinearToPhys,用以把線性地址轉化爲物理地址。但在Linux中是怎樣做的呢?

2 內存操作

在設備驅動程序中動態開闢內存,不是用malloc,而是kmalloc,或者用get_free_pages直接申請頁。釋放內存用的是kfree,或free_pages。 請注意,kmalloc等函數返回的是物理地址!而malloc等返回的是線性地址!關於kmalloc返回的是物理地址這一點本人有點不太明白:既然從線性地址到物理地址的轉換是由386cpu硬件完成的,那樣彙編指令的操作數應該是線性地址,驅動程序同樣也不能直接使用物理地址而是線性地址。但是事實上kmalloc返回的確實是物理地址,而且也可以直接通過它訪問實際的RAM,我想這樣可以由兩種解釋,一種是在覈心態禁止分頁,但是這好像不太現實;另一種是linux的頁目錄和頁表項設計得正好使得物理地址等同於線性地址。我的想法不知對不對,還請高手指教。

言歸正傳,要注意kmalloc最大隻能開闢128k-16,16個字節是被頁描述符結構佔用了。kmalloc用法參見khg。

內存映射的I/O口,寄存器或者是硬件設備的RAM(如顯存)一般佔用F0000000以上的地址空間。在驅動程序中不能直接訪問,要通過kernel函數vremap獲得重新映射以後的地址。另外,很多硬件需要一塊比較大的連續內存用作DMA傳送。這塊內存需要一直駐留在內存,不能被交換到文件中去。但是kmalloc最多隻能開闢128k的內存。這可以通過犧牲一些系統內存的方法來解決。具體做法是:比如說你的機器由32M的內存,在lilo。conf的啓動參數中加上mem=30M,這樣linux就認爲你的機器只有30M的內存,剩下的2M內存在vremap之後就可以爲DMA所用了。

請記住,用vremap映射後的內存,不用時應用unremap釋放,否則會浪費頁表。

3 中斷處理

同處理I/O端口一樣,要使用一箇中斷,必須先向系統登記。

 


irq: 是要申請的中斷。

handle:中斷處理函數指針。

flags:SA_INTERRUPT 請求一個快速中斷,0 正常中斷。

device:設備名。

如果登記成功,返回0,這時在/proc/interrupts文件中可以看你請求的中斷。

4一些常見的問題

對硬件操作,有時時序很重要。但是如果用C語言寫一些低級的硬件操作的話,gcc往往會對你的程序進行優化,這樣時序就錯掉了。如果用匯編寫呢,gcc同樣會對彙編代碼進行優化,除非你用volatile關鍵字修飾。最保險的辦法是禁止優化。這當然只能對一部分你自己編寫的代碼。如果對所有的代碼

都不優化,你會發現驅動程序根本無法裝載。這是因爲在編譯驅動程序時要用到gcc的一些擴展特性,而這些擴展特性必須在加了優化選項之後才能體現出來。

關於kernel的調試工具,我現在還沒有發現有合適的。有誰知道請告訴我,不勝感激。我一直都在printk打印調試信息,倒也還湊合。關於設備驅動程序還有很多內容,如等待/喚醒機制,塊設備的編寫等。

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