0: wince 驱动分层
看看usbddrv.cpp文件中的说明
Abstract:
This file contains code for the USBD module of the Universal Serial
Bus driver for Windows CE. The USBD driver is responsible for loading
client drivers, and providing an interface to the USB host controller
to client drivers.
+--------------+
| USB Client |
| Driver |
+--------------+
/|\
| USBDI Interface
\|/
+--------------+
| USBD |
| Driver |
+--------------+
/|\
| HCD Interface
\|/
+--------------+
| HCD (OHCD or|
| UHCD) driver|
+--------------+
/|\
| HC Interface
\|/
+--------------------+ +------------------------+
| Host controller HW | | USB Device (function) |
+--------------------+ +------------------------+
/|\ /|\
| |
+------------------------------------------+
1:理解的是Wince 对USB驱动的编译原理:
在目录 D:\WINCE600\PUBLIC\COMMON\CESYSGEN 下找到makefile 可以看到
driverlibs:\
usbclient usbclib hidmdd \
所以 usbclient usbclib hidmdd是编译为静态库的
usbhid kbdhid MouHid conshid hidparse usbmsc usbdisk6
usbd usbprn usbser :
@set TARGETTYPE=DYNLINK
@set DEFFILE=$(SG_INPUT_LIB)\$@.def
@set TARGETLIBS=%%TARGETLIBS%% $(SG_OUTPUT_SDKLIB)\coredll.lib
@set SOURCELIBS=
@set TARGETNAME=$@
$(MAKECMD) /NOLOGO $(SG_OUTPUT_OAKTGT)\$@.dll
所以usbd等是编译为动态库的。其中@是代表名字(如usbd),如果还没看懂可以参考下面定义部分:
OWNSTATICLIB=$(SG_INPUT_LIB)\$@_lib.lib
usbd hidparse:
@set TARGETLIBS=$(OWNSTATICLIB)
我猜测是usbd_lib.lib变成了usbd.dll的原因.
2: 驱动加载流程要点
1. 在系统启动之后,由设备管理器device.exe加载USBD.DLL驱动程序,入口同样是函数DllMain(),之后调用HcdAttach()函数初始化一些Hcd控制器的资源,包括一些接口函数的列表。
2 当插入USB设备之后,系统调用USBD.DLL驱动中的HcdDeviceAttached()函数。该函数内,首先调用LoadDeviceDrivers()函数来加载USB设备对应的Client层驱动,如果没有找到合适的驱动,便会调用函数GetClientDriverName(),该函数执行的功能便是提示用户无法识别USB设备(弹出对话框),请输入相应的驱动程序名称,以便系统加载:当用户输入正确的dll后,USBInstallDriver会被调用。
3LoadDeviceDrivers()函数是加载的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
4 USBD为上层Client驱动提供的接口函数。首先是USBD导出的函数,但这不是全部的接口函数,而且Client层驱动部分使用USDB库接口时,必须包含一个头文件usbdi.h,位于WINCE600\PUBLIC\COMMON\DDK\INC中,此文件定义了所有的USDB接口。下面看看usbdi.h中为上层提供了哪些接口:
首先在usbdi.h可以看到所有usbd.def导出的函数都在如下:
LIBRARY USBD
EXPORTS
HcdAttach
HcdDetach
HcdDeviceAttached
HcdDeviceDetached
HcdSelectConfiguration
HcdDeviceSuspendeResumed
RegisterClientDriverID
UnRegisterClientDriverID
OpenClientRegistryKey
GetClientRegistryPath
RegisterClientSettings
UnRegisterClientSettings
GetUSBDVersion
其中 RegisterClientDriverID 在注册表L"HKEY_LOCAL_MACHIN\\Drivers\\USB\\ClientDrivers"+id注册信息;
USBUnInstallDriver()函数是以相反的顺序解除注册信息的
GetClientRegistryPath得到RegisterClientDriverID注册好的路径:L"HKEY_LOCAL_MACHIN\\Drivers\\USB\\ClientDrivers"+id
OpenClientRegistryKey 打开这个注册键
RegisterClientSettings 注册setting到 L"HKEY_LOCAL_MACHIN\\Drivers\\USB\\ LoadClients\\/<Group1 Id>/<Group2 Id>/<Group3 Id>/"+id
UnRegisterClientSettings反操作
下面的函数是给hcd层用的,可以不用管它(在系统启动之后,由设备管理器device.exe加载USBD.DLL驱动程序,入口同样是函数DllMain(),之后调用HcdAttach()函数初始化一些Hcd控制器的资源,包括一些接口函数的列表。)
HcdAttachHcdDetach
HcdDeviceAttached
HcdDeviceDetached
HcdSelectConfiguration
HcdDeviceSuspendeResumed
特别需要注意的
HcdAttach(
LPVOID lpvHcd,
LPCHCD_FUNCS lpHcdFuncs,
LPLPVOID lppvContext)
HCD层的接口都通过 lpHcdFuncs这个参数传过来了,这种写法可以给我们自己写程序提供了一个很好的方法!
其次usbdi.h中还有一个函数指针列表结构体_USB_FUNCS,里面包含了USBD的另外一部分接口,是在def中没有导出的,通过函数指针结构体在驱动之间进行传递的。_USB_FUNCS中的函数指针的实体都在文件usbddrv.cpp的文件中,整个USB驱动只有一个_USB_FUNCS的全局变量gc_UsbFuncs,它的声明及初始化在usbd.c中。在始化.
最重要的,在client的驱动必须被实现的三个函数是:
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();
还有一个回调函数也必须实现,在usbd中被调用,Client drivers register this function with the USBD to receive device notifications.。
typedef BOOL (WINAPI *LPDEVICE_NOTIFY_ROUTINE)(LPVOID lpvNotifyParameter,
DWORD dwCode,
LPDWORD * dwInfo1,
LPDWORD * dwInfo2,
LPDWORD * dwInfo3,
LPDWORD * dwInfo4);
参考: http://jazka.blog.51cto.com/809003/743720
5 关于BOOL USBDeviceAttach(USB_HANDLE hDevice, LPCUSB_FUNCS lpUsbFuncs,
LPCUSB_INTERFACE lpInterface, LPCWSTR szUniqueDriverId,
LPBOOL fAcceptControl,
LPCUSB_DRIVER_SETTINGS lpDriverSettings, DWORD dwUnused);
第一个参数是USB_HANDLE,实际上是一个SDevice指针。定义在usbd中的usbdobj.hpp中。如下
struct SDevice
{
DWORD dwSig; // For Structure validation
#define VALID_DEVICE_SIG 0x76654453 // "SDev"
SHcd * pHcd;
CRITICAL_SECTION csPipeLock;
SPipe * apPipes[gcMaxPipes];
SPipe * pFreePipeList;
UINT iDevice;
LPCUSB_DEVICE pDeviceInfo;
CRITICAL_SECTION csSerializeNotifyRoutine;
SNotifyList * pNotifyList;
CRITICAL_SECTION csLibList;
SDriverLibs * pDriverLibs;
BYTE rgbInterfaceIndex[gcMaxPipes];
BYTE rgbInterfaceFlag[gcMaxPipes];
};
第二个参数:USBD接口函数列表结构体_USB_FUNCS也是通过该函数传入具体的Client驱动中的。
第三个参数:当前传入的USB interface,必须和你的驱动所要实现的USB interface一致,U盘是8,定义与usb100.h中;如下:
//
// defined USB device classes
//
#define USB_DEVICE_CLASS_RESERVED 0x00
#define USB_DEVICE_CLASS_AUDIO 0x01
#define USB_DEVICE_CLASS_COMMUNICATIONS 0x02
#define USB_DEVICE_CLASS_HUMAN_INTERFACE 0x03
#define USB_DEVICE_CLASS_MONITOR 0x04
#define USB_DEVICE_CLASS_PHYSICAL_INTERFACE 0x05
#define USB_DEVICE_CLASS_POWER 0x06
#define USB_DEVICE_CLASS_PRINTER 0x07
#define USB_DEVICE_CLASS_STORAGE 0x08
#define USB_DEVICE_CLASS_HUB 0x09
#define USB_DEVICE_CLASS_VENDOR_SPECIFIC 0xFF
想想看,如果一个设备有多个interface,是否意味着要实现多个驱动呢。
第四个个参数:名字而已,一般没有用。
第五个参数,fAcceptControl,表示USBDeviceAttach是否成功调用。
第六个参数 解析注册表传入的而已 ,类型定义在usbdi.h
typedef struct _USB_DRIVER_SETTINGS
{
DWORD dwCount; // @field Set to the size of this structure, in bytes
//Vendor specific (also Device Specific)
DWORD dwVendorId; // @field (Vendor) Matches vendor id in device descriptor
DWORD dwProductId; // @field (Vendor) Matches procuct id in device descriptor
DWORD dwReleaseNumber; // @field (Vendor) Matches release number in device descriptor
//Device specific
DWORD dwDeviceClass; // @field (Device) Matches device class in device descriptor
DWORD dwDeviceSubClass; // @field (Device) Matches device subclass in device descriptor
DWORD dwDeviceProtocol; // @field (Device) Matches protocol in device descriptor
//Interface specific
DWORD dwInterfaceClass; // @field (Interface) Matches class in interface descriptor
DWORD dwInterfaceSubClass; // @field (Interface) Matches subclass in interface descriptor
DWORD dwInterfaceProtocol; // @field (Interface) Matches protocol in interface descriptor
} USB_DRIVER_SETTINGS, * PUSB_DRIVER_SETTINGS, * LPUSB_DRIVER_SETTINGS;
3: 驱动加载流程与注册表
/*
Driver Loading Strategy
Client drivers are loaded based on registry keys off of Drivers/BuiltIn/USB/LoadClients.
The key is formatted as follows:
...LoadClients/<Group1 Id>/<Group2 Id>/<Group3 Id>/<Driver Name>
Where each of the group Id strings may be "Default", indicating it should be
checked for all devices, or may be of the form X, X_Y, or X_Y_Z, where the X, Y, and Z
depend on the group:
Group 1) X = Device Vendor ID
Y = Device Product ID
Z = Device Release #
Group 2) X = Device Class Code
Y = Device Sub Class Code
Z = Device Protocol Code
Group 3) X = Interface Class Code
Y = Interface Sub Class Code
Z = Interface Protocol Code
Algorithm:
Search for the default/default/default driver (this driver loaded for every device)
Search for the most general group 1 driver
Search for the most general group 1 + 2 driver
Search for the most general group 2 driver
For each interface now try:
Search for the most general group 1 + 2 + 3 driver
Search for the most general group 1 + 3 driver
Search for the most general group 2 + 3 driver
Search for the most general group 3 driver
Once the most general driver is found, the drivers attach process is called.
If the driver accepts control of the device, then no more drivers are loaded
automatically. It is the responsibility of the client driver to
load all other lower edge drivers, and all interface drivers.
*/
关于usb驱动的架构设计可以参考: Wince下usb驱动详细总结(史无前例的详细) :http://blog.csdn.net/chyuanzheng/article/details/7903214