WinCE6.0 USB Host驱动加载流程详解 (VendorId ProductIdReleaseNumber/DeviceClass DeviceSubClass DeviceProtoco)

 
    前面已经讲过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中。
    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

你可能感兴趣的:(WinCE6.0 USB Host驱动加载流程详解 (VendorId ProductIdReleaseNumber/DeviceClass DeviceSubClass DeviceProtoco))