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

    無語,編輯了好多遍了,仍是顯示不正常,就這樣吧。
    今天分析USB HOST Class部分的驅動內容。
        CLASS 錄實現的 Client 層驅動程序,經過調用 USBD 提供的接口函數來完成,文件夾下面包含的目錄以下:


    其中CLIENTCMNCOMMON包含的是公共代碼,另外四個分別是爲了實現HID設備、打印機、大容量存儲器和usb串口支持的驅動代碼。

    1STORAGE驅動
       STORAGE目錄結構以下:

    INC文件夾存放的是頭文件,CLASSUSB存儲設備的驅動程序,DISK是磁盤驅動程序。
    這裏爲何有兩個驅動?這裏引用參考資料的解釋:驅動程序工做在硬件與操做系統之間,它有兩個功能,一個是將操做系統轉發來的操做以符合指定硬件設備的形式控制硬件設備,另外一個是向操做系統提供這個訪問接口。好比說U盤,一方面驅動程序要把操做系統對U盤的識別、讀、寫等操做轉換成U盤的動做,另外一方面又告訴操做系統這是個U盤,能夠當成一個文件夾或文件系統來用,可以接受標準的文件操做命令。因此此處存在兩個驅動。
      這兩個驅動的函數導出文件內容以下:
CLASS部分:

LIBRARY USBMSC
EXPORTS
;
;     USB Storage Interface
;
                GetDWORD
                SetDWORD
                SetWORD
                UsbsGetContextFromReg
                UsbsDataTransfer
;
;     USBDI Interface
;
                USBInstallDriver
                USBDeviceAttach
                USBUnInstallDriver
DISK部分:

LIBRARY                 USBDISK6
EXPORTS
                                UsbDiskAttach
                                UsbDiskDetach
                                DSK_Init
                                DSK_Deinit
                                DSK_Open
                                DSK_Close
                                DSK_Read
                                DSK_Write
                                DSK_Seek
                                DSK_IOControl
                                DSK_PowerUp
                                DSK_PowerDown
    從上面能夠看出Storage Client驅動的總接口應該在CLASS部分,這裏包含了Client層驅動必須實現的三個接口函數,而DISK驅動是被CLASS驅動調用的,具體如何調用,下面會講到。
       USBInstallDriver()函數主要是完成Storage驅動相關注冊表信息的設置,源碼這裏就不分析了,主要看一下相關的註冊表內容。因爲這部分的驅動代碼是由微軟提供的,因此其註冊表的信息在文件WINCE600\PUBLIC\COMMON\OAK\FILES\ common.reg中。
; USB - Mass Storage Class Driver
[HKEY_LOCAL_MACHINE\Drivers\USB\LoadClients\Default\Default\8\Mass_Storage_Class]
     "DLL"="USBMSC.DLL"
     "Prefix"="DSK"

[HKEY_LOCAL_MACHINE\Drivers\USB\ClientDrivers\Mass_Storage_Class]
     "DLL"="USBMSC.DLL"
     "Prefix"="DSK"

[HKEY_LOCAL_MACHINE\Drivers\USB\ClientDrivers\Mass_Storage_Class\6]
     "DLL"="USBDISK6.DLL"
     "Prefix"="DSK"
     "Folder"="USB Disk"
     "MediaPollInterval"=dword:432     ; Media poll interval (1250 ms)
     "ReadSectorTimeout"=dword:2710    ; Read sector timeout (10 s)
     "WriteSectorTimeout"=dword:2710 ; Read sector timeout (10 s)
     "ScsiCommandTimeout"=dword:1388 ; Command timeout (5 s)
     "UnitAttnRepeat"=dword:A                ; TEST UNIT READY repeat (reduce to 1 for large USB disk keys)
     "IOCTL"=dword:4
     "IClass"="{A4E7EDDA-E575-4252-9D6B-4195D48BB865}"
        上面註冊表中的第二部分表明的大容量存儲設備的驅動,第三部分則爲具體某一類接口的 Storage 設備的驅動,好比上面的 6 表明表明的就是 SCSI 接口的大容量存儲設備。
    在完成了註冊表的相關設置以後,來看一下USBDeviceAttach()函數,這裏注意下該函數的參數LPCUSB_FUNCS UsbFuncs,這個就是以前講到的USBD一些接口函數封裝起來的函數列表結構體,該參數是由USBD驅動傳入的。這裏就不列出函數的所有源碼了,只分析一下總體的流程。
    首先調用函數ParseUsbDescriptors解析該設備,包括設備的接口描述信息和協議描述信息。以後爲該設備分配空間並進行賦值,設備結構體爲USBMSC_DEVICE,該結構體包含了USB設備的interfacepipe以及registry等,將做爲參數傳遞給其餘一些函數。下來經過註冊表查找到對應的Disk的驅動,好比USBDISK6.DLL,而後調用LoadDrvier()函數來將Disk驅動加載到本身的虛擬地址空間中,同時得到Disk驅動中UsbDiskAttach函數及UsbDiskDetach函數的地址,接着便調用UsbFuncs->lpRegisterNotificationRoutine函數註冊事件通知處理函數,這裏採用的是回調函數的方法。這裏須要說明的是該處理函數必須實現USB_CLOSE_DEVICE消息的響應,這個微軟要求任何USB設備都應該實現的,在Close的消息響應中會調用UsbDiskDetach函數釋放驅動程序資源,若是再也不有設備引用此驅動程序,則FreeLibrary()釋放該驅動庫。最後調用UsbDiskAttach函數。
    到這裏並無結束,Disk驅動尚未真正完成設備驅動初始化。爲何尚未初始化完?由於上面調用的LoadDrvier()函數僅僅是將驅動DLL加載到本身的虛擬空間中,這樣才能夠得到Disk驅動的入口UsbDiskDetach函數。在進入UsbDiskDetach函數後,仍然是進行一系列設備描述符等信息的存儲、設置等,最後調用關鍵的ActivateDevice()函數,將註冊表USB\ClientDrivers\Mass_Storage_Class\6下面的驅動USBDISK6.DLL進行加載,此時便會進入流驅動的DSK_Init函數,到這裏纔算真正的加載完畢。
    當加載了Disk驅動以後,上層應用程序即可以按照流接口的操做方法來操做Mass Storage設備,包括初始化設備、獲取設備信息、讀寫數據等,操做的接口即是DSK_IOControl,這裏再也不介紹,能夠直接查看相關代碼瞭解。
    2HID驅動
        HID目錄結構爲:
       HIDCLASS 文件夾裏面的是支持全部 HID 設備的驅動程序,包括 Mdd Pdd 兩部分, CLIENTS 文件夾是隻支持某一種 HID 設備的驅動程序,包括 USB 鍵盤、 USB 鼠標。各個部分的 def 導出文件的內容以下,爲何鍵盤的驅動和鼠標驅動有區別呢?後面給出解釋。
;HIDCLASS部分:
LIBRARY                 USBHID
EXPORTS                
                                USBInstallDriver
    USBUnInstallDriver
                                USBDeviceAttach
                                HID_Init
                                HID_Deinit
                                HID_Open
                                HID_Close
                                HID_IOControl
    Init
    Deinit
;CLIENTS\KBDHID部分:
LIBRARY                 KBDHID
EXPORTS                
                                HIDDeviceAttach
    HIDDeviceNotifications

    KBD_Init
    KBD_PreDeinit
                                KBD_Deinit
                                KBD_Open
                                KBD_Close
                                KBD_IOControl
;CLIENTS\CONSHID部分
LIBRARY                 CONSHID
EXPORTS                
                                HIDDeviceAttach
    HIDDeviceNotifications
;CLIENTS\MOUHID部分
LIBRARY                 MOUHID
EXPORTS
  HIDDeviceAttach
  HIDDeviceNotifications
    USBHID驅動中包含USB Client驅動必須具有的三個接口,那麼系統加載HID設備驅動的入口應該就在USBHID驅動中,其餘鍵盤、鼠標等設備是由USBHID來加載的。
       首先來看USBInstallDriver()函數,一樣的是完成設備驅動的相關注冊表設置。涉及到的註冊表內容爲:
[HKEY_LOCAL_MACHINE\Drivers\USB\LoadClients\Default\Default\3\Hid_Class]
     "DLL"="USBHID.DLL"

[HKEY_LOCAL_MACHINE\Drivers\USB\ClientDrivers\Hid\Instance]
     "DLL"="USBHID.DLL"

[HKEY_LOCAL_MACHINE\Drivers\USB\ClientDrivers\Hid\Hid_Class]
     "DLL"="USBHID.DLL"
     "Prefix"="HID"
     "QueuedTransferCount"=dword:2
    下面來看看USBDeviceAttach()函數,這裏主要執行三個函數:   ParseUsbDescriptors負責解析設備,判斷是否知足HID設備的interfaceCreateUsbHidDevice負責建立HID設備內容,這個比較重要,後面細談。UsbFuncs->lpRegisterNotificationRoutine註冊設備事件通知處理回調函數。
    CreateUsbHidDevice()內,先爲設備分配資源,而後執行下面的語句:ActivateDeviceEx(HID_REGKEY_SZ, NULL, 0, CLIENT_REGKEY_SZ);ActivateDeviceEx函數是用來加載驅動程序,設備管理器加載驅動也是經過調用該函數來實現的,第一個參數爲指向註冊表中包含驅動信息的鍵,這裏HID_REGKEY_SZDrivers\\USB\\ClientDrivers\\Hid\\Instance,包含的DLL名稱爲USBHID.DLL,即加載驅動USBHID.DLL,最後一個參數爲向驅動程序傳遞的參數,這裏CLIENT_REGKEY_SZDrivers\USB\ClientDrivers\Hid\Hid_Class,這個參數傳遞給了HID_Init。這裏也看出來通常流接口驅動的參數如何從設備管理器傳遞過來的。再接下來處理設備的描述符和interface相關的信息,最後調用了函數HidMdd_Attach()report descriptor信息傳遞給MDD層。
     MDD 層的 HidMdd_Attach() 函數這裏就不仔細研究,只關注這裏調用了一個函數 LoadHidClients() ,該函數也位於 MDD 層的 Hidmdd.cpp 文件中。接下來會用到另外一部分關於 HID 設備的註冊表信息,以下:
[HKEY_LOCAL_MACHINE\Drivers\HID\LoadClients\Default\Default\1_6\Keyboard]
     "DLL"="KBDHID.DLL"

[HKEY_LOCAL_MACHINE\Drivers\HID\LoadClients\Default\Default\12_1\Consumer]
     "DLL"="CONSHID.DLL"
     "RemoteWakeup"=dword:1

[HKEY_LOCAL_MACHINE\Drivers\HID\LoadClients\Default\Default\1_2\Mouse]
     "DLL"="MOUHID.DLL"
     "RemoteWakeup"=dword:1
    LoadHidClients()函數裏面實現了對Client下面的鼠標、鍵盤或其餘HID設備驅動的加載。具體流程爲:首先調用函數FindClientRegKey()在在這個函數裏面枚舉HID設備,找到相匹配的Client設備的註冊表鍵。查找的方法是在註冊表\Drivers\HID\LoadClients\下面,根據前面提到的那三組鍵值(Default等)比較設備的描述符。結合上面的註冊表信息能夠看出,從這裏能夠找到對應的鼠標或者鍵盤等Client驅動的DLL名稱。以後調用LoadDriver()函數將對應驅動的加載到本身的虛擬地址空間中,並得到對應驅動的HIDDeviceAttach函數地址,最後執行該函數,從而進入到具體的Client Hid設備驅動程序中。這裏須要注意,HIDDeviceAttach函數是一個統一的接口,即全部的Client Hid設備驅動都必須實現該函數,這一點從上面的def文件內容也能夠看出來。
        HID設備驅動的層次目錄比較複雜,到這裏尚未結束。接下來進入具體的Client Hid設備驅動裏面。這裏以USB鍵盤的驅動爲例,由於從上面def能夠看出,鍵盤的驅動實現的比較徹底,有標準的流接口。這裏還有一個註冊表須要用到,以下:
[HKEY_LOCAL_MACHINE\Drivers\HID\ClientDrivers\Keyboard]
     "DLL"="KBDHID.DLL"
     "Prefix"="KBD"
     "IClass"="{CBE6DDF2-F5D4-4e16-9F61-4CCC0B6695F3}"
     "RemoteWakeup"=dword:1
     "Flags"=dword:00010000
    在Kbdhid.cpp中找到HIDDeviceAttach()函數,開始仍是爲設備分配資源,設置描述符等信息,以後建立鍵盤的處理線程函數,接收設備發來的報告並進行處理,最後再次調用函數ActivateDevice()函數將Drivers\HID\ClientDrivers\Keyboard下面的KBDHID.DLL驅動加載。直到此時,纔會執行KBD_Init函數,完成鍵盤設備驅動的初始化。到此,整個的流程纔算走完。
    最後回答前面提出的一個,爲何USB鍵盤的驅動提供了標準的流接口驅動,而USB鼠標驅動卻沒有?這裏沒有本質的區別,只是微軟官方只提供了鍵盤的流接口驅動而已,你能夠本身添加鼠標的驅動。正由於鼠標的驅動沒有實現,能夠發現WinCE下面控制面板中,鼠標的設置是很簡單的,沒有左右鍵調換的設置。這些均可以經過添加驅動來實現的。
    3USBSER驅動
        USBSER驅動用來實現USB轉串口的接口驅動,目錄結構以下:
    能夠看出 UsbSer 驅動部分沒有前面那麼深的層次結構,加載過程比較簡單。 Usbser.def 文件的內容以下:
LIBRARY USBSER
EXPORTS  USBInstallDriver
  USBDeviceAttach
  USBUnInstallDriver
  COM_Init
  COM_Deinit
  COM_PreDeinit
  COM_Open
  COM_Close
  COM_PreClose
  COM_Read
  COM_Write
  COM_Seek
  COM_PowerDown
  COM_PowerUp
  COM_IOControl
    一樣實現了那三個必須的函數,但在USBSER驅動中多了不少類結構,USBInstallDriver()函數內容和以前的有些不一樣,先建立了一個類USBDriverClass的實例。USBDriverClass類是對USBD提供的接口又一層封轉,因此這裏經過該類的成員函數間接調用了RegisterClientDriverID()RegisterClientSettings()完成註冊表的設置。註冊表內容爲:
[HKEY_LOCAL_MACHINE\Drivers\USB\LoadClients\1118_121\Default\Default\USBSER_CLASS]
     "Prefix"="COM"
     "Dll"="USBSer.DLL"

[HKEY_LOCAL_MACHINE\Drivers\USB\ClientDrivers\USBSER_CLASS]
     "Prefix"="COM"
     "Dll"="USBSer.DLL"
     "DeviceArrayIndex"=dword:1
     "RxBufferSize"=dword:4000
     "IClass"="{CC5195AC-BA49-48a0-BE17-DF6D1B0173DD}"

[HKEY_LOCAL_MACHINE\Drivers\USB\LoadClients\1118_206\Default\Default\SERIAL_CLASS]
     "Prefix"="COM"
     "Dll"="USBSer.DLL"

[HKEY_LOCAL_MACHINE\Drivers\USB\ClientDrivers\SERIAL_CLASS]
     "Prefix"="COM"
     "Dll"="USBSer.DLL"
     "RxBufferSize"=dword:4000
     "DeviceArrayIndex"=dword:0
     "IClass"="{CC5195AC-BA49-48a0-BE17-DF6D1B0173DD}"
    從註冊表看出,微軟提供的USB轉串口驅動支持了兩種設備,分別爲COM0COM1
    USBDeviceAttach()函數中,首先建立了類SerialUsbClientDevice的一個實例,該類繼承自類UsbClientDevice,以後調用類成員函數Init()。在Init()函數裏經過ActivateDevice()函數加載了Drivers\USB\ClientDrivers\SERIAL_CLASS下面的USBSer.DLL驅動。
    在加載USBSer.DLL驅動以後,便會調用對應的COM_Init函數,該函數位於串口的驅動MDD層,傳入的參數是該類的this指針。通常串口驅動的MDD層有微軟已經實現了,與硬件無關的CSerialPDD也已經提供,咱們只須要實現與硬件相關的PDD部分就能夠了。在USBSer.DLL驅動中,有一個類UsbSerClientDriver即是從CSerialPDD類繼承過來的,這樣便實現了和串口驅動的接口。
    至於USBSer.DLL是如何USB的數據傳輸轉換爲COM口的讀寫操做等細節,之後再講。今天主要分析驅動加載的流程。
    4PRITER驅動
       PRITER目錄下面包含的文件以下:
    能夠看出, Printer 驅動結構較爲簡單,先來看看定義導出接口的 def 文件按內容:
LIBRARY                 USBPRN
EXPORTS
                                USBInstallDriver
                                USBDeviceAttach
                                USBUnInstallDriver
                                LPT_Init
                                LPT_Deinit
                                LPT_Open
                                LPT_Close
                                LPT_Read
                                LPT_Write
                                LPT_Seek
                                LPT_IOControl
                                LPT_PowerUp
                                LPT_PowerDown
    這裏看到了Client驅動必須實現三個接口函數,說明這裏就是驅動加載的總入口。USBInstallDriver()函數和以前同樣,仍是調用USBD提供的接口RegisterClientDriverID()RegisterClientSettings()完成註冊表的設置。註冊表的內容以下:
[HKEY_LOCAL_MACHINE\Drivers\USB\LoadClients\Default\Default\7\Printer_Class]
     "Prefix"="LPT"
     "Dll"="USBPRN.DLL"

[HKEY_LOCAL_MACHINE\Drivers\USB\ClientDrivers\Printer_Class]
     "Prefix"="LPT"
     "Dll"="USBPRN.DLL"
    接着來看USBDeviceAttach()函數,和以前同樣,調用ParseUsbDescriptors()函數解析設備描述符,同時申請設備資源,設置屬性、接口和管道,以後便調用ActivateDevice()函數,加載Drivers\USB\ClientDrivers\Printer_Class註冊表下面的USBPRN.DLL驅動,最後註冊事件通知處理回調函數。在加載了USBPRN.DLL驅動以後,便直接進入了LPT_Init()函數中,完成了流接口的初始化。
 
winceUSB設備驅動程序導讀
詳解WinCEUSB Host驅動開發
http://blog.csdn.net/selfref/article/details/4830961
相關文章
相關標籤/搜索