嵌入式Linux設備驅動程序開發

嵌入式Linux以其可應用於多種硬件平臺,內核高效穩定,源碼開放,軟件豐富,網絡通信和文件管理機制完善等優良特性,成為了嵌入式系統領域中的一個研究熱點。

嵌入式Linux系統中,內核提供保護機制,用戶空間的進程一般不能直接訪問硬件,進行嵌入式系統的開發,很大的工作量視為各種設備編寫驅動程序,除非系統不適用操作系統。

嵌入式Linux設備驅動程序開發流程

三類主要的設備文件類型:塊設備、字符設備和網絡設備。這種分類方法可以將控制不同的輸入/輸出設備的驅動程序與其他操作系統軟件分離開來。

字符設備與塊設備的主要區別是:在對字符設備發出讀/寫請求時,實際的硬件I/O一般緊接著發生;塊設備則不然,它利用用一塊系統內存做緩衝區,若用戶進程對設備的請求能滿足用戶的要求,就返回請求的數據,否則,就調用請求函數來進行實際的I/O操作。

網絡設備可以通過BSD套接口訪問數據。

BSD Socket 是UNIX系統中通用的網絡接口,它不僅支持各種不同的網絡類型,而且也是一種內部進程之間的通信機制。兩個通信進程都用一個 套接口來描述通信鏈路的兩端。套接口可以認為是一種特殊的管道,但和管道不同的是,套接口對於可以容納的數據的大小沒有限制。

Linux支持多種類型的套接口,也叫做套接口尋址族,這是因為每種類型的套接口都有自己的尋址方法。

Linux支持以下的套接口類型:

- UNIX UNIX域套接口

- INET Internet地址族TCP/IP協議支持通信。

- AX25 Amateur radio X25

- IPX Novell IPX

- APPLE TALK Appletalk DDP

- X25 X25

Linux的BSD套接口支持下面的幾種套接口類型:

1. 流式(stream)這些套接口提供了可靠的雙向順序數據流連接。它們可以保證數據傳輸中的完整性、正確性和單一性。INET尋址族中的TCP協議支持這種類型的套接口。

2. 數據報(Datagram)這種類型的套接口也可以像流式套接口一樣提供雙向的數據傳輸,但它們不能保證傳輸的數據一定能夠到達目的節點。即使數據能夠到達,也無法保證數據以正確的順序到達以及數據的單一性、正確性。UDP協議支持這種類型的套接口。

3. 原始(Raw)這種類型的套接口允許進程直接存取下層的協議。

4. 可靠遞送消息(Reliable Delivered Messages)這種套接口和數據報套接口一樣,只能保證數據的到達。

5. 順序數據包(Sequenced Packets)這種套接口和流式套接口相同,除了數據包的大小是固定的。

6. 數據包(Packet)這不是標準的BSD 套接口類型,而是Linux中的一種擴展。它允許進程直接存取設備層的數據包。

驅動程序共有的特性:

  1. 讀/寫:具體的讀/寫操作由驅動程序屏蔽掉了,操作系統定義好一些讀/寫接口,由驅動程序完成具體的功能,在驅動程序初始化時,需要把具有這種接口的讀/寫函數註冊到操作系統。

  2. 中斷:操作系統提供驅動程序響應中斷的能力,一般是把一箇中斷處理程序註冊到系統中,操作系統在硬件終端發生後,調用驅動程序的處理程序,Linux支持中斷的共享,即多個設備共享一箇中斷。

  3. 時鐘:操作系統應為驅動程序提供時鐘機智,一般是在預定的時間過了以後,回調註冊的時鐘函數。

實現一個嵌入式Linux設備驅動的大致流程如下:

  • 1. 定義主、次設備號,也可以動態獲取;

  • 2. 實現驅動初始化和清除函數,如果驅動程序採用模塊方式,則要實現模塊初始化和清除函數;

  • 3. 設計所要實現的文件操作,定義file_operation結構;

  • 4. 實現所需的文件操作調用,如read、write等;

  • 5. 實現終端服務函數,並用request_irq想內核註冊;

  • 6. 將驅動編譯到內核或編譯成模塊,用insmod命令加載;

  • 7. 生成設備節點文件。

設備文件的操作要複雜得多,所有其他類型的操作都可以通過VFS的ioctl低啊用來執行。為此,只需要在驅動程序中實現ioctl函數,並在其中添加相應的case即可,通過cmd區分操作,通過arg傳遞參數和結果。

模塊化驅動程序設計及框架

Linux的內核是一個整體式內核(monolithic kernel,與之對應的是微內核,micro kernel),即所有的內核功能連接在一起,在同一個地址空間執行,但完全這樣做會帶來很多不便和浪費,Linux操作系統提供了一種機制,即內核模塊來解決這個問題。

模塊是內核的一部分,而且都是設備驅動程序,但它們並沒有編譯到內核中,而是被分別編譯並鏈接成一組目標文件,根據需要動態載入模塊可以保證內核達到最小,並且具有很大的靈活性。

內核魔鎧一部分保存在kernel中,另一部分在modules包中。

兩個重要的函數

內核為以後處理某些請求而註冊自己,完成這個任務後,它的“主”函數就立即終止了,模塊的作用就是擴展內核的功能,運行在內核中模塊化的代碼,通常,一個設備驅動程序完成兩個任務:模塊的某些函數作為系統調用執行,而另一些函數則負責處理中斷。

- init_module():在模塊調入內核時調用,它在內核中註冊一定的功能函數,在註冊之後,如果有程序訪問內核模塊的某個功能,內核將查表獲得該功能在module中的位置,然後調用功能函數。

- cleanup_module():在模塊從內核中卸載時被調用,它把以前註冊的功能函數卸載掉。

init_module()函數在運行insmod命令後由系統調用,完成驅動模塊的初始化工作,clesnup_module()函數在運行rmmod命令後有系統調用。

Linux模塊調用如圖所示:

嵌入式Linux設備驅動程序開發


設備驅動加到嵌入式Linux內核中

嵌入式Linux設備驅動程序編寫完成後,需要將該驅動程序加到內核中,這要求修改嵌入式Linux的源代碼,然後重新編譯內核。具體步驟如下:

1. 將設備驅動程序文件(如:mydriver.c)複製到/linux/drivers/char/目錄下。該目錄保存了Linux下字符設備的設備驅動程序。修改該目錄下mem.c文件,在int chr_dev_init()函數中增加如下代碼:

#地方的發CONFIG_MYDRIVER

device_init();

#endif

其中,CONFIG_MYDRIVER是在配置Linux內核時賦值的。

2. 在/linux/drivers/char/目錄下Makefile中增加如下代碼:

ifeq($(CONFIG_MYDRIVER), y)

L_OBJS += mydriver.o

endif

如果在配置Linux內核時選擇了支持新定義的設備,則在編譯內核是會編譯mydriver.c生成mydriver.o文件。

3. 修改/linux/drivers/char/目錄下config.in文件,在comment character devices語句下面加上bool support for mydriver CONFIG_MYDRIVER。這樣,若內核編譯,運行make config、make menuconfig或make xconfig,那麼在配置字符設備時就會有選項support for mydriver,當選中這個選項時,設備驅動就加到內核中了。

4. 重新編譯內核,在shell中將當前目錄cd到lLinux目錄下,然後執行以下代碼:

#make menuconfig

#make dep

#make

在配置選項時,要注意選擇支持用戶添加的設備。這樣,得到的內核就包含了用戶的設備驅動程序。


分享到:


相關文章: