八、 Windows 驱动程序模型
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 世纪的驱动程序框架。
WMD 驱动程序模型
应用程序通过 API 函数调用 Win32 系统子函数,驱动程序分为设备驱动程序,总线驱动程序 (USBD) 和主控制器驱动程序 (HCD) 三层,它们均运行在系统的内核模式。设备驱动程序使用 IR P(I / ORequest Packet) 通过总线驱动程序提供的软件接口 (USBDI , USB Driver Interface) 向总线驱动程序发出 I / O 请求,并根据数据传输方向提供一个或空或满的 内存 缓冲区; USBD 负责管理数据的总线传输,也有设备驱动程序与其他软件接口的功能单元进行通信,没有直接调用 USBD ,但总有一个更低层的驱动软件发生 USBD 调用。主控制器驱动程序处在 USB 系统软件的最底层,直接与主控制器的硬件通信,它提供了只有总线驱动程序才能访问的主控制器驱动程序软件接口 H CDI (Host C ON TROL Driver Interface) 。其中,总线驱动程序和主控制器驱动程序是系统的底层驱动程序。设备驱动程序是针对某一 USB 设备的专用驱动程序。
Windows 为 USB 设备提供了底层驱动程序,与底层驱动程序接口的是 I / O 请求包 (IRP) , Windows 为应用程序提供的接口则是 API 函数。因此必须在它们之间建立一个驱动程序,在底层驱动与 Win32 应用程序之间传递消息,即设备驱动程序。 VC++ 、 VB 等软件开发的应用程序,在设备驱动程序的支持下,都可以调用 ReadFile() 、 WriteFile() 、 DeviceIo CONTROL () 等 API 函数向设备传递主机请求。 Windows 系统自动将 API 调用转化为 IRP ,设备驱动程序把它向下层驱动传递。直到完成其所指定的功能再沿驱动程序栈返回主机。
WDM 还引入了功能设备对象 FDO ( Functional Device Object )与物理设备对象 PDO ( Physical Device Object )两个新类来描述硬件,一个 PDO 对应一个真实硬件。一个硬件只允许有一个 PDO ,但却可以拥有多个 FDO ,而在驱动程序中我们不是直接操作硬件而是操作相应的 PDO 与 FDO 。驱动程序和设备对象的分层情况如图所示。
其中总线驱动程序( Bus Driver )位于最底层,控制对总线上所有设备的访问,创建 PDO 代表发现的设备。功能驱动程序( Function Driver )控制设备的主要功能,分层在总线驱动的上面,负责创建 FDO 。在 USB 情况下,功能驱动程序必须使用 USB 类驱动程序访问设备。
九、 USB 设备驱动程序开发工具
开发 USB 设备驱动程序需要专门的开发工具,目前应用广泛的工具主要有两大类。 开发设备驱动程序一般采用以下几种方法: 1) 直接使用 Windows DDK ,这种方法开发难度较大,设计者必须对整个体系结构有很好的理解和把握。 2) 使用 Driver Studio ,该 工具 软件可为设计者提供驱动程序的整体框架,设计者只需要专心于功能代码设计。 3) 使用 win Driver ,这种方法开发驱动程序很容易,但工作效率不是很高。
1. Microsoft 公司提供的 Windows DDK(Device Driver Kit) 。
它有 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 原理和编程水平的人员来说,任务相当艰巨。
2. 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 驱动程序,可以大大简化开发人员的工作量、缩短开发周期以及降低开发驱动程序的难度。
十、 USB 设备驱动程序的设计
使用 DriverStudio3.2 开发 USB 设备驱动程序。
该驱动程序的主要功能包括:从控制端点 0 读取规定个数的数据、向端点 0 发出控制命令、从端点 2 批量读数据、向端点 2 批量写数据,驱动程序的开发采用 DriverStudio3.2 驱动程序开发包及 VC++6.0 ,使用开发包中的向导程序 DriverWizard 就可以方便的生成驱动程序框架、模块及部分程序源代码,开发者只需要在功能模块中加入自己的实现程序就能完成复杂的 USB 设备驱动程序设计。
1. DriverWizard 生成一 ISP1581 驱动程序的过程:
(1) 启动 DriverWizard ,选择 DriverWorks Project 创造一个名为 USBDIO 的 VC++ 项目 ;
(2) 在驱动程序类型中选择 WDM Driver , WDM Function Driver, 在硬件设备所支持的总线类型中选择 USB(WDM Only) ,在 USB Vendor ID( 厂商识别码 ) 中填写 0741 ,在 USB Product ID( 产品识别码 ) 中填写 0821;
(3) 增加 USB 设备端点,设置端点 2 为批量输入 / 输出传输方式 ;
(4) 在驱动程序支持的功能项中选择 Read 、 Write 、 Device Control 、 Cleanup;
(5) 选择自动产生批量读及批量写程序代码 ;
(6) 在 I/O 请求 IRP 处理方式中选择 None ,即 IRP 不排队 ;
(7) 在接口的打开方式中选择 Symbolic link:UsbdioDevice ,即应用程序以符号链接名打开设备 ;
(8) 定义应用程序调用 DeviceIo Control 函数对 WDM 驱动程序通信的控制命令。
(9) 最后选择完成并确认生成新的项目信息,向导程序就会在 usbdio 目录中生成一个名为 USBDIO 的项目文件,其中包括了 ISP1581 驱动程序框架、模块及部分源代码。
2 . USB 设备驱动程序的编程
在使用 DriverWizard 生成驱动程序框架、模块及部分程序源代码后,开发者只需完成三个控制代码所对应的三个功能模块的编程:模块 USBDIO_IOCTL_ ID_CODE_Handler 的功能是从控制端点 0 读取数据,模块 USBDIO_IOCTL_ TEST_COMMAND_Handler 的功能是向控制端点 0 发送一个控制命令,模块 USBDIO_IOCTL_DMA_COMMAND _Handler 的功能是向控制端点 0 发送一个要求 USB 设备进行 DMA 传输的控制命令,下面是第一个模块的编程实例。
NTSTATUS USBDIODevice::USBDIO_IOCTL_ID_CODE_Handler(KIrp I)
{
NTSTATUS status =STATUS_SUCCESS;
t << "Entering USBDIODevice::USBDIO _IOCTL_ID_ CODE_Handler, " << I << EOL;
PURB pUrb;
ULONG numData;
numData=*(PUCHAR)I.IoctlBuffer();
// 设置读取的数据个数
pUrb=m_Lower.BuildVendorRequest((PUCHAR)I.IoctlBuffer(),// 驱动程序存放读取的数据的内存区
numData,//wLength ,读取的数据个数
0,0x0c,//bRequest 0,//wValue
TRUE,//input
TRUE,
NULL,
0x0472,//wIndex, 传输到固件程序的读数命令码
URB_FUNCTION_VENDOR_ENDPOINT,
NULL);
if(pUrb==NULL)
{
I.Information() =0;
status=STATUS_INSUFFICIENT_
RESOURCES;
}
else
{
I.Information() =numData;
tatus=m_Lower.SubmitUrb(pUrb,NULL,NULL,0);
delete pUrb;
}
return status;
}
对象 I 包含了应用程序下传的 IRP 内容,包括命令或数据等参数,函数 BuildVendorRequest 用来分配并初始化一个用于厂商请求的 URB(USB Request Block) ,该 URB 将作为下传 IRP 的一个参数,通过函数 SubmitUrb 发送给总线驱动程序,以便完成与硬件的通信。
在初始化 URB 时需要了解 USB 的传输方式及传输协议,该功能使用了 USB 的控制传输方式,该方式包括三个阶段:设置阶段、数据阶段和状态阶段,其中数据阶段可选,开发者主要关注设置阶段中的 8 个关键字节的定义, 8 字节分成了 5 个字段,定义了传输请求及相关信息。
BmRequestType : 1 字节,用来指定数据流动的方向,请求的类型,以及接收者。
bRequest : 1 字节,用来指定请求。
wValue : 2 字节,主机用来传输信息给设备,开发者可以根据情况自己定义。
wIndex : 2 字节,主机用来传输信息给设备,开发者可以根据情况自己定义。
wLength : 2 字节,包含数据阶段中接下来要传输的数据字节数目。
十一、 USB 设备驱动程序的安装及调用
1. USB 设备驱动程序的安装
驱动程序编译完成后会生成一个名为 USBDIO.SYS 的文件,即 USB 设备驱动程序,另外在使用向导程序 WizardDriver 生成驱动程序时会产生一个名为 USBDIO.INF 的驱动程序安装程序,对此程序只需稍做修改就能正常使用,具体是将类改为 USB ,即 Class=USB ,由于本驱动程序使用符号链接名打开设备,所以删除 ClassGUID 选项,注意设备标识符必需为: %DeviceDesc%=USBDIO_DDI, USBVID_0471&PID_0821 ,其中 0471 是 USB 控制芯片的厂商识别码, 0821 是 USB 设备标识码。
驱动程序安装过程是:将 USB 设备加电,连入计算机的 USB 接口,这时候会看到 Windows 操作系统提示发现新硬件,提问是否安装驱动程序,选择是,然后选择驱动程序所在文件夹,选择文件 USBDIO.INF 即可完成安装 .
2. USB 设备驱动程序的调用
为了完成对驱动程序的调用,使用 VC++6.0 编写 USB 应用程序包,程序包共由五个功能模块组成,用户通过调用这些模块即可方便的完成对 USB 外设的控制及读写,这些模块如下。
int CTRLReadData(unsigned char usbSelect , unsigned char *rbuffer , unsigned char numData) ,主要功能是读取 ISP1581 控制端点 0 发来的数据,数据存放在缓冲区 rbuffer 中。
int CTRLSendTestCommand (unsigned char usbSelect , unsigned short int testCommand) ,主要功能是发送测试命令,变量 testCommand 定义了测试命令。
int CTRLSendDMACommand (unsigned char usbSelect , unsigned char dmaDirection , unsigned char ramSelect,unsigned long dmaLength) ,主要功能是发送 DMA 传输命令,变量 dmaDirection 定义数据传输方向, ramSelect 定义将要操作的 USB 外设的存储器, dmaLength 定义了数据传输总数。
int DMARead(unsigned char usbSelect , unsigned char *rbuffer , int len , int waitTime) ,主要功能是计算机批量读取 ISP1581 中的数据,而 ISP1581 以 DMA 方式从外部 RAM 读取数据。
int DMAWrite(unsigned char usbSelect , unsigned char *rbuffer , int len , int waitTime) ,主要功能是计算机批量写数据到 ISP1581 ,而 ISP1581 将以 DMA 方式写数据到外部 RAM