4. 设备对象创建:EvtDriverDeviceAdd 实现、WDFDEVICE 初始化、设备扩展上下文分配

在 KMDF 驱动中,设备对象的创建是驱动与硬件建立联系的起点。当 PnP 管理器检测到驱动所管理的设备时,会调用驱动注册的 EvtDriverDeviceAdd 回调函数。本章将深入剖析该回调的实现细节,包括 WDFDEVICE 对象的初始化流程、设备扩展上下文的分配与访问机制,以及如何通过设备栈遍历验证设备对象的正确性。

4.1 EvtDriverDeviceAdd 回调函数

EvtDriverDeviceAdd 是 KMDF 驱动必须实现的入口点之一,其原型如下:

NTSTATUS
EvtDriverDeviceAdd(
    _In_ WDFDRIVER Driver,
    _Inout_ PWDFDEVICE_INIT DeviceInit
);

该回调在系统 IRP_MN_START_DEVICE 处理流程中被调用,主要职责包括:

  • 配置设备对象属性:通过 WdfDeviceInitAssign* 系列函数设置设备类型、特性、文件对象配置等。
  • 创建 WDFDEVICE 对象:调用 WdfDeviceCreate 完成设备对象的实例化。
  • 分配设备扩展上下文:在创建设备时指定设备扩展的大小和类型。
  • 创建 I/O 队列:为设备配置默认或自定义的 I/O 队列。
  • 创建中断对象:如果设备支持中断,在此阶段创建 WDFINTERRUPT 对象。
关键提示: PWDFDEVICE_INIT 是一个不透明结构,仅在 EvtDriverDeviceAdd 回调期间有效。所有配置操作必须在 WdfDeviceCreate 调用之前完成,之后该结构将被释放。

4.2 WDFDEVICE 初始化流程

WDFDEVICE 的初始化分为三个阶段:配置、创建、后处理。下表总结了每个阶段的关键操作:

阶段 关键函数 说明
配置 WdfDeviceInitSetDeviceType
WdfDeviceInitSetCharacteristics
WdfDeviceInitSetExclusive
设置设备的基本属性,如设备类型、特性标志、独占访问模式。
创建 WdfDeviceCreate 传入配置好的 PWDFDEVICE_INIT 和 WDFDEVICE_INIT 结构,返回 WDFDEVICE 句柄。
后处理 WdfDeviceCreateDeviceInterface
WdfDeviceSetPnpCapabilities
注册设备接口 GUID、设置 PnP 能力、创建 I/O 队列等。

以下是一个典型的 EvtDriverDeviceAdd 实现片段:

NTSTATUS
EvtDriverDeviceAdd(
    WDFDRIVER Driver,
    PWDFDEVICE_INIT DeviceInit
)
{
    WDFDEVICE device;
    NTSTATUS status;
    PDEVICE_CONTEXT devContext;
    WDF_OBJECT_ATTRIBUTES attributes;
    WDFDEVICE_INIT config;

    // 1. 配置设备属性
    WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_UNKNOWN);
    WdfDeviceInitSetExclusive(DeviceInit, FALSE);

    // 2. 设置设备扩展上下文
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, DEVICE_CONTEXT);

    // 3. 创建 WDFDEVICE 对象
    WDFDEVICE_INIT_INIT(&config);
    status = WdfDeviceCreate(&DeviceInit, &attributes, &device);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    // 4. 获取设备扩展上下文指针
    devContext = DeviceGetContext(device);
    devContext->Device = device;

    // 5. 创建 I/O 队列(省略具体实现)
    // status = QueueCreate(device);

    // 6. 创建中断对象(后续章节详述)
    // status = InterruptCreate(device);

    return STATUS_SUCCESS;
}

4.3 设备扩展上下文分配

设备扩展上下文是 KMDF 提供的一种轻量级对象关联机制,允许驱动为每个 WDFDEVICE 实例分配私有数据。其核心优势在于:

  • 类型安全:通过 WDF_DECLARE_CONTEXT_TYPE 宏声明上下文类型,编译器可进行类型检查。
  • 自动生命周期管理:上下文内存与 WDFDEVICE 对象绑定,设备销毁时自动释放。
  • 高效访问:通过 WdfObjectGetTypedContext 宏快速获取指针,无需查找表。

定义设备扩展上下文的典型步骤:

  1. 声明上下文结构体:包含设备相关的状态信息,如设备句柄、中断对象、队列句柄等。
  2. 注册上下文类型:使用 WDF_DECLARE_CONTEXT_TYPE 宏。
  3. 在创建设备时分配:通过 WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE 宏初始化属性。
  4. 访问上下文:使用 WdfObjectGetTypedContext 或自定义的 DeviceGetContext 内联函数。
// 头文件中定义
typedef struct _DEVICE_CONTEXT {
    WDFDEVICE Device;
    WDFINTERRUPT Interrupt;
    WDFQUEUE Queue;
    ULONG DeviceFlags;
} DEVICE_CONTEXT, *PDEVICE_CONTEXT;

WDF_DECLARE_CONTEXT_TYPE(DEVICE_CONTEXT);

// 内联访问函数
__inline
PDEVICE_CONTEXT DeviceGetContext(WDFDEVICE Device)
{
    return WdfObjectGetTypedContext(Device, DEVICE_CONTEXT);
}
注意事项: 设备扩展上下文的大小应尽量精简,避免在分页池中分配过大结构。对于需要大量缓冲区的场景,建议在上下文只保存指针,动态分配独立内存。

4.4 设备对象创建流程 SVG 架构图

下图展示了从 EvtDriverDeviceAdd 被调用到 WDFDEVICE 对象创建完成的完整流程,以及设备扩展上下文在其中的位置:

WDFDEVICE 对象创建与设备扩展上下文分配流程 PnP 管理器 EvtDriverDeviceAdd 配置设备属性 分配设备扩展上下文 WdfDeviceCreate WDFDEVICE 句柄 设备扩展内存 关键说明 上下文在 WdfDeviceCreate 之前通过属性结构指定 创建后自动分配内存

4.5 设备扩展上下文访问性能对比

下表对比了 KMDF 设备扩展上下文与传统 WDM 设备扩展的访问方式:

特性 KMDF 设备扩展上下文 WDM 设备扩展
类型安全 是,通过宏声明类型 否,需手动强制转换
内存管理 自动,与 WDFDEVICE 生命周期绑定 手动,需在 AddDevice 中分配
访问速度 O(1),直接指针偏移 O(1),但需计算偏移量
调试支持 支持 !wdfdevice 扩展查看 需 !devobj 手动解析
跨版本兼容 KMDF 版本无关 依赖 WDM 版本

4.6 设备栈遍历与验证

设备对象创建完成后,可以通过设备栈遍历来验证设备是否正确挂载。KMDF 提供了 WdfDeviceGetDeviceStack 函数获取设备栈信息,但更常用的调试手段是使用内核调试器命令:

  • !devstack:显示设备对象栈,包括功能设备对象(FDO)和物理设备对象(PDO)。
  • !wdfdevice:显示 KMDF 设备对象的详细信息,包括设备扩展上下文内容。
  • !wdfobject:遍历 WDF 对象树,查看设备对象的父子关系。
实战建议: 在 EvtDriverDeviceAdd 返回后,立即在调试器中执行 !wdfdevice 0xfffffa800b4a3060(替换为实际设备句柄),验证设备扩展上下文中的字段是否已正确初始化。

以下是一个使用 WinDbg 验证设备扩展上下文的示例输出片段:

0: kd> !wdfdevice 0xfffffa800b4a3060
WDFDEVICE 0xfffffa800b4a3060
  Context: 0xfffffa800b4a30e0
    Device: 0xfffffa800b4a3060
    Interrupt: 0xfffffa800b4a3200
    Queue: 0xfffffa800b4a3400
    DeviceFlags: 0x00000001
  PnP State: Started
  Power State: D0

通过上述输出,可以确认设备扩展上下文中的 Device、Interrupt、Queue 等字段均已正确赋值,设备处于启动状态。