基于WDM的USB驱动程序设计

  • 格式:doc
  • 大小:47.00 KB
  • 文档页数:3

基于WDM的USB驱动程序设计1 引言USB,全称是Universal Serial Bus(通用串行总线),它是由Compaq、Microsoft、Intel、IBM等七家公司共同开发的,旨在解决日益增加的PC外设与有限的主板插槽和端口之间的矛盾而制定的一种串行通信的标准,自1995年在Comdex上亮相以来已广泛地为各PC厂家支持。

现在市场上几乎所有的PC机器都配备了USB接口,其优点是:•速度快。

USB有高速和低速两种方式,主模式为高速模式,速率为12Mbps;另外,为了适应一些不需要很大吞吐量和很高实时性的设备,如鼠标等,USB还提供低速方式,速率为1.5Mbps。

•设备安装和配置容易。

安装USB设备不必再打开机箱,加减己安装过的设备完全不用关闭计算机。

所有USB设备支持热插拔,系统对其进行自动配置,彻底抛弃了过去的跳线和拨码开关设置。

•易于扩展。

通过使用Hub扩展可连接多达127个外设。

标准USB电缆长度为3m(5m低速)。

通过Hub或中继器可以使外设距离达到30m。

•能够采用总线供电。

USB总线提供最大达5V电压和500mA电流。

•使用灵活。

USB共有4种传输模式:控制传输(control)、同步传输(synchronization)、中断传输(interrupt)、批量传输(bulk),以适应不同设备的需要。

2 WDM驱动程序的介绍WDM(Windows Driver Model)是微软提出的一种全新的设备驱动程序模型。

它是在Windows NT内核驱动程序模型(Kernel_model Driver Mode)的基础上发展起来的,增加了对即插即用(PnP)、高级电源管理(Power Management)、Windows 管理接口(WMI)的支持。

更重要的是,WDM是一种通用的驱动模式,提供了包括USB、IEEE1394和HID等在内的一系列驱动程序类。

在Windows 98和Windows 2000中,WDM驱动程序均可正常使用。

在大多数操作系统中,应用程序和操作系统本身是分开的:操作系统代码运行在特权处理器模式(也称核心态),并有权访问系统数据和硬件;应用程序运行在非特权处理器模式(也称用户态)。

当用户态程序调用系统服务时,处理器捕获该调用,然后把调用的线程切换到核心态。

当系统服务完成后,操作系统将线程描述表切换回用户态,允许调用者继续进行。

为防止用户应用程序访问或更改重要的操作系统数据,Windows NT使用两种处理器访问模式,用户态和核心态。

用户应用程序代码在用户态下运行,OS代码(如系统服务和驱动程序)在核心态下运行。

核心态是处理器的一种执行模式,它允许访问所有的系统内存和所有的CPU指令。

Windows 2000操作系统中有两种基本的驱动程序:用户模式驱动程序和内核模式驱动程序。

USB设备驱动程序使用内核模式,按照内核模式的分层结构,驱动程序分为高层驱动、中间层驱动、底层驱动3个部分。

底层的主机控制驱动程序直接作用于硬件,中间层驱动处理包括总线类驱动和根集线器驱动,负责总线列举、电源管理等在内的USB事务以及在根集线器相连的下游设备的初始化等任务。

这些驱动程序的特权级别高于应用程序软件。

这样,处理器就为操作系统的设计者提供了一个必要的基础,保证了应用程序的不当行为不会破坏系统的稳定性。

Windows NT融合了分层操作系统和客户服务器或称为内核OS的特点。

对性能影响很大的OS组件在核心态下运行。

在核心态下,组件可以和硬件交互,也可以在组件之间交互,并且不会引起描述表切换和模式转换。

例如,内存管理器、高速缓存管理器、对象及安全管理器、网络协议、文件系统、所有线程和进程管理,都运行在核心态。

当然,所有这些组件都应该受到保护,以避免被有错误代码的应用程序侵扰,而应用程序不能直接访问OS特权部分的代码和数据(尽管它们可以快速调用其它的内核服务)。

这种保护是使Windows NT成为既坚固又稳定的应用程序服务器的原因之一。

WDM驱动程序有一个主要的初始化入口点,即一个称为DriverEntry的例程;它有一个标准的函数原型,当WDM驱动程序被装入时,内核调用DriverEntry例程。

所有对各种IRP的处理例程都在此入口函数中做出定义。

大多数的WDM设备对象,都是在即插即用管理器调用AddDevice例程入口点被创建的。

插入新设备后,当系统找到由安装信息所指示的驱动程序时,这个例程调用在此之后,一系列的即插即用IRP被发送到驱动程序,设备驱动程序可进行相应的功能处理。

3USB设备驱动程序的实现USB系统主要由主控制器(Host Controller)、USB Hub和USB外设(Peripherals Node)组成系统拓扑结构。

在应用程序可以与一个USB设备通信之前,主机需要知道设备支持哪些传输类型和终端,主机也必须分配一个地址给设备,主机通过一个被称为枚举的信息交换来完成这些工作。

枚举过程:集线器的一个任务就是检测设备的连接与断开,每个集线器都有一个中断流程来通知主机报告这些事情。

在系统启动的时候,主机查询它的根集线器来了解有哪些设备已经连接上了,包括其他集线器和连接到这些集线器的设备。

在启动后,主机持续周期性地查询了解是否有设备连接或断开。

一旦发现一个新设备,主机发送一系列的请求给这个设备的集线器,使这个集线器在主机和这个设备之间建立一个通信渠道。

然后主机试图枚举这个设备,枚举是使得主机的设备驱动程序能与这个设备通信的最基本的信息交换。

这个过程由如下动作组成:分配一个地址给设备,从设备读取描述数据,分配和载入一个设备驱动程序以及从接收到的数据中选择一个配置。

然后设备就被配置完毕,并且准备好使用它的配置中支持的任何终端来传输数据。

主机的枚举是通过给终端0发送包含标准USB请求的控制传输。

所有的USB设备必须支持控制传输、标准USB请求和终端0。

对一个成功的枚举来说,设备必须对每一个请求响应并返回请求的信息。

从用户的角度看,枚举应该是不可见和自动的,除了一些情况下如申明发现一个新设备和是否成功配置这个新设备的窗口,有时在第一次使用时,用户需要提供一个右INF文件和设备驱动程序的磁盘。

在一个USB的外设中,外设的程序代码包含了主机将请求的信息,并且程序代码必须能识别和响应这些信息的请求。

在Windows不需要编写枚举的程序,因为Windows自动处理枚举过程。

Windows将查找一个被称为INF文件的特殊文本文件,这个文件会告诉Windows哪个驱动程序适合这个设备。

4 USB驱动实例开发WDM驱动程序有两种方法,一种利用微软提供的98DDK和2000DDK驱动程序开发包,另外一种是专用驱动程序开发工具,如Compuware Numega公司的Driverstudio,KRF,Tech公司的WinDriver。

后者给出驱动程序的框架,并对DDK中操作进行封装,因此减少了开发时问,提高了效率。

DriverWorks其实是对DDK函数进行了类的封装。

因此用C++语言开发WDM,相对来说更加方便。

笔者是自主开发USB IP核,例子中主要测试批传输的读入写出。

用Driverworks生成的驱动程序框架主要包含两个类,本设备驱动程序名为Usbtran,所以这两个类分别为Usbtran和UsbtranDevice。

为了使读者对Driverworks生成的驱动程序框架有更加清楚的了解,结合驱动程序原理和DDK的做法,先对其做一个剖析。

SPec类继承Kdriver,在所有的驱动程序中,都有一个初始化的函数入口DriverEntry,该函数完成驱动程序处理例程的登记,如读写、退出等。

所有驱动程序的这一工作几乎雷同,因此DriverWorks将其封装成一个类——Kdriver,当然其中有设备的添加和管道的初始化。

本驱动程序有三个管道,其中控制端点0无须初始化,因为它总能使用。

Bulk管道的初始化如下:m_Endpoint1IN.Initialize(m_Lower,0x81,64);m_Endpoint1OUT. Initialize(m_Lower,0x1,64);UsbtranDevice类继承KpnpDevice,而KpnpDevice是从Kdevice继承而来。

Kdevice主要是以设备为对象,对设备栈进行管理以及Irp经过该设备栈时进行一些相应的动作。

而UsbtranDevice是在KDevice的基础上,处理一些来自于Pnp管理器的消息以及IO管理器的消息等。

其主要处理的消息如下:virtual NTSTATUS OnStartDevice(KIrp I);virtual NTSTATUS OnStopDevice(KIrp I);virtual NTSTATUS OnRemoveDevice(KIrp I);virtual NTSTATUS DefaultPnp(KIrp I);virtual NTSTATUS DefaultPower(KIrp I);virtual NTSTATUS OnDevicePowerUp(KIrp I);virtual NTSTATUS OnDeviceSleep(KIrp I);每一个消息函数,Driverworks都已经做好了框架,如果需要特别的处理,编程者可以在其中加入代码。

UsbtranDevice有几个特别的成员函数:KUsbLowerDevice m_Lower;KUsbInterface m_Interface;KUsbPipe m_Endpoint1In;KUsbPipe m_Endpoint1Out;在本例的框架都由driverstudio自动添加,需要改动的bulk的读写部分的代码如下t << "Entering SampleDevice::SerialRead, " << I << EOL;NTSTATUS status = STATUS_SUCCESS;PUCHAR pBuffer = (PUCHAR) I.BufferedReadDest();//取得返回数据BUFF的指针ULONG dwTotalSize = I.ReadSize(CURRENT); // Requested read sizechar buff[512];int n =512, j = (n % 26);for (int i=0; i<n; i++, j=(j + 1)%26){ buff[i] = 'a' + j; }buff[dwTotalSize]=…\0‟; //指定串尾strcpy((char *)pBuffer,buff); // 把给应用程序的数据拷贝给返回BUFFt << “The stri ng you where read is \”“ << buff << ”\“” << EOL; // 输出调试信息ULONG dwBytesRead = strlen(buff); // Count of bytes readrmation() = dwBytesRead; // 返回给应用程序的信息的字节个数I.Status() = status;m_DriverManagedQueue.PnpNextIrp(I);这样,这个驱动程序的代码就完成了,用VC++ 6.0进行编译就可到所需的驱动程序。