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

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