WDM系统入门基础篇

WDM(Win32 Driver Model),即Win32驱动程序模型,是Microsoft力推的全新驱动程序模式,旨在通过提供一种灵活的方式来简化驱动程序的开发,在实现对新硬件支持的基础上减少并降低所必须开发的驱动程序的数量和复杂性。
  除了通用的平台服务和扩展外,WDM还实现了一个模块化的、分层次类型的微型驱动程序结构(见图1)。类型驱动程序实现了支持通用总线、协议或设备类所需的功能性接口。类型驱动程序的一般特性是为逻辑设备的命令设置、协议和代码重用所需的总线接口实现标准化提供必要的条件。WDM对标准类接口的支持减少了Windows 95和Windows NT所需的设备驱动程序的数量和复杂性。Windows 2000也引入了WDM驱动程序构架。虽然WDM目前实际应用还不多,但是相信在不久的将来,在Windows平台上,WDM将成为21世纪主流的驱动模式。

  (1)Windows 操作系统

  Windows 98不像Windows 2000那样非常有规律的处理I/O操作(见图2)。应用程序调用Win32 API,而且仅当读硬盘文件、通信端口和有WDM驱动程序的设备时才调用相应的 Win32 API,对其它设备必须使用特定的机制。全部Windows 2000的内核模式I/O操作都使用一个共同的数据结构(IRP),Windows 98不具备这一特点,应用程序请求更达不到内核模式。不过,当谈到WDM驱动程序的时候,Windows 98内部的体系结构与Windows 2000是非常相似的。系统模块(NTKERN.VxD)包括大量Windows NT内核支持函数,它完成请求包IRPs的创建,把它们发送给WDM驱动程序,从这一点考虑,两个环境之间不存在差异。

  图1 WDM中设备对象和驱动程序的分层

  (2)WDM特性

  模块化的WDM体系结构中灵活统一的接口,使操作系统可以动态地配置不同的驱动程序模块来支持特定的设备。一个典型的驱动程序堆栈由通用设备、协议及特定协议和特定总线的微型驱动程序联接的总线类驱动程序构成。动态构造WDM驱动程序堆栈是实现即插即用设备支持的关键。

  WDM服务使实现一个用于Windows NT和Windows 95快速反应的模型成为可能。WDM提供了多个执行优先级,包括核心态和非核心态线程、IRQ级别和被延缓的程序调用(DPC)。所有的WDM类和微型驱动程序都作为核心态(第0层)的特权级线程(不会被CPU调度程序中断)执行。32个IRQ级可以被用于区分硬件中断服务的优先级。对于每个中断,DPC被排入队列等到被启用中断的IRQ服务例程完成后再执行。DPCs通过有效的减少中断被禁止的时间,使系统对中断的响应获得了很大的提高。对于使用多处理器的基于x86的PC系统,在Windows NT下对中断的支持是以Intel的多处理器规范1.4版本为基础的。

  对于流媒体应用程序,WDM在核心态提供了快速反应的接口来处理I/O流。WDM的流接口是通过标准的WDM类接口提供出的。

  图2 Windows 98体系结构

  (3)WDM工作原理

  WDM支持USB、IEEE 1394、ACPI等全新的硬件标准。而且以往在两个平台上同时运行时需要编写两个截然不同的驱动程序,现在只需要编写一个WDM驱动程序就可以了。WDM驱动程序也是分层的,即不同层上的驱动程序有着不同的优先级,而Windows 9x下的VxD则没有此结构。另外,WDM还引入了功能设备对象FDO(Functional Device Object)与物理设备对象PDO(Physical Device Object)两个新类来描述硬件,一个PDO对应一个真实硬件。

  另外值得注意的是,一个硬件只允许有一个PDO,却可以拥有多个FDO,在驱动程序中直接操作的不是硬件而是相应的PDO与FDO。在Ring-3与Ring-0通讯方面,系统为每一个用户请求打包形成一个IRP结构,将其发送至驱动程序,并通过识别IRP中的PDO来区别是发送给哪一个设备的。另外,在驱动程序的加载方面,WDM不通过驱动程序名称识别,而是通过一个128位的GUID来实现驱动程序的识别。

  (4)WDM与其它驱动程序的比较

  写WDM和其它模式驱动程序基本上是相同的,代码中的主要区别在于如何创建设备。在WDM驱动程序中,即插即用(PnP)管理器告知何时向系统添加一个设备,或者从系统删除设备。PnP管理器使用安装的INF文件查找新设备的正确驱动程序;而其它模式驱动程序必须发现它自己的设备,使用专门的安装程序安装。

  另外在细节上也存在很多区别,其它模式驱动程序参数一般由注册表提供,在DriverEntry里调用读注册表的函数,然后根据注册表再调用CreateDevice,但是WDM一般不是这样,这是由于Windows 2000下支持PnP,在加载的时候会发PnP消息给Driver,所以一般不需要在DriverEntry里调用CreateDevice,而是在AddDevice里创建,或者在PnP消息里创建。一般在DriverEntry里创建的是一个与设备或者对象毫无关系的虚拟设备,用于管理与Win32的通讯。如果不想对该设备做什么特别的处理,或者设备不复杂,AddDevice可以简单返回Nt_Success,不用调用CreateDevice。另外整个设备驱动树也发生了改变,从而使安装程序发生了很大的改变。

=================================================================

要想开发WDM驱动程序,我们必须首先安装DDK(设备驱动程序开发包),WDM开发环境的设置相对比较麻烦,大多数开发人员偏爱用Visual Studio进行开发,而VC++有一些非常有用的工具。
  ■安装DDK

  下面笔者将详细介绍在Windows 98、Windows NT和Windows 95下DDK的安装方法。

  1.Windows 98 DDK的安装

  这一部分描述安装Windows 98 DDK的方法。约定%98DDK%为安装的Windows 98 DDK的根目录;%MSTOOLS%为微软SDK平台的根目录;%VCPPDEV%为安装的VC++的根目录开发环境。

  使用DDK的软件平台通常为Windows 98操作系统和VC++ 4.2 or 5.0版本,若要编译视频捕获则需要VC++ 5.0版本。在安装Windows 98 DDK之前,必须先安装VC++编译器/开发环境,否则运行时,Windows 98 DDK的Setenv.bat批处理文件将不能建立正确的环境。安装DDK 资料需要IE 4.01或以上版本,如果是从光盘或测试驱动程序则需要光驱,16MB内存是不可少的,完整安装需要82MB硬盘容量。

  包括Windows 98 DDK在内的所有例子驱动程序都不需要在安装的SDK平台下构造。不过,如果你开始开发自己的驱动程序,可能需要的不是Windows 98 DDK中的头文件,而是SDK平台中的头文件,因此可以考虑两种方法:拷贝所需的头文件或SDK平台的文件到在%98DDK%和%VCPPDEV%下适当的Include 目录中;也可以直接安装SDK平台,在%98DDK%/BIN中编辑Setenv.bat文件,并运行安装在%MSTOOLS%里的Setenv.bat文件。

  用Setup程序安装DDK,步骤为:

  (1)运行Windows 98 DDK中的SETUP.EXE文件,安装Windows 98 DDK到C:/98DDK。

  (2)安装VC++ 5.0到C:/VCPPDEV。

  (3)修改CONFIG.SYS增大环境变量空间。在CONFIG.SYS文件最后加入一行:

  SHELL=C:/WINDOWS/COMMAND.COM /E:4096 /P

  在安装Windows 98 DDK之前,必须先安装VC++编译器/开发环境,否则运行时,Windows 98 DDK的Setenv.bat批处理文件将不能建立正确的环境。

  2.Windows 95 DDK的安装

  安装Windows 95 DDK一般需要先安装Win32 SDK,因为Windows 95 DDK需要Win32 SDK的16位资源编译器。但是Win32 SDK很大(一整张光盘的容量),而且国内难以买到,下面笔者介绍一种安装Windows 95 DDK的简单方法:

  (1)修改注册表,仿真Win32 SDK已经安装的情况。建立一个名为WIN32SDK.REG的注册表文件,内容为:

  REGEDIT4

  [HKEY_USERS/.Default/Software/Microsoft/Win32SDK]

  [HKEY_USERS/.Default/Software/Microsoft/Win32SDK/Directories]

  "Install Dir"="C://MSTOOLS"

  在资源管理器中双击此文件,将此文件中的内容添加到注册表。就可以安装Windows 95 DDK了。

  (2)运行Windows 95 DDK中的SETUP.EXE文件,安装Windows 95 DDK到C:/DDK。

  (3)安装MASM 6.11到C:/MASM611。安装完成后再将未安装的Windows 95 DDK中的MASM611C目录下的文件覆盖到C:/MASM611/BIN。

  (4)安装VC++ 5.0到C:/Program Files/DevStudio/VC。

  (5)建立C:/MSTOOLS/BINW16目录,拷贝资源编译器。

  Windows 95 DDK需要一个能将Win32资源的源文件编译成16位资源的资源编译器。如果有Win32 SDK,可以将BINW16目录下的文件直接拷贝到C:/MSTOOLS/BINW16;如果没有Win32 SDK,则可以使用第三方的资源编译器,这里以使用Borland的资源编译器为例:

  首先准备一套Turbo MASM 5.0,用UNPAK工具解压缩CMDLINE.PAK文件,找到下面三个文件:

    BRC.EXE

    BRCC32.EXE

    RW32CORE.DLL

  将这三个文件拷贝到C:/MSTOOLS/BINW16,并将BRC.EXE改名为RC.EXE即可。

  (6)修改CONFIG.SYS增大环境变量空间。在CONFIG.SYS文件最后加入一行:

  SHELL=C:/WINDOWS/COMMAND.COM /E:8192 /P

  7)进入Windows 95的MS-DOS方式,初始化编译环境,最好建立一个批处理文件DDKSETUP.BAT,内容编辑为:

  set SDKROOT=C:/95SDK

  call new-vars.bat   (MASM611的环境)

  call ddkenv.bat   (DDK的环境)

  call vcvars32.bat   (VC++的环境)

  C:/MASM611/BINR/NEW-VARS.BAT

  C:/DDK/DDKINIT.BAT 32 BASE

  C:/Program Files/DevStudio/VC/bin/

  VCVARS32.BAT

  编辑好之后就可以使用Windows 95 DDK了,连接时出现的警告可以不理会。

  有的DDK里面没有DDKENV.bat的批处理,如果你的DDK是CD版的,请注意保存DDK自带的DDKENV.bat,否则需要自建。DDKENV.bat内容如下:

  set DDKROOT=e:/95ddk

  set INCLUDE=%INCLUDE%;%DDKROOT%/INC32;%DDKROOT%/INC16  

  set LIB=%LIB%;%DDKROOT%/LIB

  有的DDK里面没有DDKENV.bat的批处理,如果你的DDK是CD版的,请注意保存DDK自带的DDKENV.bat,否则需要自建。

  3.NT DDK的安装

  需要准备下列软件:WIN32 SDK或者PLATFORM SDK、NT DDK、VC++ 5.0。具体安装步骤如下:

  (1)安装Win32 SDK或者PLATFORM SDK到C:/MSTOOLS。

  (2)如果是安装PLATFORM SDK,安装完成后请修改注册表:

  HKEY_CURRENT_USER/Environment ,添加一个“MSTOOLS”字符串,值为C:/MSTOOLS。

  (3)安装VC++ 5.0。

  (4)安装NT DDK到C:/NTDDK。

  ■构造环境

  下面以Windows 98驱动程序构造环境为例介绍建立WDM驱动程序构造环境的方法。

  1. 用SETENV.bat来安装驱动程序构造环境

  创建一个名为“Development Kits/Windows 98 DDK”的程序目录。这个目录包括自由构造环境项和检查构造环境项。每次重启操作系统,在构造驱动程序前,单击这些程序文件夹中合适的一项。这些项调用%98DDK%/BIN里的Setenv.bat批处理文件来创建正确的环境变量的驱动程序构造环境。

  2. 手工运行SETENV.bat

  在MS-DOS提示符下,或在“开始/运行”中使用下列语句:

  setenv  %98DDK%  [free | checked]

  例如,在C:/98ddk/bin〉提示符下,键入setenv C:/98ddk free,其中第一个参数指定DDK被安装的文件夹,注意就缺省的安装来说,目的地是/98ddk;可选的第二个参数说明目标构造环境,缺省类型是free。

  3. 构造WDM驱动程序

  使用一套规则以指定驱动程序怎样被创建,所构造的实用程序可用来在Windows 98和Windows NT上建造WDM驱动程序平台。

  在Windows 98 DDK被安装之后,WDM驱动程序构造树的工作例子和组成部分文件在硬盘上就可以得到了。驱动程序构造树根目录在%98DDK%/src。查看%98DDK%/inc里Makefile.def文件的内容,以及贯穿驱动程序构造树的各种的Dirs文件和源文件的内容,可以利用这些代码作为工作实例。

  4.构造驱动程序

  在当前目录的驱动程序构造树中创建一个子目录,然后,运行构造实用程序。在构造树的当前目录中,构造实用程序可以自动创建出驱动程序的源代码。构造实用程序在Windows 98 DDK例子驱动程序构造树的根目录下(%98DDK%/src)运行。例如,如果仅仅对为声音设备类构造的例子驱动程序有兴趣,可以设置当前目录到%98DDK%/src/audio上,然后,运行构造实用程序。

  5.检查Windows 98 DDK的安装

  经常使用的构造指令形式为build -cZ ;从而使构造实用程序做相关文件的扫描,执行完整的创建,并生成错误记录。检查安装的方法为:在/〈destination〉/src目录运行build -cZ,构造安装的例子驱动程序源代码的完整集。这个实用程序在构造驱动程序之前构造全部相关文件,自动建立文件关联关系。如果构造没有完成或报告过多的编译错误,则需要确认是否正确执行了以上的安装步骤。 

============================================

设计开发好自己的WDM驱动程序后,为了运行该驱动程序,我们必须编译和安装它们。
  ■编译设备驱动程序的方法

  安装DDK后,在DDK程序组下有Check和Free两个编译环境,Check环境用于编译带调试信息的驱动程序,Free则是编译正式发布版本的环境。通常情况下设备驱动程序的编译采用命令行的方式。通过一定的设置可以在VC ++的集成环境下编译。

  一般来说,成功编译一个最基本的设备驱动程序需要四个文件,第一个是驱动程序,即C语言源程序文件(例如vdisk.c,注意下面所有的例子都是以vdisk来说明);第二个是RC文件(例如vdisk.rc);第三个是sources文件;第四个文件是makefile.rc文件。sources文件和make文件类似,用来指定需要编译的文件以及需要连接的库文件。这三个辅助文件都很简单,在DDK samples的每个例程里都有三个这样的文件,依样画瓢就能理解它们的结构和意义。

  1.举例分析

  以下以vdisk程序为例,设vdisk.rc代码为:

  /vdisk.rc/

  #include

  #include

  #define VER_FILETYPE  VFT_DRV

  #define VER_FILESUBTYPE VFT2_DRV_SYSTEM

  #define VER_FILEDESCRIPTION_STR "SCSI VDisk Driver"

  #define VER_INTERNALNAME_STR "vdisk.sys"

  #define VER_ORIGINALFILENAME_STR "vdisk.sys"

  #include "common.ver"

  /end of vdisk.rc/

  设备驱动程序一般都使用Build实用程序来进行,Build只是NMAKE外面的一个外包装程序。Build本身其实相当简单,编译的大部分工作实际上由Build传递给NMAKE来进行。

  /SOURCES/

  TARGETNAME=vdisk

  TARGETTYPE=DRIVER

  TARGETPATH=$(BASEDIR)/lib

  TARGETLIBS=$(BASEDIR)/lib//$(DDKBUILDENV)/scsiport.lib

  INCLUDES=../../inc

  SOURCES=vdisk.c vdisk.rc

  /end of SOURCES/

  注意SOURCES的文件名没有任何扩展名。

  # makefile

  #

  # DO NOT EDIT THIS FILE!!! Edit ./sources. if you want to add a new source

  # file to this component. This file merely indirects to the real make file

  # that is shared by all the driver components of the Windows NT DDK

  #

  !INCLUDE $(NTMAKEENV)/makefile.def

  # end of makefile

  对所有驱动程序而言,makefile都是一样的,Microsoft也警告不要编辑这个文件,如果需要,可以编辑修改sources文件达到同样的效果。对于设备驱动程序,所使用的C编译器基本上无一例外地选用VC++。

  2.编译的基本步骤

  (1)首先进入check或free编译环境,初始化DDK编译环境。

  (2)运行VC安装目录下bin目录下的vcvars32.bat,初始化VC++编译环境。

  (3)运行Build.exe进行编译。

  ■设备驱动程序的安装和启动

  1.添加注册表中的键值

  Windows NT在引导的时候,通过扫描注册表构造驱动程序列表。这个列表既包括自启动的驱动程序,也包括需要手工启动的驱动程序。这个列表其实就是控制面板中设备Applet所列出来的所有设备。所有的设备驱动程序应该在注册表的HKEY_LOCAL_MACHINE/System/CurrentControl-

  Set/Services/下有相应的键值。下面以vdisk为例来说明如何添加键值:

  首先在HKEY_LOCAL_MACHINE/ System/ Current ControlSet/Services/下添加一个子项vdisk,注意这里的名称应该和你的驱动程序名称一致。例如驱动程序名称是vdisk.sys,那么这里的子项名称就是vdisk。然后在vdisk下添加以下键值:

 

名称数据类型说明
TypeREG_DWORD驱动程序的种类
StartREG_DWORD驱动程序的起始启动时间
ErrorControlREG_DWORD驱动装入失败的错误处理
GroupREG_SZ驱动程序的组名
DependOnGroupREG_MULTI_SZ所依赖的其他驱动程序
TagREG_BINARY同组内驱动程序装入顺序
Parameters(key)驱动程序特定的参数键


  Type值为1表示内核模式驱动程序;为2表示文件系统驱动程序。

  ErrorControl值为0表示日志记录错误并忽略;值为1表示日志记录错误并显示一个对话框;值为2表示日志记录错误,并用最后的正确配置重新启动;值为3表示日志记录错误,如果已经使用过正确配置,返回失败。

  在任何一个设备驱动程序中,上表中的前三项参数都是必需的。

  2.控制驱动程序的装入次序

  有时候控制多个驱动程序的装入次序是必要的。例如一套驱动程序中包括三个驱动程序,分别是jbChanger.sys,changerDisk.sys和vdisk.sys。jbChanger和changerDisk是两个SCSI类驱动程序,它们都依赖SCSI小端口(mini port驱动程序),同时changerDisk必须在jbChanger启动之后启动。vdisk是虚拟的磁盘驱动程序,它必须在jbChanger和changerDisk都启动之后才能启动成功。

  3.驱动程序的Start值

  上面注册表中驱动程序的Start值控制驱动程序在系统启动的时间。目前,Start可以取以下值,此外为该值留有扩展余地,以适用于新的要求:

  (l)0x0 (SERVICE_BOOT_START):这个值指定本驱动程序应该由操作系统装入程序启动。一般的驱动程序不会采用本值,因为系统在这个时候几乎还没有启动,大部分系统尚不可用。

  (2)0x1 (SERVICE_SYSTEM_START):该值表示在操作系统装入后但同时初始化它自己时启动驱动程序。

  (3)0x2 (SERVICE_AUTO_START):该值表示在整个系统启动并运行后由服务控制管理器装入。

  (4)0x3 (SERVICE_DEMAND_START):该值表示该驱动程序必须手工启动。可以通过控制面板的设备applet或者使用WIN32 API编程来启动。

  (5)0x4 (SERVICE_DISABLED):表示本驱动程序被禁用。

  注意在调试驱动程序的时候,最好将Start值设置为3来手工启动,这是因为如果设置为自动启动,而驱动程序在启动的过程中又发生了异常错误的话,可能导致系统不能启动。

  如果没有紧急恢复盘,首先可以尝试在启动的时候选择用已知的配置来启动系统,看是否能启动成功。如果失败,可以用DOS启动后到/%SystemRoot%/System32/Drivers目录下将出现问题的驱动程序删除,然后系统就可以启动了。

  不过如果NT安装在NTFS分区,DOS启动后将看不到这个分区,这样就必须将硬盘挂到另一NT系统上来删除这个文件了。通过设置Start可以控制驱动程序在不同的时候启动。但如果要解决依赖性问题,则需要使用Group和DependOnGroup值。

  首先要确定自己的驱动程序使用的Group名,系统有一些定义好的组名,对于当前系统存在的组名,可以观察注册表的/HKEY_LOCAL_MACHINE/System/CurrentControl-

  Set/Control/ServiceGroupOrder/List的键值。例如该值可以设置为:

  …

  SCSI miniport

  port

  Primary disk

  SCSI class

  SCSI CDROM class

  filter

  boot file system

  …

  这里每一行都是一个Group名,一般来说某个驱动程序都属于某一个Group。系统启动时按照该List下组的顺序依次启动各组里的驱动程序。例如jbChanger和changerDisk都属于SCSI Class组。如果你觉得该表中的组名都不合适,可以在该List的适当位置中添加新的组名。

  DependOnGroup值控制本驱动程序启动的时候必须先启动另一组的驱动程序,例如jbChanger和changerDisk的启动就依赖于SCSI miniport组。因此jbChanger和changerDisk的DependOnGroup值都为SCSI miniport。

  4.修改注册表的方法

  在注册表里这些值可以手工修改,也可以自己编程利用WIN32 API进行添加,同时也可以用ini文件的方式来添加。下面是一个ini(文件名为vdisk.ini)文件的例子。

  /Registry/Machine/System/CurrentControl

  Set/Services/VDisk

  Type=REG_DWORD 0x00000001

  Start=REG_DWORD 0x00000003

  ErrorControl=REG_DWORD 0x00000001

  Group=SCSI Class

  Parameters

  DriveLetter=N:

  然后以vdisk.ini为参数运行REGINI.EXE。就会自动在注册表里添加相应的项。

  在注册表里添加好这些项后,必须重新启动系统,这样所添加的设备驱动程序才能在控制面板的设备applet中列出来,再进行其他操作。

  5.启动设备驱动程序

  在添加修改好注册表后,重新启动系统,如果选择的Start值是0、1、2,如果一切正常,驱动程序就应该已经启动起来了。可以观察控制面板的设备applet中的设备列表。如果Start选择的是3,则可以直接启动。

  6.调试工具

  目前NT驱动程序的调试工具只有WINDBG和SOFTICE,WINDBG的使用需要双机环境,强力推荐使用SOFTICE。注意目前国内FTP服务器上的SOFTICE 3.2 FOR NT的Setup.ins文件是错误的,它将导致安装程序不认识你的NT,可以用3.0的setup.ins文件替代3.2的setup.ins,这样就可以安装成功。 

===========================================

通过安装DDK和相应的开发软件,我们构造好了WDM驱动程序的开发环境。接着,我们就要深入进行设计与开发工作了。
  ■WDM 驱动程序的运作流程

  WDM本身的PNP管理器被抽象地提升到了ROOT的地位。PNP管理器负责所有的总线驱动程序的加载。总线驱动程序则负责遍历所有位于总线上的设备,并且为每个设备创建相应的设备对象。当PNP管理器发现一个设备对象,就查找该对象对应的Driver。并调用该Driver的ADD DEVICE例程。如果Driver不在内存中,就先加载,然后调用ADD DEVICE例程。

  当然,总线本身并没有发出任何信号告诉PNP管理器自己的存在,所以,总线Driver是在NT的安装时设定的。而ISA设备并没有规范,因为需要KMD自己检查硬件存在及状态,所以它是老式KMD存在的惟一理由。这也是微软极力在新规范里取消ISA总线的理由之一。WDM支持PNP协议和PM协议,而且实现时仅仅需要在MAJOR FUNCTION里加入一些对PNP和PM事件响应的例程即可。

  ■驱动程序设计

  设计一个设备驱动程序,应该支持和其他相同类型设备的NT驱动程序相同的IRP_MJ_XXX和IOCTL请求代码。如果设计一个中间层NT驱动程序,应该首先确认下层驱动程序所管理的设备,因为一个高层的驱动程序必须具有低层驱动程序绝大多数IRP_MJ_XXX例程入口。高层驱动程序在接到I/O请求时,在确定自身IRP当前堆栈单元参数有效的前提下 ,设置好IRP中下一个低层驱动程序的堆栈单元,然后再调用IoCallDriver将请求传递给下层驱动程序处理。一旦决定好了驱动程序应该处理哪些IRP_MJ_XXX,就可以开始确定驱动程序应该有多少个Dispatch例程。当然也可以考虑把某些RP_MJ_XXX处理的例程合并为同一例程处理。例如在ChangerDisk和VDisk里,对IRP_MJ_CREATE和IRP_MJ_CLOSE处理的例程就是同一函数。

  一个驱动程序必须为它所管理的每个可能成为I/O请求的目标的物理和逻辑设备创建一个Device对象。一些低层的驱动程序还可能要创建一些不确定数目的Device对象。例如一个硬盘驱动程序必须为每一个物理硬盘创建一个Device对象,同时还必须为每个物理磁盘上的每个逻辑分区创建一个Device对象。

  一个高层驱动程序必须为它所代表的虚拟设备创建一个Device对象,这样更高层的驱动程序才能连接它们的Device对象到这个驱动程序的Device对象。另外,一个高层驱动程序通常为它低层驱动程序所创建的Device对象创建一系列的虚拟或逻辑Device对象。

  尽管可以分阶段来设计驱动程序,从而使一个处在开发阶段的驱动程序不必一开始就创建出所有它将要处理的所有Device对象,但从一开始就确定好最终要创建的所有Device对象将有助于设计者所要解决的任何同步问题。另外,确定所要创建的Device对象还有助于定义Device对象的Device Extension的内容和数据结构。

  ■驱动程序开发

  驱动程序的开发是一个从粗到细逐步求精的过程。NT DDK的src/目录下有一个庞大的模板代码,几乎覆盖了所有类型的设备驱动程序、高层驱动程序和过滤器驱动程序。在开始开发驱动程序之前,应该先在这个样板库下面寻找是否有和所要开发的类似类型的例程。

  例如若要开发光盘塔驱动程序,虽然DDK对光盘塔没有任何描述,但光盘塔是符合SCSI-Ⅱ规范的SCSI设备,可以在src/storage/class目录中发现很多和SCSI设备有关的驱动程序,例如SCSI Tape、SCSI Disk、SCSI CDROM等驱动程序开发时,可以参考类似驱动程序,从而减化开发难度。

  下面笔者将进一步介绍开发驱动程序的基本步骤:

  l.编写驱动程序框架

  (1)首先编写一个DriverEntry例程,并在该例程里调用IoCreateDevice来创建一个Device对象。

  (2)写一个处理IRP_MJ_CREATE请求的Dispatch例程的基本框架。如果驱动程序创建了多于一个的Device对象,则必须为IRP_MJ_CLOSE请求写一个例程,该例程通常情况下可以和DispatchCreate共用一个例程。

  (3) 编译连接驱动程序。

  2.测试驱动程序

  (1)首先在系统中安装好驱动程序,具体编译安装驱动程序请见下版的《编译安装篇》。

  (2)为NT逻辑设备名称和目标Device对象名称之间建立起符号连接,在前面已经知道Device对象名称对Win32用户模式是不可见的,是不能直接通过API来访问的,Win32 API只能访问NT逻辑设备名称。可以通过修改注册表来建立这两种名称之间的符号连接。运行Regedt32.exe在/HKEY_LOCAL_MACHINE/ System/ CurrentControlSet/ Control/ Session Manager/ DOS Devices下建立起符号连接,这种符号连接也可以在驱动程序里调用函数IoCreateSymbolicLink来创建。

  (3)完成以上所有的设置并检查无误后,我们必须重新启动Windows系统。

  (4)编写一个简单的测试程序调用Win32 API中的CreateFile函数,并以刚才命名的NT逻辑设备名打开这个设备。如果打开成功,则成功地写出了一个最简单的驱动程序了。支持更多的设备I/O请求,例如驱动程序可能需要对IRP_MJ_READ请求做出响应(完成后可用ReadFile 函数进行测试)。如果驱动程序需要能够手工卸载,那么还必须对IRP_MJ_CLOSE做出响应。为所需要处理的IRP_MJ_XXX写好处理例程,并在DriverEntry里面初始化好这些例程入口。一个低层的驱动程序需要一个StartIo、ISR和DpcForIsr例程,可能还需要一个SynchCritSection例程,如果设备使用了DMA,那么可能还需要一个AdapterControl例程。

  对于高层驱动程序可能需要一个或多个IoCompletion例程,最起码完成检查I/O状态块然后调用IoCompleteRequest的工作。如果需要,还要对Device Extension数据结构和内容做些修改。有一点必须很清楚的,就是代码运行级别的问题,即IRQL,最常见的级别是PASSIVE_LEVEL、APC_LEVEL、DISPATCH_LEVEL和DIRQL。

  在看NT DDK HELP中的函数说明的时候,要注意函数的可运行级别,比如有的函数只能在PASSIVE_LEVEL下运行,有的函数则可以在DISPATCH_LEVEL以下级别运行,级别越高的时候,对代码的要求就越严格,比如在DISPATCH_LEVEL的时候,就不能使用分页内存。通常情况下应该尽可能让代码在低运行级别如PASSIVE_LEVEL下运行,在高级别下运行过长时间将导致系统效率降低、影响系统响应的实时性。但有时候自己无法控制运行的级别,例如在调用低层Driver时使用IoCallDriver,低层Driver响应完毕后会执行completion例程,该例程运行的级别就是由低层Driver来决定。因此在编写completion例程时,应尽量将这个函数设计成能在DISPATCH_LEVEL级别运行。

  依照以上开发步骤,我们可以设计出全新的WDM设备驱动程序。 

==========================================================



  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值