WDF驱动开发-PNP和电源管理(二)

下面是一种比较典型的 PnP 和电源管理方案,WDF框架在这些情况下调用驱动程序的事件回调函数:

  • 用户插入设备
  • 用户拔出设备
  • 设备进入低功耗状态
  • 设备回到工作状态
  • PnP 管理器重新分发系统资源

这些几乎是所有的和PNP、电源管理相关的场景了,它们分别诠释了设备在各种情况下比较典型的用户场景,接下来会依次分析这些场景下WDF框架的行为。

用户插入设备

在以下方案中,设备节点包括 KMDF 总线驱动程序和支持 PnP 设备的一个或多个 KMDF 函数或Filter驱动程序。

当用户在系统运行时将设备插入总线时,设备的总线驱动程序和框架将执行以下任务:

  • 设备的总线驱动程序检测设备并调用 WdfChildListAddOrUpdateChildDescriptionAsPresent (此过程称为“动态枚举”);
  • 框架调用总线驱动程序的 EvtChildListCreateDevice 回调函数,因此总线驱动程序可以调用 WdfDeviceCreate 为物理设备创建框架设备对象 (PDO) ;
  • 框架调用总线驱动程序的 EvtDeviceResourcesQuery 和 EvtDeviceResourceRequirementsQuery 回调函数来确定设备所需的系统硬件资源;

接下来,PnP 管理器确定哪些其他驱动程序 (功能驱动程序和Filter驱动程序) 设备所需的。 如果尚未加载这些驱动程序,PnP 管理器将加载它们并调用其 DriverEntry 例程。 对于每个函数或Filter驱动程序,将执行以下操作:

1. 框架调用每个附加驱动程序的 EvtDriverDeviceAdd 回调函数,以便驱动程序可以调用 WdfDeviceCreate 来创建表示驱动程序设备的框架设备对象。 功能驱动程序 (FDO) 创建功能设备对象,Filter驱动程序 (Filter DO) 创建Filter设备对象。

2. 框架调用每个函数和Filter驱动程序的 EvtDeviceFilterRemoveResourceRequirements 回调函数,然后调用每个驱动程序的 EvtDeviceFilterAddResourceRequirements 回调函数。 在设备启动之前,框架立即调用 EvtDeviceRemoveAddedResources 回调函数。 这三个回调函数允许Filter和功能驱动程序在 PnP 管理器将资源分配给设备之前修改设备所需的硬件资源列表。 

3. 框架确保设备已达到其工作 (D0) 电源状态。

4. 对于支持设备的每个函数和Filter驱动程序,框架依次执行以下操作,一次一个驱动程序,从驱动程序堆栈中最低的驱动程序开始:

  • 框架 (调用驱动程序的 EvtDevicePrepareHardware 回调函数(如果该函数存在) ),并传递 PnP 管理器分配给设备的硬件资源列表;
  • 框架调用驱动程序的 EvtDeviceD0Entry 回调函数 (如果它存在于) ;
  • 框架 调用驱动程序的 EvtInterruptEnable 回调函数(如果每个中断存在),然后调用驱动程序的 EvtDeviceD0EntryPostInterruptsEnabled 回调函数 (如果存在) ,则调用该驱动程序的 EvtDeviceD0EntryPostEnabled 回调函数。
  • 如果硬件和驱动程序支持 DMA,框架将调用驱动程序的 EvtDmaEnablerFill、 EvtDmaEnablerEnable 和 EvtDmaEnablerSelfManagedIoStart 回调函数 (如果它们存在) 创建的每个 DMA 通道
  • 框架调用驱动程序的 EvtChildListScanForChildren 回调函数 (如果该函数存在于) ;
  • 框架启动设备的所有电源托管 I/O 队列;
  • 如果驱动程序使用自托管 I/O,框架将调用驱动程序的 EvtDeviceSelfManagedIoInit 回调函数;
用户拔出设备

当系统运行时,用户可以通过以下两种方式之一删除设备:有 条不紊地删除设备,这意味着用户通知系统设备即将 (删除,例如使用拔出或弹出硬件程序) ;或 意外删除,这意味着用户在不通知系统的情况下拔下设备。 如果总线支持意外删除 (例如 USB) ,则设备的驱动程序必须能够处理设备的突然消失。

有序删除

用户通过使用系统的拔出或弹出硬件程序、通过使用设备管理器禁用设备或推送可弹出设备的弹出按钮来请求删除。 框架允许删除或禁用设备,除非驱动程序具有:

  • 名为 WdfDeviceSetSpecialFileSupport ,并在设备上打开一个特殊文件;
  • 名为 WdfDeviceSetStaticStopRemove;
  • 提供了 EvtDeviceQueryRemove 回调函数,并且回调函数已否决删除;

对于支持该设备的每个函数和Filter驱动程序,框架从驱动程序堆栈中最高的驱动程序开始,按顺序一次执行一个驱动程序的以下操作:

  • 如果驱动程序使用自托管 I/O,框架将调用驱动程序的 EvtDeviceSelfManagedIoSuspend 回调函数;
  • 框架会停止驱动程序的所有电源托管 I/O 队列;
  • 如果硬件和驱动程序支持 DMA,框架将驱动程序的 EvtDmaEnablerSelfManagedIoStop、 EvtDmaEnablerFlush 和 EvtDmaEnablerDisable 回调 函数 (如果它们存在) 创建的每个 DMA 通道;
  • 框架 (调用驱动程序的 EvtDeviceD0ExitPreInterruptsDisabled 回调函数(如果存在) ),然后调用驱动程序的 EvtInterruptDisable 回调函数 (如果存在每个中断) ,则驱动程序可以禁用设备中断;
  • 框架 调用驱动程序的 EvtDeviceD0Exit 回调函数(如果存在) ;
  • 框架调用驱动程序的 EvtDeviceReleaseHardware 回调函数 (如果它存在) ,则向其传递 PnP 管理器分配给设备的硬件资源列表;
  • 如果驱动程序使用自托管 I/O,框架将调用驱动程序的 EvtDeviceSelfManagedIoFlush 回调函数;
  • 如果驱动程序使用自托管 I/O,框架将调用驱动程序的 EvtDeviceSelfManagedIoCleanup 回调函数;

总线驱动程序是堆栈中最后调用的驱动程序。 当框架调用总线驱动程序的 EvtDeviceD0Exit 回调函数时,回调函数将设备的电源状态 (总线) 的子设备设置为 D3。 总线驱动程序可以通过调用 WdfDeviceInitSetReleaseHardwareOrderOnFailure 来控制框架何时调用其 EvtDeviceReleaseHardware 回调函数。

意外删除

用户意外拔下设备。 设备总线的总线驱动程序发现设备缺失,并调用 WdfChildListUpdateChildDescriptionAsMissing。

对于支持该设备的每个函数和Filter驱动程序,框架从驱动程序堆栈中最高的驱动程序开始,按顺序一次执行一个驱动程序的以下操作:

1. 框架调用驱动程序的 EvtDeviceSurpriseRemoval 回调函数 ((如果存在) )。

2. 如果设备处于工作状态 (D0) 拔出设备时的状态:

  • 框架会停止驱动程序的所有电源托管 I/O 队列;
  • 如果驱动程序使用自托管 I/O,框架将调用驱动程序的 EvtDeviceSelfManagedIoSuspend 回调函数;
  • 如果硬件和驱动程序支持 DMA,框架将驱动程序的 EvtDmaEnablerSelfManagedIoStop、 EvtDmaEnablerFlush 和 EvtDmaEnablerDisable 回调 函数 (如果它们存在) 创建的每个 DMA 通道;
  • 框架调用驱动程序的 EvtDeviceD0ExitPreInterruptsDisabled 和 EvtInterruptDisable 回调函数 (如果它们存在) ,以便驱动程序可以禁用设备中断;
  • 框架 调用驱动程序的 EvtDeviceD0Exit 回调函数(如果存在) ;

3. 框架调用驱动程序的 EvtDeviceReleaseHardware 回调函数 (如果它存在) ,将 PnP 管理器分配给设备的硬件资源列表传递。
4. 如果驱动程序使用自托管 I/O,框架将调用驱动程序的 EvtDeviceSelfManagedIoFlush 回调函数。
5. 如果驱动程序使用自托管 I/O,框架将调用驱动程序的 EvtDeviceSelfManagedIoCleanup 回调函数。
请注意,设备随时可能会意外删除。 因此,框架可能会在前面步骤中显示的时间调用驱动程序的 EvtDeviceSurpriseRemoval 回调函数。 例如,如果用户在设备进入低功耗状态时意外拔下电源,框架可能会在调用 EvtDeviceReleaseHardware 回调函数后调用 EvtDeviceSurpriseRemoval 回调函数。 不得以假定在特定序列中调用 EvtDeviceSurpriseRemoval 回调函数和其他回调函数的方式编写代码。

此外,框架不会将设备的 EvtDeviceSurpriseRemoval 回调函数与该设备前面步骤中列出的任何回调函数同步。 因此, EvtDeviceSurpriseRemoval 回调函数可能会运行,而前面列出的另一个回调函数也在运行。

设备进入低功耗状态

如果发生以下情况之一,设备将使其工作 (D0) 状态,并进入低功耗状态:

  • 设备 处于空闲状态 访问,并且能够在系统保持工作 (S0) 状态时进入低功耗空闲状态;
  • 系统的电源状态已从其工作 (S0) 状态更改为低功耗状态。 驱动程序可以调用 WdfDeviceGetSystemPowerAction 来确定系统电源状态更改的原因;

对于支持该设备的每个函数和筛选器驱动程序,框架从驱动程序堆栈中最高的驱动程序开始,按顺序一次执行一个驱动程序的以下操作:

  • 如果驱动程序使用自托管 I/O,框架将调用驱动程序的 EvtDeviceSelfManagedIoSuspend 回调函数;
  • 框架会停止驱动程序的所有电源托管 I/O 队列,并调用其 EvtIoStop 回调函数 (如果它们存在) ;
  • 如果驱动程序是设备的电源策略所有者,框架将调用其 EvtDeviceArmWakeFromS0、 EvtDeviceArmWakeFromSx 或 EvtDeviceArmWakeFromSxWithReason 回调函数;
  • 如果硬件和驱动程序支持 DMA,框架会调用驱动程序的 EvtDmaEnablerSelfManagedIoStop、 EvtDmaEnablerFlush 和 EvtDmaEnablerDisable 回调 函数 (如果它们存在) 创建的每个 DMA 通道;
  • 框架 调用驱动程序的 EvtDeviceD0ExitPreInterruptsDisabled 回调函数(如果存在) ,然后调用驱动程序的 EvtInterruptDisable 回调函数 (如果存在每个中断) ,以便驱动程序可以禁用设备中断;
  • 框架 调用驱动程序的 EvtDeviceD0Exit 回调函数(如果存在) ;

总线驱动程序是堆栈中最后调用的驱动程序。 当框架调用总线驱动程序的 EvtDeviceD0Exit 回调函数时,回调函数将设备的电源状态 (总线) 的子设备设置为低功耗状态。 框架指定 D3 低功耗状态,除非电源策略所有者指定了不同的低功耗状态。

注意:总线驱动程序用于设置子设备的电源状态的方法特定于总线。 例如,PCI 总线电源管理规范定义 16 位电源管理控制/状态寄存器 (PMCSR) 。 最低两位 (“PowerState”) 都决定设备的当前电源状态,并用于将设备设置为新的电源状态。 pci.sys当 PDO 收到IRP_MN_SET_POWER/D3 时,它会读取 PMCSR,将 PowerState 位更改为 11b (功率级别 D3) ,并写回 PMCSR。

设备回到工作状态

如果发生以下情况之一,处于低功耗状态的设备将返回到其工作状态:

  • 设备检测到外部事件并在其总线上触发唤醒信号。 检测唤醒信号的总线驱动程序调用 WdfDeviceIndicateWakeStatus。 因此,框架调用总线驱动程序的 EvtDeviceDisableWakeAtBus 回调函数;
  • 设备处于空闲状态,驱动程序调用 WdfDeviceStopIdle;
  • 系统的电源状态已从低功率状态更改为其工作 (S0) 状态;

在上述每种情况下,框架都会调用总线驱动程序的 EvtDeviceD0Entry 回调函数,然后将设备 (总线) 的子设备还原到其工作 (D0) 状态。

对于支持设备的每个函数和Filter驱动程序,框架依次执行以下操作,一次一个驱动程序,从驱动程序堆栈中最低的驱动程序开始:

  • 框架调用驱动程序的 EvtDeviceD0Entry 回调函数 (如果它存在于) 。
  • 框架 调用驱动程序的 EvtInterruptEnable 回调函数(如果每个中断存在) ,然后调用驱动程序的 EvtDeviceD0EntryPostInterruptsEnabled 回调函数 (如果它存在) ,则调用该驱动程序的 EvtDeviceD0EntryPostEnabled 回调函数) 。
  • 如果硬件和驱动程序支持 DMA,框架将调用驱动程序的 EvtDmaEnablerFill、 EvtDmaEnablerEnable 和 EvtDmaEnablerSelfManagedIoStart 回调函数 (如果它们对于创建的每个 DMA 通道存在) ;
  • 如果驱动程序是设备的电源策略所有者,框架将调用其 EvtDeviceDisarmWakeFromS0 或 EvtDeviceDisarmWakeFromSx 回调函数';
  • 框架调用驱动程序的 EvtChildListScanForChildren 回调函数 (如果它存在于) ;
  • 框架会重启驱动程序的所有电源托管 I/O 队列,并在必要时 (调用其 EvtIoResume 回调函数) ;
  • 如果驱动程序使用自托管 I/O,框架将调用驱动程序的 EvtDeviceSelfManagedIoRestart 回调函数;
PnP 管理器重新分发系统资源

如果用户将设备添加到系统,并且设备需要 PnP 管理器已分配给另一个设备的系统资源,则 PnP 管理器会尝试重新分配资源。

在此过程中,PnP 管理器会停止设备并将其从工作 (D0) 状态中取出。 然后,它将新资源列表传递给设备,以便它们可以使用新资源重启。

重新分发资源时,如果设备的驱动程序之一具有:PnP 管理器不会更改设备的资源分配:

  • 名为 WdfDeviceSetSpecialFileSupport ,并在设备上打开一个特殊文件;
  • 名为 WdfDeviceSetStaticStopRemove;
  • 提供了一个 EvtDeviceQueryStop 回调函数,并且回调函数已否决了重新分配;
Power-Down序列

对于支持要停止的设备的每个函数和筛选器驱动程序,框架从驱动程序堆栈中最高的驱动程序开始,按顺序一次执行一个驱动程序的以下操作:

  • 如果驱动程序使用自托管 I/O,框架将调用驱动程序的 EvtDeviceSelfManagedIoSuspend 回调函数;
  • 框架停止设备的所有电源托管 I/O 队列;
  • 如果硬件和驱动程序支持 DMA,框架会针对创建的每个 DMA 通道调用驱动程序的 EvtDmaEnablerSelfManagedIoStop、 EvtDmaEnablerFlush 和 EvtDmaEnablerDisable 回调 函数;
  • 调用驱动程序的 EvtDeviceD0ExitPreInterruptsDisabled 和 EvtInterruptDisable 回调函数 (如果它们存在) ,以便驱动程序可以禁用设备中断;
  • 框架调用驱动程序的 EvtDeviceD0Exit 回调函数(如果存在) ;
  • 框架调用驱动程序的 EvtDeviceReleaseHardware 回调函数(如果该函数存在) ,传递 PnP 管理器分配给设备的硬件资源列表;

总线驱动程序是堆栈中最低的驱动程序,最后调用。 当框架调用总线驱动程序的 EvtDeviceD0Exit 回调函数时,它将句柄传递给表示设备的 PDO 的框架设备对象和 WdfPowerDeviceD3Final 的 TargetState 值。 总线驱动程序可以通过调用 WdfDeviceInitSetReleaseHardwareOrderOnFailure 来控制框架何时调用其 EvtDeviceReleaseHardware 回调函数。

Power-Up序列

第一个调用的驱动程序是总线驱动程序。 当框架调用总线驱动程序的 EvtDeviceD0Entry 回调函数时,回调函数会将设备 (总线) 的子设备还原到其工作 (D0) 状态。

对于支持该设备的每个函数和筛选器驱动程序,框架按顺序一次执行一个驱动程序,从驱动程序堆栈中最低的驱动程序开始:

  • 框架调用驱动程序的 EvtDevicePrepareHardware 回调函数(如果存在) ,并将 PnP 管理器分配给设备的硬件资源列表传递;
  • 框架调用驱动程序的 EvtDeviceD0Entry 回调函数 (如果存在) ;
  • 框架调用驱动程序的 EvtInterruptEnable 和 EvtDeviceD0EntryPostInterruptsEnabled 回调函数 (如果它们存在) ,以便驱动程序可以启用设备中断;
  • 如果硬件和驱动程序支持 DMA,框架会针对创建的每个 DMA 通道调用驱动程序的 EvtDmaEnablerFill、 EvtDmaEnablerEnable 和 EvtDmaEnablerSelfManagedIoStart 回调函数;
  • 框架调用驱动程序的 EvtChildListScanForChildren 回调函数 (如果存在) ;
  • 框架重启设备的所有电源托管 I/O 队列;
  • 如果驱动程序使用自我管理的 I/O,框架将调用驱动程序的 EvtDeviceSelfManagedIoRestart 回调函数;
  • 15
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值