windows驱动开发-功能驱动的生命周期

发布于:2024-05-06 ⋅ 阅读:(28) ⋅ 点赞:(0)

PNP设备的功能驱动生命周期有一些稍微特殊的地方没下面看一下具体的,

功能驱动的设备启动

功能驱动程序设置 IoCompletion 例程,在设备堆栈中向下传递 IRP_MN_START_DEVICE 请求,并推迟其启动操作,直到所有较低的驱动程序都已完成 IRP。 有关使用内核事件和 IoCompletion 例程推迟IRP处理的详细信息,请参阅将PnPIRP处理推迟到较低的驱动程序完成。

当其 DispatchPnP 例程在所有较低级驱动程序使用IRP完成后重新获得控制权时,功能驱动程序将执行其启动设备的任务。 功能驱动程序使用如下过程启动设备:

1. 如果较低版本的驱动程序失败,IRP (IoCallDriver) 返回错误,请不要继续处理 IRP。 执行任何必要的清理并从 DispatchPnP 例程返回, 转到最后一个步骤;

2. 如果较低的驱动程序已成功处理 IRP,请启动设备:启动设备的确切步骤因设备而异。 此类步骤可能包括映射 I/O 空间、初始化硬件寄存器、将设备设置为 D0 电源状态,以及使用 IoConnectInterrupt 连接中断。 如果驱动程序在 IRP_MN_STOP_DEVICE 请求后重启设备,则驱动程序可能具有要还原的设备状态;

必须先打开设备,然后任何驱动程序才能访问它。 如果应为设备启用唤醒,其电源策略所有者 (通常功能驱动程序) 应在设备通电后和完成 IRP_MN_START_DEVICE 请求之前发送等待/唤醒 IRP;

3. 启动IRP持有队列中的 IRP:清除驱动程序定义的HOLD_NEW_REQUESTS标志,并启动IRP持有队列中的 IRP。 驱动程序应在首次启动设备时以及查询停止或停止IRP后重启设备时执行此操作。 

4. [可选]通过调用 IoSetDeviceInterfaceState 为设备启用接口:启用驱动程序以前在其 AddDevice 例程中注册的接口。

在 Windows 2000 及更高版本的 Windows 上,在 IRP_MN_START_DEVICEIRP完成之前,PnP 管理器不会发送设备接口到达通知,指示设备的所有驱动程序已完成其启动操作。 在设备的所有驱动程序完成启动IRP之前,PnP 管理器还会使到达的任何创建请求失败;

5. 完成 IRP:功能驱动程序的 IoCompletion 例程STATUS_MORE_PROCESSING_REQUIRED返回,如 将PnPIRP处理推迟到较低驱动程序完成中所述,因此功能驱动程序的 DispatchPnP 例程必须调用 IoCompleteRequest 以恢复 I/O 完成处理。

如果功能驱动程序的启动操作成功,驱动程序会将 Irp-IoStatus.Status> 设置为 STATUS_SUCCESS,调用 IoCompleteRequest,优先级提升为 IO_NO_INCREMENT,并从其 DispatchPnP 例程返回STATUS_SUCCESS。

如果功能驱动程序在其启动操作期间遇到错误,驱动程序会在IRP中设置错误状态,使用 IO_NO_INCREMENT调用 IoCompleteRequest ,并从其 DispatchPnP 例程返回错误。

如果较低级驱动程序失败,IRP 通过IoCallDriver 返回错误),功能驱动程序使用 IO_NO_INCREMENT 调用 IoCompleteRequest,并从其 DispatchPnP 例程返回 IoCallDriver 错误。 在这种情况下,功能驱动程序不会设置 Irp->IoStatus.Status,因为状态已由IRP失败的较低驱动程序设置。

当功能驱动程序收到IRP_MN_START_DEVICE请求时,它应检查 IrpSp->Parameters.StartDevice.AllocatedResources 和 IrpSp->Parameters.StartDevice.AllocatedResourcesTranslated 中的结构,它们分别描述了PnP 管理器分配给设备的原始资源和已翻译的资源。 驱动程序应在设备扩展中保存每个资源列表作为调试辅助。

资源列表 CM_RESOURCE_LIST 结构配对,其中原始列表的每个元素对应于已翻译列表的同一元素。 例如,如果 AllocatedResources.List[0] 描述原始 I/O 端口范围,则 AllocatedResourcesTranslated.List[0] 描述转换后的相同范围。 每个已翻译的资源都包含一个物理地址和资源类型。

如果为驱动程序分配了已转换的内存资源 (CmResourceTypeMemory) ,则必须调用 MmMapIoSpace 将物理地址映射到可访问设备寄存器的虚拟地址。 要使驱动程序以独立于平台的方式运行,它应检查每个返回的已翻译资源,并在必要时对其进行映射。

功能驱动程序应执行以下操作以响应 IRP_MN_START_DEVICE ,以确保访问所有设备资源:

  • 将 IrpSp->Parameters.StartDevice.AllocatedResources 复制到设备扩展;
  • 将 IrpSp->Parameters.StartDevice.AllocatedResourcesTranslated 复制到设备扩展;
  • 在循环中,检查 AllocatedResourcesTranslated 中的每个描述符元素。 如果描述符资源类型为 CmResourceTypeMemory,请调用 MmMapIoSpace,传递已转换资源的物理地址和长度;

当驱动程序收到 IRP_MN_STOP_DEVICE、 IRP_MN_REMOVE_DEVICE或 IRP_MN_SURPRISE_REMOVAL 请求时,它必须通过在类似的循环中调用 MmUnmapIoSpace 来释放映射。 如果驱动程序必须使IRP_MN_START_DEVICE请求失败,则驱动程序还应调用 MmUnmapIoSpace。

功能设备的删除

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

功能驱动程序在其 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发送到父总线设备之前,已从子设备中删除任何功能和Filter驱动程序, 确保它们的FDO 和筛选器DO 已被删除。 子 PDO 可能仍然存在,因此总线驱动程序必须先删除子 PDO,然后才能删除总线设备。

2. 驱动程序是否已处理此 FDO 的先前 IRP_MN_SURPRISE_REMOVAL 请求?如果是这样,请执行任何剩余的清理,并跳到步骤 8。驱动程序通常在设备扩展中维护一个标志,指示驱动程序是否已处理设备的 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由父总线驱动程序完成。