朱 寧
(淮南礦業(yè)集團(tuán)有限責(zé)任公司裝備管理中心)
嵌入式系統(tǒng)Linux及USB驅(qū)動(dòng)開發(fā)
朱 寧①
(淮南礦業(yè)集團(tuán)有限責(zé)任公司裝備管理中心)
嵌入式ARM處理器是當(dāng)今世界上最流行的嵌入式處理器,廣泛應(yīng)用于個(gè)人通信等嵌入式領(lǐng)域?;贚inux良好的開放性和USB總線極佳的通用性,本文通過在ARM920T處理器的開發(fā)板上實(shí)現(xiàn)嵌入式Linux系統(tǒng),并重點(diǎn)描述了Linux USB設(shè)備驅(qū)動(dòng)程序的實(shí)現(xiàn)過程。
嵌入式Linux;設(shè)備驅(qū)動(dòng);USB
Linux操作系統(tǒng)主要由4大部分組成:用戶應(yīng)用程序、操作系統(tǒng)服務(wù)、操作系統(tǒng)內(nèi)核、硬件系統(tǒng)。操作系統(tǒng)內(nèi)核是Linux操作系統(tǒng)中最核心的部分,也是嵌入式系統(tǒng)開發(fā)中修改、移植的重點(diǎn)。一個(gè)最小型化的Linux系統(tǒng),其內(nèi)核的構(gòu)造見圖1。
圖1 Linux系統(tǒng)的內(nèi)核構(gòu)造示意圖
ARM9中上運(yùn)行的Linux系統(tǒng)不同于傳統(tǒng)意義上的PC版GNU/Linux,ARM9芯片本身的存儲(chǔ)單元有限,它所要運(yùn)行的系統(tǒng)軟件和應(yīng)用程序都是裝載到外部存儲(chǔ)器里面的。為了適應(yīng)ARM9的硬件要求,完善的支持Linux系統(tǒng)的運(yùn)行,必須對(duì)通用的GNU/Linux進(jìn)行定制和裁減。
一個(gè)小型的嵌入式Linux系統(tǒng)需要包括兩個(gè)基本元素:引導(dǎo)工具(boot模塊)和Linux微內(nèi)核。引導(dǎo)工具一般稱為啟動(dòng)代碼,主要工作是對(duì)嵌入式系統(tǒng)中的CPU、FLASH、SRAM、系統(tǒng)CACHE等硬件進(jìn)行初始化,復(fù)雜的啟動(dòng)代碼還提供了類似于操作系統(tǒng)的指令集對(duì)這些硬件設(shè)備進(jìn)行修改和升級(jí),比較有代表性的是U-BOOT程序。
1)引導(dǎo)模塊(boot)。引導(dǎo)加載程序又稱為Boot loader,它是CPU加電以后運(yùn)行的第一段程序。因此,Bootloader的設(shè)計(jì)是嵌入式Linux開發(fā)的基礎(chǔ),其基本功能是初始化硬件設(shè)備、建立內(nèi)存空間的映射圖,從而為調(diào)用嵌入式Linux內(nèi)核準(zhǔn)備好硬件環(huán)境。
SRAM、SDRAM等存儲(chǔ)設(shè)備屬于揮發(fā)性的存儲(chǔ)器[1],掉電以后其中的內(nèi)容就會(huì)全部丟失,所以必須把操作系統(tǒng)的內(nèi)核鏡像存放在Flash等不揮發(fā)性存儲(chǔ)介質(zhì)上。但是操作系統(tǒng)在運(yùn)行時(shí),需要?jiǎng)討B(tài)的創(chuàng)建一些如數(shù)據(jù)段、堆棧、頁(yè)表(針對(duì)使用虛擬地址的操作系統(tǒng))等內(nèi)容,所以需要在RAM中運(yùn)行操作系統(tǒng)。因此,就需要一個(gè)應(yīng)到程序把操作系統(tǒng)的內(nèi)核鏡像從Flash存儲(chǔ)器拷貝到RAM中,然后再?gòu)腞AM中執(zhí)行操作系統(tǒng)的內(nèi)核。Bootloader就是可以完成這樣一種功能的程序。從本質(zhì)上來講,Bootloader不屬于操作系統(tǒng)內(nèi)核,因?yàn)樗鼉H僅起到一個(gè)創(chuàng)立初始化環(huán)境和引導(dǎo)內(nèi)核的作用。
U-Boot從功能上來分主要分為兩大部分[2],一是Boot,包括主要的初始化代碼和自解壓源代碼,用于建立U-Boot,并使其進(jìn)入可操作的狀態(tài),更加特殊的是,它能啟動(dòng)CPU的高速時(shí)鐘,并能描述片上的存儲(chǔ)器。二是自解壓可執(zhí)行文件,這是一個(gè)gunzip程序優(yōu)化的版本,它能將U-Boot解壓到RAM中并跳轉(zhuǎn)去自我運(yùn)行,開發(fā)者不參與下載數(shù)據(jù)。
U-Boot的啟動(dòng)分析[3]:U-Boot與目標(biāo)板的硬件有高度的依存關(guān)系,它的啟動(dòng)過程主要分為兩個(gè)階段,即stage1和stage2:stage1用匯編語(yǔ)言編寫,通常是與CPU的體系結(jié)構(gòu)有關(guān),如設(shè)備初始化代碼等,由源碼cpu/start.s實(shí)現(xiàn)。stage2為C語(yǔ)言程序,相對(duì)stage1具有更好的移植性和可讀性,用來加載操作系統(tǒng)內(nèi)核,有源碼lib_arm/board.c實(shí)現(xiàn)。
Stage1的具體步驟:a)目標(biāo)板的硬件設(shè)備初始化,包括初始化CPU的關(guān)鍵寄存器。b)為加載stage2的代碼準(zhǔn)備RAM空間。c)拷貝stage2的代碼到RAM空間中。d)設(shè)置好堆棧。e)跳轉(zhuǎn)到stage2的C入口點(diǎn)。
而stage2的具體步驟:a)初始化本階段要使用到的硬件設(shè)備。b)檢測(cè)系統(tǒng)內(nèi)存映射。c)將kernel映像和根文件系統(tǒng)ramdisk映像從FLASH上載到RAM空間。d)為內(nèi)核設(shè)置啟動(dòng)參數(shù)。e)調(diào)用內(nèi)核。
本文U-Boot的移植方法是結(jié)合嵌入式開發(fā)板HHARM9200的源碼結(jié)構(gòu)。/HHARM9200目錄下的子目錄(/HHARM9200/bootloader/)可以通過修改這些源碼來修改bootloader;這里的bootloader for AT91RM9200分為3個(gè):
A T91RM9200-Loader:生成loader.bin,在CPU內(nèi)部SRAM中運(yùn)行:
MEMORY{
Ram:ORIGIN=0x200000,L ENGTH= 0x3000
}
0x200000就是INTERNAL SRAM的地址。
Simple_boot:生成boot.bin,燒到FLASH上運(yùn)行。
u-boot-0.4.8:生成u-boot.bin,可在SDRAM中運(yùn)行,實(shí)際應(yīng)用是燒到FLASH上,由上面的boot.bin加載到SDRAM中運(yùn)行,見圖2。
2)內(nèi)核的定制和裁減。嵌入式系統(tǒng)開發(fā)一般都是通過宿主機(jī)(PC)與目標(biāo)板的模式。因?yàn)閷?duì)于嵌入式系統(tǒng)的開發(fā),沒有足夠的資源在本機(jī)(開發(fā)板)運(yùn)行開發(fā)工具和調(diào)試工具。開發(fā)時(shí)使用宿主機(jī)上的交叉編譯、匯編及連接工具形成可執(zhí)行的二進(jìn)制代碼,然后把可執(zhí)行文件下載到目標(biāo)機(jī)上運(yùn)行。
在宿主機(jī)上建立交叉編譯調(diào)試的開發(fā)環(huán)境需要許多的軟件模塊協(xié)同工作,這在套件光盤的安裝中已經(jīng)自動(dòng)完成了。在PC上安裝附帶光盤后會(huì)在根目錄下生成工作目錄:⑴/HHARM9200(內(nèi)含Linux內(nèi)核、應(yīng)用程序源代碼以及各個(gè)工具軟件);⑵Linux內(nèi)核源代碼目錄(/HHARM9200/linux-2.4.19-rmk7/);⑶應(yīng)用程序目錄(/HHARM9200/applications)。
圖2 地址空間分布示意圖
通過make menuconfig則出現(xiàn)可對(duì)內(nèi)核和驅(qū)動(dòng)模塊進(jìn)行選擇和配置的界面,且看到內(nèi)核版本為: Linux Kernel-2.4.19-rmk7。
以上配置完成設(shè)置后,執(zhí)行make zImage即可編譯生成自己定制的內(nèi)核映像文件,并自動(dòng)被復(fù)制到/ tftpboot/目錄下以供燒寫。編譯過程可以如下觀察:
編譯過程如下:
這里生成的是kernel目錄下的vmlinux文件。它的地址分布就是由/HHARM9200/linux-2.4.19 -rmk7/arch/arm/vmlinux.lds文件指定的。
下面這句則說明了System.map文件是如何生成的:
/usr/local/arm/2.95. 3/bin/arm-linuxnmvmlinux|grep
-v‘(complied)|(.o$)|([aUw])\ (..ng$)|(LASH[RL]DI‘|sort>System. map
然后將這個(gè)內(nèi)核用gzip進(jìn)行壓縮后,與head.S這個(gè)解壓縮代碼部分整合到一起生成zImage。在壓縮過程中先將ELF格式的vmlinux轉(zhuǎn)換為二進(jìn)制格式,這樣的好處是可以使文件大大縮小,并且由原來的2 M多變?yōu)?00~800字節(jié),更適合嵌入式系統(tǒng)。
/usr/local/arm/2.95.3/bin/arm-linux-objcopy-Obinary-R.note-R.comment-S/ HHARM9200/linux-2.4.19-rmk7/vmlinux temp
gzip-9
/usr/local/arm/2.95. 3/bin/arm-linux-ld -r -o temp.o -b binary temp.gz
rm-f temp temp.gz
/usr/local/arm/2.95.3/bin/arm-linux-ld -p -X -T vmlinux.lds head.o misc.o head-AT91RM9200.o temp.o/usr/
local/arm/2.95.3/lib/gcc-lib/arm-linux/2.95.2/ soft-float/libgcc.a -o vmlinux
make[2]: Leaving directory
‘/HHARM9200/linux-2.4.19-rmk7/arch/arm/ boot/compressed’
/usr/local/arm/2.95.3/bin/arm-linux-objcopy
-O binary -R.note -R.comment -S compressed/vmlinux zImage
cp-f zImage/tftpboot/
上述表明:首先用temp作為臨時(shí)文件名,對(duì)內(nèi)核進(jìn)行g(shù)zip壓縮,且轉(zhuǎn)換為.o格式的文件并將解壓縮部分代碼和壓縮內(nèi)核鏈接為一個(gè)新的vmlinux,然后再次將解壓縮代碼部分的ELF格式轉(zhuǎn)換為二進(jìn)制文件格式,即最終的zImage文件,并復(fù)制到/tftpboot目錄下,以供TFTP下載燒寫。
1)USB驅(qū)動(dòng)概述。從2.2.18版內(nèi)核開始, Linux增加了對(duì)USB的支持,2.4.x版本的內(nèi)核對(duì)USB的支持已比較完善。其主機(jī)驅(qū)動(dòng)程序已經(jīng)包括在內(nèi)核中,而設(shè)備驅(qū)動(dòng)需要程序員根據(jù)不同的設(shè)備進(jìn)行編寫。在設(shè)備驅(qū)動(dòng)程序中,有一個(gè)叫做“USB核心”的子系統(tǒng)[4],它的作用是提供支持USB設(shè)備驅(qū)動(dòng)程序的API和USB的主機(jī)驅(qū)動(dòng)程序。它提供了許多數(shù)據(jù)結(jié)構(gòu),宏定義和功能函數(shù)來對(duì)硬件或設(shè)備進(jìn)行支持。在Linux下編寫USB設(shè)備的驅(qū)動(dòng)程序從嚴(yán)格意義上講,就是使用這些USB核心的子系統(tǒng)定義的數(shù)據(jù)結(jié)構(gòu),宏和函數(shù)來編寫數(shù)據(jù)的處理功能。
2)USB設(shè)備連接與初始化。USB設(shè)備接入主機(jī)后,主機(jī)枚舉識(shí)別設(shè)備及設(shè)備狀態(tài)的變化,通過總線的上拉電阻判斷新設(shè)備的連接是全速還是低速設(shè)備,并發(fā)送復(fù)位信號(hào)。設(shè)備端在收到復(fù)位信號(hào)后,對(duì)總線作出相應(yīng),主機(jī)通過默認(rèn)地址0收到設(shè)備響應(yīng)后,為設(shè)備分配一個(gè)空閑的地址,以后就通過這個(gè)地址和設(shè)備進(jìn)行通信。主機(jī)讀取設(shè)備的相應(yīng)描述符對(duì)設(shè)備進(jìn)行配置,若設(shè)備符合要求就發(fā)送配置完畢的信號(hào)給主機(jī),然后調(diào)用設(shè)備驅(qū)動(dòng)的probe函數(shù)對(duì)設(shè)備進(jìn)行初始化。如下是一個(gè)簡(jiǎn)單的probe函數(shù)[5]:
static void*naked_usb_probe(struct usb_device*dev)
{//檢查硬件定義與驅(qū)動(dòng)是否相符……
{//初始化設(shè)備相關(guān)資源
if(device->obuf=(char)kma11oc(OBUF_ SIZE,GFP_KERNEL)){…}
device->dev=dev;
……
printk(“My usb device pluged in. ”);
return;}}
在初始化中如果設(shè)備有中斷端點(diǎn),則填充中斷請(qǐng)求URB,并提交給主機(jī),然后再對(duì)這個(gè)特定數(shù)據(jù)結(jié)構(gòu)相應(yīng)字段分配緩沖區(qū)、賦初值、并返回此數(shù)據(jù)結(jié)構(gòu),完成對(duì)設(shè)備的初始化。
3)USB設(shè)備驅(qū)動(dòng)框架。設(shè)備驅(qū)動(dòng)完成的功能主要是:提供與應(yīng)用程序的接口;讀取并解析USB設(shè)備特有的描述符;獲得設(shè)備提供的傳輸通道;發(fā)送設(shè)備特有的和基本的USB命令請(qǐng)求;通過設(shè)備提供的傳輸通道與設(shè)備進(jìn)行數(shù)據(jù)傳輸;通過USB命令請(qǐng)求重新配置沒備。
USB設(shè)備驅(qū)動(dòng)在內(nèi)核模塊中必須注冊(cè)2個(gè)入口點(diǎn)和1個(gè)設(shè)備節(jié)點(diǎn)。對(duì)于特別的USB設(shè)備一個(gè)驅(qū)動(dòng)可以注冊(cè)一對(duì)文件操作符和一個(gè)次設(shè)備號(hào)。Linux usb驅(qū)動(dòng)程序首先時(shí)調(diào)用usb_register()函數(shù)在linux usb子系統(tǒng)里注冊(cè)[6],其注冊(cè)結(jié)構(gòu)如下:
struct usb_driver{
const char*name;//模塊名字
void*(*probe)(struct usb_device*,unsigned int);//probe功能入口點(diǎn)
void(*disconnect)(struct usb_device*,void*);//disconnect功能入口點(diǎn)
struct list_head driver_list;//為子系統(tǒng)內(nèi)部初始化用,一般為0
struct file_operations*fops;//文件操作列表指針
int minor;//次設(shè)備號(hào)
};
USB設(shè)備注冊(cè)后,驅(qū)動(dòng)就已經(jīng)和設(shè)備綁定了,任何用戶態(tài)程序要操作此設(shè)備都可以通過file_operations結(jié)構(gòu)所定義的函數(shù)進(jìn)行。結(jié)構(gòu)中struct usb_ driver這個(gè)數(shù)據(jù)結(jié)構(gòu)在內(nèi)核中注冊(cè)一個(gè)USB設(shè)備驅(qū)動(dòng)程序。其中包括有指向設(shè)備初始化函數(shù)的指針*probe()、指向設(shè)備卸載函數(shù)指針*disconnect()以及對(duì)設(shè)備進(jìn)行具體操作的File_operations函數(shù)指針Fops,用于識(shí)別設(shè)備的Usb_device_id數(shù)據(jù)結(jié)構(gòu)Id_ table。由于一個(gè)設(shè)備驅(qū)動(dòng)程序可以支持多個(gè)相同或同類的設(shè)備,在Usb_driver結(jié)構(gòu)中還包含一個(gè)整型的次設(shè)備號(hào)Minor,每個(gè)次設(shè)備號(hào)和一個(gè)具體設(shè)備對(duì)應(yīng),USB規(guī)范中規(guī)定此設(shè)備號(hào)必須是16的倍數(shù),一個(gè)USB設(shè)備驅(qū)動(dòng)程序最多可支持16個(gè)設(shè)備。
當(dāng)USB設(shè)備連接到系統(tǒng)時(shí),主機(jī)系統(tǒng)調(diào)用初始化函數(shù)probe(),并讀取Usb_dev結(jié)構(gòu)。在Usb_dev結(jié)構(gòu)中主要描述了USB設(shè)備的硬件信息:設(shè)備、接口等描述符,以及符合的USB規(guī)范等。根據(jù)這些硬件信息判斷該設(shè)備是否被驅(qū)動(dòng)程序所支持,然后給設(shè)備分配一個(gè)特定的數(shù)據(jù)結(jié)構(gòu)。
4)USB設(shè)備驅(qū)動(dòng)的釋放及卸載。設(shè)備的卸載之前首先要釋放對(duì)設(shè)備的控制權(quán)[7],減少模塊引用計(jì)數(shù)器等函數(shù):
static int usb_device_close(struct inode*inode,struct file*file)
{……}
module_dec_use_count;//減少模塊引用計(jì)數(shù)設(shè)備卸載模塊和初始化模塊相反,主要是釋放在初始化時(shí)分配的各種資源以及取消在初始化函數(shù)中提交的中斷請(qǐng)求URB等。
static void disconnect_device(struct usb_device*dev,void*ptr)
{…}
kfree(device->iobuffer);
kfree(device->retbuffer);//釋放在內(nèi)核中申請(qǐng)的內(nèi)存
…;
本文介紹了嵌入式Linux系統(tǒng)中USB設(shè)備驅(qū)動(dòng)程序開發(fā)的基本原理,通過分析USB驅(qū)動(dòng)程序開發(fā)的程序框架和重要數(shù)據(jù)結(jié)構(gòu),對(duì)Linux文件系統(tǒng)機(jī)制及USB底層驅(qū)動(dòng)進(jìn)行了一定的研究。USB的廣泛應(yīng)用其正在成為外設(shè)與PC機(jī)及膝上型電腦連接的工業(yè)標(biāo)準(zhǔn)USB外設(shè)主要是便攜式設(shè)備,隨著其數(shù)量的不斷增多,設(shè)備之間無主機(jī)參與的直接通信成為亟待解決的問題。通過研究并調(diào)試基于嵌入式Linux系統(tǒng)的USB底層驅(qū)動(dòng),取得了較滿意的效果。Linux系統(tǒng)版本在不斷更新,基于2.6版本的Linux嵌入式操作還有待于進(jìn)一步的驗(yàn)證。
[1] 沈 沙,蘇佳寧,田駿驊,等.μClinux操作系統(tǒng)在嵌入式SOC平臺(tái)上的移植[J].計(jì)算機(jī)工程與應(yīng)用,2004(26):104-108.
[2] 童大鵬,冉蜀陽(yáng),張 礁,等.基于AT91RM9200微控制器的BootLoader的分析與開發(fā)[J].微計(jì)算機(jī)應(yīng)用,2005,26(3):345-349.
[3] 朱繼杭,楊世武.基于AT91RM9200的U-Boot移植方法[J].儀器儀表用戶,2005,12(6):121-122.
[4] 張宏偉.Linux下USB設(shè)備驅(qū)動(dòng)程序的編寫[J].計(jì)算機(jī)應(yīng)用研究,2001(9):141-146.
[5] (美)諾頓·格蕾菲斯著,翟大昆譯.Linux實(shí)用指南[M].北京:機(jī)械工業(yè)出版社,1999:79-80.
[6] (美)馬克斯韋爾.Linux內(nèi)核源代碼分析[M].北京:機(jī)械工業(yè)出版社,2000:57-58.
[7] 陳華瑛,李建國(guó).UNIX操作系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)[M].北京:電子工業(yè)出版社,1999:45-47.
Embedded Linux and USB Driver Development
Zhu Ning
Implanting the dyadic ARM processor is that the most popular implanting the dyadic processor,apply to an individual broadly communicate by letter and so on implants the dyadic field in the world nowadays.Owing to fine Linux opening to the outside world and extremely nice general availability of USB highway,the main body of a book is passed realizing the process implanting dyadic Linux system,and concentrating on having described Linux USB equipment driver to come true on ARM920T processor exploitation board.
Embedded Linux;Device Driver;USB
book=4,ebook=123
TD672
A
1672-0652(2010)04-0052-05
2010-03-22
朱 寧 男 1981年出生 2006年畢業(yè)于安徽理工大學(xué) 助理工程師 淮南 232082