Windows Driver Model

Windows驱动程式模型（Windows Driver Model，简称WDM），是微软Windows操作系统的驱动程式设计架构，包括对于各项装置（Device）的支援，如键盘（Keyboard）、鼠标（Mouse）、网络卡（Network Driver Interface Specification，Ndis）、通用序列总线（Universal Serial Bus，USB）等。WDM可支援Windows 98, Windows 98 Second Edition, Windows Me, Windows 2000, Windows XP以及Windows Server 2003在x86平台上的建制工作。这个架构分成好几个管理层面：

- 核心（Kernel）
- 物件管理（Object Manager）
- 执行（Executive）
- 输入输出管理（I/O Manager）
- 内存管理（Memory Manager）
- 行程服务（Process Service）
- 执行时期函式库（Run-time Library）
- 电源管理（Power Manager）
- 随插即用子系统（Plug and Play Subsystem）
- 视窗管理指令（Windows Management Instrumentation，WMI）
- 核心流（Kernel Streaming）
- 硬件抽象层（Hardware Abstraction Layer）

## 种类

驱动程式的种类有总线驱动程式（bus driver）、功能驱动程式（function driver）、筛选驱动程式（filter driver）三种：

- 总线驱动程式（bus drivers）
- 虚拟总线驱动程式（Virtual bus drivers）：呼叫IoInvalidateDeviceRelations通知PnP Manager。
- 功能驱动程式（function drivers）
- 微埠型驱动程式（miniport drivers）：属于USB, Audio, SCSI以及network的转换器（adapters）。
- 虚拟装置驱动程式（Virtual device drivers）
- 类型驱动程式（Class drivers）
- 筛选性驱动程式（filter drivers）
- 上层筛选性驱动程式（upper-filter drivers）
- 下层筛选性驱动程式（lower-filter drivers）

## 程式进入点

如同许多语言从main函式开始，WDM驱动程式的载入（Loading）乃至于动作，以及其卸载（Unload）行为，都有其步骤与规则。WDM驱动程式可以动态式的载入与卸载，当侦测到装置（Device）插入的时候，依据“PnP Manager”会自动地载入相对应的装置驱动程式，然后成为“Driver Object”，并呼叫DriverEntry函式。所有的WDM驱动程式，都必须拥有这个DriverEntry常式（routine），而且“此一名称不可改变”的，所有的驱动程式也是从这里开始执行的，I/O Manager首先呼叫驱动程式的`DriverEntry()`。DriverEntry在DDK Compiler编译出来的输出符号表是“`DriverEntry@8`”，原因是微软的C编译器把stdcall函示（例如VcDCall）的名称加上“记号”，加上一个＠符号，在附上参数的总字节个数，8指此function的所有参数所占的byte数。

DriverEntry函式有两个参数，其中第一个参数`PDRIVER_OBJECT DriverObject`是指向该驱动程式对应的物件指标；`PUNICODE_STRING RegistryPath`，驱动程式的服务主要键码，这个参数的使用时机并不多。以下是一个简单而标准的DriverEntry基本实作：

```c
 NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING pRegistryString)
 {
   //PDEVICE_OBJECT DriverObject;
   UNICODE_STRING deviceName;
   RtlInitUnicodeString( &deviceName, DEVICE_NAME );
       
   status = IoCreateDevice( DriverObject, 0, &deviceName, 
      FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, true, &pDeviceObj );
 
   UNICODE_STRING linkName;
   RtlInitUnicodeString( &linkName, LINK_NAME );
   status = IoCreateSymbolicLink( &linkName, &deviceName );
 
   DriverObject->DriverUnload = DriverUnload;
   DriverObject->MajorFunction[IRP_MJ_CREATE] =
   DriverObject->MajorFunction[IRP_MJ_CLOSE] =
   DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = xxDriverDispatch;
   DriverObject->DriverUnload = xxUnload;
 
   return STATUS_SUCCESS;
 }
```

## IRQL

DriverEntry运作在IRQL = PASSIVE_LEVEL等级，所以可以使用分页内存。WDM有数个经常实作的PASSIVE_LEVEL等级如下：

- DriverEntry
- Dispatch Function：DispatchXxx
- Unload：UnloadXxx
- AddDevice: XxxAddDevice
- Reinitialize: XxxReinitialize

另外还有几个DISPATCH_LEVEL等级的函式如下：

- StartIo
- AdapterControl
- ControllerControl
- IoTimer
- Dpc（延迟程序呼叫，Deferred Procedure Call）

## 派送行程

当DriverEntry函式完成物件的初始化与系统注册之后，接下来的重点便会落在Dispatch Function身上。每当I/O Manager得到一个请求时（例如按键，移动鼠标），它使用请求的函数代码（IoControlCode）呼叫驱动程序中几个Dispatch行程。

```c
 NTSTATUS xxDispatch (IN PDEVICE_OBJECT DeviceObject,IN PIRP pIrp)
 { 
    NTSTATUS ntStatus = STATUS_SUCCESS;
    ULONG IoControlCodes = 0;             
    PIO_STACK_LOCATION IrpStack=NULL;   
 
    pIrp->IoStatus.Status = STATUS_SUCCESS;
    pIrp->IoStatus.Information = 0;
 
    IrpStack = IoGetCurrentIrpStackLocation(pIrp);    
 
    switch (IrpStack->MajorFunction)
    { 
        case IRP_MJ_CREATE:
            break;
        case IRP_MJ_CLOSE:
            break;
        case IRP_MJ_DEVICE_CONTROL:
            IoControlCodes=IrpStack->Parameters.DeviceIoControl.IoControlCode;
            switch (IoControlCodes)
            { 
                case IOCTL_1:
                    break;
                case IOCTL_2:
                    break;
                default:
                    pIrp->IoStatus.Status = STATUS_INVALID_PARAMETER;
                    break;
            }
            break;
        default:
            break;
    }
    ntStatus=pIrp->IoStatus.Status;
 
    IoCompleteRequest(pIrp,IO_NO_INCREMENT);
 
    return ntStatus;
 }
```

## 卸载

Unload负责在驱动程式被停止前做一些必要的处理动作，如释放资源，记录最后状态等。

```c
 VOID DriverUnLoad (IN PDRIVER_OBJECT DriverObject)
 { 
   if (DeviceObject) {
   IoUnregisterFileSystem(DeviceObject);
   IoDeleteDevice(DeviceObject);
   //DeviceObject = 0;
 }
```

## 编译

驱动程式的编译需要使用DDK（Device Driver Kit）中的build指令，它是一道命令行程序，一般会在后面加上参数：-ceZ。例如：

```dos
 C:\driver sample>build.exe -ceZ
```

DDK可用于建立用于 Windows 2000、Windows XP、Microsoft Windows Server 2003、Vista的建置环境，但在Windows操作系统并非预设的功能，必须另行安装。安装完成后你会看到Build Enviroment，free是指release版，check则是debug版。

- Windows XP checked 64 Bit Build Environment
- Windows XP checked Build Environment
- Windows XP free 64 Bit Build Environment
- Windows XP free Build Environment

build指令一开始呼叫Build.exe编译连结器，从系统“环境变数”（Environment Variable）Include中得到引用文件的地址，然后呼叫Visual C++的编译链接器Nmake.exe进行实际的编译链接工作。在编译过程中遇到的错误，遇到的警告，会记录到buildxxx.log，buildxxx.wrn，buildxxx.err等文件中。

## 安装

安装过程分成两个步骤：

- 首先将编译成的.sys文件复制到Windows NT的System32\\Drivers\\目录下。
- 接着在Registry的HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\下建立和.sys文件同名的键，然后在之下建立名为Start、Type、ErrorControl的三种REG_DWORD类型的键值。

## 范例

Microsoft DDK提供了大量的WDM范例（samples）参考，这些范例随着DDK的安装，会进驻我们的系统之中（WINDDK\xx00\src）。一般人不大可能从轮子造起一个新的驱动程式，大部分要靠“既有的范例”来改良一个新的驱动程式。

- 1394
- Audio
- AVStream
- filesys
- general
- hid
- input
- ir
- kernel

## 批评

- WDM学习曲线过长。
- 电源管理（power management）事件与随插即用（Plug-and-play）差异太大。导致系统的睡眠（sleep）与清醒（wake up）状态容易产生问题。
- I/O cancellation几乎不可能达成。
- 每支驱动程式动辄要撰写数千行的程式码。
- 不支援纯使用者模式（pure user-mode）驱动程式。

## 相关条目

- Windows Driver Foundation
- Windows Display Driver Model
- VxD

## 外部链接

- Windows Driver Foundation Homepage
- Introducing Windows Driver Framework，written by well-known Windows driver developer, Walter Oney.
- Building and deploying a basic WDF Kernel Mode Driver，CodeProject
- Developing a WDF USB Kernel Mode Driver for the OSR USB FX2，CodeProject
- A simple demo for WDM Driver development

## 参考

- Walter Oney，《Windows Driver Model》