4、设备启动流程:设备枚举、资源分配、启动顺序、OnD0Entry与OnD0Exit回调详解

UMDF 驱动框架中,设备启动流程是 PnP 管理器与驱动交互的核心环节。本章深入剖析从设备枚举到驱动进入工作状态(D0)的完整路径,重点解析资源分配策略、启动顺序约束以及 OnD0Entry / OnD0Exit 回调的实现细节。

核心要点: 设备启动流程本质上是 PnP 状态机从 Stopped → Started 的迁移过程,驱动开发者必须理解每个阶段的责任边界,避免在错误阶段访问未就绪的硬件资源。

4.1 设备枚举与资源分配

PnP 管理器通过总线驱动(如 PCI、USB)枚举设备,收集硬件资源需求(I/O 端口、内存范围、中断、DMA 通道)。UMDF 驱动不直接参与枚举,但必须通过 IPnpCallbackHardware 接口接收分配结果。

设备枚举与资源分配流程 总线驱动枚举 PnP 管理器收集资源 资源仲裁与分配 UMDF 驱动接收资源 驱动验证资源完整性 资源映射到设备上下文 说明:资源分配在 IRP_MN_START_DEVICE 中完成,UMDF 驱动通过 IPnpCallbackHardware::OnPrepareHardware 获取资源列表。
资源类型与 UMDF 接口映射
资源类型 数据结构 UMDF 回调接口 典型用途
I/O 端口范围 CM_PARTIAL_RESOURCE_DESCRIPTOR OnPrepareHardware 配置寄存器访问
内存映射范围 CM_PARTIAL_RESOURCE_DESCRIPTOR OnPrepareHardware MMIO 寄存器、FIFO 缓冲区
中断 CM_PARTIAL_RESOURCE_DESCRIPTOR OnPrepareHardware 设备中断处理
DMA 通道 DMA_ADAPTER_INFO OnD0Entry (通过 DMA 接口) DMA 传输控制

4.2 启动顺序与状态机

设备启动遵循严格的顺序约束。PnP 管理器按以下阶段推进,每个阶段对应一个 IRP 或回调:

  1. 资源分配阶段: 发送 IRP_MN_START_DEVICE,触发 OnPrepareHardware。
  2. DMA 适配器初始化: 驱动创建 DMA 适配器对象(如果设备支持 DMA)。
  3. 电源状态转换: 从 D3(关闭)→ D0(工作),触发 OnD0Entry。
  4. 中断连接: 注册中断服务例程(如果设备使用中断)。
  5. DMA 通道准备: 分配 DMA 通道并配置传输参数。
UMDF 设备启动顺序状态机 Stopped ResourceAssigned DMAReady D0 (Running) OnPrepareHardware DMA 适配器创建 OnD0Entry OnD0Exit / 资源释放 ⚠ 关键约束: 1. 必须在 OnPrepareHardware 之后才能访问硬件资源。 2. DMA 适配器必须在 OnD0Entry 之前创建,否则 DMA 传输会失败。

4.3 OnD0Entry 回调详解

OnD0Entry 是驱动从低功耗状态(D1/D2/D3)进入全功能状态(D0)时被调用的关键回调。它属于 IPnpCallbackPower 接口,在电源 IRP(IRP_MN_SET_POWER)处理路径中被触发。

最佳实践: OnD0Entry 中只做与电源状态恢复相关的操作,如恢复寄存器配置、重新初始化 DMA 通道、启动定时器等。不要在此处分配资源或枚举设备——这些应在 OnPrepareHardware 中完成。

4.3.1 函数签名与参数

HRESULT OnD0Entry(
    _In_ IWDFDevice* pDevice,
    _In_ WDF_POWER_DEVICE_STATE previousState
);
参数 类型 说明
pDevice IWDFDevice* 指向设备对象的指针,用于访问设备上下文和 I/O 目标
previousState WDF_POWER_DEVICE_STATE 进入 D0 之前的电源状态(D1/D2/D3),帮助驱动判断恢复深度

4.3.2 典型实现模板

HRESULT CMyDevice::OnD0Entry(
    IWDFDevice* pDevice,
    WDF_POWER_DEVICE_STATE previousState
)
{
    HRESULT hr = S_OK;
    CMyDeviceContext* pContext = nullptr;

    // 1. 获取设备上下文
    pContext = GetDeviceContext(pDevice);
    if (nullptr == pContext) {
        return E_UNEXPECTED;
    }

    // 2. 根据 previousState 决定恢复策略
    if (previousState == WdfPowerDeviceD3) {
        // 完全断电恢复:需要完整重新初始化
        hr = RestoreHardwareState(pContext);
        if (FAILED(hr)) {
            return hr;
        }
    } else if (previousState == WdfPowerDeviceD1 ||
               previousState == WdfPowerDeviceD2) {
        // 浅睡眠恢复:只需恢复部分寄存器
        hr = RestorePartialState(pContext);
        if (FAILED(hr)) {
            return hr;
        }
    }

    // 3. 重新初始化 DMA 通道(如果设备使用 DMA)
    hr = ReinitializeDmaChannel(pContext);
    if (FAILED(hr)) {
        return hr;
    }

    // 4. 启动 I/O 处理
    pContext->m_bDeviceActive = TRUE;
    StartIoProcessing(pContext);

    return S_OK;
}

4.4 OnD0Exit 回调详解

OnD0Exit 是设备从 D0 状态退出时的清理回调。它负责在设备进入低功耗状态前,安全地停止 I/O、保存硬件状态并释放 DMA 资源。

常见错误: 在 OnD0Exit 中直接调用 IWDFDevice::Release 或删除设备对象。OnD0Exit 只应暂停设备活动,不应销毁设备实例。设备对象的生命周期由 PnP 管理器控制。

4.4.1 函数签名与参数

HRESULT OnD0Exit(
    _In_ IWDFDevice* pDevice,
    _In_ WDF_POWER_DEVICE_STATE newState
);
参数 类型 说明
pDevice IWDFDevice* 指向设备对象的指针
newState WDF_POWER_DEVICE_STATE 设备即将进入的目标电源状态(D1/D2/D3)

4.4.2 典型实现模板

HRESULT CMyDevice::OnD0Exit(
    IWDFDevice* pDevice,
    WDF_POWER_DEVICE_STATE newState
)
{
    HRESULT hr = S_OK;
    CMyDeviceContext* pContext = GetDeviceContext(pDevice);

    if (nullptr == pContext) {
        return E_UNEXPECTED;
    }

    // 1. 停止 I/O 处理
    pContext->m_bDeviceActive = FALSE;
    StopIoProcessing(pContext);

    // 2. 等待所有未完成的 DMA 传输完成
    hr = WaitForDmaCompletion(pContext, 5000); // 5 秒超时
    if (FAILED(hr)) {
        // 超时处理:强制中止 DMA
        AbortDmaTransfer(pContext);
    }

    // 3. 保存硬件状态(如果进入 D3,必须保存)
    if (newState == WdfPowerDeviceD3) {
        hr = SaveHardwareState(pContext);
        if (FAILED(hr)) {
            return hr;
        }
    }

    // 4. 释放 DMA 通道资源
    ReleaseDmaChannel(pContext);

    return S_OK;
}

4.5 OnD0Entry 与 OnD0Exit 的对称性

这两个回调必须成对实现,形成完整的电源管理生命周期。下表总结了它们的对称关系:

操作 OnD0Entry OnD0Exit
I/O 处理 启动 I/O 队列处理 停止 I/O 队列处理
DMA 通道 重新初始化 DMA 通道 释放 DMA 通道资源
硬件寄存器 恢复寄存器配置 保存寄存器状态
中断 连接中断(如果支持运行时 Dx) 断开中断
设备上下文标志 设置 m_bDeviceActive = TRUE 设置 m_bDeviceActive = FALSE
OnD0Entry 与 OnD0Exit 对称生命周期 OnD0Entry • 启动 I/O • 初始化 DMA • 恢复寄存器 • 设置 Active 标志 进入 D0 OnD0Exit • 停止 I/O • 等待 DMA 完成 • 保存寄存器 • 清除 Active 标志 电源状态循环:D0 ↔ Dx

4.6 常见问题与调试建议

  • 资源未就绪: 在 OnD0Entry 中访问硬件资源前,必须确认 OnPrepareHardware 已成功完成。使用设备上下文中的标志位跟踪资源状态。
  • DMA 死锁: 如果 OnD0Exit 中等待 DMA 完成超时,检查 DMA 传输是否被 I/O 取消路径阻塞。建议使用单独的取消回调处理。
  • 状态丢失: 进入 D3 后硬件状态会丢失,务必在 OnD0Exit 中保存所有易失寄存器,并在 OnD0Entry 中完整恢复。
  • 调试工具: 使用 !wdfkd.wdfdevice 和 !wdfkd.wdfpower 调试器扩展查看设备电源状态和回调调用历史。
调试检查清单:
  1. 确认 OnPrepareHardware 返回 S_OK。
  2. 检查 OnD0Entry 中 previousState 参数是否符合预期。
  3. 验证 DMA 适配器句柄在 OnD0Entry 中非 NULL。
  4. 使用 WPP 软件跟踪记录每个回调的进入和退出时间戳。