《電子技術應用》
您所在的位置:首頁 > 嵌入式技術 > 設計應用 > 向嵌入式Linux移植實時設備驅動程序
向嵌入式Linux移植實時設備驅動程序
摘要: 本文將縱覽幾種常用的內存映射I/O方法,它們經常出現于舊的嵌入式應用中。它們涵蓋的范圍,包括從對中斷服務例程的特殊使用和用戶線程對硬件訪問,到出現于有些ROTS中的半規(guī)范化驅動程序模型。它對于移植RTOS代碼到規(guī)范化模式的Linux設備啟動程序具有啟發(fā)性,并且介紹了一些方法。特別地,本文會重點討論和比較RTOS代碼中的內存映射,Linux基于I/O調度隊列的移植,和重新定義RTOSI/O,以便在本地Linux驅動程序和守護進程里應用。
Abstract:
Key words :

Linux暴風雨般地占領了嵌入式系統(tǒng)市場。根據工業(yè)分析家分析,大約1/3到1/2的新的32位和64位嵌入式系統(tǒng)設計采用了Linux。嵌入式Linux已經在很多應用領域顯示出優(yōu)勢,比如SOHO家庭網絡和成像/多功能外設,并在以下幾方面具備巨大的跨越式發(fā)展前景:(NAS/SAN)存儲,家庭數字娛樂(HDTV/PVR/DVR/STB)和手持設備/無線設備,特別是數字移動電話。

新的嵌入式Linux應用不會象掌握在智慧和工藝之神-羅神手中那樣,會突然從開發(fā)者的頭腦中爆發(fā)出來。大量的項目必須采用數千行的,甚至數百萬行的過去的現成代碼。成百上千的嵌入式項目已經成功地將其它平臺的現成代碼移植到Linux之上,比如WindRiverVxWorks和pSOS,VRTX,Nucleus和其它RTOS,這些移植工作現在仍然有價值和現實意義。

到目前為止,大多數的關于移植舊的RTOS應用到嵌入式Linux的文獻,已經在關注RTOS接口(API),任務,調度模式和怎樣將他們映射到相應的用戶空間去。在嵌入式程序的密集I/O空間中,同樣重要的是,將RTOS的應用硬件接口代碼向具有更加規(guī)范化模式的Linux設備啟動程序的移植。

本文將縱覽幾種常用的內存映射I/O方法,它們經常出現于舊的嵌入式應用中。它們涵蓋的范圍,包括從對中斷服務例程的特殊使用和用戶線程對硬件訪問,到出現于有些ROTS中的半規(guī)范化驅動程序模型。它對于移植RTOS代碼到規(guī)范化模式的Linux設備啟動程序具有啟發(fā)性,并且介紹了一些方法。特別地,本文會重點討論和比較RTOS代碼中的內存映射,Linux基于I/O調度隊列的移植,和重新定義RTOSI/O,以便在本地Linux驅動程序和守護進程里應用。

RTOSI/O概念

“不規(guī)范”是能夠描述大多數在基于RTOS系統(tǒng)里的I/O的最佳詞語。大多數RTOS針對較早的無MMU的CPU而設計,忽略了內存管理,即使當MMU問世也是這樣,不區(qū)分物理地址和邏輯地址。大多數RTOS還全部在特權態(tài)(系統(tǒng)模式)運行,表面上看增強了性能。像這樣,全部的RTOS應用和系統(tǒng)代碼都能夠訪問整個機器地址空間,內存映射設備和I/O指令。實際上,將RTOS應用程序代碼同驅動程序代碼區(qū)分開非常困難,即使它們是有差別的。

這個不規(guī)范的結構導致了I/O的特殊實現。在很多情況下,完全缺乏對一種設備驅動程序模型的認同。根據這種工作的平等和沒有分層的特性,回顧在基于RTOS軟件中使用的一些重要概念和實踐非常有指導意義。

在線內存映射訪問

當在上個世紀八十年代中期商業(yè)化的RTOS產品可以買到的時候,大多數嵌入式軟件包含巨大的主循環(huán),主循環(huán)帶有針對嚴格時間操作的注冊I/O和中斷服務例程。開發(fā)人員將RTOS和執(zhí)行程序設計進他們的項目,主要為了加強同時性和幫助多任務同步,但是避開其它任何有“妨礙“的構造。同樣地,即使一個RTOS提供了I/O調用形式方法,嵌入式程序員繼續(xù)使用直接的I/O操作:

#defineDATA_REGISTER0xF00000F5

chargetchar(void){

return(*((char*)DATA_REGISTER));/*readfromport*/

}

voidputchar(charc){

*((char*)DATA_REGISTER)=c;/*writetoport*/

}

多數受過訓練的開發(fā)者常常將這樣的直接I/O代碼從硬件代碼獨立分離開。但是我還曾遇見大量的意大利面條式的I/O處理代碼。

當普遍深入使用直接內存映射I/O的時候,對Linux開始接觸的嵌入式開發(fā)人員總是面臨將所有的這類代碼移植到用戶空間,將定義寄存器地址的#define語句轉換成mmap()調用。這種處理方法對于一些種類的原型很好,但是不能支持中斷處理,限制了實時響應,特別不安全,不適合作為商業(yè)發(fā)布。

RTOS中斷服務例程

在Linux中,中斷服務專屬于內核的范圍。在一個RTOS中,中斷服務例程代碼是自由形態(tài)而且與應用程序代碼沒有區(qū)別(不外乎返回序列)。很多RTOS提供系統(tǒng)調用或者宏,來讓代碼自己檢測它自己的切換點(比如WindRiverVxWorks的intContext())。中斷服務例程通常也使用標準的庫函數,伴隨著可重入性和可移植性問題。大多數RTOS支持注冊中斷服務例程代碼,中斷仲裁句柄和中斷服務例程調度。一些非常原始的嵌入式執(zhí)行程序,僅僅支持在硬件矢量表里插入中斷服務例程的開始地址。即使你試圖直接在用戶程序空間執(zhí)行讀和寫的操作,你不得不將你的Linux中斷服務例程放入內核程序空間。

RTOSI/O子系統(tǒng)

大多數RTOS會提供一個定制的標準C運行庫(比如pSOS的pREPC),或者可以從獨立軟件開發(fā)商的編譯器中選擇打補丁的C庫(libc)同樣可以得到glibc。這樣,在最小化情況下,多數的RTOS支持標準C類型I/O的一個子集(open/close/read/write/ioctl)。大多數情況下,這些調用和從他們衍生出來的調用可以轉化為圍繞基本I/O的非常薄的封裝程序。有趣的是,因為大多數的?RTOS不支持文件系統(tǒng),這些平臺不提供針對flash和旋轉媒質的抽象文件存儲,常常使用完全不同的代碼和/或者不同的應用程序接口(API)(比如pSOS的pHILE)。

WindRiverVxWorks在這方面比其它多數RTOS平臺做的較好些,提供功能豐富的I/O子集,主要克服了網絡接口/多媒體接口里的集成和廣泛化障礙。

延時處理

很多RTOS也支持一種叫”下半部“("bottomhalf")的機制,它針對可中斷和/或者可搶占切換的I/O延時處理方法。其他RTOS沒有這樣的機制,但是替代地提供類似中斷嵌套的機制來獲得同樣的效果。

典型RTOS應用I/O架構

下面描述一個典型的I/O配置(僅僅輸入)和它向主要應用程序傳遞數據的路徑處理過程依次如下:

*一個硬件中斷觸發(fā)一個中斷服務例程的執(zhí)行。

*中斷服務例程做基本的處理和完成本地的輸入操作,或者讓RTOS調度延時的處理。在一些情況下,延時處理過程由在Linux里面被叫做用戶進程來處理,在這里就是通常的RTOS任務。

*無論在何時何地獲得數據(中斷服務例程或者延時切換),準備好的數據被放進隊列(RTOS中斷服務例程能夠訪問應用程序隊列通過應用程序接口(API)和其它進程間通信(?IPC),請看下面的API表)。

*一個或者多個應用任務然后從隊列讀消息,來取出數據。

在傳統(tǒng)的RTOS和Linux之間的典型I/O的比較輸出常常由類似的機制來完成。替代使用write()或者相似的系統(tǒng)調用,一個或者多個RTOS應用程序任務,將準備好的數據放進隊列。隊列中的數據由以下過程取出:一個I/O程序或者響應”準備好發(fā)送”中斷的中斷服務例程,一個系統(tǒng)時鐘,或者其它阻塞在獲取隊列中的應用任務,然后直接執(zhí)行I/O操作(可以是輪詢,也可以是通過DMA)。

將RTOSI/O映射進Linux

上面描述的基于隊列的生產/消費I/O模型,僅僅是很多種在傳統(tǒng)設計中所采用的特別方法之一。讓我們繼續(xù)用這個直接的例子,來討論幾種在嵌入式Linux下的實現:

大規(guī)模移植到用戶空間

對于勉強了解Linux設備驅動設計細節(jié),或者非常匆忙的開發(fā)者,可能將大多數這樣基于隊列設計程序完整無缺地移植到用戶空間。在這種驅動程序映射配置中,內存映射的物理I/O口通過函數mmap()提供的指針可以在用戶空間操作。

#include

#defineREG_SIZE0x4/*deviceregistersize*/

#defineREG_OFFSET0xFA400000

/*physicaladdressofdevice*/

void*mem_ptr;/*de-referenceformemory-mappedaccess*/

intfd;

fd=open("/dev/mem",O_RDWR);/*openphysicalmemory(mustberoot)*/

mem_ptr=mmap((void*)0x0,REG_AREA_SIZE,PROT_READ+PROT_WRITE,

MAP_SHARED,fd,REG_OFFSET);

/*actualcalltommap()*/

一個基于進程的用戶線程進行與基于RTOS的中斷服務例程或者延時任務一樣的操作,然后使用SVR4進程間通信函數msgsnd()將消息放進隊列,等待被另一個本地線程或者另一個進程利用函數msgrcv()來獲取。這種快速”臟的”處理方法是好的原型,同時對于建立可發(fā)布型代碼帶來了巨大的挑戰(zhàn)。首先重要的是需要在用戶空間掃描中斷。象DOS仿真(DOSEMU)項目提供基于信號的帶SIG(Silly中斷發(fā)生器)中斷I/O,但是用戶空間的中斷處理過程非常慢(毫秒量級中斷延時,所替代的基于內核的中斷服務例程中斷延時為數十微秒)。更進一步講,在用戶空間的切換調度不能保證用戶空間的I/O線程100%的及時執(zhí)行,即使采用可搶占Linux內核和實時調度策略。

此內容為AET網站原創(chuàng),未經授權禁止轉載。