朱 寧
(淮南礦業集團有限責任公司裝備管理中心)
嵌入式系統Linux及USB驅動開發
朱 寧①
(淮南礦業集團有限責任公司裝備管理中心)
嵌入式ARM處理器是當今世界上最流行的嵌入式處理器,廣泛應用于個人通信等嵌入式領域。基于Linux良好的開放性和USB總線極佳的通用性,本文通過在ARM920T處理器的開發板上實現嵌入式Linux系統,并重點描述了Linux USB設備驅動程序的實現過程。
嵌入式Linux;設備驅動;USB
Linux操作系統主要由4大部分組成:用戶應用程序、操作系統服務、操作系統內核、硬件系統。操作系統內核是Linux操作系統中最核心的部分,也是嵌入式系統開發中修改、移植的重點。一個最小型化的Linux系統,其內核的構造見圖1。

圖1 Linux系統的內核構造示意圖
ARM9中上運行的Linux系統不同于傳統意義上的PC版GNU/Linux,ARM9芯片本身的存儲單元有限,它所要運行的系統軟件和應用程序都是裝載到外部存儲器里面的。為了適應ARM9的硬件要求,完善的支持Linux系統的運行,必須對通用的GNU/Linux進行定制和裁減。
一個小型的嵌入式Linux系統需要包括兩個基本元素:引導工具(boot模塊)和Linux微內核。引導工具一般稱為啟動代碼,主要工作是對嵌入式系統中的CPU、FLASH、SRAM、系統CACHE等硬件進行初始化,復雜的啟動代碼還提供了類似于操作系統的指令集對這些硬件設備進行修改和升級,比較有代表性的是U-BOOT程序。
1)引導模塊(boot)。引導加載程序又稱為Boot loader,它是CPU加電以后運行的第一段程序。因此,Bootloader的設計是嵌入式Linux開發的基礎,其基本功能是初始化硬件設備、建立內存空間的映射圖,從而為調用嵌入式Linux內核準備好硬件環境。
SRAM、SDRAM等存儲設備屬于揮發性的存儲器[1],掉電以后其中的內容就會全部丟失,所以必須把操作系統的內核鏡像存放在Flash等不揮發性存儲介質上。但是操作系統在運行時,需要動態的創建一些如數據段、堆棧、頁表(針對使用虛擬地址的操作系統)等內容,所以需要在RAM中運行操作系統。因此,就需要一個應到程序把操作系統的內核鏡像從Flash存儲器拷貝到RAM中,然后再從RAM中執行操作系統的內核。Bootloader就是可以完成這樣一種功能的程序。從本質上來講,Bootloader不屬于操作系統內核,因為它僅僅起到一個創立初始化環境和引導內核的作用。
U-Boot從功能上來分主要分為兩大部分[2],一是Boot,包括主要的初始化代碼和自解壓源代碼,用于建立U-Boot,并使其進入可操作的狀態,更加特殊的是,它能啟動CPU的高速時鐘,并能描述片上的存儲器。二是自解壓可執行文件,這是一個gunzip程序優化的版本,它能將U-Boot解壓到RAM中并跳轉去自我運行,開發者不參與下載數據。
U-Boot的啟動分析[3]:U-Boot與目標板的硬件有高度的依存關系,它的啟動過程主要分為兩個階段,即stage1和stage2:stage1用匯編語言編寫,通常是與CPU的體系結構有關,如設備初始化代碼等,由源碼cpu/start.s實現。stage2為C語言程序,相對stage1具有更好的移植性和可讀性,用來加載操作系統內核,有源碼lib_arm/board.c實現。
Stage1的具體步驟:a)目標板的硬件設備初始化,包括初始化CPU的關鍵寄存器。b)為加載stage2的代碼準備RAM空間。c)拷貝stage2的代碼到RAM空間中。d)設置好堆棧。e)跳轉到stage2的C入口點。
而stage2的具體步驟:a)初始化本階段要使用到的硬件設備。b)檢測系統內存映射。c)將kernel映像和根文件系統ramdisk映像從FLASH上載到RAM空間。d)為內核設置啟動參數。e)調用內核。
本文U-Boot的移植方法是結合嵌入式開發板HHARM9200的源碼結構。/HHARM9200目錄下的子目錄(/HHARM9200/bootloader/)可以通過修改這些源碼來修改bootloader;這里的bootloader for AT91RM9200分為3個:
A T91RM9200-Loader:生成loader.bin,在CPU內部SRAM中運行:
MEMORY{
Ram:ORIGIN=0x200000,L ENGTH= 0x3000
}
0x200000就是INTERNAL SRAM的地址。
Simple_boot:生成boot.bin,燒到FLASH上運行。
u-boot-0.4.8:生成u-boot.bin,可在SDRAM中運行,實際應用是燒到FLASH上,由上面的boot.bin加載到SDRAM中運行,見圖2。
2)內核的定制和裁減。嵌入式系統開發一般都是通過宿主機(PC)與目標板的模式。因為對于嵌入式系統的開發,沒有足夠的資源在本機(開發板)運行開發工具和調試工具。開發時使用宿主機上的交叉編譯、匯編及連接工具形成可執行的二進制代碼,然后把可執行文件下載到目標機上運行。
在宿主機上建立交叉編譯調試的開發環境需要許多的軟件模塊協同工作,這在套件光盤的安裝中已經自動完成了。在PC上安裝附帶光盤后會在根目錄下生成工作目錄:⑴/HHARM9200(內含Linux內核、應用程序源代碼以及各個工具軟件);⑵Linux內核源代碼目錄(/HHARM9200/linux-2.4.19-rmk7/);⑶應用程序目錄(/HHARM9200/applications)。

圖2 地址空間分布示意圖
通過make menuconfig則出現可對內核和驅動模塊進行選擇和配置的界面,且看到內核版本為: Linux Kernel-2.4.19-rmk7。

以上配置完成設置后,執行make zImage即可編譯生成自己定制的內核映像文件,并自動被復制到/ 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
然后將這個內核用gzip進行壓縮后,與head.S這個解壓縮代碼部分整合到一起生成zImage。在壓縮過程中先將ELF格式的vmlinux轉換為二進制格式,這樣的好處是可以使文件大大縮小,并且由原來的2 M多變為700~800字節,更適合嵌入式系統。
/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作為臨時文件名,對內核進行gzip壓縮,且轉換為.o格式的文件并將解壓縮部分代碼和壓縮內核鏈接為一個新的vmlinux,然后再次將解壓縮代碼部分的ELF格式轉換為二進制文件格式,即最終的zImage文件,并復制到/tftpboot目錄下,以供TFTP下載燒寫。
1)USB驅動概述。從2.2.18版內核開始, Linux增加了對USB的支持,2.4.x版本的內核對USB的支持已比較完善。其主機驅動程序已經包括在內核中,而設備驅動需要程序員根據不同的設備進行編寫。在設備驅動程序中,有一個叫做“USB核心”的子系統[4],它的作用是提供支持USB設備驅動程序的API和USB的主機驅動程序。它提供了許多數據結構,宏定義和功能函數來對硬件或設備進行支持。在Linux下編寫USB設備的驅動程序從嚴格意義上講,就是使用這些USB核心的子系統定義的數據結構,宏和函數來編寫數據的處理功能。
2)USB設備連接與初始化。USB設備接入主機后,主機枚舉識別設備及設備狀態的變化,通過總線的上拉電阻判斷新設備的連接是全速還是低速設備,并發送復位信號。設備端在收到復位信號后,對總線作出相應,主機通過默認地址0收到設備響應后,為設備分配一個空閑的地址,以后就通過這個地址和設備進行通信。主機讀取設備的相應描述符對設備進行配置,若設備符合要求就發送配置完畢的信號給主機,然后調用設備驅動的probe函數對設備進行初始化。如下是一個簡單的probe函數[5]:
static void*naked_usb_probe(struct usb_device*dev)
{//檢查硬件定義與驅動是否相符……
{//初始化設備相關資源
if(device->obuf=(char)kma11oc(OBUF_ SIZE,GFP_KERNEL)){…}
device->dev=dev;
……
printk(“My usb device pluged in. ”);
return;}}
在初始化中如果設備有中斷端點,則填充中斷請求URB,并提交給主機,然后再對這個特定數據結構相應字段分配緩沖區、賦初值、并返回此數據結構,完成對設備的初始化。
3)USB設備驅動框架。設備驅動完成的功能主要是:提供與應用程序的接口;讀取并解析USB設備特有的描述符;獲得設備提供的傳輸通道;發送設備特有的和基本的USB命令請求;通過設備提供的傳輸通道與設備進行數據傳輸;通過USB命令請求重新配置沒備。
USB設備驅動在內核模塊中必須注冊2個入口點和1個設備節點。對于特別的USB設備一個驅動可以注冊一對文件操作符和一個次設備號。Linux usb驅動程序首先時調用usb_register()函數在linux usb子系統里注冊[6],其注冊結構如下:
struct usb_driver{
const char*name;//模塊名字
void*(*probe)(struct usb_device*,unsigned int);//probe功能入口點
void(*disconnect)(struct usb_device*,void*);//disconnect功能入口點
struct list_head driver_list;//為子系統內部初始化用,一般為0
struct file_operations*fops;//文件操作列表指針
int minor;//次設備號
};
USB設備注冊后,驅動就已經和設備綁定了,任何用戶態程序要操作此設備都可以通過file_operations結構所定義的函數進行。結構中struct usb_ driver這個數據結構在內核中注冊一個USB設備驅動程序。其中包括有指向設備初始化函數的指針*probe()、指向設備卸載函數指針*disconnect()以及對設備進行具體操作的File_operations函數指針Fops,用于識別設備的Usb_device_id數據結構Id_ table。由于一個設備驅動程序可以支持多個相同或同類的設備,在Usb_driver結構中還包含一個整型的次設備號Minor,每個次設備號和一個具體設備對應,USB規范中規定此設備號必須是16的倍數,一個USB設備驅動程序最多可支持16個設備。
當USB設備連接到系統時,主機系統調用初始化函數probe(),并讀取Usb_dev結構。在Usb_dev結構中主要描述了USB設備的硬件信息:設備、接口等描述符,以及符合的USB規范等。根據這些硬件信息判斷該設備是否被驅動程序所支持,然后給設備分配一個特定的數據結構。
4)USB設備驅動的釋放及卸載。設備的卸載之前首先要釋放對設備的控制權[7],減少模塊引用計數器等函數:
static int usb_device_close(struct inode*inode,struct file*file)
{……}
module_dec_use_count;//減少模塊引用計數設備卸載模塊和初始化模塊相反,主要是釋放在初始化時分配的各種資源以及取消在初始化函數中提交的中斷請求URB等。
static void disconnect_device(struct usb_device*dev,void*ptr)
{…}
kfree(device->iobuffer);
kfree(device->retbuffer);//釋放在內核中申請的內存
…;
本文介紹了嵌入式Linux系統中USB設備驅動程序開發的基本原理,通過分析USB驅動程序開發的程序框架和重要數據結構,對Linux文件系統機制及USB底層驅動進行了一定的研究。USB的廣泛應用其正在成為外設與PC機及膝上型電腦連接的工業標準USB外設主要是便攜式設備,隨著其數量的不斷增多,設備之間無主機參與的直接通信成為亟待解決的問題。通過研究并調試基于嵌入式Linux系統的USB底層驅動,取得了較滿意的效果。Linux系統版本在不斷更新,基于2.6版本的Linux嵌入式操作還有待于進一步的驗證。
[1] 沈 沙,蘇佳寧,田駿驊,等.μClinux操作系統在嵌入式SOC平臺上的移植[J].計算機工程與應用,2004(26):104-108.
[2] 童大鵬,冉蜀陽,張 礁,等.基于AT91RM9200微控制器的BootLoader的分析與開發[J].微計算機應用,2005,26(3):345-349.
[3] 朱繼杭,楊世武.基于AT91RM9200的U-Boot移植方法[J].儀器儀表用戶,2005,12(6):121-122.
[4] 張宏偉.Linux下USB設備驅動程序的編寫[J].計算機應用研究,2001(9):141-146.
[5] (美)諾頓·格蕾菲斯著,翟大昆譯.Linux實用指南[M].北京:機械工業出版社,1999:79-80.
[6] (美)馬克斯韋爾.Linux內核源代碼分析[M].北京:機械工業出版社,2000:57-58.
[7] 陳華瑛,李建國.UNIX操作系統設計與實現[M].北京:電子工業出版社,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年畢業于安徽理工大學 助理工程師 淮南 232082