KMDF以内部状态机形式支持集成的PNP和电源管理。事件关联到状态的转移,而驱动提供回调例程在状态改变时调用。
如果你熟悉WDM驱动,你可能记得在系统状态改变时,WDM驱动必须确定自己设备的正确电源状态并派发电源管理请求将设备在合适的时机更改状态。KMDF状态机自动处理系统电源事件到设备电源事件的转换。比如,KMDF通知驱动:
· 当系统休眠或睡眠时,将设备转移到低电源状态。
· 使能设备唤醒信号,如果设备空闲,系统在运行时可以触发它。
· 使能设备唤醒信号,系统在睡眠状态可以触发它。
KMDF自动提供设备父/子关系上的正确处理。如果父/子设备都断电了而子设备必须上电,KMDF自动将父设备上电然后将子设备上电。
为了管理空闲设备,KMDF状态机在设备空闲时会通知驱动把设备从工作状态中移除并放在指定的低电源状态,当有请求需要处理时将设备返回到工作状态。
为完成这些电源转换,驱动包含了一套回调例程。这些例程按定好的顺序调用,且每个例程都遵守“约定”,这样在调用驱动来完成操作时设备和系统都能够保证处于特定状态。这让驱动断电空闲设备容易得多。驱动简单地设置适当超时值和设备的低电源状态,并告诉KMDF这些参数;KMDF在正确时机调用驱动给设备断电。
此外,框架收到的但还没有投递给设备驱动的请求,会影响设备的电源状态。如果驱动配置电源管理队列,框架会在它投递请求给设备前自动回复设备电源。它也会自动停止和启动队列以响应PNP和电源事件。
最后,管理设备电源策略的驱动可以指定用户是否能同时控制设备空闲时的行为和设备唤醒系统的能力。所有驱动必须做的是在它初始化电源策略设置时指定合适的枚举值。KMDF通过WMI可以使用需要的属性页,由设备管理器显示。
为设备操作做准备,KMDF以固定流程调用驱动的回调例程。流程根据驱动在设备栈中的角色有所不同。
图表4说明了使设备进入完全工作状态过程中涉及到的FDO或filter DO回调函数,从图表最底下的设备插入状态开始。
图表 4. FDO和Filter DO的设备枚举和启动流程
长水平线标记启动设备的相关步骤。左边一列描述了步骤,右边一列列出了完成步骤用到的事件回调。
在图表最下方,设备还不在系统中。当用户插入设备时,KMDF开始调用驱动的EvtDriverDeviceAdd回调函数让驱动可以创建一个设备对象来表示设备。KMDF继续逐步按流程调用驱动的回调例程,直到设备可以工作。记住KMDF是按图表中从下往上的顺序调用回调函数的,所以EvtDeviceFilterRemoveResourceRequirements在EvtDeviceFilterAddResourceRequirements之前调用,等等。
如图表所示,如果设备停下重新平衡资源或者物理上存在但是不在工作状态,并不是所有的步骤都要重新进行。
图表5说明了使设备进入完全工作状态过程中涉及到的总线驱动(PDO)回调函数,从图表最底下的设备插入状态开始。
图表 5. PDO的设备添加/启动流程
直到相应设备从系统中物理移除了,KMDF才会物理上删除PDO。比如,如果用户在设备管理器中禁止了设备但是没有移除它,KMDF仍然保留其设备对象。所以,图表最下方的三个步骤只在PNP枚举过程中出现——也就是,在初始启动或者用户插入新设备时。
如果设备之前被禁止但是没有物理移除,KMDF从调用EvtDevicePrepareHardware回调函数开始。
KMDF从运行状态中移除设备有若干原因:
· 由于设备空闲或者系统进入睡眠状态,设备进入低电源状态。
· 重新平衡资源。
· 在用户请求有序的移除后,设备会移除。
· 响应用户在设备管理器的请求而禁止设备。
同枚举和上电一样,回调的流程依据驱动在设备管理中的角色。
图表6说明了跟FDO或filter DO的断电和移除相关的回调函数流程。流程从图表最上方,工作状态下(D0)的运行设备开始。
图表 6. FDO 和 Filter DO的设备断电和正常移除流程
如图表所示,KMDF断电和移除流程包含了调用相应“撤销”回调函数,和KMDF在设备准备运行过程中调用的函数正好相反。
图表7说明了跟PDO的断电和移除相关的回调函数流程。
图表 7. PDO的设备断电和正常移除流程
直到设备从系统物理移除,KMDF才物理上删除PDO。比如,如果用户在设备管理器中禁止了设备,或者使用安全移除硬件工具停止了设备但是没有物理上移除它,KMDF仍保留PDO。如果设备之后重新启用,KMDF使用同一个PDO并从EvtDevicePrepareHardware回调开始启动流程,如前图表5所示。
如果用户没有提示地移除了设备,简单地拔出设备而没有没有使用设备管理器或者安全移除硬件工具,设备就被认为是“意外移除了”。若此发生,KMDF用略微不同的移除流程。如果其他驱动在此设备上调用了IoInvalidateDeviceState,即使设备仍物理存在,也会根据意外移除流程进行。
在意外移除流程中,KMDF在调用其他任何回调之前先调用EvtDeviceSurpriseRemoval回调。当流程结束时,KMDF销毁设备对象。
所有可移除设备的驱动必须确保在关闭和启动过程中的回调函数能处理失败,特别是由于硬件移除造成的失败。试图访问硬件不应该无限期等待,应当设置超时或者看门狗定时器。
图表8说明意外移除的流程。
图表 8. 意外移除流程
如果设备移除时不是在工作状态,KMDF在EvtDeviceSurpriseRemoval之后立即调用EvtDeviceReleaseHardware事件回调。忽略中间的步骤,这些步骤已经在设备离开工作状态时执行过了。
WMI提供给驱动输出信息给其他组件的途径。驱动一般用WMI来:
· 允许用户模式应用查询和设置设备相关信息,比如超时参数。
· 允许有一定特权的管理员运行运程系统上的应用来控制设备。
支持WMI的驱动注册成信息的提供者,还注册一个或多个信息实例。每个WMI提供者都关联到一个特殊的全局唯一标示(GUID)。其他组件可以注册相同的GUID从实例获取数据。用户模式组件请求WMI实例数据要调用COM函数,系统将其转换为IRP_MJ_SYSTEM_CONTROL请求并发送到目标提供者。
KMDF通过WMI请求处理程序支持WMI请求,提供给驱动以下特性:
· 默认的WMI处理。驱动不支持WMI数据就不需要注册为WMI数据提供者;KMDF处理所有的IRP_MJ_SYSTEM_CONTROL请求。
· 单独实例有自己的回调,而不是在设备对象水平上的回调,所以不同的实例可以进行不同的动作。
· 缓存大小的检查确保WMI请求的缓存大小符合相关提供者和实例的需要。
默认WMI处理包括了支持设备管理器的电源管理项的复选框。这些复选框允许用户来控制设备是否能唤醒系统以及系统是否能在设备空闲时将其断电。WDM驱动必须包含代码来支持对应这些复选框的WMI控制,但是KMDF驱动却不需要了。如果驱动允许电源策略选项的这些特性,那么KMDF自动处理这些请求。
当配置WMI提供者对象(WDFWMIPROVIDER)时,驱动允许缓存大小的检查。在WDF_WMI_PROVIDER_CONFIG结果中,驱动可以指定提供给EvtWmiInstanceQueryInstance和EvtWmiInstanceSetInstance回调函数需要的最小缓存大小。如果驱动指定了这样的参数,KMDF会在收到IRP_MJ_SYSTEM_CONTROL请求时检查缓存大小,并只在提供的缓存足够大时才调用回调。如果驱动没有配置缓存大小——因为实例大小是动态的或者在创建提供者时不知道其大小——驱动应当设置为零并且应由回调函数检查缓存大小。
当KMDF收到了目标是KMDF驱动的IRP_MJ_SYSTEM_CONTROL请求时,它如下处理:
· 如果驱动注册为WMI提供者并注册了一个或多个实例,那么WMI处理程序为实例调用合适的回调函数。
· 如果驱动没有注册任何WMI实例,WMI处理程序这样响应请求,提供请求的数据(如果可以的话),传递请求至下一层驱动,或者请求失败。
像所有KMDF对象一样,WMI实例对象(WDFWMIINSTANCE)有小下文域。驱动可以把WDFWMIINSTANCE对象的上下文域当做只读数据源,那么就可以最轻松地获取数据。驱动可以在创建后的任何时候删除WDFWMIINSTANCE对象。
WMI回调函数不跟设备的PNP和电源管理状态同步。所以,当WMI事件发生时,即使设备不在工作状态,KMDF也会调用驱动的WMI回调函数。