转载自http://jazka.blog.51cto.com/809003/745815
无语,编辑了好多遍了,还是显示不正常,就这样吧。
今天分析USB HOST Class部分的驱动内容。
CLASS目
录实现的是Client层驱动程序,通过调用USBD提供的接口函数来完成,文件夹下面包含的目录如下:
其中CLIENTCMN和COMMON包含的是公共代码,另外四个分别是为了实现HID设备、打印机、大容量存储器和usb串口支持的驱动代码。
1、STORAGE驱动
Storage驱动相关注册表信息的设置,源码这里就不分析了,主要看一下相关的注册表内容。由于这部分的驱动代码是由微软提供的,所以其注册表的信息在文件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}"
[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}"
在完成了注册表的相关设置之后,来看一下USBDeviceAttach()函数,这里注意下该函数的参数LPCUSB_FUNCS UsbFuncs,这个就是之前讲到的USBD一些接口函数封装起来的函数列表结构体,该参数是由USBD驱动传入的。这里就不列出函数的全部源码了,只分析一下整体的流程。
首先调用函数ParseUsbDescriptors解析该设备,包括设备的接口描述信息和协议描述信息。之后为该设备分配空间并进行赋值,设备结构体为USBMSC_DEVICE,该结构体包含了USB设备的interface、pipe以及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,这里不再介绍,可以直接查看相关代码了解。
2、HID驱动
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
"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设备的interface。CreateUsbHidDevice负责创建HID设备内容,这个比较重要,后面细谈。UsbFuncs->lpRegisterNotificationRoutine注册设备事件通知处理回调函数。
在CreateUsbHidDevice()内,先为设备分配资源,然后执行下面的语句:ActivateDeviceEx(HID_REGKEY_SZ, NULL, 0, CLIENT_REGKEY_SZ);ActivateDeviceEx函数是用来加载驱动程序,设备管理器加载驱动也是通过调用该函数来实现的,第一个参数为指向注册表中包含驱动信息的键,这里HID_REGKEY_SZ为Drivers\\USB\\ClientDrivers\\Hid\\Instance,包含的DLL名称为USBHID.DLL,即加载驱动USBHID.DLL,最后一个参数为向驱动程序传递的参数,这里CLIENT_REGKEY_SZ为Drivers\USB\ClientDrivers\Hid\Hid_Class,这个参数传递给了HID_Init。这里也看出来一般流接口驱动的参数如何从设备管理器传递过来的。再接下来处理设备的描述符和interface相关的信息,最后调用了函数HidMdd_Attach()将report descriptor信息传递给MDD层。
LoadHidClients(),该函数也位于
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
"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文件内容也可以看出来。
USB键盘的驱动为例,因为从上面USB转串口的接口驱动,目录结构如下:
可以看出UsbSer驱动部分没有前面那么深的层次结构,加载过程比较简单。Usbser.def文件的内容如下:
同样实现了那三个必须的函数,但在USBDriverClass的实例。RegisterClientDriverID()和Client驱动必须实现三个接口函数,说明这里就是驱动加载的总入口。RegisterClientDriverID()和<span lang="EN-US" times="" new="" roman';="" "="" style="padding: 0px; margin: 0px;">RegisterClientSettings()完成注册表的设置。注册表的内容如下:
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
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
[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"
"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()函数中,完成了流接口的初始化。