羅志娟
(長沙航空職業(yè)技術學院,湖南 長沙 410124)
嵌入式系統(tǒng)是電子技術及計算機技術完美結合的產物。Linux因可應用于多種硬件平臺、內核高效穩(wěn)定、軟件資源豐富、源代碼開放、網絡通信和文件管理機制完善等特點,已成為最具優(yōu)勢的嵌入式操作系統(tǒng)。[1]在嵌入式Linux操作系統(tǒng)中,出于對內核的保護,用戶一般不能直接訪問物理硬件資源,需要調用驅動程序實現(xiàn)對外設的控制。嵌入式系統(tǒng)的開發(fā),重點和難點就在于驅動程序的開發(fā),設備驅動程序的不同正是各內核版本之間的主要區(qū)別。
設備驅動程序作為機器硬件和操作系統(tǒng)內核之間的接口,屏蔽掉硬件的細節(jié),而應用程序通過系統(tǒng)調用與操作系統(tǒng)內核連接,將硬件設備看成是一個設備文件,操作硬件設備時就像操作普通文件一樣,如圖1所示。
圖1 兩種不同形式的生命教育體系
Linux操作系統(tǒng)下主要有塊設備和字符設備兩種設備文件類型。[2]當字符設備發(fā)出讀/寫請求時,實際的硬件I/O設備緊接著發(fā)生,如鼠標、鍵盤;而塊設備不同,它利用一塊系統(tǒng)內存作為緩沖區(qū),只有當用戶進程能滿足用戶對設備的請求,才會返回請求的數(shù)據(jù),否則將調用請求函數(shù)來進行實際的輸入輸出操作,如硬盤。
Linux操作系統(tǒng)中,所有的設備文件都有文件屬性、主設備號和從設備號。[3]其中文件屬性用來區(qū)分設備類型(c表示字符設備,b表示塊設備);主設備號用來標識驅動程序,從設備號標識同設備驅動程序下的不同硬件設備。值得注意的是,主設備號必須與登記設備驅動程序時申請的主設備號一致,否則用戶進程無法訪問驅動程序。用戶進程通過設備文件就可以與實際的硬件通信了。
進行嵌入式系統(tǒng)的開發(fā),只要用到操作系統(tǒng),編寫設備驅動程序就必不可少。嵌入式設備硬件種類異常豐富,雖然Linux內核源代碼中已包含了很多的設備驅動程序,但仍不可能包括所有設備驅動,對于所需要的驅動程序,用戶可以在硬件生產廠家或Internet上尋找,若找不到就要根據(jù)相近硬件的驅動程序來改寫或重新編寫。
用戶進程要與硬件打交道必須通過設備文件,而操作設備文件就是進行系統(tǒng)調用,數(shù)據(jù)結構file_operations在驅動程序與系統(tǒng)調用的關聯(lián)過程中起著關鍵作用。[4]不同的內核版本,file_operations 結構存在著差異,file_operations的典型結構如下所示:
linux的設備驅動程序就是通過file_operations結構完成工作的。[5]它中間的每個成員都與一個系統(tǒng)調用相對應。當用戶進程要操作設備文件時,系統(tǒng)調用利用其主設備號找到與之對應的驅動程序,同時獲得file_operations中相應的函數(shù)指針,使該函數(shù)獲得控制權。[6]因此,驅動程序開發(fā)的主要工作就是根據(jù)設備的需求,不斷完善file_operations中的各個子函數(shù)。
給出一個簡單的字符設備驅動程序(test_driver.c)實例,介紹字符設備驅動程序實現(xiàn)全過程。該設備名稱為 test,主設備號為 240,驅動在Linux2.6內核中成功加載,并在長沙佳程科技公司提供的 VCM-L600稅控收款機(采用三星S3C44B0芯片)上編寫測試程序,調試成功。
2.2.1 主體程序編寫
主體程序的編寫實際上就是實現(xiàn)數(shù)據(jù)結構file_operations中某些子函數(shù)。本例中,當調用read時,函數(shù)read_test將被調用,它的作用是把1寫進用戶的緩沖區(qū)。其中,buf是用戶進程空間的一個地址,read函數(shù)的一個參數(shù),當read_test被調用時,系統(tǒng)進入了核心態(tài),buf這個地址是不能直接使用的,要使用kernel提供的函數(shù)__put_user()把數(shù)據(jù)傳送給用戶。而且在向用戶空間拷貝數(shù)據(jù)之前,還要利用verify_area函數(shù)驗證buf是否可用。函數(shù)read_test具體實現(xiàn)如下:
2.2.2 模塊加載與卸載
驅動程序可以按照動態(tài)加載編譯成模塊(modules)或靜態(tài)編譯進內核(kernel)兩種方式編譯。靜態(tài)編譯進內核時,內核一旦啟動,驅動程序即將自動加載,這樣不僅會增加內核大小,也會使得內核的源文件被改動,設備也無法動態(tài)卸載,極不利于調試。故一般最基本的核心代碼編譯進內核中,而其他的編譯為內核的模塊文件。為了方便設備的卸載,通常采用動態(tài)編譯的方式。在動態(tài)編譯過程中使用insmod命令將模塊動態(tài)加載到正在運行的內核,不需要時使用rmmod命卸載模塊。
在動態(tài)編譯過程中,若要將模塊調入內存,則需使用insmod命令調用函數(shù)module_init,向系統(tǒng)的字符設備表登記一個字符設備test。Linux系統(tǒng)中若要在系統(tǒng)中登記字符型設備,則要調用內核函數(shù)register_chrdev。它含有三個參數(shù):第一個設備號,用戶可以根據(jù)需要指定,本例中指定為240,若為0,系統(tǒng)返回一個尚未被占用的設備號,第二個設備文件名,第三個為該驅動入口點的文件操作結構指針。如果登記成功,返回設備的主設備號,設備名將會出現(xiàn)在/proc/devices文件里;否則返回一個負值。若要卸載模塊,則要用rmmod命令調用函數(shù)module_exit,內核函數(shù)unregister_chrdev會將字符設備test在系統(tǒng)字符設備表中占有的表項釋放。
設備驅動程序編寫完成后,下一步就要將該驅動程序添加至內核中。要將驅動成功添加至內核就必須修改Linux的源代碼,然后將內核重新編譯。驅動程序嵌入內核可分為以下六個步驟:
1)將驅動源文件(test_driver.c)復制到linux2.4drivercharvcmdrv目錄下。該目錄保存了Linux下字符設備的驅動程序。
2)修改上述目錄下的makefile文件,在文件中添加語句:
obj-$(config_vcm600_TEST)+=vcmdrv/test_driver.o
在配置Linux內核時若選擇了支持新定義的設備,該語句可使得編譯內核時將源文件test_driver.c 編譯成目標文件 test_driver.o。
3)修改該目錄下的config.in文件,在comment character devices?語句后添加語句:
bool‘VCM -L600 test driver’CONFI6_VCM600_TEST
4)打開 vendors/Samsung/44B0/下 makefile文件,添加節(jié)點:
Test,C,240,0
5)修改vendors/Samsung/44B0/下 rc文件,實現(xiàn)掛載
6)編譯內核,利用make menuconfig將驅動程序的目標文件(test_driver.o)生成于內核目錄driver/char/vcmdrv下。
至此驅動程序被加載進內核,若在/romfs/dev目錄中看到@test?.C.240.0,說明該驅動已成功加載。
為進一步驗證驅動程序成功加載進內核,可編寫一應用程序調用該驅動。應用程序(test_apply.c)如下,該程序打開設備test,即調入test的驅動程序,通過驅動程序中定義的read函數(shù)讀取10個1存入buf中,最后在終端打印buf中的數(shù)據(jù)。
#include <stdio.h> //stdio.h標準I/O 庫,提供了帶緩沖的文件操作功能
#include <sys/types.h>//types.h中定義基本系統(tǒng)數(shù)據(jù)類型
#include <sys/stat.h> //stat.h 提供文件狀態(tài)
#include <fcntl.h > //fcntl.h定義了一組基于C的非緩沖的文件操作函數(shù),實現(xiàn)對文件的控制
應用程序編寫好后,需將源文件編譯為目標文件,通常有兩種方法:①利用編譯命令arm-elfgcc -elf2flt -o test_driver test_driver.c,將源程序編譯為目標文件;②寫好makefile文件放在與驅動程序相同目錄下,在命令行輸入make命令編譯。這里采用第二種方法,makefile文件如下:
執(zhí)行make命令調用makefile文件,生成應用程序test_apply.c的目標文件 test_apply.o。Makefile文件中,將應用程序的目標文件放在了nfs中,便于實現(xiàn)掛載。
重新編譯內核后,將加載了新驅動的內核打包并利用tftp命令下載至目標機中,即可進入minicom終端,運行程序,查看結果。
通過介紹嵌入式Linux設備驅動程序的工作原理,以字符設備驅動程序為實例,簡述了字符設備驅動程序開發(fā)的基本流程。雖然驅動程序的種類隨著設備的增加而日益繁多,而且某些設備驅動程序的開發(fā)還需要中斷服務,但驅動程序和內核之間有嚴格定義、管理的接口,所以其基本的結構和開發(fā)過程都是建立在規(guī)范的基礎上的,各種驅動程序的結構和開發(fā)過程是一致的,熟悉了驅動開發(fā)的基本流程后,就可進行各種設備驅動程序的開發(fā)了。
[1]李俊.嵌入式 Linux設備驅動開發(fā)詳解[M].北京:人民郵電出版社,2008.
[2]張威,黃沖.嵌入式Linux設備驅動的設計方法研究[J].江西師范大學學報(自然科學版),2007,(7).
[3]戴明華,李長云,等.嵌入式Linux驅動程序框架研究綜述[J].長沙大學學報,2012,(3).
[4]曹穎鵬.基于嵌入式Linux驅動程序的研究與設計[D].西安:西安電子科技大學,2010.
[5]如何編寫 Linux設備驅動程序[EB/OL].http://bbs.chinaunix.net/thread-2047152-1 -1.html
[6]王驍俊.RMLinux在嵌入式設備上的移植及驅動開發(fā)[D]上海:上海交通大學,2008.