收藏 分享(赏)

USB接口通信(驱动)的设计与实现.doc

上传人:HR专家 文档编号:7521690 上传时间:2019-05-20 格式:DOC 页数:54 大小:288KB
下载 相关 举报
USB接口通信(驱动)的设计与实现.doc_第1页
第1页 / 共54页
USB接口通信(驱动)的设计与实现.doc_第2页
第2页 / 共54页
USB接口通信(驱动)的设计与实现.doc_第3页
第3页 / 共54页
USB接口通信(驱动)的设计与实现.doc_第4页
第4页 / 共54页
USB接口通信(驱动)的设计与实现.doc_第5页
第5页 / 共54页
点击查看更多>>
资源描述

1、 桂林电子工业学院毕业设计(论文)专用纸 第 1 页 共 54 页1引言WDM 是“Windows 驱动程序模型 ”的简称,即“Windows Driver Model”。实际上它是一系列集成在操作系统之中的常规系统服务集,用于简化硬件驱动程序的编写,并保证它们在 Windows 98/Me/2000 中的二进制兼容,WDM(Windows Driver Model)模型是从 WinNT3.51 和 WinNT4 的内核模式设备驱动程序发展而来的。WDM 主要的变化是增加了对即插即用、电源管理、Windows Management Interface(WMI)、设备接口的支持。WDM 模型的主

2、要目标,是实现能够跨平台使用、更安全、更灵活、编制更简单的 Windows 设备驱动程序。WDM 采用了“ 基于对象”的技术,建立了一个分层的驱动程序结构。WDM 首先在 Windows98 中实现,在 Windows2000 中得到了进一步的完善,并在后续开发的 Windows 操作系统中都将存在,比如 Windows Me 和 Windows XP。微软在通过 WDM 模型的引入,希望减轻设备驱动程序的开发难度和周期,逐渐规范设备驱动程序的开发,应该说,WDM 将成为以后设备驱动程序的主流。USB 技术的全称是通用串行总线,是英文 Universal Serial Bus 的缩写。它是一种

3、应用在 PC 领域的新型接口技术,虽然 USB2.0 已经被广泛应用,但是初始的 Windows 2000 是支持 USB1.0 协议的,如果希望支持 USB2.0 协议,需要在微软网站上下载升级包。实际上,对于键盘或者鼠标来说,传输的速度非常小,使用 USB1.0 或者是USB2.0 的区别并不大。闪存盘之类的存储设备,则需要重视传输速度。USB1.0 版本主要应用在鼠标,键盘等 HID 设备上,这就是本驱动程序中引用的头文件版本是USB1.0 的原因。本毕业设计的目的是希望对 Windows 2000 操作系统体系结构和驱动程序开发以及调试等方面的问题有一个比较深入的了解,对 USB 协议

4、和 USB 体系有做一个比较深入的了解。并开发出一个 USB 键盘驱动。这个 USB 键盘驱动程序应当可以替代系统原有的键盘驱动程序,并可以正常工作。本论文设计的驱动程序在 Windows 2000 下运行,开发环境为 VC6.0 和DDK2000。1 WDM 驱动程序模型概述驱动程序在任何操作系统下都和系统内核有着密切的关系。设备驱动程序是一个包含了许多操作系统可调用例程的容器,这句 Walter Oney 曾说过的话,抽象的描述了设备驱动程序的本质。1.1 Windows 2000 概述图 11 中概括了 Windows 200 系统中的组件,Windows 2000 操作系统是由不同层次

5、的模块共同组成的。该图着重描述了驱动程序开发者所关心的特征。工作在Windows 2000 操作系统平台上的软件要么执行在用户模式中,要么执行在内核模式中。当用户模式程序需要读取设备数据时,就调用 Win32 API 函数,如 ReadFile. Win32 子桂林电子工业学院毕业设计(论文)专用纸 第 2 页 共 54 页2系统模块通过调用平台相关的系统服务接口实现 API,而平台相关的系统服务将调用内核模式支持例程。在 ReadFile 调用中,调用首先到达系统 DLL(NTDLL.DLL)中的一个入口点,NtReadFile 函数。然后这个用户模式的 NtReadFile 函数接着调用系

6、统服务接口,最后由系统服务接口调用内核模式中的服务例程,该例程同样名为NtReadFile。应用程序Win32 子系统设备驱动硬件抽象层硬件IO 管理器用户模式内核模式Win32API 调用系统服务接口传递 IRP 给驱动程序派遣函数HAL 调用平台相关操作图 11 Windows 组件模型系统中还有许多与 NtReadFile 相似的服务例程;它们同样运行在内核模式中,为应用程序请求提供服务,并以某种方式与设备交互。这些服务例程首先检查从用户态传递给它们的参数以保护系统安全或防止用户态程序非法存取数据,然后创建一个称为“I/0 请求包(IRP)” 的数据结构,并把这个数据结构送到某个驱动程序

7、的入口点。驱动程序完成一个 I/0 操作后,通过调用一个特殊的内核模式服务例程来完成该IRP。完成操作是处理 IRP 的最后动作,它使等待的应用程序恢复运行。1.2 Windows 2000 中的驱动程序类型桂林电子工业学院毕业设计(论文)专用纸 第 3 页 共 54 页3虚拟设备驱动程序(VDD )内核模式驱动程序文件系统驱动程序遗留设备驱动程序PnP驱动程序显示驱动程序WDM驱动程序类驱动程序微型(mini)驱动程序图 12 Windows 2000 中的设备驱动程序种类Windows 2000 系统可以使用多种驱动程序,图 1-2 显示了其中几种。虚拟设备驱动程序(VDD)可以使 DOS

8、 应用程序访问 x86 平台上的硬件。VDD 通过屏蔽 I/O 权限掩码来捕获端口存取操作,它基本上是模拟硬件操作,这对于那些直接对裸机硬件编程的应用程序特别有用。尽管这种驱动程序在 Windows 98 和 Windows 2000 中共享一个名称并且有相同的功能,但实际上它们的工作方式完全不同。我们用VDD 缩写代表这种驱动程序,用 VxD 缩写代表 Windows 98 中的虚拟设备驱动程序以示区别。内核模式驱动程序的分类包含许多子类。PnP 驱动程序就是一种遵循 Windows 2000即插即用协议的内核模式驱动程序。WDM 驱动程序是一种 PnP 驱动程序,它同时还遵循电源管理协议,

9、并能在Windows98 和 Windows 2000 间实现源代码级兼容。WDM 驱动程序还细分为类驱动程序(classdriver)和微型驱动程序 (minidriver),类驱动程序管理属于己定义类的设备,微型驱动程序向类驱动程序提供厂商专有的支持。显示驱动程序是用于显示和打印设备的内核模式驱动程序。文件系统驱动程序在本地硬盘或网络上实现标准 PC 文件系统模型(包括多层次目录结构和命名文件概念)。遗留设备驱动程序也是一种内核模式驱动程序,它直接控制一个硬件设备而不用其它驱动程序帮助。这种驱动程序主要包括 Windows NT 早期版本的驱动程序,它们可以不做修改地运行在 Windows

10、 2000 中。桂林电子工业学院毕业设计(论文)专用纸 第 4 页 共 54 页41.3 WDM 驱动程序类型WDM( Windows Driver Model)模型是从 WinNT3.51 和 WinNT4 的内核模式设备驱动程序发展而来的。WDM 主要的变化是增加了对即插即用、电源管理、Windows Management Interface(WMI)、设备接口的支持。WDM 模型的主要目标,是实现能够跨平台使用、更安全、更灵活、编制更简单的 Windows 设备驱动程序。WDM 采用了“ 基于对象”的技术,建立了一个分层的驱动程序结构。 WDM 首先在 Windows98 中实现,在 W

11、indows2000 中得到了进一步的完善,并在后续开发的 Windows 操作系统中都将存在,比如 Windows Me 和 Windows XP。微软在通过 WDM 模型的引入,希望减轻设备驱动程序的开发难度和周期,逐渐规范设备驱动程序的开发,应该说,WDM 将成为以后设备驱动程序的主流。在 WDM 模型中,每个硬件设备至少有两个驱动程序:一个功能驱动程序(function driver)和一个总线驱动程序(bus driver) 。一个设备还可能有过滤驱动程序(filter driver) ,用来变更标准设备驱动程序的行为。这些服务于同一个设备的驱动程序组成了一个链表,称为设备栈。详细的

12、描述见图 1-3。可选的上层过滤驱动程序功能驱动程序可选的底层过滤驱动程序可选的总线过滤驱动程序总线驱动程序设备驱动程序总线驱动程序图 1-3 驱动程序的种类总线驱动程序总线驱动程序为实际的 IO 总线服务,比如 IEEE 1394。在 WDM 的定义中,一个总线是这样的设备,它用来连接其他的物理的、逻辑的、虚拟的设备。总线包括传统的总线 SCSI 和 PCI,也包括并口、串口、以及 i8042 端口。微软已经为 Windows 操作系统提供了总线驱动程序。总线驱动程序已经包含在操作系统里了,用户不必安装。一个总线驱动程序负责以下的工作:桂林电子工业学院毕业设计(论文)专用纸 第 5 页 共

13、54 页5枚举总线上的设备;向操作系统报告总线上的动态事件;响应即插即用和电源管理的 IO 请求;提供总线的多路存取(对于一些总线) ;管理总线上的设备;功能驱动程序功能驱动程序是物理设备的主要驱动程序,它实现设备的具体功能,一般由设备的生产商来编写。功能驱动程序的主要功能是:提供对设备的操作接口;操作对设备的读写;管理设备的电源策略;过滤驱动程序过滤驱动程序是一个可选项,当一个用户需要改变或新添一些功能到一个设备、一类设备或一种总线时,就可以编写一个过滤驱动程序。在设备栈里,过滤驱动程序安装在一个或几个设备驱动程序的上面或下面。过滤驱动程序拦截对具体设备、类设备、总线的请求,做相应的处理,以

14、改变设备的行为或添加新的功能。但过滤驱动程序只处理那些它所关心的 IO 请求,对于其他的请求可以交给其他的驱动程序来处理,这样可以非常灵活改变设备的行为,至少用户会这样看。比如:一个 USB 键盘的上层过滤驱动程序可以强制执行附加的安全检查。一个鼠标的低层过滤驱动程序,通过对鼠标移动的数据做非线性的转换,可以得到一个有加速效果的鼠标轨迹。功能驱动程序的组成功能驱动程序由类驱动程序和微型驱动程序(Minidriver)组成。类驱动程序实现了某一类设备的常用操作,由微软提供,驱动程序的开发者可以只编写非常小的微型驱动程序,去处理具体设备特殊的操作,而对于其他大量的常规操作,可以调用该类的类驱动程序

15、,这也是 WDM 驱动程序的优点之一。微软提供的类驱动程序处理常用的系统任务,比如,即插即用功能和电源管理。类驱动程序保证了操作系统在处理类似的任务时的一致性,从而提高了系统的稳定性。设备生产商提供微型驱动程序,以实现自己设备的特殊功能,同时调用合适的类驱动程序完成其他的通用工作。将大量的标准操作的代码通过各种类驱动程序来实现,并集成在操作系统中,这样的方式可以有效的减少具体设备的微型驱动程序的大小,也就减小了程序出错的可能。如果某一类设备存在着工业标准,微软就会提供一个该类设备的 WDM 类驱动程序。这个类驱动程序实现了该类设备所有必须的任务,但不实现任何具体设备所特有桂林电子工业学院毕业设

16、计(论文)专用纸 第 6 页 共 54 页6的东西。比如,微软提供的 HID(人工输入设备)类驱动程序的实现,是根据 USB HID 类规范 v.11 的规定,但并不实现任何一种具体设备的特殊功能,比如,USB 键盘、鼠标、游戏控制等等。本文所设计的驱动程序就是一个功能驱动程序,它是将 USB 驱动程序与微型驱动程序(Minidriver) 结合起来,驱动 USB 键盘的一个驱动程序.微软支持的 WDM 总线和类驱动程序图 1-4 微软支持的 WDM 总线和类驱动程序对于图 1-4,本文只描述其中的人工输入设备(HID) 和 USB 部分。因为这是在 USB键盘驱动程序设计中所涉及到的两个方面

17、。USB 总线驱动程序枚举和控制低速的 USB总线。USB 客户驱动程序使用各种 IOCTL 通过 USB 类驱动程序访问它们的设备。人工输入设备(HID)类驱动程序管理多种总线(如 USB)间的数据与指令语法翻译。大多数时候,本类驱动控制由用户交互接口传来的数据,如键盘,鼠标和游戏杆等。1.4 驱动程序的分层结构FiDOFDOFiDOPDO上层过滤驱动程序功能驱动程序低层过滤驱动程序总线驱动程序IRP图 15 WDM 中设备对象和驱动程序的层次结构WDM 模型使用了如图 15 的层次结构。图中左边是一个设备对象堆栈。设备对象是系统为帮助软件管理硬件而创建的数据结构。一个物理硬件可以有多个这样

18、的数据结构。处于堆栈最底层的设备对象称为物理设备对象(physical device object),或简称桂林电子工业学院毕业设计(论文)专用纸 第 7 页 共 54 页7为 PDO。在设备对象堆栈的中间某处有一个对象称为功能设备对象(functional device object),或简称 FDO。在 FDO 的上面和下面还会有一些过滤器设备对象(filter device object)。位于 FDO 上面的过滤器设备对象称为上层过滤器,位于 FDO 下面(但仍在PDO 之上)的过滤器设备对象称为下层过滤器。操作系统中的即插即用管理器(PnP Manager)根据设备驱动程序的指令来建

19、立这个数据对象堆栈。前面我们已经知道,总线驱动程序的作用之一是枚举总线上的设备,当总线驱动程序检测到一个设备时,PnP 管理器就马上建立一个 PDO。当建立好 PDO之后,PnP 管理器通过查找注册表来找到其他的过滤驱动程序和功能驱动程序。设备的安装程序负责建立这些注册表里的表项,驱动程序的安装,是根据 INF 文件中的指令进行的。注册表中的表项指明了各种驱动程序在数据对象堆栈中的位置,于是 PnP管理器开始装载最低层的过滤驱动程序,并调用该驱动程序的 AddDevice 函数。该函数在数据对象堆栈中建立一个 FiDO,同时也将前面建立的 PDO 和这个 FiDO 联系在一起。PnP 管理器反

20、复的实现该过程,装载其他位置靠上的低层过滤驱动程序、功能驱动程序、上层过滤驱动程序,直到该堆栈完成。应用程序对设备的存取通过提交 IO 请求包(IRP)来进行。在操作系统中,对设备的存取过程是这样的:操作系统中的 IO 管理器接受 IO 请求(通常是由用户态的应用程序发出的) ,建立相应的 IRP 来描述它,将 IRP 发送给合适的驱动程序,然后跟踪执行过程,当操作完成后,将返回的状态通知请求的发起者。操作系统中的 IO管理器、即插即用管理器、电源管理器都使用 IRP 来与内核模式驱动程序、WDM 驱动程序进行通信,并且,各驱动程序之间的通信也是依靠 IRP。在 WDM 驱动程序中,IRP 首

21、先从最上层进入,如图 15 里右手边的箭头,然后,依次往下传送。在每一层,驱动程序自行决定对 IRP 的处理。有时,一个驱动程序除了把继续 IRP 向下传递外,并不做任何事情。有时,一个驱动程序会完全接管 IRP,不再把它向下传递了。当然,一个驱动程序也可以处理 IRP 后,再把它继续向下传递。这取决于驱动程序的功能和 IRP 的含义。从这里,可以知道微软在 WDM 模型中使用分层的驱动程序结构的原因了,通过分层的方法,在处理对设备的 IO 请求时,利用添加合适的驱动程序层的方法,从而非常灵活的改变设备的行为,以实现不同设备的功能。1.5 IO 请求包(IRP)操作系统使用 I/0 请求包(I

22、RP)数据结构与内核模式驱动程序通信。这个数据结构很重要,需要了解它的创建、发送、处理,以及最后的销毁。可以说,IO 请求包(IRP)才是 WDM 驱动程序结构的最重点,只有真正了解处理 IRP 的过程,才算是真正懂得了设备驱动的原理。1.5.1IRP 结构桂林电子工业学院毕业设计(论文)专用纸 第 8 页 共 54 页8图 16 I/O 请求包数据结构MdlAddress(PMDL)域指向一个内存描述符表(MDL),该表描述了一个与该请求关联的用户模式缓冲区。如果顶级设备对象的 Flags 域为 DO_DIRECT_IO,则 I/O 管理器为 IRP_MJ_READ 或 IRP_MJ_WRI

23、TE 请求创建这个 MDL。如果一个IRP_MJ_DEVICE_CONTROL 请求的控制代码指定 METHOD_IN_DIRECT 或METHOD_OUT_DIRECT 操作方式,则 I/O 管理器为该请求使用的输出缓冲区创建一个 MDL。MDL 本身用于描述用户模式虚拟缓冲区,但它同时也含有该缓冲区锁定内存页的物理地址。为了访问用户模式缓冲区,驱动程序必须做一点额外工作。Flags(ULONG)域包含一些对驱动程序只读的标志。但这些标志与 WDM 驱动程序无关。AssociatedIrp(union)域是一个三指针联合。其中,与 WDM 驱动程序相关的指针是AssociatedIrp.Sy

24、stemBuffer。 SystemBuffer 指针指向一个数据缓冲区,该缓冲区位于内核模式的非分页内存中。对于 IRP_MJ_READ 和 IRP_MJ_WRITE 操作,如果顶级设备指定 DO_BUFFERED_IO 标志,则 I/O 管理器就创建这个数据缓冲区。对于IRP_MJ_DEVICE_CONTROL 操作,如果 I/O 控制功能代码指出需要缓冲区(见第九章),则 I/O 管理器就创建这个数据缓冲区。I/O 管理器把用户模式程序发送给驱动程序的数据复制到这个缓冲区,这也是创建 IRP 过程的一部分。这些数据可以是与 WriteFile 调用有关的数据,或者是 DeviceIoCo

25、ntrol 调用中所谓的输入数据。对于读请求,设备驱动程序把读出的数据填到这个缓冲区,然后 I/O 管理器再把缓冲区的内容复制到用户模式缓冲区。对于指定了 METHOD_BUFFERED 的 I/O 控制操作,驱动程序把所谓的输出数据放到这个缓冲区,然后 I/O 管理器再把数据复制到用户模式的输出缓冲区。桂林电子工业学院毕业设计(论文)专用纸 第 9 页 共 54 页9IoStatus(IO_STATUS_BLOCK)是一个仅包含两个域的结构,驱动程序在最终完成请求时设置这个结构。IoStatus.Status 域将收到一个 NTSTATUS 代码,而IoStatus.Information

26、的类型为 ULONG_PTR,它将收到一个信息值,该信息值的确切含义要取决于具体的 IRP 类型和请求完成的状态。 Information 域的一个公认用法是用于保存数据传输操作,如 IRP_MJ_READ,的流量总计。某些 PnP 请求把这个域作为指向另外一个结构的指针,这个结构通常包含查询请求的结果。RequestorMode 将等于一个枚举常量 UserMode 或 KernelMode,指定原始 I/O 请求的来源。驱动程序有时需要查看这个值来决定是否要信任某些参数。PendingReturned(BOOLEAN)如果为 TRUE,则表明处理该 IRP 的最低级派遣例程返回了 STAT

27、US_PENDING。完成例程通过参考该域来避免自己与派遣例程间的潜在竞争。Cancel(BOOLEAN)如果为 TRUE,则表明 IoCancelIrp 已被调用,该函数用于取消这个请求。如果为 FALSE,则表明没有调用 IoCancelIrp 函数。取消 IRP 是一个相对复杂的主题,我将在本章的最后详细描述它。CancelIrql(KIRQL)是一个 IRQL 值,表明那个专用的取消自旋锁是在这个 IRQL 上获取的。当你在取消例程中释放自旋锁时应参考这个域。CancelRoutine(PDRIVER_CANCEL)是驱动程序取消例程的地址。你应该使用IoSetCancelRoutin

28、e 函数设置这个域而不是直接修改该域。UserBuffer(PVOID) 对于 METHOD_NEITHER 方式的IRP_MJ_DEVICE_CONTROL 请求,该域包含输出缓冲区的用户模式虚拟地址。该域还用于保存读写请求缓冲区的用户模式虚拟地址,但指定了 DO_BUFFERED_IO 或DO_DIRECT_IO 标志的驱动程序,其读写例程通常不需要访问这个域。当处理一个METHOD_NEITHER 控制操作时,驱动程序能用这个地址创建自己的 MDL。Tail.Overlay 是 Tail 联合中的一种结构,它含有几个对 WDM 驱动程序有潜在用途的成员。由于篇幅有限,这里不再讨论。1.5

29、.2IRP 处理的 “标准模型”(1)创建 IRPIRP 开始于某个实体调用 I/O 管理器函数创建它。在上图中,我使用术语 “I/O 管理器”来描述这个实体,尽管系统中确实有一个单独的系统部件用于创建 IRP。事实上,更精确地说,应该是某个实体创建了 IRP,并不是操作系统的某个例程创建了 IRP。例如,你的驱动程序有时会创建 IRP,而此时出现在图中第一个方框中的实体就应该是你的驱动程序。可以使用下面任何一种函数创建 IRP: IoBuildAsynchronousFsdRequest 创建异步 IRP(不需要等待其完成)。该函数和下一个函数仅适用于创建某些类型的 IRP。 桂林电子工业学

30、院毕业设计(论文)专用纸 第 10 页 共 54 页10IoBuildSynchronousFsdRequest 创建同步 IRP(需要等待其完成)。 IoBuildDeviceIoControlRequest 创建一个同步 IRP_MJ_DEVICE_CONTROL 或IRP_MJ_INTERNAL_DEVICE_CONTROL 请求。 IoAllocateIrp 创建上面三个函数不支持的其它种类的 IRP。 前两个函数中的 Fsd 表明这些函数专用于文件系统驱动程序(FSD)。虽然 FSD 是这两个函数的主要使用者,但其它驱动程序也可以调用这些函数。DDK 还公开了一个IoMakeAsso

31、ciatedIrp 函数,该函数用于创建某些 IRP 的从属 IRP。WDM 驱动程序不应该使用这个函数。(2)发往派遣例程创建完 IRP 后,你可以调用 IoGetNextIrpStackLocation 函数获得该 IRP 第一个堆栈单元的指针。然后初始化这个堆栈单元。在初始化过程的最后,你需要填充MajorFunction 代码。堆栈单元初始化完成后,就可以调用 IoCallDriver 函数把 IRP 发送到设备驱动程序:PDEVICE_OBJECT DeviceObject; /给定的设备对象PIO_STACK_LOCATION stack = IoGetNextIrpStackLo

32、cation(Irp); /获得指针stack-MajorFunction = IRP_MJ_Xxx;NTSTATUS status = IoCallDriver(DeviceObject, Irp);IoCallDriver 函数的第一个参数是你在某处获得的设备对象的地址。我将在本章的结尾处描述获得设备对象指针的两个常用方法。在这里,我们先假设你已经有了这个指针。IRP 中的第一个堆栈单元指针被初始化成指向该堆栈单元之前的堆栈单元,因为 I/O 堆栈实际上是 IO_STACK_LOCATION 结构数组,你可以认为这个指针被初始化为指向一个不存在的“-1”元素,因此当我们要初始化第一个堆栈单

33、元时我们实际需要的是“下一个”堆栈单元。 IoCallDriver 将沿着这个堆栈指针找到第 0 个表项,并提取我们放在那里的主功能代码,在上例中为 IRP_MJ_Xxx。然后 IoCallDriver 函数将利用DriverObject 指针找到设备对象中的 MajorFunction 表。IoCallDriver 将使用主功能代码索引这个表,最后调用找到的地址(派遣函数)。派遣函数要对 IRP 的处理做出决定,有三种选择:派遣函数立即完成该 IRP。 把该 IRP 传递到处于同一堆栈的下层驱动程序。 排队该 IRP 以便由这个驱动程序中的其它例程来处理。 每处理一个 IRP,I/O 管理器

34、就调用一次 StartIo 例程:StartIo 例程在 DISPATCH_LEVEL 级上获得控制,这意味着该函数不能生成任何页故障。另外,设备对象的 CurrentIrp 域和 Irp 参数都指向 I/O 管理器送来的 IRP。桂林电子工业学院毕业设计(论文)专用纸 第 11 页 共 54 页11StartIo 的工作是就着手处理 IRP。如何做要完全取决于你的设备。通常你需要访问硬件寄存器,但可能有其它例程,如你的中断服务例程,或者是驱动程序中的其它例程也需要访问这些寄存器。实际上,有时着手一个新操作的最容易的方式是在设备扩展中保存某些状态信息,然后伪造一个中断。由于这些方法的执行都需要

35、在一个自旋锁的保护之下,而这个自旋锁与保护你的 ISR 所使用的是同一个自旋锁,所以正确的方法是调用 KeSynchronizeExecution 函数。对于图中的中断服务例程,当设备完成数据传输后,它将以硬件中断形式发出通知。DpcForIsr 例程在 DISPATCH_LEVEL 级上获得控制。通常,它的工作就是完成 IRP(导致最近的中断发生)。但一般情况下,它通过调用 IoCompleteRequest 函数把剩余的工作交给完成例程来做。图 17 I/O 请求包处理流程1.5.3 完成 I/O 请求派遣函数也可以在下面这两种情况下完成 IRP: 如果请求是错误的( 可以以容易的检测方式

36、查明,例如要求打印机倒纸请求或卸载键盘请求) ,则派遣例程应以失败方式完成该请求并返回适当的出错代码。 如果请求要求得到的仅是派遣函数可以容易确定的信息(例如一个询问驱动程序版本号的控制请求),则派遣例程应立即给出回答并完成请求,返回成功状态码。 完成机制是这样的,完成一个 IRP 必须先填充 IoStatus 块的 Status 和 Information成员,然后调用 IoCompleteRequest 例程。Status 值就是 NTSTATUS.H 中定义的状态代码。表中简要地列出了常用的状态代码。而 Information 值要取决于你完成的是何种类桂林电子工业学院毕业设计(论文)专

37、用纸 第 12 页 共 54 页12型的 IRP 以及是成功还是失败。通常情况下,如果 IRP 完成失败(即,完成的结果是某种错误状态),你应把 Information 域置 0。如果你成功地完成了一个数据传输 IRP,通常应该把 Information 域设置成传输的字节量。表 1-1 状态代码状态代码 描述STATUS_SUCCESS 正常完成STATUS_UNSUCCESSFUL 请求失败,没有描述失败原因的代码STATUS_NOT_IMPLEMENTED 一个没有实现的功能STATUS_INVALID_HANDLE 提供给该操作的句柄无效STATUS_INVALID_PARAMETER

38、 参数错误STATUS_INVALID_DEVICE_REQUEST 该请求对这个设备无效STATUS_END_OF_FILE 到达文件尾STATUS_DELETE_PENDING 设备正处于被从系统中删除过程中STATUS_INSUFFICIENT_RESOURCES 没有足够的系统资源(通常是内存)来执行该操作为了了解低级驱动程序的 I/O 请求的结果,需要安装一个完成例程,调用IoSetCompletionRoutine 函数:IoSetCompletionRoutine(Irp,CompletionRoutine,context,InvokeOnSuccess,InvokeOnErro

39、r,InvokeOnCancel);Irp 就是你要了解其完成的请求。CompletionRoutine 是被调用的完成例程的地址,context 是任何一个指针长度的值,将作为完成例程的参数。InvokeOnXxx 参数是布尔值,它们指出在三种不同的环境中是否需要调用完成例程: InvokeOnSuccess 你希望完成例程在 IRP 以成功状态(返回的状态代码通过了NT_SUCCESS 测试) 完成时被调用。 InvokeOnError 你希望完成例程在 IRP 以失败状态(返回的状态代码未通过了NT_SUCCESS 测试) 完成时被调用。 InvokeOnCancel 如果驱动程序在完成

40、 IRP 前调用了 IoCancelIrp 例程,你希望在此时调用完成例程。IoCancelIrp 将在 IRP 中设置取消标志,该标志也是调用完成例程的条件。一个被取消的 IRP 最终将以 STATUS_CANCELLED(该状态代码不能通过NT_SUCCESS 测试) 或任何其它状态完成。如果 IRP 以失败方式完成,并且你也指定桂林电子工业学院毕业设计(论文)专用纸 第 13 页 共 54 页13了 InvokeOnError 参数,那么是 InvokeOnError 本身导致了完成例程的调用。相反,如果 IRP 以成功方式完成,并且你也指定了 InvokeOnSuccess 参数,那么

41、是InvokeOnSuccess 本身导致了完成例程的调用。在这两种情况中,InvokeOnCancel 参数将是多余的。如果你省去 InvokeOnSuccess 和 InvokeOnError 中的任何一个参数或两个都省去,并且 IRP 也被设置了取消标志,那么 InvokeOnCancel 参数将导致完成例程的调用。 这三个标志中至少有一个设置为 TRUE。注意,IoSetCompletionRoutine 是一个宏,所以你应避免使用有副作用的参数。这三个标志参数和一个函数指针参数在宏中被引用了两次。IoSetCompletionRoutine 将把完成例程地址和上下文参数安装到下一个I

42、O_STACK_LOCATION 中,即下一层驱动程序将在那个堆栈单元中找到这些参数。因此,最底层的驱动程序不应该安装一个完成例程。1.5.4 向下级传递请求WDM 使用分层设备对象结构的目的就是使 IRP 能方便地从一层驱动程序传递到下一层驱动程序。有两种情况,有时候我们需要考虑 IRP 传递到下层驱动程序之后的事情,这时需要复制堆栈单元。这里一般不考虑。1.5.5 取消 I/O 请求程序有时会取消它们原来请求的 IRP。应用程序可能发出某些需要长时间才能完成的请求,然后这个应用程序结束执行,而这个 IRP 仍然是未完成的。这种情况在WDM 模型中尤为常见,例如当新硬件插入系统时,驱动程序必

43、须停止执行以等待配置管理器重新分配硬件资源,设备电源关闭时也是这样。为了在内核模式中取消一个请求,IRP 的创建者需调用 IoCancelIrp 函数。如果某线程终止时,它发出的请求仍然未完成,则操作系统自动为每个 IRP 调用IoCancelIrp。用户模式应用程序调用 CancelIo 函数可以取消给定线程发出的所有未完成的异步操作。IoCancelIrp 仅仅是简单地设置 IRP 的 Cancel 标志位然后调用 IRP 的取消例程。即:它并不知道是否修改过 IRP 指针,也不知道是否正在处理这个 IRP,所以它必须依靠一个提供的取消例程来做大部分 IRP 取消工作。1.6 本设计开发的

44、驱动程序描述根据前面的描述,本论文所涉及的驱动程序,是一个功能驱动程序,它涉及到USB 和 HID 两个类。这个驱动程序之上并没有过滤驱动程序,功能驱动程序将调用总线驱动程序的一些功能来完成自己的功能。从功能方面来说,一个驱动程序可以做的工作有:初始化它自己创建和删除设备桂林电子工业学院毕业设计(论文)专用纸 第 14 页 共 54 页14处理 Win32 打开和关闭文件句柄的请求处理 Win32 输入/输出( I/O)请求串行化对设备的访问访问硬件调用其他驱动程序取消 I/O 请求超时 I/O 请求处理一个可热插拔的设备被加入或删除的情况处理电源管理请求使用 WMI(Windows Mana

45、gement Instrumentation)和 NT 事件向系统管理员报告只有“初始化 ”模块是必不可少的,本设计中,只用到了其中部分模块。因为对一个键盘驱动来说,许多功能是用不到的。2 设计方案及设计工具、环境选择2.1 键盘驱动程序设计方案及设计工具键盘驱动有很多种设计工具,除了用 DDK 开发之外,还可以用Windriver,DriverStudio 等开发工具开发。一般的来说,使用封装的更高层的工具象Windriver,开发起来周期较短,也更容易些,但是出了问题也更难调试。作为一个毕业设计,为了更深入的了解 Windows 驱动模型,应该选择使用 DDK 开发。了解到键盘首先是一个

46、HID 设备,Windows 系统是将键盘作为 HID 设备处理。因此,在开发键盘驱动的时候,是在一个 HID minidriver 的框架下来实现的,HID 类驱动中有一些 IOCTL(输入输出控制) ,我们所做的就是要建立起来这个框架,并且填充这些 IOCTL,来实现驱动程序的完全功能。那么怎么来实现读写键盘的功能成为下一步考虑的问题,因为键盘是一个 USB 键盘,这时候我们用到了 USB 类的 IOCTL,在第一章里提到了 Windows 包含的各种类驱动,可以看到 USB 类和 HID 类都列在其中。事实上在这里它们结合起来组成了一个完整的 USB 键盘驱动程序。通过调用 USB 类的

47、 USBDI,我们可以实现读写键盘,启动键盘(USB 设别) ,停止键盘(USB 设备) ,移除键盘(USB 设备)等一系列必须的事件的响应。将实现的代码添加到 HID minidriver 中。这样,HID 类的接口得以实现,对下面的一层则使用了 USB 驱动程序接口(USBDI) 。一个完整的驱动程序的设计方案大致如上。事实上,如果只是做一个简单的访问键盘的程序,而不是将其嵌入在系统中,作为驱动程序的话,只需要 USB 类的特性就够了。那样的话,整个题目的难度会降低一些。2.2 环境设置2.2.1DDK 的安装DDK 是驱动程序开发工具包,不同的操作系统有不同的版本,本论文设计的驱动桂林电

48、子工业学院毕业设计(论文)专用纸 第 15 页 共 54 页15程序是在 Windows 2000 环境下,因此使用 Windows 2000DDK。安装 DDK 之后,需要把 DDK 的 bin 目录加入到 VC的目录列表中,这样一些使用到 DDK 头文件的客户程序,可以方便的找到它们要用的头文件。而不用专门拷贝出来。DDK 当中有一个 setenv.bat,它来为 VC使用 DDK 进行开发做一些环境设置,并且检查开发工具 VC的版本,是否安装。DDK2000 支持 VC5 和 VC6,这也是为什么作者一开始选择 VC.NET 作为开发工具,但是后来又转向 VC6.0 的原因。2.2.1m

49、akefile 构造 环境当创建新的 Makefile 项目时,Visual Studio 缺省提供两个 build 配置“Win32 Debug”和“Win32 Release”,build 命令行中的设置,根据程序所在驱动器位置的不同而需要改变。build 命令行运行 MakeDrvr.bat 批处理文件,使用 DDKROOT 环境变量,如果在Visual Studio 中请求一个完整的重新构造,把选项 -nmake /a 添加到这个命令行。设置输出文件名,使得在 build 菜单中显示正确的名字。代码清单 21 Win32 自由配置设置build 命令行 MakeDrvr %DDKroot% e:lcsDriver checked全部重新构造选项 -nmake /a输出文件名 lcsDriver.sy浏览信息文件名 objchki386lcsDriver.bsc如果在以上配置中,checked 改

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报