WinCE6.0 USB Host驱动加载流程详解(一)

    前面已经讲过 WinCE6.0 USB 驱动的整体结构,今天来看看 USB Host 驱动部分。 可能是因为 USB Host 驱动一般不修改的原因,这方面的资料少的可怜,所以只能自己研究了。分析的源码 微软已经提供了,在目录 WINCE600\PUBLIC\COMMON\OAK\DRIVERS\USB 下面。
     该目录下包含 CLASS USBD HCD COMMON INC 文件夹,其中 COMMON INC 文件夹中包含的一个关于锁功能的文件 lock.c HCD 文件夹中是对 USB1.1 USB2.0 等协议的支持,为 USBD 提供操作控制器的接口,一般不会去做修改,这里不去深究。

USBD
       USBD 文件夹实现的是一组接口,利用这组接口,上层 Client 层设备驱动程序来实现设备访问以及驱动程序管理的功能。这里也是所有 USB Host 驱动加载的总入口, USBD 目录如下:
       USBD 驱动最后生成的库为 usbd.dll Usdb.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
USBD USB 设备建立通讯管道的函数
OpenPipe                            AbortPipeTransfers        ResetPipe                            ClosePipe
IsPipeHalted                 IsDefaultPipeHalted       ResetDefaultPipe
USBD 针对总线上数据打包的函数
GetFrameNumber          GetFrameLength           TakeFrameLengthControl
SetFrameLength            ReleaseFrameLengthControl
USBD USB 设备进行交互的函数
OpenClientRegistryKey         RegisterNotificationRoutine         UnRegisterNotificationRoutine
GetUSBDVersion                 LoadGenericInterfaceDriver         TranslateStringDescr
FindInterface                       RegisterClientDriverID                UnRegisterClientDriverID
GetDeviceInfo                     RegisterClientSettings                  UnRegisterClientSettings

    今天先到这里,明天继续。

参考资料:
wince USB 设备驱动程序导读
http://blog.csdn.net/caowenbin/article/details/2030938
详解 WinCE USB Host 驱动开发
http://blog.csdn.net/selfref/article/details/4830961

你可能感兴趣的:(host,休闲,USB驱动,WinCE6.0,驱动加载流程)