3. KMDF 驱动入口与初始化:DriverEntry、WDF_DRIVER_CONFIG 与 EvtDriverDeviceAdd
KMDF 驱动的初始化流程是驱动加载与设备管理的起点。本章深入剖析 DriverEntry 函数的职责、WDF_DRIVER_CONFIG 结构的配置方法、EvtDriverDeviceAdd 回调的触发时机,以及完整的设备初始化流程。理解这些核心机制,是后续中断处理与 DMA 性能调优的基础。
核心要点: KMDF 驱动初始化遵循“驱动对象创建 → 配置结构填充 → 设备添加回调 → 设备对象初始化”的固定流水线。任何环节的配置错误都会导致驱动加载失败或设备无法识别。
3.1 DriverEntry 函数:驱动的入口点
DriverEntry 是 KMDF 驱动的第一个执行函数,由操作系统在驱动加载时调用。它的主要职责是创建一个 WDF 驱动对象,并注册必要的回调函数。
函数原型与参数
NTSTATUS
DriverEntry(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath
);
| 参数 | 类型 | 说明 |
|---|---|---|
| DriverObject | PDRIVER_OBJECT | WDM 驱动对象指针,KMDF 框架会将其与 WDF 驱动对象关联 |
| RegistryPath | PUNICODE_STRING | 驱动在注册表中的服务路径,用于读取配置信息 |
标准实现步骤
- 调用 WdfDriverCreate:创建 WDF 驱动对象,传入
WDF_DRIVER_CONFIG结构。 - 检查返回值:确保
WdfDriverCreate返回STATUS_SUCCESS。 - 可选:读取注册表:通过
RegistryPath获取驱动自定义配置。 - 返回状态:成功返回
STATUS_SUCCESS,失败返回相应错误码。
最佳实践:
DriverEntry 应保持轻量,仅完成框架初始化。复杂的硬件初始化应推迟到 EvtDriverDeviceAdd 回调中执行。
3.2 WDF_DRIVER_CONFIG 结构:驱动的配置蓝图
WDF_DRIVER_CONFIG 结构是 KMDF 驱动初始化的核心配置载体,它定义了驱动的基本属性和回调函数入口。
结构定义与关键字段
typedef struct _WDF_DRIVER_CONFIG {
ULONG Size; // 结构大小,必须初始化为 sizeof(WDF_DRIVER_CONFIG)
PFN_WDF_DRIVER_DEVICE_ADD EvtDriverDeviceAdd; // 设备添加回调(必需)
PFN_WDF_DRIVER_UNLOAD EvtDriverUnload; // 驱动卸载回调(可选)
ULONG DriverInitFlags; // 驱动初始化标志
ULONG DriverPoolTag; // 驱动内存池标签
} WDF_DRIVER_CONFIG, *PWDF_DRIVER_CONFIG;
| 字段 | 必需/可选 | 说明 |
|---|---|---|
| Size | 必需 | 必须设置为 sizeof(WDF_DRIVER_CONFIG),用于版本兼容性检查 |
| EvtDriverDeviceAdd | 必需 | 当 PnP 管理器检测到设备时调用的回调函数 |
| EvtDriverUnload | 可选 | 驱动卸载时的清理回调,释放全局资源 |
| DriverInitFlags | 可选 | 控制驱动行为,如 WDF_DRIVER_INIT_FLAG_NO_DISPATCH |
| DriverPoolTag | 可选 | 用于调试的内存池标签,建议使用 4 字符标识 |
初始化宏
KMDF 提供了便捷的初始化宏 WDF_DRIVER_CONFIG_INIT,自动填充 Size 字段并设置 EvtDriverDeviceAdd:
WDF_DRIVER_CONFIG config;
WDF_DRIVER_CONFIG_INIT(&config, MyEvtDriverDeviceAdd);
// 可选:设置卸载回调
config.EvtDriverUnload = MyEvtDriverUnload;
config.DriverPoolTag = 'DMA1';
常见错误: 忘记调用
WDF_DRIVER_CONFIG_INIT 或手动设置 Size 字段,会导致 WdfDriverCreate 返回 STATUS_INVALID_PARAMETER。始终使用初始化宏。
3.3 EvtDriverDeviceAdd 回调:设备发现的响应者
EvtDriverDeviceAdd 是 KMDF 驱动中最重要的回调函数之一。当 PnP 管理器枚举到驱动支持的设备时,框架会调用此回调,驱动在此函数中完成设备对象的创建和初始化。
函数原型
NTSTATUS
MyEvtDriverDeviceAdd(
_In_ WDFDRIVER Driver,
_Inout_ PWDFDEVICE_INIT DeviceInit
);
| 参数 | 说明 |
|---|---|
| Driver | 由 WdfDriverCreate 创建的 WDF 驱动对象句柄 |
| DeviceInit | 指向 WDFDEVICE_INIT 结构的指针,用于配置设备属性 |
标准实现步骤
- 配置设备属性:通过
WdfDeviceInitAssignName、WdfDeviceInitSetDeviceType等函数设置设备名称、类型、特性。 - 创建设备对象:调用
WdfDeviceCreate,传入DeviceInit和WDF_OBJECT_ATTRIBUTES。 - 创建 I/O 队列:为设备创建默认或自定义的 I/O 队列,注册请求处理回调。
- 初始化中断与 DMA:配置中断对象和 DMA 适配器(后续章节详述)。
- 返回状态:成功返回
STATUS_SUCCESS。
性能提示:
EvtDriverDeviceAdd 在 PASSIVE_LEVEL 执行,适合进行内存分配和初始化。避免在此函数中执行耗时操作,以免阻塞 PnP 枚举过程。
3.4 设备初始化流程:从 DriverEntry 到设备就绪
KMDF 设备的完整初始化流程跨越多个阶段,从驱动加载到设备可被应用程序访问。以下表格总结了关键阶段及其执行顺序:
| 阶段 | 执行函数/回调 | IRQL | 主要操作 |
|---|---|---|---|
| 1. 驱动加载 | DriverEntry | PASSIVE_LEVEL | 创建 WDF 驱动对象,注册回调 |
| 2. 设备枚举 | PnP 管理器 | PASSIVE_LEVEL | 检测设备,调用 EvtDriverDeviceAdd |
| 3. 设备创建 | EvtDriverDeviceAdd | PASSIVE_LEVEL | 配置属性,创建 WDF 设备对象 |
| 4. 设备初始化 | EvtDevicePrepareHardware | PASSIVE_LEVEL | 分配硬件资源,映射内存空间 |
| 5. 中断初始化 | EvtDeviceD0Entry | DIRQL | 连接中断,启用 DMA 引擎 |
| 6. 设备就绪 | — | PASSIVE_LEVEL | 设备可接收 I/O 请求 |
调试建议: 在
DriverEntry 和 EvtDriverDeviceAdd 入口处添加 DbgPrint 或 TraceEvents,便于跟踪初始化流程。使用 WinDbg 的 !wdfdriverinfo 命令可查看驱动对象状态。
3.5 完整代码示例:最小 KMDF 驱动初始化
以下代码展示了一个完整的 KMDF 驱动入口与设备初始化实现,包含错误处理和资源清理:
#include <ntddk.h>
#include <wdf.h>
DRIVER_INITIALIZE DriverEntry;
EVT_WDF_DRIVER_DEVICE_ADD MyEvtDriverDeviceAdd;
NTSTATUS
DriverEntry(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath
)
{
NTSTATUS status;
WDF_DRIVER_CONFIG config;
// 1. 初始化配置结构
WDF_DRIVER_CONFIG_INIT(&config, MyEvtDriverDeviceAdd);
config.DriverPoolTag = 'DMA1';
// 2. 创建 WDF 驱动对象
status = WdfDriverCreate(
DriverObject,
RegistryPath,
WDF_NO_OBJECT_ATTRIBUTES,
&config,
WDF_NO_HANDLE
);
if (!NT_SUCCESS(status)) {
DbgPrint("WdfDriverCreate failed: 0x%X\n", status);
return status;
}
DbgPrint("KMDF Driver loaded successfully\n");
return STATUS_SUCCESS;
}
NTSTATUS
MyEvtDriverDeviceAdd(
_In_ WDFDRIVER Driver,
_Inout_ PWDFDEVICE_INIT DeviceInit
)
{
NTSTATUS status;
WDFDEVICE device;
WDF_OBJECT_ATTRIBUTES attributes;
WDF_IO_QUEUE_CONFIG queueConfig;
UNREFERENCED_PARAMETER(Driver);
// 1. 设置设备属性
WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_UNKNOWN);
// 2. 创建设备对象
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
status = WdfDeviceCreate(&DeviceInit, &attributes, &device);
if (!NT_SUCCESS(status)) {
DbgPrint("WdfDeviceCreate failed: 0x%X\n", status);
return status;
}
// 3. 创建默认 I/O 队列
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig, WdfIoQueueDispatchSequential);
status = WdfIoQueueCreate(device, &queueConfig, WDF_NO_OBJECT_ATTRIBUTES, WDF_NO_HANDLE);
if (!NT_SUCCESS(status)) {
DbgPrint("WdfIoQueueCreate failed: 0x%X\n", status);
return status;
}
DbgPrint("Device created successfully\n");
return STATUS_SUCCESS;
}
关键检查点:
- 确保
WDF_DRIVER_CONFIG_INIT在WdfDriverCreate之前调用。 EvtDriverDeviceAdd中必须调用WdfDeviceCreate,否则设备无法被系统识别。- 所有资源分配失败时,必须返回错误码,框架会自动清理已分配的资源。
3.6 初始化性能优化要点
驱动初始化阶段的性能直接影响系统启动速度和设备枚举效率。以下是针对 KMDF 初始化的优化建议:
- 延迟初始化:将非关键的硬件初始化推迟到
EvtDevicePrepareHardware或EvtDeviceD0Entry中执行,减少DriverEntry和EvtDriverDeviceAdd的耗时。 - 内存预分配:在
EvtDriverDeviceAdd中预分配设备上下文所需的内存,避免运行时动态分配带来的性能抖动。 - 避免阻塞调用:初始化期间不要等待用户模式组件或长时间 I/O 操作,使用工作项(WorkItem)异步处理。
- 使用对象属性:通过
WDF_OBJECT_ATTRIBUTES设置上下文大小和清理回调,减少手动内存管理开销。
性能指标参考: 在典型硬件上,
DriverEntry 应控制在 1ms 以内,EvtDriverDeviceAdd 应控制在 10ms 以内。超过这些阈值可能影响系统启动体验。