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