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