摘要 本文介绍WDM型USB设备驱动程序的基本概念、结构和特性,并说明了利用Driverworks进行开发的方法。
关键词 WDM;USB设备驱动程序;Driverworks
导言
通用串行总线USB是1995年康柏、微软、IBM、DEC等公司推广的一种新型的通信标准,该总线接口具有安装方便、高带宽、易于扩展等优点,已逐渐成为现代数据传输的发展趋势。而 <!----><!---->在开发USB设备时,设备驱动程序的设计是其中一项重要的技术环节,它直接影响整个设备系统的性能。Windows98及其更高版本的操作系统对USB总线提供了全面的支持,并且用WDM驱动程序模型代替了VxD设备驱动程序。WDM支持USB协议,并为其提供了高效的开发平台。因此WDM已成为开发USB外设驱动程序所必须而有效的技术。
Windows驱动程序模型(WDM)
Windows环境下驱动程序共有三类,一类是VxD( Virtual Device Driver,虚拟设备驱动程序),起源于Windows 3.1 时代,用于Windows 95/98/Me操作系统中;一类是KMD( Kernel Mode Driver,内核模式驱动程序),用于Windows NT下;还有一类就是WDM(Win32 Driver Mode,Win32驱动程序模型),是微软从Windows 98开始,推出的一个新的驱动类型,它是一个跨平台的驱动程序模型,不仅如此WDM驱动程序还可以在不修改源代码的情况下经过重新编译后在非Intel平台上运行,毫不夸张地讲,WDM算得上是21世纪的驱动程序框架。
WDM是在NT4.0驱动程序结构上发展起来的,所以它与NT4.0的驱动程序极为相似,但是它却有了本质上的提高,增加了对即插即用(PnP)、高级电源管理(PowerManagement)、Windows管理接口(WMl)的支持。更重要的是,WDM是一种通用的驱动模式,提供了包括USB、IEEEl394和HID等在内的一系列驱动程序类。
1、WDM驱动程序的分层结构
WDM体系结构实行分层处理,即设备驱动被分成了若干层,典型地分成:高层驱动程序、中间层驱动程序、底层驱动程序。每层驱动再把I/O请求划分成更简单的请求,以传给更下层的驱动执行。最底层的驱动程序在收到I/O请求后,通过硬件抽象层,与硬件发生作用,从而完成I/O请求工作。在这样的架构下,上面的驱动层就不需要对每个操作系统都要开发一遍了。如图1所示
WDM还引入了功能设备对象FDO(Functional Device Object)与物理设备对象PDO(Physical Device Object)两个新类来描述硬件,一个PDO对应一个真实硬件。一个硬件只允许有一个PDO,但却可以拥有多个FDO,而在驱动程序中我们不是直接操作硬件而是操作相应的PDO与FDO。驱动程序和设备对象的分层情况如图2所示。
其中总线驱动程序(Bus Driver)位于最底层,控制对总线上所有设备的访问,创建PDO代表发现的设备。功能驱动程序(Function Driver)控制设备的主要功能,分层在总线驱动的上面,负责创建FDO。在USB情况下,功能驱动程序必须使用USB类驱动程序访问设备。
2、USB驱动程序的WDM结构
USB驱动程序的构成层次如图3所示。其中USB客户驱动程序通过Windows系统提供的USB类驱动程序接口(USBDI)与下层驱动程序通信。
在USB设备可用之前,必须对其进行配置和接口选择,然后所选择的接口的各个管道才是可用的。在USBDI的基础上进行编程将大大简化,用户不用关心IRP的类型,而只需要在相应的分发例程中通过构造USB块并将其通过USBDI发送下去就可以实现对USB设备的控制了。
设备的访问
WDM不是通过驱动程序名称,而是通过一个128位的全局惟一标识符(GUID)实现驱动程序的识别。在应用程序与WDM驱动程序通信方面系统为每一个用户请求打包形成一个I/O请求包(IRP)结构,将其发送至驱动程序,并通过识别IRP中PDO来区别是发送给哪一个设备的。内核通常通过发送IRP来运行驱动程序中的代码。
WDM驱动程序有一个主要的初始化入口点,即一个必须称为DriverEntry的例程。它有一个标准的函数原型。当WDM驱动程序被装入时,内核调用DriverEntry例程。驱动程序的DriverEntry例程必须设置一系列的回调例程来处理IRP。每个回调例程有一个标准的函数原型。内核会在合适的环境下调用这个例程。
大多数的WDM设备对象都是在即插即用管理器调用AddDevice例程入口点时被创建的。插入新设备后,当系统找到由安装信息文件所批示的驱动程序时,这个例程被调用。在此之后,一系列即插即用IRP被发送到驱动程序,设备驱动程序可进行相应的功能处理。
USB设备驱动程序开发工具
开发USB设备驱动程序需要专门的开发工具,目前应用广泛的工具主要有两大类。
一类是Microsoft公司提供的Windows DDK(Device Driver K <!----><!---->it)。它有Windows 98 DDK和Windows 2000 DDK两个版本。Windows 98 DDK能够开发Windows 95/98/Me/NT下的VxD、KMD和WDM驱动程序。Windows 2000 DDK 能够开发Windows 98/Me/NT/2000下的KMD和WDM驱动程序。由于DDK基于汇编语言的编程方式和内核模式的调用,对没有深厚的OS原理和编程水平的人员来说,任务相当艰巨。
另一类是NuMega公司提供的DriverStudio,它是一个大的开发工具包,包含VtoolsD、SoftICE和DriverWorks等开发工具。 VtoolsD开发包提供了对VxD编程的C/C++类库支持,利用VtoolsD中的QuickVxD工具可以快速生成VxD的C/C++代码框架,开发者可以在此基础上根据各自的需要添加自己的代码。DriverWorks用于开发KMD和WDM驱动程序,并且对DDK函数进行了类的封装,从而为开发Windows NT、Windows 2000和Widnwos98 WDM设备驱动程序提供了一个自动化的方法。
DriverWorks,提供了VC++下的开发向导Driver Wizard,按照它的提示可以迅速地生成驱动程序的框架。这个框架结构提供可以正确执行WDM动态环境中IRP的请求,而且,也包含用于简化系统提供的标准类驱动程序(如HID、流)和总线驱动程序(如PCI和USB)接口的类等。
总之,利用DriverWorks开发WDM驱动程序,可以大大简化开发人员的工作量、缩短开发周期以及降低开发驱动程序的难度。[1]
用DriverWorks开发WDM型USB设备驱动程序
要想使用DriverWorks,必须先安装以下软件:DriverStudio2.5,VisualStudio6.0,以及Windows2000DDK。由于DriverWorks所用的类库是对DDK的库函数的封装,还必须在Visual C++中编译需要的库文件[3],下面介绍怎样在VC环境中创建自己的库文件。
(1) 启动Visual C++。
(2) 选择菜单File\Open Workspace。打开位于DriverStudio\DriverWorks\Source\vdwlibs.dsw的工作空间文件。
(3) 选择菜单Build\Batch Build,在弹出的对话框架中选择你想编译的库。
(4) 点击对话框中的Build即可编译你选择的库。
下面就通过一个例子来说明利用DriverWorks开发USB设备驱动程序的方法。
这个USB设备有3个双向端点,每个端点的配置如下:
端点 | 类型 | 地址 缓冲区(字节) |
0 IN/OUT | 控制(Control) | 0x80/0x00 16/16 |
1 IN/OUT | 块(Bulk) | 0x81/0x01 16/16 |
2 IN/OUT | 块(Bulk) | 0x82/0x02 64/64 |
NTSTATUS TESTDevice::TEST_IOCTL_LED_Handler(KIrp I) { NTSTATUS status = STATUS_SUCCESS; t << "Entering TESTDevice::TEST_IOCTL_LED_Handler, " << I << EOL; // TODO: Verify that the input parameters are correct // If not, return STATUS_INVALID_PARAMETER if(I.IoctlOutputBufferSize()||!I.IoctlBuffer()||(I.IoctlInputBufferSizer()!=sizeof(UCHAR))) // TODO: Handle the the TEST_IOCTL_LED request, or // defer the processing of the IRP (i.e. by queuing) and set // status to STATUS_PENDING. PURB pUrb=m_Lower.BuildVendorRequest(NULL,//transfer buffer 0, //transfer buffer size 0, //request reserved bits (UCHAR)(*PUCHAR)I.IoctlBuffer()), //request. 1=LED_ON, 0=LED_OFF 0 // Value ); //transmit status=m_Lower.SubmitUrb(pUrb,NULL,Null,5000L); } // TODO: Assuming that the request was handled here. Set I.Information // to indicate how much data to copy back to the user. I.Information() = 0; I.Status()=status; return status; } |
开发环境: VC6 WindowsXP DDK 使用许可:代码是免费使用 讲解: 一 枚举USB设备
通过枚举USB控制器->枚举此控制器上的USB HUB->枚举HUB的各个端口->获得设备信息。 枚举控制器。计算机上的USB主控制器以HCD1,HCD2等命名。通过控制器名称,使用CCreateFile 打开它。使用DeviceIoControl即可得到其驱动程序名,以及与它连接的HUB的名称。用CCreateFile打开HUB,获得连接信息。再枚举HUB的各个端口即可获得连接的设备信息。 二 枚举HID设备 HID设备是微软定义的标准人机接口规范。比如USB鼠标,USB游戏手柄等。不用查找具体设备的GUID,使用API HidD_GetHidGuid(&guidHID)即可得到GUID。有了GUID通过API SetupDiEnumDeviceInterfaces可获得是否有设备连接。如果此类设备连接通过SetupDiGetDeviceInterfaceDetail获得它的设备路径信息。使用CCreateFile 打开它,通过HidD_GetAttributes获得其基本属性信息。使用DeviceIoControl可以获得更详细的属性。在本代码中如果计算机上插有USB游戏手柄,可获得其信息。但不知道为什么xp下却不能获得USB鼠标的信息。 三 枚举U盘 先用GetDriveType API获得设备的类型,若类型为REMOVABLE(当然有些大容量U盘可能报告为FIXED,那就需要其他方法来确定了),即可能是U盘。用CCreateFile 打开之后,再用IOCTL_STORAGE_QUERY_PROPERTY为参数的DeviceIoControl来获得其属性。 结束语: 示例工程在winxp+xpDDK+VC6下编译通过。 USB设备种类比较多,也比较特殊,不同厂商的硬件不同,控制软件也不尽相同(我想主要是ICTL码不同,也不容易查到)。使得访问USB口的设备不象串口并口那么方便。这个例程只是展示了访问的基本方法。其中还有些问题还没有解决,发出来希望大家解决之后能通知我或者发表出来。
wsprintf(HCName, "\\\\.\\HCD%d", HCNum); hHCDev = CreateFile(HCName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); 其他信息:参考了USBPort,USBview等代码,一并致谢。 |