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中爲上層提供了哪些接口。