WinCE6.0 USB Host驅動加載流程詳解(一)

    前面已經講過WinCE6.0USB驅動的整體結構,今天來看看USB Host驅動部分。可能是因爲USB Host驅動一般不修改的原因,這方面的資料少的可憐,所以只能自己研究了。分析的源碼微軟已經提供了,在目錄WINCE600\PUBLIC\COMMON\OAK\DRIVERS\USB下面。
    該目錄下包含CLASSUSBDHCDCOMMONINC文件夾,其中COMMONINC文件夾中包含的一個關於鎖功能的文件lock.cHCD文件夾中是對USB1.1USB2.0等協議的支持,爲USBD提供操作控制器的接口,一般不會去做修改,這裏不去深究。


USBD

       USBD文件夾實現的是一組接口,利用這組接口,上層Client層設備驅動程序來實現設備訪問以及驅動程序管理的功能。這裏也是所有USB Host驅動加載的總入口,USBD目錄如下:
       USBD驅動最後生成的庫爲usbd.dllUsdb.def文件的內容如下:
LIBRARY                 USBD
EXPORTS
                                HcdAttach
                                HcdDetach
                                HcdDeviceAttached
                                HcdDeviceDetached
                                HcdSelectConfiguration
                                RegisterClientDriverID
                                UnRegisterClientDriverID
                                OpenClientRegistryKey
                                GetClientRegistryPath
                                RegisterClientSettings
                                UnRegisterClientSettings
                                GetUSBDVersion
    在系統啓動之後,由設備管理器device.exe加載USBD.DLL驅動程序,入口同樣是函數DllMain(),之後調用HcdAttach()函數初始化一些Hcd控制器的資源,包括一些接口函數的列表。具體有哪些函數,後面會講到。到這裏USBD.DLL啓動完成。

    一般來說,大部分驅動都是由device.exe進程根據註冊表信息進行加載的,當第一次插入USB設備時,由於註冊表不存在相關的信息,會提示未能識別的USB設備,要求用戶輸入驅動程序的名稱,即驅動DLL的文件名。那麼下面看看這一過程在代碼中是如何實現的?

    當插入USB設備之後,系統調用USBD.DLL驅動中的HcdDeviceAttached()函數。該函數內,首先調用LoadDeviceDrivers()函數來加載USB設備對應的Client層驅動,具體如何調用下面再講。如果沒有找到合適的驅動,加載失敗了,便會調用函數GetClientDriverName(),該函數執行的功能便是提示用戶無法識別USB設備,請輸入相應的驅動程序名稱,以便系統加載。

    解釋了上面的問題後,看一下LoadDeviceDrivers()函數是如何查找正確的Client層驅動的。當插入USB設備之後,系統會讀取USB的設備描述符,然後根據描述符的值在註冊表HKEY_LOCAL_MACHINE\Drivers\USB\LoadClients下面進行掃描來查找相應的驅動程序。該註冊表的鍵值格式爲:LoadClients/<Group1 Id>/<Group2 Id>/<Group3 Id>/<Driver Name>

這裏稱爲第一組\第二組\第三組,每組又是由三個值中間加下劃線組成,如下:

第一組:dwVendorId_dwProductId_dwReleaseNumber

第二組:dwDeviceClass_dwDeviceSubClass_dwDeviceProtocol

第三組:dwInterfaceClass_dwInterfaceSubClass_dwInterfaceProtocol

    如果有一個值設置爲USB_NO_INFO,則鍵名不包括該值。如果整個組中每個值都設置成USB_NO_INFO,則鍵名爲Default。具體的每組包含的值的意義,請查閱相關資料。

    在掃描註冊表找到相應的驅動之後,LoadDeviceDrivers()函數調用LoadUSBClient()函數加載Client驅動。加載的流程爲:LoadUSBClient()函數調用LoadRegisteredDriver()函數,在LoadRegisteredDriver()內,獲取到Client驅動的DLL名稱之後,調用LoadDriver()函數將驅動程序加載到自己的虛擬地址空間,接着便通過GetProcAddress()函數獲得Client驅動中USBDeviceAttach()函數的地址,最後執行USBDeviceAttach()函數,運行Client驅動程序。

    回到上面,如果沒有找到匹配的驅動,則會提示用戶輸入驅動的名稱,在用戶輸入之後,HcdDeviceAttached()便調用InstallClientDriver()函數,該函數裏面通過LoadLibrary()函數將驅動程序映射到當前的虛擬地址空間,接着通過GetProcAddress()函數獲得Client驅動中USBInstallDrvier()函數的地址,同時執行該函數完成相關註冊表的設置。最後回到循環中,繼續執行LoadDeviceDrivers()函數。

       上面運行的LoadDriver()LoadLibrary()函數會在第一次加載對應的驅動的時候,運行驅動程序的DllMain()入口函數。到這裏就解釋了從USBD驅動轉向了Client驅動的整個過程。

解釋一下上面提到的幾個函數。USBInstallDriver函數負責向註冊表添加USB設備驅動的信息,以便下次插入時,能夠識別該USB設備。USBUnInstallDriver是在設備被移除後清理寫入註冊表的配置。USBDeviceAttach是在每次插入USB設備時,由系統調用來初始化USB設備、獲取USB信息、配置USB以及申請資源。這裏需要注意的是下面將要提到的USBD接口函數列表結構體_USB_FUNCS也是通過該函數傳入具體的Client驅動中的。上面的這三個接口函數是每一個Client層驅動必須實現的接口。

       下面看一下USBD爲上層Client驅動提供了哪些接口函數。首先是USBD導出的函數,但這不是全部的接口函數,而且Client層驅動部分使用USDB庫接口時,必須包含一個頭文件usbdi.h,位於WINCE600\PUBLIC\COMMON\DDK\INC中,此文件定義了所有的USDB接口。下面看看usbdi.h中爲上層提供了哪些接口。
BOOL USBDeviceAttach(USB_HANDLE hDevice, LPCUSB_FUNCS lpUsbFuncs,
                                         LPCUSB_INTERFACE lpInterface, LPCWSTR szUniqueDriverId,
                                         LPBOOL fAcceptControl,
                                         LPCUSB_DRIVER_SETTINGS lpDriverSettings, DWORD dwUnused);
BOOL USBInstallDriver(LPCWSTR szDriverLibFile);
BOOL USBUnInstallDriver();
       上面的三個函數在usbdi.h文件中只是聲明,具體實現在Client驅動中。所有的Client驅動部分必須實現這三個接口。
VOID GetUSBDVersion(LPDWORD lpdwMajorVersion, LPDWORD lpdwMinorVersion);
BOOL RegisterClientDriverID(LPCWSTR szUniqueDriverId);
BOOL UnRegisterClientDriverID(LPCWSTR szUniqueDriverId);
BOOL RegisterClientSettings(LPCWSTR lpszDriverLibFile,
                                                        LPCWSTR lpszUniqueDriverId, LPCWSTR szReserved,
                                                        LPCUSB_DRIVER_SETTINGS lpDriverSettings);
BOOL UnRegisterClientSettings(LPCWSTR lpszUniqueDriverId, LPCWSTR szReserved,
                                                            LPCUSB_DRIVER_SETTINGS lpDriverSettings);
HKEY OpenClientRegistryKey(LPCWSTR szUniqueDriverId);
BOOL GetClientRegistryPath(LPWSTR szRegistryPath, DWORD dwRegPathUnit, LPCWSTR szUniqueDriverId);
    可以看出上面幾個函數都是通過def文件顯式導出的。除此之外,usbdi.h中還有一個函數指針列表結構體_USB_FUNCS,裏面包含了USBD的另外一部分接口,是在def中沒有導出的,通過函數指針結構體在驅動之間進行傳遞的。_USB_FUNCS中的函數指針的實體都在文件usbddrv.cpp的文件中,整個USB驅動只有一個_USB_FUNCS的全局變量gc_UsbFuncs,它的聲明及初始化在usbd.c中。在始化ddrv.c

    USBD提供的主要接口函數歸類如下:

USBD的傳輸函數

IssueControlTransfer      IssueBulkTransfer     IssueInterruptTransfer              IssueIsochTransfer

IsTransferComplete              GetTransferStatus     GetIsochResults                AbortTransfer

CloseTransfer

USBDUSB設備建立通訊管道的函數

OpenPipe                            AbortPipeTransfers        ResetPipe                            ClosePipe

IsPipeHalted                 IsDefaultPipeHalted       ResetDefaultPipe

USBD針對總線上數據打包的函數

GetFrameNumber          GetFrameLength           TakeFrameLengthControl

SetFrameLength            ReleaseFrameLengthControl

USBDUSB設備進行交互的函數

OpenClientRegistryKey         RegisterNotificationRoutine         UnRegisterNotificationRoutine

GetUSBDVersion                 LoadGenericInterfaceDriver         TranslateStringDescr

FindInterface                       RegisterClientDriverID                UnRegisterClientDriverID

GetDeviceInfo                     RegisterClientSettings                  UnRegisterClientSettings

    今天先到這裏,明天繼續。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章