开发WDM型USB设备驱动程序

  摘要 本文介绍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型USB设备驱动程序
图1


  WDM还引入了功能设备对象FDO(Functional Device Object)与物理设备对象PDO(Physical Device Object)两个新类来描述硬件,一个PDO对应一个真实硬件。一个硬件只允许有一个PDO,但却可以拥有多个FDO,而在驱动程序中我们不是直接操作硬件而是操作相应的PDO与FDO。驱动程序和设备对象的分层情况如图2所示。

 

开发WDM型USB设备驱动程序
图2


  其中总线驱动程序(Bus Driver)位于最底层,控制对总线上所有设备的访问,创建PDO代表发现的设备。功能驱动程序(Function Driver)控制设备的主要功能,分层在总线驱动的上面,负责创建FDO。在USB情况下,功能驱动程序必须使用USB类驱动程序访问设备。

  2、USB驱动程序的WDM结构

  USB驱动程序的构成层次如图3所示。其中USB客户驱动程序通过Windows系统提供的USB类驱动程序接口(USBDI)与下层驱动程序通信。

 

开发WDM型USB设备驱动程序
图3


  在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

  驱动程序需要实现的功能就是控制设备上的LED灯的亮和灭,以及通过端点2对设备进行读写。

  (1) 首先,通过快捷方式“Setup DDK and Start MSVC”来启动VC IDE。这个快捷方式所指向的程序,会进行一些必要的设置,然后再启动VC IDE,这样我们的程序就可以使用DDK和DW的头文件和库了。

  (2) 从VC IDE的菜单“DriverStudio”中选择“DriverWizard”,之后按对话框的提示填写项目名称及所在目录。并按向导选择驱动程序类型为WDM,总线类型为USB。

  (3) 按端点的配置表进行端点的设置。由于在USB中规定端点0是必须存在的,所以我们只需对端点1和端点2进行定义即可。

  (4) 选择端点2产生BULK Read及BULK Write 的代码,DriverWorks会自动产生对端点2的读写代码,不用修改,就可以直接使用。

  (5) 增加一个IOCTL来控制USB设备的LED灯。IOCTL代码名称取为TEST_IOCTL_LED,其他值默认。

  (6) 最后按下“ Finish”按钮,就结束了Wizard。

  在此过程中只针对需要修改的步骤进行了说明,未加说明的部分均可直接按“Next”按钮。

  到此,我们已经创建好了一个基本的驱动程序,其中的块读写代码都由DriverWorks自动生成,无需另外加代码,只有厂商请求通过Ioctl来进行,控制LED灯的亮灭。代码如下:

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;
}

  这个函数控制LED灯,它是通过USB Vendor Request来向设备传送的。其中request=1的时候表示让L ED亮,request=0的时候让LED来。它通过DeviceIoControl由上层应用程序传下来。

  现在,驱动程序完成了,为确保最后生成的USB驱动程序能编译成功,一定要注意设置DriverWorks中的Driver Build Setting(在VC++6.0的菜单中)中的[basedir]和[CPU]等项。设置好后,选择“Build/Batch Build”,在弹出的对话框中进行正确的选择,之后按下”Rebuild”按钮,稍等片刻即可生成最终的.sys驱动程序。

   结束语

  WDM作为一种设备驱动程序模型,自Windows 2000以来已成为一种统一的模式。同时USB技术得到广泛应用,进行USB设备驱动程序的开发成为必不可少的技术环节,而使用DriverWorks开发USB设备驱动程序,简单方便。

 

 

开发环境: VC6 WindowsXP DDK
测试环境: WindowsXP

使用许可:代码是免费使用

讲解:
功能介绍:现在USB设备却很多,因此对USB设备的查找与读写就必不可少了。但是能找到关于USB读写的资料很少。这里使用VC++示范了一些获得USB的信息的方法。

一 枚举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等代码,一并致谢。

你可能感兴趣的:(游戏,编程,windows,vc++,IT厂商)