在函数驱动程序中删除设备

删除设备时,函数驱动程序必须撤消为添加和启动设备而执行的任何操作。 此讨论包括外围设备的功能驱动程序和总线设备的功能驱动程序。

函数驱动程序在其 DispatchPnP 例程中使用如下过程删除设备:

  1. 这是总线设备的函数驱动程序吗?

    如果是这样,可能会删除总线上设备的任何未完成子 PDO。

    如果总线驱动程序处理了针对子设备的上一个 IRP_MN_SURPRISE_REMOVAL 请求,但驱动程序尚未收到后续 IRP_MN_REMOVE_DEVICE 请求,则总线驱动程序将使子 PDO 保持不变。 稍后,当关闭子设备的所有句柄时,PnP 管理器将发送子设备的删除 IRP,而总线驱动程序此时会删除子 PDO。

    如果总线驱动程序处理了设备的上一 个IRP_MN_REMOVE_DEVICE 请求,并且没有后续 IRP_MN_SURPRISE_REMOVAL 请求,则总线驱动程序会删除子 PDO。 在这种情况下,PnP 管理器确保在将删除 IRP 发送到父总线设备之前,已从子设备中删除任何函数和筛选器驱动程序, (FDO 和筛选器DO 已被删除) 。 子 PDO 可能仍然存在,因此总线驱动程序必须先删除子 PDO,然后才能删除总线设备。

  2. 驱动程序是否已处理此 FDO 的先前 IRP_MN_SURPRISE_REMOVAL 请求?

    如果是这样,请执行任何剩余的清理,并跳到步骤 8 IoCallDriver

    驱动程序通常在设备扩展中维护一个标志,指示驱动程序是否已处理设备的 IRP_MN_SURPRISE_REMOVAL 请求。

  3. 如果驱动程序以前为设备启用了唤醒,请取消 IRP_MN_WAIT_WAKE 请求。

  4. 确保设备处于非活动状态。

    如果设备尚未处于非活动状态以响应之前的 IRP_MN_QUERY_REMOVE_DEVICE,驱动程序必须将设备标记为不接受新请求,并且必须完成此驱动程序中排队的任何请求。 驱动程序必须使需要访问设备的任何未完成请求失败。

    驱动程序可以使用 IoXxxRemoveLockXxx 例程对未完成的 I/O 进行计数,并设置指示删除处理可以继续的事件。

  5. 执行任何关机操作。

    设备的每个驱动程序在收到 IRP_MN_REMOVE_DEVICE 请求时执行其关机操作(如果有)。 设备的电源策略所有者(通常是函数驱动程序)不会发送单独的 IRP_MN_SET_POWER 请求,将设备电源状态设置为 D3。 父总线驱动程序通常会关闭槽,并在总线驱动程序获取删除 IRP 时使用 PoSetPowerState 通知电源管理器。 有关其他信息,请参阅 电源管理

  6. 通过调用 IoSetDeviceInterfaceState 禁用任何设备接口。

  7. 释放驱动程序正在使用的设备的任何硬件资源。

    具体操作取决于设备和驱动程序,但可能包括断开 IoDisconnectInterrupt 中断的连接、使用 MmUnmapIoSpace 释放物理地址范围以及释放 I/O 端口。

  8. IRP_MN_REMOVE_DEVICE 请求向下传递到下一个驱动程序。

    使用 IoSkipCurrentIrpStackLocation 为下一个较低驱动程序设置 IRP 堆栈位置,并使用 IoCallDriver 将 IRP 传递给下一个驱动程序。

    驱动程序无需等待基础驱动程序完成其删除操作,然后再继续其删除活动。

  9. 使用 IoDetachDevice 从设备堆栈中删除设备对象。

    将指向下一个较低设备对象的指针指定为 TargetDevice 参数。 驱动程序从调用 IoAttachDeviceToDeviceStack 在驱动程序的 AddDevice 例程中接收此类指针。

  10. 清理任何特定于设备的分配、内存、事件等。

  11. 使用 IoDeleteDevice 释放 FDO。

  12. DispatchPnP 例程返回,从 IoCallDriver 传播返回状态。

函数驱动程序不会为删除 IRP 指定 IoCompletion 例程,也不会完成 IRP。 删除 IRP 由父总线驱动程序完成。