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 的初始化分为三个阶段:配置、创建、后处理。下表总结了每个阶段的关键操作:
| 阶段 | 关键函数 | 说明 |
|---|---|---|
| 配置 | WdfDeviceInitSetDeviceTypeWdfDeviceInitSetCharacteristicsWdfDeviceInitSetExclusive |
设置设备的基本属性,如设备类型、特性标志、独占访问模式。 |
| 创建 | WdfDeviceCreate |
传入配置好的 PWDFDEVICE_INIT 和 WDFDEVICE_INIT 结构,返回 WDFDEVICE 句柄。 |
| 后处理 | WdfDeviceCreateDeviceInterfaceWdfDeviceSetPnpCapabilities |
注册设备接口 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宏快速获取指针,无需查找表。
定义设备扩展上下文的典型步骤:
- 声明上下文结构体:包含设备相关的状态信息,如设备句柄、中断对象、队列句柄等。
- 注册上下文类型:使用
WDF_DECLARE_CONTEXT_TYPE宏。 - 在创建设备时分配:通过
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE宏初始化属性。 - 访问上下文:使用
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 对象创建完成的完整流程,以及设备扩展上下文在其中的位置:
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 等字段均已正确赋值,设备处于启动状态。