隨著USB2.0裝置的不斷增加,USB裝置驅動開發在嵌入式開發中變的越來越重要。Windows CE支援USB 2.0更是對這一波新技術浪潮產生巨大的推動。近期我負責一個這樣的項目,在WinCE下開發USB介面的外圍裝置驅動。當時做這個項目花費了我相當多的時間和精力,錯走許多冤枉路使我精疲力盡。
項目需求是在已調好的ARM9板子上開發USB WiFi無線網卡的驅動程式,具體要求是驅動程式平台是WinCE,CPU類型支援ARM構架,要能比較方便地移植到X86;驅動介面類型是USB2.0 和Wlan 802.11b。後來因為串連效率一直有問題,就東改西改,最後改的是一塌糊塗。幸好老闆比較寬容,給了我充裕的時間和支援,這裡將關於USB驅動開發的點滴理解與大家分享。
本機裝置是指整合進平台的裝置,其中包括顯示、觸摸面板、音頻、串列埠、LED、電池和PC卡插座等。如果沒有這些本機裝置整個系統就不能和使用者資訊交流,例如觸摸面板和顯示等。本機驅動程式一般設計為動態連結程式庫,但有兩個例外:電池和LED驅動程式由於小而設計為靜態庫(當建立CE映像時與GWES模組連結)。這些裝置相應的驅動程式是在WinCE平台開發過程中由OEM開發的,它們儲存在ROM或快閃記憶體內。通常只有OEM才會對本機裝置驅動程式進行修改,其它自由裝置生產商只提供附加的硬體裝置,對本機裝置驅動程式不會有過多涉及。
與本機驅動程式不同的是,所有可載入流驅動程式都共用一個公用介面。該介面由每個驅動程式內的10個功能或記錄點組成,這些功能與應用程式所用的檔案 API中的功能匹配。因此,控制可載入裝置的流介面驅動程式一般由應用程式存取,流介面驅動程式由一個特殊檔案來將裝置功能展現給應用程式的,該檔案可被開啟、讀取、寫入和關閉。例如,使用者將一個GPS裝置與平台相連後,就可啟動有GPS功能的應用程式來存取並使用該裝置。WinCE是使用已有的API來讓應用程式存取這些驅動程式,而不是建立新的API。
層次型驅動是指分為兩層,較上層的MDD和比較下層的PDD。MDD實現的是和平台無關的功能,它描述了一個通用的驅動程式架構;而PDD是和硬體以及平台相關的程式碼群組成。MDD調用PDD中特定的介面來擷取硬體相關的資訊。當使用層次型驅動的時候,一般只需要基於相近的樣列驅動程式,針對特定的硬體只修改PDD程式,MDD建立的架構可繼續使用。但由於層次間介面的層層調用以及訊息的傳遞,使得處理速度相對於獨立驅動程式要慢。因此,在嵌入式即時要求苛刻的環境下,層次型驅動顯得不是很適合。
簡單的說,獨立驅動是把PDD與MDD寫在一起,沒有做嚴格的區分,通常這種驅動比較簡單,比如ATADISK。至於本機驅動和載入式流驅動是從驅動與系統其它模組(調用者)的介面形式上做的分類。所以,一個載入式驅動程式可以是獨立的流式驅動,例如ATADISK;也可以是分層的流式驅動,例如 OHCI。也就是說,獨立和分層是驅動實現方式上的分類,而本機和載入流式則是驅動模型上的分類。所謂本機驅動就是作業系統有保留專門的介面,而載入流式驅動是指編寫DLL檔案匯出各種流式介面函數的介面。
隨著USB2.0裝置的不斷增加,USB裝置驅動開發在嵌入式開發中變的越來越重要。Windows CE支援USB 2.0更是對這一波新技術浪潮產生巨大的推動。近期我負責一個這樣的項目,在WinCE下開發USB介面的外圍裝置驅動。當時做這個項目花費了我相當多的時間和精力,錯走許多冤枉路使我精疲力盡。
項目需求是在已調好的ARM9板子上開發USB WiFi無線網卡的驅動程式,具體要求是驅動程式平台是WinCE,CPU類型支援ARM構架,要能比較方便地移植到X86;驅動介面類型是USB2.0 和Wlan 802.11b。後來因為串連效率一直有問題,就東改西改,最後改的是一塌糊塗。幸好老闆比較寬容,給了我充裕的時間和支援,這裡將關於USB驅動開發的點滴理解與大家分享。
2. USB載入式流介面驅動要點分析
為了支援不同類型的外圍裝置,WinCE平台提供了具有定製介面的流介面驅動程式模型。因為大部分USB外圍裝置由於功能性更適合流介面驅動的結構,所以一般都採用載入式流介面驅動程式模型來開發USB裝置驅動程式。
(1)USB系統結構分析
WinCE下USB系統軟體由兩層組成:較高USB裝置驅動程式層和較低的USB函數層。較低的USB函數層本身又由兩部分組成:較高的通用序列匯流排驅動程式(USBD)模組和較低的主控制器驅動程式(HCD)模組。通過HCD模組功能和USBD模組實現高層的USBD介面函數,USB裝置驅動程式就能與外圍裝置進行通訊。
在資料轉送的過程中,操作流程通常按下列的次序進行:①USB裝置驅動程式進行資料轉送的初始化,即通過USBD介面函數給USBD模組發送資料轉送的請求。②USBD模組將該請求分成一些單獨的事務。③HCD模組排出事務次序。④主控制器硬體執行事務。這裡需要提醒的是,所有的事務都是從主機發出的,外圍裝置完全是被動接受型的。
(2)USB裝置驅動程式進入點函數
從結構分析我們可知,所有的USB裝置驅動程式必須在它們的DLL庫設定一定的進入點與USBD模組進行適當的互動。設定進入點函數有兩個作用:一是使得 USBD 模組能與外部裝置互動;二是使得驅動程式能建立和管理任何可能需要的註冊鍵。
下面簡要介紹相關函數的作用:USBDeviceAttach是當 USB 裝置串連到主要電腦時運行,USBD模組會調用這個函數初始化USB裝置,取得USB裝置資訊和配置USB裝置,並且申請必需的資源。 USBInstallDrive是在第一次載入USB裝置驅動程式時首先被調用,它使得驅動程式能建立需要的註冊鍵,用於將一個驅動程式所需的註冊表資訊寫入到HKEY_LOCAL_MACHINE\Drivers\USB\ClientDrivers目錄下,例如裝置名稱等。需要注意的是,USB裝置驅動程式不使用標準的註冊表函數,而是使用RegisterClientDriverID()、RegisterClientSettings()函數來註冊相應的裝置資訊。
USBUninstallDriver是在使用者刪除USB裝置驅動程式時調用,負責刪除註冊鍵並釋放其它相關資源。它通過調用 UnRegisterClientSettings()和UnRegisterClientDriverID()函數來刪除由驅動程式的 USBInstallDriver()函數建立的所有註冊鍵。因此,我們在驅動程式中就需要嚴格按照這三個函數的原型來實現,否則就不能為裝置管理員所識別。
1. 什麼是WinCE裝置驅動程式?
(1)從驅動載入方式來區分
在深入探討Windows CE所支援的外圍裝置驅動程式之前,先瞭解在WinCE平台上使用的兩種裝置:內建裝置和可安裝裝置。因此,從驅動載入方式來看WinCE可分為本機裝置驅動(Built-In Driver)、可載入驅動(Loadable Driver)以及混合型驅動。
①本機裝置驅動
本機裝置驅動即Native Device Drivers。WinCE設計成可直接使用內建裝置,這些裝置由本機驅動過程式控制制,而本機驅動程式又與WinCE的核心組件緊密相連。這些驅動對應的裝置通常在系統啟動時,在GWES的進程空間內被載入,因此它們不是以獨立的DLL形式存在,也因此要求每一個本機驅動程式都必須與稱為裝置驅動程式介面 (DDI)的特定介面一致。
本機裝置是指整合進平台的裝置,其中包括顯示、觸摸面板、音頻、串列埠、LED、電池和PC卡插座等。如果沒有這些本機裝置整個系統就不能和使用者資訊交流,例如觸摸面板和顯示等。本機驅動程式一般設計為動態連結程式庫,但有兩個例外:電池和LED驅動程式由於小而設計為靜態庫(當建立CE映像時與GWES模組連結)。這些裝置相應的驅動程式是在WinCE平台開發過程中由OEM開發的,它們儲存在ROM或快閃記憶體內。通常只有OEM才會對本機裝置驅動程式進行修改,其它自由裝置生產商只提供附加的硬體裝置,對本機裝置驅動程式不會有過多涉及。
②可載入裝置驅動
可載入裝置是指可與平台串連和分離的第三方介面裝置,可由使用者隨時安裝和卸載。這種外圍裝置的驅動也被稱為流驅動,這些驅動可以在系統啟動時或者和啟動後的任何時候由裝置管理員動態載入。通常這類驅動是以DLL動態連結程式庫的形式存在,系統載入後這些驅動程式也只是以使用者態的角色運行。可載入驅動程式是通過檔案操作API來從裝置管理員和應用程式獲得命令。在WinCE典型的可載入驅動有:PCMCIA driver(PCMCIA.dll)、Serial driver(SERIAL.dll)、ATAFLASH driver(ATA.dll)、Ethernet driver(NE2000.dll,SMSC100FD.dll)。
與本機驅動程式不同的是,所有可載入流驅動程式都共用一個公用介面。該介面由每個驅動程式內的10個功能或記錄點組成,這些功能與應用程式所用的檔案 API中的功能匹配。因此,控制可載入裝置的流介面驅動程式一般由應用程式存取,流介面驅動程式由一個特殊檔案來將裝置功能展現給應用程式的,該檔案可被開啟、讀取、寫入和關閉。例如,使用者將一個GPS裝置與平台相連後,就可啟動有GPS功能的應用程式來存取並使用該裝置。WinCE是使用已有的API來讓應用程式存取這些驅動程式,而不是建立新的API。
(2)從驅動程式層次上分類
一般可以分為獨立驅動和層次型驅動兩類。獨立驅動程式是指將驅動程式編寫成同時包含Model Device Driver(MDD)和Platform Dependent Driver(PDD)層的獨立驅動。使用獨立驅動的好處在於可以省去MDD和PDD層驅動之間的資訊傳遞,這一點在即時處理中非常重要。獨立驅動的程式碼封裝括插斷服務常式和平台相關處理函數。另外,如果裝置的操作和MDD驅動層的介面描述相吻合,用獨立驅動程式可以提高處理效能。
層次型驅動是指分為兩層,較上層的MDD和比較下層的PDD。MDD實現的是和平台無關的功能,它描述了一個通用的驅動程式架構;而PDD是和硬體以及平台相關的程式碼群組成。MDD調用PDD中特定的介面來擷取硬體相關的資訊。當使用層次型驅動的時候,一般只需要基於相近的樣列驅動程式,針對特定的硬體只修改PDD程式,MDD建立的架構可繼續使用。但由於層次間介面的層層調用以及訊息的傳遞,使得處理速度相對於獨立驅動程式要慢。因此,在嵌入式即時要求苛刻的環境下,層次型驅動顯得不是很適合。
簡單的說,獨立驅動是把PDD與MDD寫在一起,沒有做嚴格的區分,通常這種驅動比較簡單,比如ATADISK。至於本機驅動和載入式流驅動是從驅動與系統其它模組(調用者)的介面形式上做的分類。所以,一個載入式驅動程式可以是獨立的流式驅動,例如ATADISK;也可以是分層的流式驅動,例如 OHCI。也就是說,獨立和分層是驅動實現方式上的分類,而本機和載入流式則是驅動模型上的分類。所謂本機驅動就是作業系統有保留專門的介面,而載入流式驅動是指編寫DLL檔案匯出各種流式介面函數的介面。
3.USB裝置流介面驅動的實現步驟
從WinCE USB裝置驅動模型及結構分析中,我們可以清晰的看到主機和外設之間的實現方式。在主機端,通過USBD模組和HCD模組使用預設的PIPE訪問一個通用的邏輯裝置,實際上就是說USBD和HCD是一組訪問所有USB裝置的邏輯介面,它們負責管理所有USB裝置的串連、載入、移除、資料轉送和通用配置。其中HCD是主機控制驅動,是為USBD提供底層的功能訪問服務,USBD是USB匯流排驅動,位於HCD的上層,利用HCD的服務提供較高層次的功能。因此,實現USB載入流驅動程式大致需要完成以下步驟:
(1)選擇代表裝置的檔案名稱首碼。首碼非常重要,裝置管理員在註冊表中通過首碼來識別裝置。同時,在流介面命名時也將這個首碼作為進入點函數的首碼,如果裝置首碼為XXX,那麼流介面對應為XXX_Close,XXX_Init等。
(2)設定驅動的各個進入點函數。所謂進入點是指提供給裝置管理員的標準檔案I/O介面。在產生一個DLL後,就用裝置檔案名稱首碼替換名字中的XXX。因此,每個載入式流介面驅動程式必須實現XXX_Init()、XXX_IOControl()以及XXX_PowerUp()等一組標準的函數,用來完成標準的檔案I/O函數和電源管理等。
(3)建立.DEF檔案。當裝置管理員初始化USB裝置編譯出來的流介面函數後,還必須建立一個.def檔案。DEF檔案定義了DLL要匯出的介面集,而且載入式流驅動大多是以DLL形式存在的,所以應將DLL和DEF的檔案名稱統一起來。DEF檔案告訴連結程式需要輸出什麼樣的函數,最後將驅動程式編譯到核心中去,這樣這個USB裝置流介面驅動程式就可以被應用程式調用。
(4)在註冊表中為驅動程式建立表項。在註冊表中建立驅動程式進入點,這樣裝置管理員才能識別和管理這個驅動。此外,註冊表中還能儲存額外的資訊,這些資訊可以在驅動運行之後被使用到。
在這次USB驅動開發過程中,錯走許多冤枉路使我叫苦連天。我感受最深的是由於WinCE提供了通用序列匯流排驅動程式(USBD)模組、USBD介面函數全集、樣本主機控制器驅動程式(HCD)模組。所以,我們只需要根據USB裝置硬體特性,利用USBD提供的不同函數,實現流介面函數與外圍裝置的互動。在沒有特別的情況下,我最大的收穫經驗是把這些公用的來源程式照搬過來,能極大的縮短開發週期,從而能更快速地進行嵌入式開發。