diff options
| author | Dave Wilson <[email protected]> | 2015-03-17 19:50:07 -0700 |
|---|---|---|
| committer | Dave Wilson <[email protected]> | 2015-03-17 19:50:07 -0700 |
| commit | 97cf5197cf5b882b2c689d8dc2b555f2edf8f418 (patch) | |
| tree | 46f3701832d70b420eb0fc0eb93261f9da45db3f /pofx/WDF/Driver/MultiComp/lib | |
| parent | ef1905bf1e8825bb31120dfb27e0daf3154d859a (diff) | |
Initial publish
Diffstat (limited to 'pofx/WDF/Driver/MultiComp/lib')
| -rw-r--r-- | pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj | 183 | ||||
| -rw-r--r-- | pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj.Filters | 35 | ||||
| -rw-r--r-- | pofx/WDF/Driver/MultiComp/lib/WdfPoFxPriv.h | 382 | ||||
| -rw-r--r-- | pofx/WDF/Driver/MultiComp/lib/component.c | 562 | ||||
| -rw-r--r-- | pofx/WDF/Driver/MultiComp/lib/device.c | 868 | ||||
| -rw-r--r-- | pofx/WDF/Driver/MultiComp/lib/init.c | 1336 | ||||
| -rw-r--r-- | pofx/WDF/Driver/MultiComp/lib/s0idle.c | 471 | ||||
| -rw-r--r-- | pofx/WDF/Driver/MultiComp/lib/s0idle.h | 113 |
8 files changed, 3950 insertions, 0 deletions
diff --git a/pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj b/pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj new file mode 100644 index 00000000..ec187a39 --- /dev/null +++ b/pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj @@ -0,0 +1,183 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{21F832DD-6A4C-4E5F-941E-15BF98CE4137}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{568CF874-EF69-46EF-AAE1-E59FAE847FBA}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>StaticLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>StaticLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>StaticLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>StaticLibrary</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems"> + <ClCompile Include="init.c; device.c; component.c; s0idle.c"> + <WppEnabled>true</WppEnabled> + <WppKernelMode>true</WppKernelMode> + <WppScanConfigurationData>WdfPoFxPriv.h</WppScanConfigurationData> + </ClCompile> + </ItemGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>WdfPoFx</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>WdfPoFx</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>WdfPoFx</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>WdfPoFx</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj.Filters b/pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj.Filters new file mode 100644 index 00000000..50376050 --- /dev/null +++ b/pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj.Filters @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{A155D773-8FFA-4E28-9C42-A363BDD03866}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{7CBF3FE5-FDC1-4483-B02B-EDCEEAF60743}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{D4BDD463-7BCE-4779-856D-4D42D6FBB5E1}</UniqueIdentifier> + </Filter> + <Filter Include="Driver Files"> + <Extensions>inf;inv;inx;mof;mc;</Extensions> + <UniqueIdentifier>{29D299CC-0D9F-4BB4-8E92-8A1E35E7A85E}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="component.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="device.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="init.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="s0idle.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/pofx/WDF/Driver/MultiComp/lib/WdfPoFxPriv.h b/pofx/WDF/Driver/MultiComp/lib/WdfPoFxPriv.h new file mode 100644 index 00000000..6e294572 --- /dev/null +++ b/pofx/WDF/Driver/MultiComp/lib/WdfPoFxPriv.h @@ -0,0 +1,382 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + WdfPoFxPriv.h + +Abstract: + Private header file for the power framework helper library. Contains + structures and function headers that are used by various source files in the + library. + +Environment: + + Kernel mode + +--*/ +#if !defined(_WDFPOFXPRIV_H_) +#define _WDFPOFXPRIV_H_ + +#include <ntddk.h> +#include <ntintsafe.h> +#include "WdfPoFx.h" + +#define DO_NOTHING() (0) + +// +// The following structure is used to capture the driver layer's settings for a +// device +// +typedef struct _HELPER_DEVICE_INIT { + // + // KMDF callbacks registered by the driver layer. The power framework helper + // library invokes these callbacks when KMDF invokes that corresponding + // callback that the library has registered. + // + PFN_WDF_DEVICE_SELF_MANAGED_IO_INIT EvtDeviceSelfManagedIoInit; + PFN_WDF_DEVICE_SELF_MANAGED_IO_FLUSH EvtDeviceSelfManagedIoFlush; + PFN_WDF_DEVICE_SELF_MANAGED_IO_RESTART EvtDeviceSelfManagedIoRestart; + PFN_WDF_DEVICE_D0_ENTRY EvtDeviceD0Entry; + + // + // Callbacks that notify the driver layer about registration and + // unregistration with the power framework. The power framework helper + // library invokes these callbacks after registering with the power + // framework and before unregistering from the power framework. + // + PPFH_CALLBACK_POHANDLE_AVAILABLE PfhCallbackPoHandleAvailable; + PPFH_CALLBACK_POHANDLE_UNAVAILABLE PfhCallbackPoHandleUnavailable; + + // + // The driver layer's WDM pre-process callback for power IRPs. The power + // framework helper library invokes this callback when KMDF invokes the + // corresponding callback that the library has registered. + // + PFN_WDFDEVICE_WDM_IRP_PREPROCESS EvtDeviceWdmPowerIrpPreprocess; + + // + // Memory object containing the array of power IRP minor functions that the + // driver layer is interested in preprocessing. + // + WDFMEMORY PowerIrpPreprocessMinorFunctions; + + // + // The following members track the status of the initialization performed by + // the driver layer for a given device. They are used in verification. + // + BOOLEAN PnpPowerEventCallbacksIntercepted; + BOOLEAN PoHandleAvailabilityCallbacksSet; + BOOLEAN PowerIrpPreprocessCallbackAssigned; +#if PFH_S0IDLE_SUPPORTED + BOOLEAN S0IdleConfigSet; + + // + // The driver's S0-idle configuration + // + PFH_S0IDLE_CONFIG S0IdleConfig; +#endif + +} HELPER_DEVICE_INIT, *PHELPER_DEVICE_INIT; + +// +// The following structure is used to capture the driver layer's settings for a +// component queue +// +typedef struct _HELPER_QUEUE_INIT { + // + // Component number of the component that the queue is associated with + // + ULONG Component; + + // + // The driver layer's EvtIoCanceledOnQueue callback for the component queue. + // The power framework helper library invokes this callback when KMDF + // invokes the corresponding callback that the library has registered. + // + PFN_WDF_IO_QUEUE_IO_CANCELED_ON_QUEUE EvtIoCanceledOnQueue; + + // + // The following members track the status of the initialization performed by + // the driver layer for a given queue. They are used in verification. + // + BOOLEAN ComponentQueueConfigIntercepted; + BOOLEAN ComponentSet; +} HELPER_QUEUE_INIT, *PHELPER_QUEUE_INIT; + +// +// Enumeration of KMDF objects that can be initialized using the power framework +// helper library's initializer object. +// +typedef enum _HELPER_INIT_TYPE { + HelperInitTypeNone, + HelperInitTypeDevice, + HelperInitTypeQueue, +} HELPER_INIT_TYPE, *PHELPER_INIT_TYPE; + +// +// The following structure represents the object context space for the power +// framework helper library's initializer object. +// +typedef struct _HELPER_INIT { + // + // Type of KMDF object being initialized using the power framework helper + // library's initializer object. + // + HELPER_INIT_TYPE InitType; + union { + // + // Driver layer's device initialization settings + // + HELPER_DEVICE_INIT DeviceInit; + + // + // Driver layer's queue initialization settings + // + HELPER_QUEUE_INIT QueueInit; + } u; +} HELPER_INIT, *PHELPER_INIT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(HELPER_INIT, + HelperGetInitContext) + +// +// The following structure represents the array of power IRP minor functions +// that the driver layer is interested in preprocessing +// +#pragma warning(push) +#pragma warning(disable:4200) +typedef struct _POFX_DRIVER_LAYER_POWER_IRP_PREPROCESS_INFO { + // + // Number of power IRP minor functions that the driver layer is interested + // in preprocessing + // + ULONG NumMinorFunctions; + + // + // Array of power IRP minor functions that the driver layer is interested in + // preprocessing + // + UCHAR MinorFunctions[]; +} POFX_DRIVER_LAYER_POWER_IRP_PREPROCESS_INFO, + *PPOFX_DRIVER_LAYER_POWER_IRP_PREPROCESS_INFO; +#pragma warning(pop) + +// +// Per-component information maintained by the power framework helper library +// +typedef struct _POFX_COMPONENT_INFO { + // + // Queue associated with the component + // + WDFQUEUE Queue; + + // + // Whether or not the component is active + // + BOOLEAN IsActive; +} POFX_COMPONENT_INFO, *PPOFX_COMPONENT_INFO; + +// +// The following structure represents the driver layer's power framework +// callbacks. The power framework helper library invokes these callbacks when +// the power framework invokes the corresponding callbacks that the library has +// registered. +// +typedef struct _POFX_DRIVER_LAYER_POWER_CALLBACKS { + PPO_FX_COMPONENT_IDLE_STATE_CALLBACK ComponentIdleStateCallback; + PPO_FX_COMPONENT_ACTIVE_CONDITION_CALLBACK ComponentActiveConditionCallback; + PPO_FX_COMPONENT_IDLE_CONDITION_CALLBACK ComponentIdleConditionCallback; + PPO_FX_DEVICE_POWER_REQUIRED_CALLBACK DevicePowerRequiredCallback; + PPO_FX_DEVICE_POWER_NOT_REQUIRED_CALLBACK DevicePowerNotRequiredCallback; +} POFX_DRIVER_LAYER_POWER_CALLBACKS, *PPOFX_DRIVER_LAYER_POWER_CALLBACKS; + +// +// The following structure represents the power framework helper library's +// device context space. +// +typedef struct _POFX_DEVICE_CONTEXT { + // + // Pointer to the power framework settings registered by the power framework + // helper library + // + PPO_FX_DEVICE PoFxDeviceInfo; + + // + // Pointer to the driver layer's power framework callbacks + // + POFX_DRIVER_LAYER_POWER_CALLBACKS DriverLayerPoFxCallbacks; + + // + // Pointer to the driver layer's context for power framework callbacks + // + PVOID DriverLayerPoFxContext; + + // + // Pointer to the information that is tracked by the power framework helper + // library for each component + // + PPOFX_COMPONENT_INFO ComponentInfo; + + // + // POHANDLE representing the registration with the power framework + // + POHANDLE PoHandle; + + // + // Array of power IRP minor functions that the driver layer is interested in + // preprocessing. + // + PPOFX_DRIVER_LAYER_POWER_IRP_PREPROCESS_INFO + DriverLayerPowerIrpPreprocessInfo; + + // + // The driver layer's settings for the device + // + HELPER_DEVICE_INIT DeviceInitSettings; + + // + // Whether or not we should call PoFxReportDevicePoweredOn from our next + // EvtDeviceD0Entry callback + // + BOOLEAN ShouldReportDevicePoweredOn; + +#if PFH_S0IDLE_SUPPORTED + // + // Whether or not we invoked WdfDeviceStopIdle during device start so that + // the device remains in D0 until the power framework permits it to go to Dx + // + BOOLEAN StopIdleInvokedOnDeviceStart; + + // + // Work item that is queued in response to device-power-required callback. + // When the work item callback is invoked, we make a blocking call to + // WdfDeviceStopIdle in order to bring the device to D0. This blocking call + // cannot be made within the device-power-required callback because it can + // be invoked at dispatch level. Therefore, we make this call at passive + // level from a work item callback. + // + WDFWORKITEM PowerRequiredWorkItem; +#endif + +} POFX_DEVICE_CONTEXT, *PPOFX_DEVICE_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(POFX_DEVICE_CONTEXT, HelperGetDeviceContext) + +// +// The following structure represents the power framework helper library's +// component queue context space. +// +typedef struct _POFX_QUEUE_CONTEXT { + // + // The driver layer's settings for the component queue + // + HELPER_QUEUE_INIT QueueInitSettings; +} POFX_QUEUE_CONTEXT, *PPOFX_QUEUE_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(POFX_QUEUE_CONTEXT, HelperGetQueueContext) + +// +// Power framework helper library's KMDF callbacks +// +EVT_WDF_DEVICE_SELF_MANAGED_IO_INIT _PfhEvtSelfManagedIoInit; +EVT_WDF_DEVICE_SELF_MANAGED_IO_FLUSH _PfhEvtSelfManagedIoFlush; +EVT_WDF_DEVICE_SELF_MANAGED_IO_RESTART _PfhEvtSelfManagedIoRestart; + +EVT_WDF_DEVICE_D0_ENTRY _PfhEvtD0Entry; + +EVT_WDFDEVICE_WDM_IRP_PREPROCESS _PfhEvtWdmPowerIrpPreprocess; + +EVT_WDF_IO_QUEUE_IO_CANCELED_ON_QUEUE _PfhEvtRequestCanceledOnComponentQueue; + +// +// Power framework helper library's power framework callbacks +// +PO_FX_COMPONENT_IDLE_STATE_CALLBACK _PfhComponentIdleStateCallback; +PO_FX_COMPONENT_ACTIVE_CONDITION_CALLBACK _PfhComponentActiveConditionCallback; +PO_FX_COMPONENT_IDLE_CONDITION_CALLBACK _PfhComponentIdleConditionCallback; +PO_FX_DEVICE_POWER_REQUIRED_CALLBACK _PfhDevicePowerRequiredCallback; +PO_FX_DEVICE_POWER_NOT_REQUIRED_CALLBACK _PfhDevicePowerNotRequiredCallback; + +// +// Routines used by source files within the power framework helper library +// +PHELPER_DEVICE_INIT +GetDeviceInitSettings( + _In_ WDFOBJECT Initializer + ); + +VOID +UnregisterWithPowerFrameworkWorker( + _In_ WDFDEVICE Device + ); + +BOOLEAN +IsDeviceInitialized( + _In_ WDFDEVICE Device + ); + +BOOLEAN +IsQueueInitialized( + _In_ WDFQUEUE Queue + ); + +BOOLEAN +ArePowerFrameworkSettingsAvailable( + _In_ WDFDEVICE Device + ); + +NTSTATUS +CopyAndUpdateMinorFunctionsArray( + _In_ WDFOBJECT Initializer, + _In_reads_opt_(NumMinorFunctions) PUCHAR MinorFunctions, + _In_ ULONG NumMinorFunctions, + _Out_ WDFMEMORY * MinorFunctionsMemory, + _Outptr_result_buffer_(*UpdatedNumMinorFunctions) PUCHAR *UpdatedMinorFunctions, + _Out_ PULONG UpdatedNumMinorFunctions + ); + +NTSTATUS +CopyMinorFunctionsArray( + _In_ WDFDEVICE Device, + _In_ WDFMEMORY SourceMemory, + _Out_ WDFMEMORY * DestinationMemory + ); + +#include "s0idle.h" + +// +// Define the tracing flags. +// +// The (0,0,0,0,0) GUID below is a dummy. The driver layer's GUID is the one +// that gets used. +// +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID( \ + WdfPoFxTraceControl, (0,0,0,0,0), \ + WPP_DEFINE_BIT(WDFPOFX_ALL_INFO) \ + ) + +#define WPP_FLAG_LEVEL_LOGGER(flag, level) \ + WPP_LEVEL_LOGGER(flag) + +#define WPP_FLAG_LEVEL_ENABLED(flag, level) \ + (WPP_LEVEL_ENABLED(flag) && \ + WPP_CONTROL(WPP_BIT_ ## flag).Level >= level) + +// +// This comment block is scanned by the trace preprocessor to define our +// Trace function. +// +// begin_wpp config +// FUNC Trace{FLAG=WDFPOFX_ALL_INFO}(LEVEL, MSG, ...); +// end_wpp +// + +#endif // _WDFPOFXPRIV_H_ diff --git a/pofx/WDF/Driver/MultiComp/lib/component.c b/pofx/WDF/Driver/MultiComp/lib/component.c new file mode 100644 index 00000000..b0e55f1f --- /dev/null +++ b/pofx/WDF/Driver/MultiComp/lib/component.c @@ -0,0 +1,562 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + component.c + +Abstract: + This module contains routines that implement power management for a + component of the device + +Environment: + + Kernel mode + +--*/ + +#include "WdfPoFxPriv.h" +#include "component.tmh" + +VOID +AcquirePowerReferenceForRequest( + _In_ WDFQUEUE Queue + ) +/*++ +Routine description: + This routine acquires a power reference on behalf of a request, if the + request is being forwarded to a component queue. If the request is not being + forwarded to a component queue, then this routine does nothing. + +Arguments: + Queue - Handle to the KMDF queue object to which the request is being + forwarded + +Return value: + None +--*/ +{ + PPOFX_QUEUE_CONTEXT qCtx = NULL; + WDFDEVICE device = NULL; + PPOFX_DEVICE_CONTEXT devCtx = NULL; + ULONG component; + + // + // Get the queue context + // + qCtx = HelperGetQueueContext(Queue); + if (NULL == qCtx) { + // + // The request is not being forwarded to a component queue. So there is + // no need to take a power reference at this time. + // + return; + } + + // + // Get the component on which the power reference needs to be taken + // + component = qCtx->QueueInitSettings.Component; + + // + // Get the device that the queue is associated with + // + device = WdfIoQueueGetDevice(Queue); + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(device); + + // + // Take the power reference + // + PoFxActivateComponent(devCtx->PoHandle, component, 0 /* Flags */); + return; +} + +VOID +ReleasePowerReferenceForRequest( + _In_ WDFREQUEST Request + ) +/*++ +Routine description: + This routine releases a power reference on behalf of a request, if a power + reference had been taken previously. If no power reference had been taken + previously for this request, then this routine does nothing. + +Arguments: + Request - Handle to the KMDF request object on behalf of which the power + reference needs to be released + +Return value: + None +--*/ +{ + WDFQUEUE queue = NULL; + PPOFX_QUEUE_CONTEXT qCtx = NULL; + WDFDEVICE device = NULL; + PPOFX_DEVICE_CONTEXT devCtx = NULL; + ULONG component; + + // + // Get the queue that the request is associated with + // + queue = WdfRequestGetIoQueue(Request); + if (NULL == queue) { + return; + } + + // + // Get the queue context + // + qCtx = HelperGetQueueContext(queue); + if (NULL == qCtx) { + // + // The request is currently not associated with a component queue. So + // no power reference has been taken on behalf of the request and hence + // there is no power reference to release at this time. + // + return; + } + + // + // Get the component on which the power reference has been taken on behalf + // of this request + // + component = qCtx->QueueInitSettings.Component; + + // + // Get the device that the queue is associated with + // + device = WdfIoQueueGetDevice(queue); + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(device); + + // + // Release the power reference that was previously taken + // + PoFxIdleComponent(devCtx->PoHandle, component, 0 /* Flags */); + + return; +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +PfhForwardRequestToQueue( + _In_ WDFREQUEST Request, + _In_ WDFQUEUE Queue + ) +// See comments in WdfPoFx.h +{ + NTSTATUS status; + + // + // If the request currently has a power reference on a component, then + // release the power reference. + // + ReleasePowerReferenceForRequest(Request); + + // + // If the destination queue requires a power reference on a component, then + // take the power reference now. + // + AcquirePowerReferenceForRequest(Queue); + + // + // Queue the request to the caller-specified queue + // + status = WdfRequestForwardToIoQueue(Request, Queue); + if (FALSE == NT_SUCCESS(status)) { + // + // If the queue has not been purged or drained, forwarding a request to + // a queue should always succeeded. + // + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfRequestForwardToIoQueue failed with %!status!.", + status); + WdfVerifierDbgBreakPoint(); + } + + return; +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +PfhCompleteRequest( + _In_ WDFREQUEST Request, + _In_ NTSTATUS Status, + _In_ ULONG_PTR Information + ) +// See comments in WdfPoFx.h +{ + // + // If the request currently has a power reference on a component, then + // release the power reference. + // + ReleasePowerReferenceForRequest(Request); + + // + // Complete the request + // + WdfRequestCompleteWithInformation(Request, Status, Information); +} + +VOID +_PfhEvtRequestCanceledOnComponentQueue( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request + ) +/*++ +Routine Description: + + This routine is invoked when an IO request is canceled while it is in a + component queue + +Arguments: + + Queue - Handle to the framework queue object for the component queue + + Request - Handle to the framework request object for the request being + canceled + +Return Value: + + None + +--*/ +{ + PPOFX_QUEUE_CONTEXT qCtx = NULL; + PFN_WDF_IO_QUEUE_IO_CANCELED_ON_QUEUE evtIoCanceledOnQueue; + + if (FALSE == IsQueueInitialized(Queue)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInitializeComponentQueueSettings has not yet been " + "called for WDFQUEUE %p.", + Queue); + WdfVerifierDbgBreakPoint(); + } + + // + // Get the queue context + // + qCtx = HelperGetQueueContext(Queue); + + evtIoCanceledOnQueue = qCtx->QueueInitSettings.EvtIoCanceledOnQueue; + if (NULL != evtIoCanceledOnQueue) { + // + // The driver layer supplied a canceled-on-queue callback, so invoke it + // + evtIoCanceledOnQueue(Queue, Request); + + } else { + // + // Just complete the request + // + PfhCompleteRequest(Request, STATUS_CANCELLED, 0 /* Information */); + } + return; +} + +VOID +ComponentActive( + _In_ PPOFX_DEVICE_CONTEXT DevCtx, + _In_ ULONG Component + ) +/*++ +Routine Description: + + In this routine, we perform operations that are needed when a component + becomes active. + +Arguments: + + DevCtx - Pointer to our context space for the device object + + Component - The component that has become active + +Return Value: + + None + +--*/ +{ + // + // Mark the component as active + // + DevCtx->ComponentInfo[Component].IsActive = TRUE; + + if (NULL != DevCtx->ComponentInfo[Component].Queue) { + // + // Start the component-specific queue + // + WdfIoQueueStart(DevCtx->ComponentInfo[Component].Queue); + } + + return; +} + +VOID +ComponentIdle( + _In_ PPOFX_DEVICE_CONTEXT DevCtx, + _In_ ULONG Component + ) +/*++ +Routine Description: + + In this routine, we perform operations that are needed when a component + becomes idle. + +Arguments: + + DevCtx - Pointer to our context space for the device object + + Component - The component that has become idle + +Return Value: + + None + +--*/ +{ + ULONG queueRequests; + + // + // Mark the component as idle + // + DevCtx->ComponentInfo[Component].IsActive = FALSE; + + if (NULL != DevCtx->ComponentInfo[Component].Queue) { + // + // NOTE: Given that the component is idle, there shouldn't be any + // outstanding requests sitting in the component queue. There may be + // some driver-owned requests, but they would all have dropped their + // power references by now and would be about to complete. We also know + // that no new requests will get added to the queue while this callback + // is running. The reason is described below. + // + // We do not add any new request to this queue until the + // PoFxActivateComponent call for that request has returned. If + // PoFxActivateComponent is called on another thread while this callback + // is running or about to run, then the power framework makes sure + // that PoFxActivateComponent does not return until this callback has + // finished running. Therefore we know that no new request would get + // added to this queue while we are attempting to stop the queue + // within this callback. + // + WdfIoQueueGetState(DevCtx->ComponentInfo[Component].Queue, + &queueRequests, + NULL /* DriverRequests */); + if (0 != queueRequests) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Expected the queue for component %d to have no " + "requests, but there are currently %d requests in the queue.", + Component, + queueRequests); + WdfVerifierDbgBreakPoint(); + } + + // + // Stop the component-specific queue. + // + // No need for synchronous stop. Given that the component is idle, + // there anyway shouldn't be any outstanding requests in sitting in + // the queue. As explained above, there can be up to one outstanding + // request that is delivered to the driver layer, but that would + // also be just about to complete. We stop this queue only to + // prevent new requests that get added to the queue from being + // dispatched to the driver layer until the component becomes active + // again. + // + WdfIoQueueStop(DevCtx->ComponentInfo[Component].Queue, + NULL, // StopComplete + NULL // Context + ); + } + + return; +} + +VOID +_PfhComponentActiveConditionCallback( + _In_ PVOID Context, + _In_ ULONG Component + ) +/*++ +Routine Description: + + The power framework invokes this routine to notify us that one of our + components has become active. + +Arguments: + + Context - Context that we passed in to the power framework + + Component - Index of component that has become active or idle + +Return Value: + + None + +--*/ +{ + WDFDEVICE device = NULL; + PPOFX_DEVICE_CONTEXT devCtx = NULL; + PPO_FX_COMPONENT_ACTIVE_CONDITION_CALLBACK + componentActiveConditionCallback = NULL; + + // + // Get the handle to the framework device object + // + device = (WDFDEVICE) Context; + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(device); + + // + // Component is active + // + ComponentActive(devCtx, Component); + + // + // If the driver layer supplied a component-active-condition callback, + // invoke it + // + componentActiveConditionCallback = + devCtx->DriverLayerPoFxCallbacks.ComponentActiveConditionCallback; + if (NULL != componentActiveConditionCallback) { + componentActiveConditionCallback(devCtx->DriverLayerPoFxContext, + Component); + } + + return; +} + +VOID +_PfhComponentIdleConditionCallback( + _In_ PVOID Context, + _In_ ULONG Component + ) +/*++ +Routine Description: + + The power framework invokes this routine to notify us that one of our + components has become idle. + +Arguments: + + Context - Context that we passed in to the power framework + + Component - Index of component that has become active or idle + +Return Value: + + None + +--*/ +{ + WDFDEVICE device = NULL; + PPOFX_DEVICE_CONTEXT devCtx = NULL; + PPO_FX_COMPONENT_IDLE_CONDITION_CALLBACK + componentIdleConditionCallback = NULL; + + // + // Get the handle to the framework device object + // + device = (WDFDEVICE) Context; + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(device); + + // + // Component is idle + // + ComponentIdle(devCtx, Component); + + // + // If the driver layer supplied a component-idle-condition callback, invoke + // it + // + componentIdleConditionCallback = + devCtx->DriverLayerPoFxCallbacks.ComponentIdleConditionCallback; + if (NULL != componentIdleConditionCallback) { + componentIdleConditionCallback(devCtx->DriverLayerPoFxContext, + Component); + } else { + // + // Complete the transition to idle + // + PoFxCompleteIdleCondition(devCtx->PoHandle, Component); + } + + return; +} + +VOID +_PfhComponentIdleStateCallback( + _In_ PVOID Context, + _In_ ULONG Component, + _In_ ULONG State + ) +/*++ +Routine Description: + + The power framework invokes this routine to change the F-state of one of our + components. + +Arguments: + + Context - Context that we passed in to the power framework + + Component - Index of component for which the F-state change is to be made + + State - The new F-state to transition the component to + +Return Value: + + None + +--*/ +{ + WDFDEVICE device = NULL; + PPOFX_DEVICE_CONTEXT devCtx = NULL; + PPO_FX_COMPONENT_IDLE_STATE_CALLBACK componentIdleStateCallback = NULL; + // + // Get the handle to the framework device object + // + device = (WDFDEVICE) Context; + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(device); + + // + // If the driver layer supplied a component-idle-state callback, invoke it + // + componentIdleStateCallback = + devCtx->DriverLayerPoFxCallbacks.ComponentIdleStateCallback; + if (NULL != componentIdleStateCallback) { + componentIdleStateCallback(devCtx->DriverLayerPoFxContext, + Component, + State); + } else { + // + // Complete the idle state transition + // + PoFxCompleteIdleState(devCtx->PoHandle, Component); + } +} diff --git a/pofx/WDF/Driver/MultiComp/lib/device.c b/pofx/WDF/Driver/MultiComp/lib/device.c new file mode 100644 index 00000000..aa17eaea --- /dev/null +++ b/pofx/WDF/Driver/MultiComp/lib/device.c @@ -0,0 +1,868 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + device.c + +Abstract: + This module contains routines that implement power management for the device + +Environment: + + Kernel mode + +--*/ + +#include "WdfPoFxPriv.h" +#include "device.tmh" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, _PfhEvtSelfManagedIoInit) +#pragma alloc_text(PAGE, _PfhEvtSelfManagedIoFlush) +#endif + +VOID +UnregisterWithPowerFrameworkWorker( + _In_ WDFDEVICE Device + ) +/*++ +Routine description: + This routine unregisters a device with the power framework. This routine + can be called in EvtDeviceSelfManagedioRestart, which can not be made + pageable, therefore this function should also not be pageable. + +Arguments: + Device - Handle to the KMDF device object + +Return value: + None +--*/ +{ + PPOFX_DEVICE_CONTEXT devCtx; + PPFH_CALLBACK_POHANDLE_UNAVAILABLE pfhCallbackPoHandleUnavailable; + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(Device); + + if (NULL == devCtx->PoHandle) { + // + // We didn't successfully register with the power framework, so nothing + // to do here. + // + return; + } + + // + // Notify the driver layer that the POHANDLE is about to be invalidated + // + pfhCallbackPoHandleUnavailable = + devCtx->DeviceInitSettings.PfhCallbackPoHandleUnavailable; + if (NULL != pfhCallbackPoHandleUnavailable) { + pfhCallbackPoHandleUnavailable(Device, devCtx->PoHandle); + } + + // + // Unregister with the power framework + // + PoFxUnregisterDevice(devCtx->PoHandle); + devCtx->PoHandle = NULL; + + return; +} + +NTSTATUS +RegisterWithPowerFrameworkWorker( + _In_ WDFDEVICE Device + ) +/*++ +Routine description: + This routine registers a device with the power framework. + +Arguments: + Device - Handle to the KMDF device object + +Return value: + An NTSTATUS value representing success or failure of the function. +--*/ +{ + NTSTATUS status; + PPOFX_DEVICE_CONTEXT devCtx; + PPFH_CALLBACK_POHANDLE_AVAILABLE pfhCallbackPoHandleAvailable; + ULONG i; + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(Device); + + if (NULL != devCtx->PoHandle) { + // + // Already registered with power framework, nothing to do here. + // + status = STATUS_SUCCESS; + goto exit; + } + + for (i=0; i < (devCtx->PoFxDeviceInfo->ComponentCount); i++) { + // + // Initially, all components are active + // + devCtx->ComponentInfo[i].IsActive = TRUE; + + // + // For PDOs, we unregister when the device is disabled and re-register + // when the device is re-enabled. Before re-registering, we need to + // start the component queues. They would all be in the purged state + // when the device was disabled. + // NOTE: It is okay to start an already-started queue, so we can handle + // first registration and re-registration in the same way. + // + if (NULL != devCtx->ComponentInfo[i].Queue) { + WdfIoQueueStart(devCtx->ComponentInfo[i].Queue); + } + } + + // + // Register with the power framework + // + status = PoFxRegisterDevice( + WdfDeviceWdmGetPhysicalDevice(Device), + devCtx->PoFxDeviceInfo, + &(devCtx->PoHandle) + ); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PoFxRegisterDevice failed with %!status!.", + status); + goto exit; + } + + // + // Notify the driver layer that the POHANDLE is available + // + pfhCallbackPoHandleAvailable = + devCtx->DeviceInitSettings.PfhCallbackPoHandleAvailable; + if (NULL != pfhCallbackPoHandleAvailable) { + status = pfhCallbackPoHandleAvailable(Device, devCtx->PoHandle); + if (FALSE == NT_SUCCESS(status)) { + goto exit; + } + } + + // + // Tell the power framework to start its power management + // + PoFxStartDevicePowerManagement(devCtx->PoHandle); + + status = STATUS_SUCCESS; + +exit: + if (FALSE == NT_SUCCESS(status)) { + // + // If an error occurred, we need to unregister with the power framework. + // Note: the function below handles the case where we haven't registered + // yet. + // + UnregisterWithPowerFrameworkWorker(Device); + } + return status; +} + +NTSTATUS +_PfhEvtSelfManagedIoInit( + _In_ WDFDEVICE Device + ) +/*++ +Routine Description: + + In this routine, we initialize self-managed I/O operations. + +Arguments: + + Device - Handle to the KMDF device object + +Return Value: + + An NTSTATUS value representing success or failure of the function. + +--*/ +{ + NTSTATUS status; + PPOFX_DEVICE_CONTEXT devCtx; + PFN_WDF_DEVICE_SELF_MANAGED_IO_INIT evtDeviceSelfManagedIoInit; + + PAGED_CODE(); + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(Device); + + // + // If the driver layer supplied a self-managed-IO-init callback, invoke it. + // We need to do this before checking if power framework settings are + // available so that the driver has the opportunity to specify power + // framework settings in the self-managed-IO-init callback. + // + evtDeviceSelfManagedIoInit = + devCtx->DeviceInitSettings.EvtDeviceSelfManagedIoInit; + if (NULL != evtDeviceSelfManagedIoInit) { + status = evtDeviceSelfManagedIoInit(Device); + if (FALSE == NT_SUCCESS(status)) { + goto exit; + } + } + + if (FALSE == ArePowerFrameworkSettingsAvailable(Device)) { + status = STATUS_INVALID_DEVICE_REQUEST; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInitializePowerFrameworkSettings has not yet " + "been called for WDFDEVICE %p. %!status!.", + Device, + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + // + // If S0-idle power management is supported, prevent the device from + // powering down to Dx due to S0-idle. We will allow the device to power + // down to Dx only after our device-power-not-required callback is invoked. + // + STOP_DEVICE_IDLE_ON_DEVICE_START(status, Device, devCtx, exit); + + // + // Register with the power framework. + // + // The EvtDeviceSelfManagedIoInit callback is called after the first D0 + // entry, but not after subsequent D0 entries. This means that it is not + // called after D0 entries that occur due to S0-idle, system resuming from + // sleep or resource rebalance. Therefore, we register with the power + // framework here. We retain the registration until the device is removed. + // + // Note: The function below handles the case where the driver layer already + // registered with the power framework by calling + // PfhRegisterDeviceProactive. + // + status = RegisterWithPowerFrameworkWorker(Device); + if (FALSE == NT_SUCCESS(status)) { + goto exit; + } + + status = STATUS_SUCCESS; + +exit: + return status; +} + +VOID +_PfhEvtSelfManagedIoFlush( + _In_ WDFDEVICE Device + ) +/*++ +Routine Description: + + In this routine, we flush self-managed I/O operations. + +Arguments: + + Device - Handle to the KMDF device object + +Return Value: + + None + +--*/ +{ + PPOFX_DEVICE_CONTEXT devCtx; + PFN_WDF_DEVICE_SELF_MANAGED_IO_FLUSH evtDeviceSelfManagedIoFlush; + + PAGED_CODE(); + + if (FALSE == ArePowerFrameworkSettingsAvailable(Device)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInitializePowerFrameworkSettings has not yet " + "been called for WDFDEVICE %p.", + Device); + WdfVerifierDbgBreakPoint(); + } + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(Device); + + if (devCtx->ShouldReportDevicePoweredOn) { + // + // We received an S0 IRP and we were supposed to call + // PoFxReportDevicePoweredOn after entering D0 in response to the S0 + // IRP. But an error occurred that prevented us from entering D0 and + // KMDF has initiated a device teardown as a result of this error. + // Before we attempt to unregister with the power framework, we should + // call PoFxReportDevicePoweredOn in order to bring the power framework + // to a consistent state. + // + devCtx->ShouldReportDevicePoweredOn = FALSE; + PoFxReportDevicePoweredOn(devCtx->PoHandle); + } + + // + // The EvtDeviceSelfManagedIoFlush callback is called when the device is + // being removed. In this callback, we unregister with the power framework. + // Note: the function below handles the case where we did not register + // successfully. + // + UnregisterWithPowerFrameworkWorker(Device); + + // + // Perform tasks specific to S0-idle power management (if enabled) + // + SELF_MANAGED_IO_FLUSH_FOR_S0_IDLE(devCtx); + + // + // If the driver layer supplied a self-managed-IO-flush callback, invoke it + // + evtDeviceSelfManagedIoFlush = + devCtx->DeviceInitSettings.EvtDeviceSelfManagedIoFlush; + if (NULL != evtDeviceSelfManagedIoFlush) { + evtDeviceSelfManagedIoFlush(Device); + } + + return; +} + +BOOLEAN +IsS0Irp( + _In_ PIRP Irp, + _In_ BOOLEAN StackLocationAdjusted + ) +/*++ +Routine description: + This routine determines whether the given IRP is an S0 IRP. + +Arguments: + Irp - Pointer to the IRP + + StackLocationAdjusted - A BOOLEAN value that indicates whether or not the + IRP's stack location has already been adjusted for forwarding to the + next layer in the device stack. If TRUE, it means that the IRP stack + location has already been adjusted and this routine must use the next + IRP stack location. If FALSE, it means that the IRP stack location has + not yet been adjusted and this routine must use the previous IRP stack + location. + +Return value: + TRUE if the IRP is an S0 IRP, FALSE otherwise +--*/ +{ + PIO_STACK_LOCATION stackLoc; + + if (StackLocationAdjusted) { + stackLoc = IoGetNextIrpStackLocation(Irp); + } else { + stackLoc = IoGetCurrentIrpStackLocation(Irp); + } + + if ((IRP_MJ_POWER == stackLoc->MajorFunction) && + (IRP_MN_SET_POWER == stackLoc->MinorFunction) && + (SystemPowerState == stackLoc->Parameters.Power.Type) && + (PowerSystemWorking == stackLoc->Parameters.Power.State.SystemState)) { + + return TRUE; + + } else { + + return FALSE; + } +} + +NTSTATUS +PreprocessIrpAndDispatch( + _In_ WDFDEVICE Device, + _Inout_ PIRP Irp, + _In_ BOOLEAN StackLocationAdjusted + ) +/*++ +Routine description: + This routine pre-processes a power IRP and dispatches it to KMDF. + +Arguments: + Device - Handle to the KMDF device object + + Irp - Pointer to the IRP + + StackLocationAdjusted - A BOOLEAN value that indicates whether or not the + IRP's stack location has already been adjusted for forwarding to the + next layer in the device stack. If TRUE, it means that the IRP stack + location has already been adjusted and this routine must use the next + IRP stack location. If FALSE, it means that the IRP stack location has + not yet been adjusted and this routine must use the previous IRP stack + location. + +Return value: + The NTSTATUS value that is returned from the + WdfDeviceWdmDispatchPreprocessedIrp call made by this routine to dispatch + the IRP to KMDF. +--*/ +{ + NTSTATUS status; + PPOFX_DEVICE_CONTEXT devCtx = NULL; + + if (IsS0Irp(Irp, StackLocationAdjusted)) { + // + // We have received an S0 IRP. The power framework requires that we call + // PoFxReportDevicePoweredOn after we have returned to D0 as a result of + // receiving an S0 IRP. Therefore, we make a note in our device context + // that we need to call PoFxReportDevicePoweredOn from our next + // EvtDeviceD0Entry callback. + // + devCtx = HelperGetDeviceContext(Device); + devCtx->ShouldReportDevicePoweredOn = TRUE; + } + + // + // Forward the IRP for KMDF to handle + // + if (FALSE == StackLocationAdjusted) { + IoSkipCurrentIrpStackLocation(Irp); + } + status = WdfDeviceWdmDispatchPreprocessedIrp(Device, Irp); + + return status; +} + +NTSTATUS +_PfhEvtWdmPowerIrpPreprocess( + _In_ WDFDEVICE Device, + _Inout_ PIRP Irp + ) +/*++ +Routine description: + This routine receives power IRPs before KMDF has processed them. It pre- + processes the power IRP before eventually forwarding it to KMDF. + +Arguments: + Device - Handle to the KMDF device object + + Irp - Pointer to the IRP + +Return value: + If the driver layer is not interested in pre-processing the IRP, the return + value is the NTSTATUS value that is returned from the + WdfDeviceWdmDispatchPreprocessedIrp call made by this routine to dispatch + the IRP to KMDF. + + If the driver layer is interested in pre-processing the IRP, the return + value is the NTSTATUS value that is returned by the driver layer's WDM pre- + process routine for this IRP. +--*/ +{ + NTSTATUS status; + PIO_STACK_LOCATION stackLoc; + UCHAR minorFunction; + PPOFX_DEVICE_CONTEXT devCtx = NULL; + BOOLEAN invokeDriverLayerCallback = FALSE; + ULONG i; + PFN_WDFDEVICE_WDM_IRP_PREPROCESS evtDeviceWdmPowerIrpPreprocess = NULL; + + if (FALSE == ArePowerFrameworkSettingsAvailable(Device)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInitializePowerFrameworkSettings has not yet " + "been called for WDFDEVICE %p.", + Device); + WdfVerifierDbgBreakPoint(); + } + + // + // Get the minor function + // + stackLoc = IoGetCurrentIrpStackLocation(Irp); + minorFunction = stackLoc->MinorFunction; + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(Device); + + // + // Check if the driver layer is interested in pre-processing an IRP with + // this minor function code. + // + for (i=0; + i < devCtx->DriverLayerPowerIrpPreprocessInfo->NumMinorFunctions; + i++) { + + if (minorFunction == + devCtx->DriverLayerPowerIrpPreprocessInfo->MinorFunctions[i]) { + // + // The driver layer is interested in this minor function code + // + invokeDriverLayerCallback = TRUE; + break; + } + } + + if (invokeDriverLayerCallback) { + // + // Invoke the driver layer's callback. After the driver layer has + // pre-processed the IRP, it will call + // PfhWdmDispatchPreprocessedPowerIrp. We will pre-process the IRP + // at that time. + // + evtDeviceWdmPowerIrpPreprocess = + devCtx->DeviceInitSettings.EvtDeviceWdmPowerIrpPreprocess; + ASSERT(NULL != evtDeviceWdmPowerIrpPreprocess); + + status = evtDeviceWdmPowerIrpPreprocess(Device, Irp); + + } else { + // + // Preprocess IRP and dispatch to KMDF + // + status = PreprocessIrpAndDispatch(Device, + Irp, + FALSE /* StackLocationAdjusted */); + } + + return status; +} + + +_IRQL_requires_max_(DISPATCH_LEVEL) +NTSTATUS +PfhWdmDispatchPreprocessedPowerIrp( + _In_ WDFDEVICE Device, + _Inout_ PIRP Irp + ) +// See comments in WdfPoFx.h +{ + PIO_STACK_LOCATION stackLoc; + + if (FALSE == ArePowerFrameworkSettingsAvailable(Device)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInitializePowerFrameworkSettings has not yet " + "been called for WDFDEVICE %p.", + Device); + WdfVerifierDbgBreakPoint(); + } + + // + // Verify that this is a power IRP + // + stackLoc = IoGetNextIrpStackLocation(Irp); + if (IRP_MJ_POWER != stackLoc->MajorFunction) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhWdmDispatchPreprocessedPowerIrp must be called " + "for power IRPs only. It has been called for IRP %p, which is not" + " a power IRP.", + Irp); + WdfVerifierDbgBreakPoint(); + } + + // + // The driver layer would have already adjusted the stack location for KMDF + // + return PreprocessIrpAndDispatch(Device, + Irp, + TRUE /* StackLocationAdjusted */); +} + +NTSTATUS +_PfhEvtD0Entry( + _In_ WDFDEVICE Device, + _In_ WDF_POWER_DEVICE_STATE PreviousState + ) +/*++ +Routine Description: + + In this routine, we perform operations that are needed when the device + enters the D0 power state. + +Arguments: + + Device - Handle to the KMDF device object + + PreviousState - A WDF_POWER_DEVICE_STATE-typed enumerator that identifies + the previous device power state. + +Return Value: + + An NTSTATUS value representing success or failure of the function. + +--*/ +{ + NTSTATUS status; + PPOFX_DEVICE_CONTEXT devCtx = NULL; + PFN_WDF_DEVICE_D0_ENTRY evtDeviceD0Entry; + + if (FALSE == IsDeviceInitialized(Device)) { + status = STATUS_INVALID_DEVICE_REQUEST; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInitializeDeviceSettings has not yet been " + "called for WDFDEVICE %p. %!status!.", + Device, + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(Device); + + // + // If the driver layer supplied a D0Entry callback, invoke it + // + evtDeviceD0Entry = devCtx->DeviceInitSettings.EvtDeviceD0Entry; + if (NULL != evtDeviceD0Entry) { + status = evtDeviceD0Entry(Device, PreviousState); + if (FALSE == NT_SUCCESS(status)) { + goto exit; + } + } + + if (devCtx->ShouldReportDevicePoweredOn) { + // + // This is the first time we are entering D0 after receiving an S0 IRP. + // We should call PoFxReportDevicePoweredOn. + // + devCtx->ShouldReportDevicePoweredOn = FALSE; + PoFxReportDevicePoweredOn(devCtx->PoHandle); + } + + status = STATUS_SUCCESS; + +exit: + + return status; +} + +NTSTATUS +_PfhEvtSelfManagedIoRestart( + _In_ WDFDEVICE Device + ) +/*++ +Routine Description: + + In this routine, we restart self-managed I/O operations. + +Arguments: + + Device - Handle to the KMDF device object + +Return Value: + + An NTSTATUS value representing success or failure of the function. + +--*/ +{ + NTSTATUS status; + PPOFX_DEVICE_CONTEXT devCtx = NULL; + PFN_WDF_DEVICE_SELF_MANAGED_IO_RESTART evtDeviceSelfManagedIoRestart; + + if (FALSE == ArePowerFrameworkSettingsAvailable(Device)) { + status = STATUS_INVALID_DEVICE_REQUEST; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInitializePowerFrameworkSettings has not yet " + "been called for WDFDEVICE %p. %!status!.", + Device, + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(Device); + + // + // If the driver layer supplied a self-managed-IO-restart callback, invoke + // it + // + evtDeviceSelfManagedIoRestart = + devCtx->DeviceInitSettings.EvtDeviceSelfManagedIoRestart; + if (NULL != evtDeviceSelfManagedIoRestart) { + status = evtDeviceSelfManagedIoRestart(Device); + if (FALSE == NT_SUCCESS(status)) { + goto exit; + } + } + + // + // For a PDO, if this is the first start after the device was disabled and + // if S0-idle power management is supported for the device, then we prevent + // the device from powering down to Dx due to S0-idle. We will allow the + // device to power down to Dx only after our device-power-not-required + // callback is invoked. + // + // NOTE: The function below figures out whether this is the first start + // after the device was disabled and does the right thing based on that. + // + STOP_DEVICE_IDLE_ON_DEVICE_START(status, Device, devCtx, exit); + + // + // For a PDO, we need to register with the power framework in the + // self-managed-IO-restart callback if the device was disabled and then + // re-enabled. In this case, we would have unregistered with the power + // framework in the self-managed-IO-flush callback. Therefore, we need to + // re-register now. + // + // NOTE: The function below handles the case where we are already registered + // with the power framework. + // + status = RegisterWithPowerFrameworkWorker(Device); + if (FALSE == NT_SUCCESS(status)) { + goto exit; + } + + status = STATUS_SUCCESS; + +exit: + return status; +} + +VOID +_PfhDevicePowerRequiredCallback( + _In_ PVOID Context + ) +/*++ +Routine Description: + + The power framework invokes this routine to notify us that we need to + enter/remain in the D0 state + +Arguments: + + Context - Context that we passed in to the power framework + +Return Value: + + None + +--*/ +{ + WDFDEVICE device = NULL; + PPOFX_DEVICE_CONTEXT devCtx = NULL; + + // + // Get the handle to the KMDF device object + // + device = (WDFDEVICE) Context; + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(device); + + // + // Perform actions specific to S0-idle power management (if enabled) + // + DEVICE_POWER_REQUIRED_FOR_S0_IDLE(device, devCtx); + return; +} + +VOID +_PfhDevicePowerNotRequiredCallback( + _In_ PVOID Context + ) +/*++ +Routine Description: + + The power framework invokes this routine to notify us that we may enter + a low-power Dx state + +Arguments: + + Context - Context that we passed in to the power framework + +Return Value: + + None + +--*/ +{ + WDFDEVICE device = NULL; + PPOFX_DEVICE_CONTEXT devCtx = NULL; + + // + // Get the handle to the KMDF device object + // + device = (WDFDEVICE) Context; + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(device); + + // + // Perform actions specific to S0-idle power management (if enabled) + // + DEVICE_POWER_NOT_REQUIRED_FOR_S0_IDLE(device, devCtx); + + // + // Tell the power framework that we've finished processing device-power- + // not-required + // + PoFxCompleteDevicePowerNotRequired(devCtx->PoHandle); + + return; +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +PfhRegisterDeviceProactive( + _In_ WDFDEVICE Device + ) +// See comments in WdfPoFx.h +{ + NTSTATUS status; + PPOFX_DEVICE_CONTEXT devCtx; + + if (FALSE == ArePowerFrameworkSettingsAvailable(Device)) { + status = STATUS_INVALID_DEVICE_REQUEST; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInitializePowerFrameworkSettings has not yet " + "been called for WDFDEVICE %p. %!status!.", + Device, + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(Device); + + if (NULL != devCtx->PoHandle) { + status = STATUS_INVALID_DEVICE_REQUEST; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhRegisterDeviceProactive was called when the " + "device was already registered with the power framework. " + "%!status!.", + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + status = RegisterWithPowerFrameworkWorker(Device); + if (FALSE == NT_SUCCESS(status)) { + goto exit; + } + + status = STATUS_SUCCESS; + +exit: + return status; +} diff --git a/pofx/WDF/Driver/MultiComp/lib/init.c b/pofx/WDF/Driver/MultiComp/lib/init.c new file mode 100644 index 00000000..780434b5 --- /dev/null +++ b/pofx/WDF/Driver/MultiComp/lib/init.c @@ -0,0 +1,1336 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + init.c + +Abstract: + This module contains routines that are used to initialize the power + framework helper library + +Environment: + + Kernel mode + +--*/ + +#include "WdfPoFxPriv.h" +#include "init.tmh" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, GetDeviceInitSettings) +#pragma alloc_text(PAGE, PfhInitializerCreate) +#pragma alloc_text(PAGE, CopyAndUpdateMinorFunctionsArray) +#pragma alloc_text(PAGE, PfhAssignWdmPowerIrpPreProcessCallback) +#pragma alloc_text(PAGE, PfhInterceptWdfPnpPowerEventCallbacks) +#pragma alloc_text(PAGE, PfhSetPoHandleAvailabilityCallbacks) +#pragma alloc_text(PAGE, CopyMinorFunctionsArray) +#pragma alloc_text(PAGE, PfhInitializeDeviceSettings) +#pragma alloc_text(PAGE, PfhInitializePowerFrameworkSettings) +#endif + +PHELPER_DEVICE_INIT +GetDeviceInitSettings( + _In_ WDFOBJECT Initializer + ) +/*++ +Routine description: + This routine gets a pointer to the location in the initializer object's + context space where we store the driver layer's device object settings. + +Arguments: + Initializer - Handle to the initializer object that is being used to + initialize our device object settings. + +Return value: + Pointer to a HELPER_DEVICE_INIT structure where the driver layer's device + object settings are stored. +--*/ +{ + PHELPER_INIT initContext = NULL; + + PAGED_CODE(); + + initContext = HelperGetInitContext(Initializer); + + if ((initContext->InitType != HelperInitTypeNone) && + (initContext->InitType != HelperInitTypeDevice)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Cannot get device initialization settings for " + "initializer object %p because it is currently being used to " + "initialize a different object.", + Initializer); + WdfVerifierDbgBreakPoint(); + return NULL; + } + + if (initContext->InitType == HelperInitTypeNone) { + initContext->InitType = HelperInitTypeDevice; + } + + return &(initContext->u.DeviceInit); +} + +PHELPER_QUEUE_INIT +GetQueueInitSettings( + _In_ WDFOBJECT Initializer + ) +/*++ +Routine description: + This routine gets a pointer to the location in the initializer object's + context space where we store the driver layer's queue object settings. + +Arguments: + Initializer - Handle to the initializer object that is being used to + initialize our queue object settings. + +Return value: + Pointer to a HELPER_QUEUE_INIT structure where the driver layer's queue + object settings are stored. +--*/ +{ + PHELPER_INIT initContext = NULL; + + initContext = HelperGetInitContext(Initializer); + + if ((initContext->InitType != HelperInitTypeNone) && + (initContext->InitType != HelperInitTypeQueue)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Cannot get queue initialization settings for " + "initializer object %p because it is currently being used to " + "initialize a different object.", + Initializer); + WdfVerifierDbgBreakPoint(); + return NULL; + } + + if (initContext->InitType == HelperInitTypeNone) { + initContext->InitType = HelperInitTypeQueue; + } + + return &(initContext->u.QueueInit); +} + +BOOLEAN +IsDeviceInitialized( + _In_ WDFDEVICE Device + ) +/*++ +Routine description: + This routine determines whether we have initialized our settings for the + given device object. + +Arguments: + Device - Handle to the KMDF device object + +Return value: + TRUE if we have initialized our settings for the device object, FALSE + otherwise. +--*/ +{ + PPOFX_DEVICE_CONTEXT devCtx; + devCtx = HelperGetDeviceContext(Device); + return (NULL != devCtx); +} + +BOOLEAN +ArePowerFrameworkSettingsAvailable( + _In_ WDFDEVICE Device + ) +/*++ +Routine description: + This routine determines whether the driver layer has provided us with its + power framework settings for the given device object. + +Arguments: + Device - Handle to the KMDF device object + +Return value: + TRUE if we the driver layer has provided us with its power framework + settings for the device object, FALSE otherwise. +--*/ +{ + PPOFX_DEVICE_CONTEXT devCtx; + devCtx = HelperGetDeviceContext(Device); + return ((NULL != devCtx) && (NULL != devCtx->PoFxDeviceInfo)); +} + +BOOLEAN +IsQueueInitialized( + _In_ WDFQUEUE Queue + ) +/*++ +Routine description: + This routine determines whether we have initialized our settings for the + given queue object. + +Arguments: + Queue - Handle to the KMDF queue object + +Return value: + TRUE if we have initialized our settings for the queue object, FALSE + otherwise. +--*/ +{ + PPOFX_QUEUE_CONTEXT qCtx; + qCtx = HelperGetQueueContext(Queue); + return (NULL != qCtx); +} + +VOID +ResetInitializer( + _In_ WDFOBJECT Initializer + ) +/*++ +Routine description: + This routine prepares the given initializer object for initializing a new + KMDF object. + +Arguments: + Initializer - Handle to the initializer object + +Return value: + None +--*/ +{ + PHELPER_INIT initContext = NULL; + + // + // We want to make sure that when the initializer context is zeroed, the + // default S0-idle power management configuration for the device is + // PfhS0IdleNotSupported. + // + C_ASSERT(0 == PfhS0IdleNotSupported); + + // + // Reset the initializer context + // + initContext = HelperGetInitContext(Initializer); + RtlZeroMemory(initContext, sizeof(*initContext)); + initContext->InitType = HelperInitTypeNone; + + return; +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +PfhInitializerCreate( + _Out_ WDFOBJECT * Initializer + ) +// See comments in WdfPoFx.h +{ + NTSTATUS status; + WDFOBJECT initializer = NULL; + WDF_OBJECT_ATTRIBUTES objectAttributes; + + PAGED_CODE(); + + // + // Create an initializer object + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&objectAttributes, HELPER_INIT); + status = WdfObjectCreate(&objectAttributes, &initializer); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfObjectCreate failed with %!status!.", + status); + goto exit; + } + + // + // Initialize context + // + ResetInitializer(initializer); + + *Initializer = initializer; + + status = STATUS_SUCCESS; + +exit: + return status; +} + +NTSTATUS +CopyAndUpdateMinorFunctionsArray( + _In_ WDFOBJECT Initializer, + _In_reads_opt_(NumMinorFunctions) PUCHAR MinorFunctions, + _In_ ULONG NumMinorFunctions, + _Out_ WDFMEMORY * MinorFunctionsMemory, + _Outptr_result_buffer_(*UpdatedNumMinorFunctions) PUCHAR *UpdatedMinorFunctions, + _Out_ PULONG UpdatedNumMinorFunctions + ) +/*++ +Routine description: + This routine takes an array of power IRP minor functions and makes a copy of + it. If the input array does not include IRP_MN_SET_POWER as one of the minor + functions, the copy of the array made by this routine is expanded to include + IRP_MN_SET_POWER too. This enables us to register a WDM pre-process callback + for IRP_MN_SET_POWER, regardless of whether the driver layer is interested + in that minor function. + +Arguments: + Initializer - Handle to the initializer object + + MinorFunctions - Caller-initialized array of minor functions that the driver + layer is interested in for the IRP_MJ_POWER major function. + + NumMinorFunctions - The number of minor functions in the MinorFunctions + array + + MinorFunctionsMemory - Pointer to a location that receives a handle to the + memory object created by this routine to store a copy of the minor + functions + + UpdatedMinorFunctions - Pointer to a location that receives an array of + minor functions that the driver layer is interested in, plus + IRP_MN_SET_POWER if it was not present in the original array + + UpdatedNumMinorFunctions - Pointer to a location that receives the number of + minor functions in the UpdatedMinorFunctions array + +Return value: + An NTSTATUS value representing success or failure of the function. +--*/ +{ + NTSTATUS status; + BOOLEAN driverLayerPreprocessesSetPower = FALSE; + UCHAR setPowerMinorFunction; + ULONG i; + ULONG driverLayerMinorFunctions = 0; + ULONG extraMinorFunctions = 0; + ULONG totalMinorFunctions = 0; + ULONG totalMinorFunctionsSize = 0; + ULONG memorySize = 0; + WDF_OBJECT_ATTRIBUTES objectAttributes; + WDFMEMORY memory = NULL; + PPOFX_DRIVER_LAYER_POWER_IRP_PREPROCESS_INFO memoryBuffer = NULL; + ULONG minorFunctionsOffset; + ULONG minorFunctionsSize; + ULONG extraMinorFunctionOffset; + + PAGED_CODE(); + + // + // Check if the driver layer needs to preprocess IRP_MN_SET_POWER + // + if ((NULL != MinorFunctions) && (0 != NumMinorFunctions)) { + + driverLayerMinorFunctions = NumMinorFunctions; + + for (i=0; i < NumMinorFunctions; i++) { + if (IRP_MN_SET_POWER == MinorFunctions[i]) { + driverLayerPreprocessesSetPower = TRUE; + break; + } + } + } + + // + // If the driver layer does not preprocess IRP_MN_SET_POWER, then we need to + // update the minor functions array with an extra entry for IRP_MN_SET_POWER + // + extraMinorFunctions = driverLayerPreprocessesSetPower ? 0 : 1; + + // + // Allocate memory to store a copy of the minor functions array + // + status = RtlULongAdd(driverLayerMinorFunctions, + extraMinorFunctions, + &totalMinorFunctions); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to compute total minor functions count for " + "power IRP preprocessing. RtlUlongAdd failed with %!status!.", + status); + goto exit; + } + + ASSERT(totalMinorFunctions > 0); + + status = RtlULongMult(sizeof(MinorFunctions[0]), + totalMinorFunctions, + &totalMinorFunctionsSize); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to compute buffer size needed to store the " + "functions array. RtlULongMult failed with %!status!.", + status); + goto exit; + } + status = RtlULongAdd(sizeof(POFX_DRIVER_LAYER_POWER_IRP_PREPROCESS_INFO), + totalMinorFunctionsSize, + &memorySize); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to compute buffer size needed to store the " + "minor functions array and count. RtlUlongAdd failed with " + "%!status!.", + status); + goto exit; + } + if (0 == memorySize) { + status = STATUS_INVALID_BUFFER_SIZE; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to set memorySize. Failed with %!status!.", + status); + goto exit; + } + WDF_OBJECT_ATTRIBUTES_INIT(&objectAttributes); + objectAttributes.ParentObject = Initializer; // auto-delete when parent + // deleted + #pragma prefast(suppress:28160, "memorySize > 0") + status = WdfMemoryCreate(&objectAttributes, + NonPagedPool, + 0, // PoolTag + memorySize, + &memory, + (PVOID*) &memoryBuffer); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to allocate memory to store the minor functions" + " array and count. WdfMemoryCreate failed with %!status!.", + status); + goto exit; + } + + // + // We'll remember only the minor functions that the driver layer is + // interested in. If we add IRP_MN_SET_POWER to that list, we won't need to + // remember that beyond initialization time. + // + memoryBuffer->NumMinorFunctions = driverLayerMinorFunctions; + + // + // Copy the minor functions array + // + minorFunctionsOffset = + FIELD_OFFSET(POFX_DRIVER_LAYER_POWER_IRP_PREPROCESS_INFO, MinorFunctions); + minorFunctionsSize = driverLayerMinorFunctions * + sizeof(MinorFunctions[0]); // already performed + // intsafe math above + // won't overflow + if (driverLayerMinorFunctions > 0) { + if (0 == minorFunctionsSize) { + status = STATUS_INVALID_BUFFER_SIZE; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to set minorFunctionsSize. Failed with %!status!.", + status); + goto exit; + } + #pragma prefast(suppress:28160, "minorFunctionsSize > 0") + status = WdfMemoryCopyFromBuffer(memory, + minorFunctionsOffset, + memoryBuffer->MinorFunctions, + minorFunctionsSize); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to copy driver layer's minor functions " + "array. WdfMemoryCopyFromBuffer failed with %!status!.", + status); + goto exit; + } + } + + if (extraMinorFunctions > 0) { + + ASSERT(extraMinorFunctions == 1); + + // + // Add an extra minor function for IRP_MN_SET_POWER since it was not in + // the driver layer's list. + // NOTE: This minor function is added at the end and is not reflected in + // memoryBuffer.NumMinorFunctions. + // + extraMinorFunctionOffset = + minorFunctionsOffset + minorFunctionsSize;// already performed intsafe + // math above so this won't + // overflow + setPowerMinorFunction = IRP_MN_SET_POWER; + status = WdfMemoryCopyFromBuffer(memory, + extraMinorFunctionOffset, + &setPowerMinorFunction, + sizeof(setPowerMinorFunction)); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to add an extra minor function " + "(IRP_MN_SET_POWER) to the minor functions array. " + "WdfMemoryCopyFromBuffer failed with %!status!.", + status); + goto exit; + } + } + + *MinorFunctionsMemory = memory; + *UpdatedMinorFunctions = memoryBuffer->MinorFunctions; + *UpdatedNumMinorFunctions = + totalMinorFunctions;// Do not use memoryBuffer.NumMinorFunctions + // here because it does not include the extra + // minor function that we might have added. + + // + // If we added an extra minor function, it is present in the buffer but not + // reflected in memoryBuffer.NumMinorFunctions + // + ASSERT((memoryBuffer->NumMinorFunctions + extraMinorFunctions) == + totalMinorFunctions); + + status = STATUS_SUCCESS; + +exit: + if (FALSE == NT_SUCCESS(status)) { + if (NULL != memory) { + WdfObjectDelete(memory); + } + } + + return status; +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +PfhAssignWdmPowerIrpPreProcessCallback( + _In_ WDFOBJECT Initializer, + _In_ PWDFDEVICE_INIT DeviceInit, + _In_opt_ PFN_WDFDEVICE_WDM_IRP_PREPROCESS EvtDeviceWdmPowerIrpPreprocess, + _In_reads_opt_(NumMinorFunctions) PUCHAR MinorFunctions, + _In_ ULONG NumMinorFunctions + ) +// See comments in WdfPoFx.h +{ + NTSTATUS status; + WDFMEMORY minorFunctionsMemory = NULL; + ULONG numMinorFunctions = 0; + PUCHAR minorFunctions = NULL; + PHELPER_DEVICE_INIT deviceInitSettings = NULL; + + PAGED_CODE(); + + deviceInitSettings = GetDeviceInitSettings(Initializer); + + if (deviceInitSettings->PowerIrpPreprocessCallbackAssigned) { + status = STATUS_INVALID_DEVICE_REQUEST; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhAssignWdmPowerIrpPreProcessCallback has already" + " been called on initialier %p. It should not be called again " + "before the initializer has been used to initialize a KMDF device" + " object. %!status!.", + Initializer, + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + // + // Copy the minor functions array and, if needed, update it to include + // IRP_MN_SET_POWER. + // + status = CopyAndUpdateMinorFunctionsArray(Initializer, + MinorFunctions, + NumMinorFunctions, + &minorFunctionsMemory, + &minorFunctions, + &numMinorFunctions); + if (FALSE == NT_SUCCESS(status)) { + goto exit; + } + + // + // Assign the WDM preprocess callback + // + status = WdfDeviceInitAssignWdmIrpPreprocessCallback( + DeviceInit, + _PfhEvtWdmPowerIrpPreprocess, + IRP_MJ_POWER, + minorFunctions, + numMinorFunctions + ); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfDeviceInitAssignWdmIrpPreprocessCallback failed " + "with %!status!.", + status); + goto exit; + } + + deviceInitSettings->PowerIrpPreprocessMinorFunctions = minorFunctionsMemory; + deviceInitSettings->EvtDeviceWdmPowerIrpPreprocess = + EvtDeviceWdmPowerIrpPreprocess; + deviceInitSettings->PowerIrpPreprocessCallbackAssigned = TRUE; + + status = STATUS_SUCCESS; + +exit: + if (FALSE == NT_SUCCESS(status)) { + if (NULL != minorFunctionsMemory) { + WdfObjectDelete(minorFunctionsMemory); + } + } + return status; +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +PfhInterceptWdfPnpPowerEventCallbacks( + _In_ WDFOBJECT Initializer, + _Inout_ PWDF_PNPPOWER_EVENT_CALLBACKS DriverLayerPnpPowerCallbacks + ) +// See comments in WdfPoFx.h +{ + PHELPER_DEVICE_INIT deviceInitSettings = NULL; + + PAGED_CODE(); + + deviceInitSettings = GetDeviceInitSettings(Initializer); + + if (deviceInitSettings->PnpPowerEventCallbacksIntercepted) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInterceptWdfPnpPowerEventCallbacks has already been" + " called on initialier %p. It should not be called again before " + "the initializer has been used to initialize a KMDF device " + "object.", + Initializer); + WdfVerifierDbgBreakPoint(); + } + + // + // Save the driver layer's callbacks that we are going to replace + // + deviceInitSettings->EvtDeviceSelfManagedIoInit = + DriverLayerPnpPowerCallbacks->EvtDeviceSelfManagedIoInit; + deviceInitSettings->EvtDeviceSelfManagedIoFlush = + DriverLayerPnpPowerCallbacks->EvtDeviceSelfManagedIoFlush; + deviceInitSettings->EvtDeviceSelfManagedIoRestart = + DriverLayerPnpPowerCallbacks->EvtDeviceSelfManagedIoRestart; + deviceInitSettings->EvtDeviceD0Entry = + DriverLayerPnpPowerCallbacks->EvtDeviceD0Entry; + + // + // Replace the driver layer's callbacks with our own + // + DriverLayerPnpPowerCallbacks->EvtDeviceSelfManagedIoInit = + _PfhEvtSelfManagedIoInit; + DriverLayerPnpPowerCallbacks->EvtDeviceSelfManagedIoFlush = + _PfhEvtSelfManagedIoFlush; + DriverLayerPnpPowerCallbacks->EvtDeviceSelfManagedIoRestart = + _PfhEvtSelfManagedIoRestart; + DriverLayerPnpPowerCallbacks->EvtDeviceD0Entry = + _PfhEvtD0Entry; + + deviceInitSettings->PnpPowerEventCallbacksIntercepted = TRUE; + + return; +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +PfhSetPoHandleAvailabilityCallbacks( + _In_ WDFOBJECT Initializer, + _In_ PPFH_CALLBACK_POHANDLE_AVAILABLE PfhCallbackPoHandleAvailable, + _In_ PPFH_CALLBACK_POHANDLE_UNAVAILABLE PfhCallbackPoHandleUnavailable + ) +// See comments in WdfPoFx.h +{ + PHELPER_DEVICE_INIT deviceInitSettings = NULL; + + PAGED_CODE(); + + deviceInitSettings = GetDeviceInitSettings(Initializer); + + if (deviceInitSettings->PoHandleAvailabilityCallbacksSet) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhSetPoHandleAvailabilityCallbacks has already been " + "called on initialier %p. It should not be called again before " + "the initializer has been used to initialize a KMDF device " + "object.", + Initializer); + WdfVerifierDbgBreakPoint(); + } + + // + // Save the driver layer's callbacks + // + deviceInitSettings->PfhCallbackPoHandleAvailable = + PfhCallbackPoHandleAvailable; + deviceInitSettings->PfhCallbackPoHandleUnavailable = + PfhCallbackPoHandleUnavailable; + + deviceInitSettings->PoHandleAvailabilityCallbacksSet = TRUE; + return; +} + +NTSTATUS +CopyMinorFunctionsArray( + _In_ WDFDEVICE Device, + _In_ WDFMEMORY SourceMemory, + _Out_ WDFMEMORY * DestinationMemory + ) +/*++ +Routine description: + This routine creates a new memory object to store the array of power IRP + minor functions that the driver layer is interested in. The array is + currently stored in a memory object that we had created as a child of the + initializer object that was being used to initialize the device object. Now + that the initialization of the device object is about to complete, we need + to make a copy of the contents of that memory object. Once the device object + initialization is complete, the driver layer is free to delete the + initializer object, which will result in the original memory object also + getting delete. That is why we copy the contents to a new memory object. + +Arguments: + Device - Handle to the KMDF device object + + SourceMemory - Handle to the memory object that contains the array of power + IRP minor functions that the driver layer is interested in + + DestinationMemory - Pointer to a location that receives a handle to a new + memory object created by this routine. The new memory object contains a + copy of the a + +Return value: + An NTSTATUS value representing success or failure of the function. +--*/ +{ + NTSTATUS status; + PPOFX_DRIVER_LAYER_POWER_IRP_PREPROCESS_INFO powerIrpPreprocessInfo = NULL; + size_t powerIrpPreprocessInfoSize; + WDF_OBJECT_ATTRIBUTES objectAttributes; + WDFMEMORY memory = NULL; + + PAGED_CODE(); + + // + // Get the source memory buffer + // + powerIrpPreprocessInfo = WdfMemoryGetBuffer(SourceMemory, + &powerIrpPreprocessInfoSize); + if (0 == powerIrpPreprocessInfoSize) { + status = STATUS_INVALID_BUFFER_SIZE; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to get powerIrpPreprocessInfoSize. Failed with %!status!.", + status); + goto exit; + } + + // + // Create a new memory object to hold a copy of the buffer + // + WDF_OBJECT_ATTRIBUTES_INIT(&objectAttributes); + objectAttributes.ParentObject = Device; // auto-delete when parent deleted + status = WdfMemoryCreate(&objectAttributes, + NonPagedPool, + 0, // PoolTag + powerIrpPreprocessInfoSize, + &memory, + NULL // Buffer + ); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to allocate memory for the driver layer's minor" + " functions array. WdfMemoryCreate failed with %!status!.", + status); + goto exit; + } + + // + // Copy the buffer into the new memory object + // + status = WdfMemoryCopyFromBuffer(memory, + 0, // DestinationOffset + powerIrpPreprocessInfo, + powerIrpPreprocessInfoSize); + if (FALSE == NT_SUCCESS(status)) { + goto exit; + } + + *DestinationMemory = memory; + + status = STATUS_SUCCESS; + +exit: + return status; +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +PfhInitializeDeviceSettings( + _In_ WDFDEVICE Device, + _In_ WDFOBJECT Initializer + ) +// See comments in WdfPoFx.h +{ + NTSTATUS status; + WDF_OBJECT_ATTRIBUTES objectAttributes; + PPOFX_DEVICE_CONTEXT devCtx; + PHELPER_DEVICE_INIT deviceInitSettings = NULL; + WDFMEMORY memory = NULL; + + PAGED_CODE(); + + deviceInitSettings = GetDeviceInitSettings(Initializer); + + if (IsDeviceInitialized(Device)) { + status = STATUS_INVALID_DEVICE_REQUEST; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInitializeDeviceSettings has already been called " + "for WDFDEVICE %p. It should not be called again. %!status!.", + Device, + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + if (FALSE == deviceInitSettings->PowerIrpPreprocessCallbackAssigned) { + status = STATUS_INVALID_DEVICE_REQUEST; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhAssignWdmPowerIrpPreProcessCallback has not yet " + "been called for WDFDEVICE %p. %!status!.", + Device, + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + if (FALSE == deviceInitSettings->PnpPowerEventCallbacksIntercepted) { + status = STATUS_INVALID_DEVICE_REQUEST; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInterceptWdfPnpPowerEventCallbacks has not yet been" + " called for WDFDEVICE %p. %!status!.", + Device, + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + // + // Allocate our context for this device + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&objectAttributes, + POFX_DEVICE_CONTEXT); + status = WdfObjectAllocateContext((WDFOBJECT) Device, + &objectAttributes, + (PVOID*) &devCtx); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfObjectAllocateContext failed with %!status!", + status); + goto exit; + } + + // + // Copy the device init settings + // + devCtx->DeviceInitSettings = *deviceInitSettings; + + // + // Copy the minor functions array into a new memory object that is a child + // of the device object. The array is currently in a memory object that is + // a child of the initializer object. The initializer object can be deleted + // by the driver layer any time after initialization is complete, so we need + // to make a copy now. + // + status = CopyMinorFunctionsArray( + Device, + devCtx->DeviceInitSettings.PowerIrpPreprocessMinorFunctions, + &memory + ); + if (FALSE == NT_SUCCESS(status)) { + goto exit; + } + + // + // Perform initialization specific to S0-idle support (if enabled) + // + INITIALIZE_DEVICE_SETTINGS_FOR_S0_IDLE(Device, devCtx); + + // + // In our device context, replace the pointer to the initializer's memory + // object with a pointer to the new memory object that we just created. + // + devCtx->DeviceInitSettings.PowerIrpPreprocessMinorFunctions = memory; + + // + // For convenient access, also save the memory object's buffer in our device + // context. + // + devCtx->DriverLayerPowerIrpPreprocessInfo = WdfMemoryGetBuffer( + memory, + NULL // BufferSize + ); + + // + // By default, we do not need to call PoFxReportDevicePoweredOn from our + // EvtDeviceD0Entry callback. We only need to call it in the + // EvtDeviceD0Entry callback that is invoked right after we receive an S0 + // IRP. + // + devCtx->ShouldReportDevicePoweredOn = FALSE; + + // + // The initializer can now be used to initialize some other object + // + ResetInitializer(Initializer); + + status = STATUS_SUCCESS; + +exit: + return status; +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +PfhInitializePowerFrameworkSettings( + _In_ WDFDEVICE Device, + _In_ PPO_FX_DEVICE PoFxDeviceInfo + ) +// See comments in WdfPoFx.h +{ + NTSTATUS status; + WDF_OBJECT_ATTRIBUTES objectAttributes; + PPOFX_DEVICE_CONTEXT devCtx; + ULONG i; + ULONG pofxExtraComponentsSize; + ULONG pofxDeviceInfoSize; + WDFMEMORY memory = NULL; + ULONG idleStatesSize; + PVOID idleStates = NULL; + PPO_FX_DEVICE poFxDeviceInfo = NULL; + PPOFX_COMPONENT_INFO componentInfo = NULL; + ULONG componentInfoSize; + + PAGED_CODE(); + + if (FALSE == IsDeviceInitialized(Device)) { + status = STATUS_INVALID_DEVICE_REQUEST; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInitializeDeviceSettings has not yet been called " + "for WDFDEVICE %p. %!status!.", + Device, + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + if (ArePowerFrameworkSettingsAvailable(Device)) { + status = STATUS_INVALID_DEVICE_REQUEST; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInitializePowerFrameworkSettings has already been " + "called for WDFDEVICE %p. It should not be called again. " + "%!status!.", + Device, + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(Device); + + // + // Allocate memory to store power framework settings + // + status = RtlULongMult(sizeof(PO_FX_COMPONENT), + (PoFxDeviceInfo->ComponentCount - 1), + &pofxExtraComponentsSize); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to compute buffer size needed for extra " + "components. RtlULongMult failed with %!status!.", + status); + goto exit; + } + status = RtlULongAdd(sizeof(PO_FX_DEVICE), + pofxExtraComponentsSize, + &pofxDeviceInfoSize); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to compute buffer size needed for power " + "framework settings. RtlUlongAdd failed with %!status!.", + status); + goto exit; + } + if (0 == pofxDeviceInfoSize) { + status = STATUS_INVALID_BUFFER_SIZE; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to set pofxDeviceInfoSize. Failed with %!status!.", + status); + goto exit; + } + + WDF_OBJECT_ATTRIBUTES_INIT(&objectAttributes); + objectAttributes.ParentObject = Device; // auto-delete when parent deleted + status = WdfMemoryCreate(&objectAttributes, + NonPagedPool, + 0, // PoolTag + pofxDeviceInfoSize, + &memory, + (PVOID*) &poFxDeviceInfo); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to allocate memory for power framework " + "settings. WdfMemoryCreate failed with %!status!.", + status); + goto exit; + } + + devCtx->PoFxDeviceInfo = poFxDeviceInfo; + + // + // Copy power framework settings + // + status = WdfMemoryCopyFromBuffer(memory, + 0, // DestinationOffset + PoFxDeviceInfo, + pofxDeviceInfoSize); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to copy power framework settings. " + "WdfMemoryCopyFromBuffer failed with %!status!.", + status); + goto exit; + } + + // + // Save the driver layer's callbacks and replace them with our own + // + devCtx->DriverLayerPoFxCallbacks.ComponentIdleStateCallback = + devCtx->PoFxDeviceInfo->ComponentIdleStateCallback; + devCtx->DriverLayerPoFxCallbacks.ComponentActiveConditionCallback = + devCtx->PoFxDeviceInfo->ComponentActiveConditionCallback; + devCtx->DriverLayerPoFxCallbacks.ComponentIdleConditionCallback = + devCtx->PoFxDeviceInfo->ComponentIdleConditionCallback; + devCtx->DriverLayerPoFxCallbacks.DevicePowerRequiredCallback = + devCtx->PoFxDeviceInfo->DevicePowerRequiredCallback; + devCtx->DriverLayerPoFxCallbacks.DevicePowerNotRequiredCallback = + devCtx->PoFxDeviceInfo->DevicePowerNotRequiredCallback; + devCtx->PoFxDeviceInfo->ComponentIdleStateCallback = + _PfhComponentIdleStateCallback; + devCtx->PoFxDeviceInfo->ComponentActiveConditionCallback = + _PfhComponentActiveConditionCallback; + devCtx->PoFxDeviceInfo->ComponentIdleConditionCallback = + _PfhComponentIdleConditionCallback; + devCtx->PoFxDeviceInfo->DevicePowerRequiredCallback = + _PfhDevicePowerRequiredCallback; + devCtx->PoFxDeviceInfo->DevicePowerNotRequiredCallback = + _PfhDevicePowerNotRequiredCallback; + + // + // Save the driver layer's context and replace it with our own + // + devCtx->DriverLayerPoFxContext = devCtx->PoFxDeviceInfo->DeviceContext; + devCtx->PoFxDeviceInfo->DeviceContext = (PVOID) Device; + + // + // Store the idle states for each component + // + for (i=0; i < devCtx->PoFxDeviceInfo->ComponentCount; i++) { + + status = RtlULongMult(sizeof(PO_FX_COMPONENT_IDLE_STATE), + PoFxDeviceInfo->Components[i].IdleStateCount, + &idleStatesSize); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to compute buffer size needed for idle " + "states for component %d. RtlUlongMult failed with " + "%!status!.", + i, + status); + goto exit; + } + if (0 == idleStatesSize) { + status = STATUS_INVALID_BUFFER_SIZE; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to set idleStatesSize. Failed with %!status!.", + status); + goto exit; + } + + WDF_OBJECT_ATTRIBUTES_INIT(&objectAttributes); + objectAttributes.ParentObject = Device;//auto-delete when parent deleted + status = WdfMemoryCreate(&objectAttributes, + NonPagedPool, + 0, // PoolTag + idleStatesSize, + &memory, + &idleStates); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to allocate memory for idle states for " + "component %d. WdfMemoryCreate failed with %!status!.", + i, + status); + goto exit; + } + + status = WdfMemoryCopyFromBuffer( + memory, + 0, // DestinationOffset + devCtx->PoFxDeviceInfo->Components[i].IdleStates, + idleStatesSize + ); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to copy idle states for component %d. " + "WdfMemoryCopyFromBuffer failed with %!status!.", + i, + status); + goto exit; + } + devCtx->PoFxDeviceInfo->Components[i].IdleStates = idleStates; + } + + // + // Allocate memory to store our private, per-component information + // + status = RtlULongMult(sizeof(POFX_COMPONENT_INFO), + devCtx->PoFxDeviceInfo->ComponentCount, + &componentInfoSize); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to compute buffer size needed for storing " + "private, per-component information. RtlULongMult failed with " + "%!status!.", + status); + goto exit; + } + if (0 == componentInfoSize) { + status = STATUS_INVALID_BUFFER_SIZE; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to set componentInfoSize. Failed with %!status!.", + status); + goto exit; + } + + WDF_OBJECT_ATTRIBUTES_INIT(&objectAttributes); + objectAttributes.ParentObject = Device; // auto-delete when parent deleted + status = WdfMemoryCreate(&objectAttributes, + NonPagedPool, + 0, // PoolTag + componentInfoSize, + &memory, + (PVOID*) &componentInfo); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to allocate memory for storing private, per-" + "component information. WdfMemoryCreate failed with %!status!.", + status); + goto exit; + } + RtlZeroMemory(componentInfo, componentInfoSize); + devCtx->ComponentInfo = componentInfo; + + status = STATUS_SUCCESS; + +exit: + return status; +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +PfhInterceptComponentQueueConfig( + _In_ WDFOBJECT Initializer, + _Inout_ PWDF_IO_QUEUE_CONFIG DriverLayerQueueConfig + ) +// See comments in WdfPoFx.h +{ + PHELPER_QUEUE_INIT queueInitSettings = NULL; + + queueInitSettings = GetQueueInitSettings(Initializer); + + if (queueInitSettings->ComponentQueueConfigIntercepted) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInterceptComponentQueueConfig has already been " + "called on initialier %p. It should not be called again before " + "the initializer has been used to initialize a KMDF queue " + "object.", + Initializer); + WdfVerifierDbgBreakPoint(); + } + + // + // Save the driver layer's callbacks that we are going to replace + // + queueInitSettings->EvtIoCanceledOnQueue = + DriverLayerQueueConfig->EvtIoCanceledOnQueue; + + // + // Replace the driver layer's callbacks with our own + // + DriverLayerQueueConfig->EvtIoCanceledOnQueue = + _PfhEvtRequestCanceledOnComponentQueue; + + queueInitSettings->ComponentQueueConfigIntercepted = TRUE; + + return; +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +PfhSetComponentForComponentQueue( + _In_ WDFOBJECT Initializer, + _In_ ULONG Component + ) +// See comments in WdfPoFx.h +{ + PHELPER_QUEUE_INIT queueInitSettings = NULL; + + queueInitSettings = GetQueueInitSettings(Initializer); + + if (queueInitSettings->ComponentSet) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhSetComponentForComponentQueue has already been " + "called on initialier %p. It should not be called again before " + "the initializer has been used to initialize a KMDF queue " + "object.", + Initializer); + WdfVerifierDbgBreakPoint(); + } + + queueInitSettings->Component = Component; + + queueInitSettings->ComponentSet = TRUE; + + return; +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +NTSTATUS +PfhInitializeComponentQueueSettings( + _In_ WDFQUEUE Queue, + _In_ WDFOBJECT Initializer + ) +// See comments in WdfPoFx.h +{ + NTSTATUS status; + WDFDEVICE device = NULL; + ULONG component; + WDF_OBJECT_ATTRIBUTES objectAttributes; + PPOFX_DEVICE_CONTEXT devCtx; + PPOFX_QUEUE_CONTEXT qCtx; + PHELPER_QUEUE_INIT queueInitSettings = NULL; + + queueInitSettings = GetQueueInitSettings(Initializer); + + if (IsQueueInitialized(Queue)) { + status = STATUS_INVALID_DEVICE_REQUEST; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInitializeComponentQueueSettings has already been " + "called for WDFQUEUE %p. It should not be called again. " + "%!status!.", + Queue, + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + device = WdfIoQueueGetDevice(Queue); + if (FALSE == ArePowerFrameworkSettingsAvailable(device)) { + status = STATUS_INVALID_DEVICE_REQUEST; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInitializePowerFrameworkSettings has not yet been " + "called for WDFDEVICE %p. %!status!.", + device, + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + if (FALSE == queueInitSettings->ComponentQueueConfigIntercepted) { + status = STATUS_INVALID_DEVICE_REQUEST; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInterceptComponentQueueConfig has not yet been " + "called for WDFQUEUE %p. %!status!.", + Queue, + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + if (FALSE == queueInitSettings->ComponentSet) { + status = STATUS_INVALID_DEVICE_REQUEST; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhSetComponentForComponentQueue has not yet been " + "called for WDFQUEUE %p. %!status!.", + Queue, + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(device); + + // + // Validate the component number + // + component = queueInitSettings->Component; + if (component >= devCtx->PoFxDeviceInfo->ComponentCount) { + status = STATUS_INVALID_PARAMETER; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Component number %d is invalid. Component count is %d." + " %!status!.", + component, + devCtx->PoFxDeviceInfo->ComponentCount, + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + // + // Allocate our context for this queue + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&objectAttributes, + POFX_QUEUE_CONTEXT); + status = WdfObjectAllocateContext((WDFOBJECT) Queue, + &objectAttributes, + (PVOID*) &qCtx); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfObjectAllocateContext failed with %!status!", + status); + goto exit; + } + + if (NULL != devCtx->ComponentInfo[component].Queue) { + status = STATUS_INVALID_DEVICE_REQUEST; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Component %d has already been associated with WDFQUEUE" + " %p. Associating a component with more than one queue is not " + "supported. %!status!.", + component, + devCtx->ComponentInfo[component].Queue, + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + // + // Copy the queue init settings + // + qCtx->QueueInitSettings = *queueInitSettings; + + // + // The initializer can now be used to initialize some other object + // + ResetInitializer(Initializer); + + // + // Save the queue handle in our private component information + // + devCtx->ComponentInfo[component].Queue = Queue; + + status = STATUS_SUCCESS; + +exit: + return status; +} diff --git a/pofx/WDF/Driver/MultiComp/lib/s0idle.c b/pofx/WDF/Driver/MultiComp/lib/s0idle.c new file mode 100644 index 00000000..59ab1bd3 --- /dev/null +++ b/pofx/WDF/Driver/MultiComp/lib/s0idle.c @@ -0,0 +1,471 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + s0idle.c + +Abstract: + This module contains routines that are used to implement support for + S0-idle power management. Note the use of the compile-time switch + PFH_S0IDLE_SUPPORTED to control whether or not S0-idle power management + support is implemented in the power framework helper library. Drivers that + do not require S0-idle power management support should set the + PFH_S0IDLE_SUPPORTED compile-time switch to 0 in order to reduce the + code size of the power framework helper library. + +Environment: + + Kernel mode + +--*/ + +#if PFH_S0IDLE_SUPPORTED + +#include "WdfPoFxPriv.h" +#include "s0idle.tmh" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, PfhSetS0IdleConfiguration) +#pragma alloc_text(PAGE, InitializeDeviceSettingsForS0Idle) +#pragma alloc_text(PAGE, _PowerRequiredPassiveHandler) +#endif + +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +PfhSetS0IdleConfiguration( + _In_ WDFOBJECT Initializer, + _In_ PFH_S0IDLE_CONFIG S0IdleConfig + ) +// See comments in WdfPoFx.h +{ + PHELPER_DEVICE_INIT deviceInitSettings = NULL; + + PAGED_CODE(); + + deviceInitSettings = GetDeviceInitSettings(Initializer); + + if (deviceInitSettings->S0IdleConfigSet) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhSetS0IdleConfiguration has already been called on " + "initialier %p. It should not be called again before the " + "initializer has been used to initialize a KMDF device object.", + Initializer); + WdfVerifierDbgBreakPoint(); + } + + // + // Save the driver layer's S0-idle configuration + // + deviceInitSettings->S0IdleConfig = S0IdleConfig; + + deviceInitSettings->S0IdleConfigSet = TRUE; + return; +} + +NTSTATUS +InitializeDeviceSettingsForS0Idle( + _In_ WDFDEVICE Device, + _Inout_ PPOFX_DEVICE_CONTEXT DevCtx + ) +/*++ +Routine Description: + In this routine we initialize our S0-idle power management related settings + for a device. + +Arguments: + + Device - Handle to the KMDF device object + + DevCtx - Pointer to our context space for the device object + +Return Value: + + An NTSTATUS value representing success or failure of the function. +--*/ +{ + NTSTATUS status; + WDF_OBJECT_ATTRIBUTES objectAttributes; + WDF_WORKITEM_CONFIG workItemConfig; + + PAGED_CODE(); + + // + // Create a work item that we'll queue in response to the device-power- + // required callback. This is only needed if S0-idle power management is + // supported. Also, queuing a work item can cause pageable data to be + // accessed, so we do this only if the device is not in the paging path. + // Therefore, we create the work item only if we know that the device will + // never be in the paging path. + // + if (PfhS0IdleSupportedPowerPageable == + DevCtx->DeviceInitSettings.S0IdleConfig) { + + WDF_OBJECT_ATTRIBUTES_INIT(&objectAttributes); + objectAttributes.ParentObject = Device;//auto-delete when device deleted + + WDF_WORKITEM_CONFIG_INIT(&workItemConfig, _PowerRequiredPassiveHandler); + workItemConfig.AutomaticSerialization = FALSE; + + status = WdfWorkItemCreate(&workItemConfig, + &objectAttributes, + &(DevCtx->PowerRequiredWorkItem)); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfWorkItemCreate failed with %!status!", + status); + goto exit; + } + } + + + // + // Initialize StopIdleInvokedOnDeviceStart to FALSE so that when the device + // is started, we invoke WdfDeviceStopIdle to prevent device idling until + // the power framework permits it. + // + DevCtx->StopIdleInvokedOnDeviceStart = FALSE; + + status = STATUS_SUCCESS; + +exit: + return status; +} + +NTSTATUS +StopDeviceIdleOnDeviceStart( + _In_ WDFDEVICE Device, + _In_ PPOFX_DEVICE_CONTEXT DevCtx + ) +/*++ +Routine Description: + This routine is invoked during device start. In this routine, we invoke + WdfDeviceStopIdle in order to prevent the device from going to Dx as a + result of S0-idle power management. We do this only if S0-idle power + management is enabled and then only in the following cases: + 1. the device is being started for the first time after creation + 2. the device is being started for the first time after being disabled + (applicable to PDOs only) + + When WdfDeviceStopIdle is called KMDF increments its power idle reference + count. Device idling can occur only when the power idle reference count + drops to 0. Thus by calling WdfDeviceStopIdle, we prevent device idling + until we make a balancing call to WdfDeviceResumeIdle to decrement the power + idle reference count. + + When a device is disabled, KMDF resets the power idle reference count of the + device to 0. This is the reason we need to call WdfDeviceStopIdle in case #2 + above. + +Arguments: + + Device - Handle to the KMDF device object + + DevCtx - Pointer to our context space for the device object + +Return Value: + + An NTSTATUS value representing success or failure of the function. + +--*/ +{ + NTSTATUS status; + + if (DevCtx->StopIdleInvokedOnDeviceStart) { + // + // This is neither the first device start after creation nor the first + // device start after it was disabled. So nothing to do here. + // + status = STATUS_SUCCESS; + goto exit; + } + + DevCtx->StopIdleInvokedOnDeviceStart = TRUE; + + // + // If S0-idle power management is supported, prevent device idling until the + // power framework permits. + // + if ((PfhS0IdleSupportedPowerPageable == + DevCtx->DeviceInitSettings.S0IdleConfig) || + (PfhS0IdleSupportedNotPowerPageable == + DevCtx->DeviceInitSettings.S0IdleConfig)) { + // + // We are currently in the process of entering D0. Therefore we specify + // 'FALSE' for the WaitForD0 parameter in the WdfDeviceDeviceStopIdle + // call below. Specifying 'TRUE' would result in the deadlock because we + // can't block waiting for D0 while we are in the process of entering + // D0. However, the call to WdfDeviceStopIdle will ensure that once we + // enter D0, we will remain in D0 until WdfDeviceResumeIdle is called. + // + status = WdfDeviceStopIdle(Device, FALSE /* WaitForD0 */); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfDeviceStopIdle failed for WDFDEVICE %p. " + "%!status!.", + Device, + status); + goto exit; + } + } + + status = STATUS_SUCCESS; + +exit: + return status; +} + +VOID +DevicePowerRequiredForS0Idle( + _In_ WDFDEVICE Device, + _In_ PPOFX_DEVICE_CONTEXT DevCtx + ) +/*++ +Routine Description: + + In this routine we perform actions that are necessary in response to the + PO_FX_DEVICE_POWER_REQUIRED_CALLBACK callback when S0-idle power management + support is enabled for the device. + +Arguments: + + Device - Handle to the KMDF device object + + DevCtx - Pointer to our context space for the device object + +Return Value: + + None + +--*/ +{ + PPO_FX_DEVICE_POWER_REQUIRED_CALLBACK devicePowerRequiredCallback = NULL; + PDEVICE_OBJECT wdmDeviceObject = NULL; + + // + // If the driver layer supplied a device-power-required callback, invoke it + // + devicePowerRequiredCallback = + DevCtx->DriverLayerPoFxCallbacks.DevicePowerRequiredCallback; + if (NULL != devicePowerRequiredCallback) { + devicePowerRequiredCallback(DevCtx->DriverLayerPoFxContext); + } + + if (PfhS0IdleNotSupported == DevCtx->DeviceInitSettings.S0IdleConfig) { + // + // S0-idle power management is not supported. This means that the device + // always remains in D0, unless there is a system sleep transition, + // resource rebalance or device removal. In other words, the device + // never leaves D0 because of S0-idle. So we can immediately report to + // the power framework that the device is powered on. + // + PoFxReportDevicePoweredOn(DevCtx->PoHandle); + + } else if (PfhS0IdleSupportedPowerPageable == + DevCtx->DeviceInitSettings.S0IdleConfig) { + // + // S0-idle power management is supported and the device is not in the + // paging path. This means that we can access pageable data during the + // power transition. + // + // We need to bring the device to D0 and invoke + // PoFxReportDevicePoweredOn when the device is in D0. In order to + // achieve this we need to make a blocking call to WdfDeviceStopIdle, + // which causes KMDF to bring the device to D0. + // + // We cannot make a blocking call here because: + // * Device-power-required can be invoked at dispatch level + // * Even if device-power-required is invoked at passive level, it + // might get invoked in the context of an I/O dispatch routine of a + // power-managed queue. KMDF does not support blocking calls to + // WdfDeviceStopIdle from within the context of an I/O dispatch + // routine of a power-managed queue because it can lead to a + // deadlock in some situations. + // Therefore we queue a system work item for making the blocking call to + // WdfDeviceStopIdle. + // + // !!! IMPORTANT NOTE !!! + // + // Queuing a system work item can cause pageable data to be accessed. + // Therefore, we can do it only if we know that the device is not in the + // paging path and that we can safely access pageable data during power + // transitions. + // + + // + // Verify that the DO_POWER_PAGABLE flag is set in the device object + // + wdmDeviceObject = WdfDeviceWdmGetDeviceObject(Device); + if (0 == (wdmDeviceObject->Flags & DO_POWER_PAGABLE)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WDFDEVICE %p is in the paging path, but " + "PfhSetNotPowerPageable has not been invoked for it.", + Device); + WdfVerifierDbgBreakPoint(); + } + + // + // As described in the above comments, we queue a work item to bring the + // device to D0. + // + WdfWorkItemEnqueue(DevCtx->PowerRequiredWorkItem); + + } else { + // + // !!! IMPORTANT NOTE !!! + // + // S0-idle power management is supported and the device might be in the + // paging path. This means that we cannot access pageable data during + // the power transition. As described in the comments above, queueing a + // system work item to bring the device to D0 can cause pageable data to + // be accessed. + // + // Therefore, the current implementation of the power framework helper + // library does not automatically bring the device to D0 in response to + // a device-power-required callback, if the device is in the paging path + // and cannot access pageable data during power transitions. In such + // cases the driver layer should supply its own device-power-required + // callback. In this callback, it should queue work to its own dedicated + // worker thread (instead of a system work item) to make a blocking call + // to WdfDeviceStopIdle. When WdfDeviceStopIdle returns, the device is + // guaranteed to be in D0 so the driver can call + // PoFxReportDevicePoweredOn at that point. + // + ASSERT(PfhS0IdleSupportedNotPowerPageable == + DevCtx->DeviceInitSettings.S0IdleConfig); + + DO_NOTHING(); + } + + return; +} + +VOID +DevicePowerNotRequiredForS0Idle( + _In_ WDFDEVICE Device, + _In_ PPOFX_DEVICE_CONTEXT DevCtx + ) +/*++ +Routine Description: + + In this routine we perform actions that are necessary in response to the + PO_FX_DEVICE_POWER_NOT_REQUIRED_CALLBACK callback when S0-idle power + management support is enabled for the device. + +Arguments: + + Device - Handle to the KMDF device object + + DevCtx - Pointer to our context space for the device object + +Return Value: + + None + +--*/ +{ + PPO_FX_DEVICE_POWER_NOT_REQUIRED_CALLBACK + devicePowerNotRequiredCallback = NULL; + + // + // If the driver layer supplied a device-power-not-required callback, invoke + // it + // + devicePowerNotRequiredCallback = + DevCtx->DriverLayerPoFxCallbacks.DevicePowerNotRequiredCallback; + if (NULL != devicePowerNotRequiredCallback) { + devicePowerNotRequiredCallback(DevCtx->DriverLayerPoFxContext); + } + + if ((PfhS0IdleSupportedPowerPageable == + DevCtx->DeviceInitSettings.S0IdleConfig) || + (PfhS0IdleSupportedNotPowerPageable == + DevCtx->DeviceInitSettings.S0IdleConfig)) { + // + // If S0-idle power management is supported, enable KMDF to power down + // the device to Dx. + // + WdfDeviceResumeIdle(Device); + } + + return; +} + +VOID +_PowerRequiredPassiveHandler( + _In_ WDFWORKITEM WorkItem + ) +/*++ +Routine Description: + + This is the callback function invoked at passive level in the context of a + system worker thread when we queue a work item in response to the device- + power-required callback. + +Arguments: + + WorkItem - Handle to the KMDF work item object + +Return Value: + + None + +--*/ +{ + NTSTATUS status; + WDFDEVICE device = NULL; + PPOFX_DEVICE_CONTEXT devCtx = NULL; + + PAGED_CODE(); + + // + // Get the handle to the KMDF device object + // + device = WdfWorkItemGetParentObject(WorkItem); + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(device); + + // + // Make a blocking call to WdfDeviceStopIdle in order to get KMDF to bring + // the device to D0 and/or keep it in D0. + // + status = WdfDeviceStopIdle(device, TRUE /*WaitForD0*/); + if (FALSE == NT_SUCCESS(status)) { + // + // The call to WdfDeviceStopIdle failed. However, we need to call + // PoFxReportDevicePoweredOn regardless of success or failure because + // we need to unblock the power framework which is waiting for us to + // make this call. In case of failure, KMDF would have already declared + // the device to be in a failed state and initiated a PNP removal. + // + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfDeviceStopIdle failed for WDFDEVICE %p. " + "%!status!.", + device, + status); + + // + // Fall through to PoFxReportDevicePoweredOn call below + // || || || + // || || || + // \/ \/ \/ + // + } + + // + // Inform the power framework that the device is now powered on + // + PoFxReportDevicePoweredOn(devCtx->PoHandle); + return; +} + +#endif // PFH_S0IDLE_SUPPORTED
\ No newline at end of file diff --git a/pofx/WDF/Driver/MultiComp/lib/s0idle.h b/pofx/WDF/Driver/MultiComp/lib/s0idle.h new file mode 100644 index 00000000..0ad10327 --- /dev/null +++ b/pofx/WDF/Driver/MultiComp/lib/s0idle.h @@ -0,0 +1,113 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + s0idle.h + +Abstract: + Private header file that enables the power framework library to be + conditionally compiled either with or without S0-idle power management + support, based on the value of the PFH_S0IDLE_SUPPORTED compile-time + switch. + +Environment: + + Kernel mode + +--*/ + +#if !defined(_S0IDLE_H_) +#define _S0IDLE_H_ + +EVT_WDF_WORKITEM _PowerRequiredPassiveHandler; + +#if PFH_S0IDLE_SUPPORTED + +// +// The power framework library is compiled with S0-idle power management support +// +NTSTATUS +InitializeDeviceSettingsForS0Idle( + _In_ WDFDEVICE Device, + _Inout_ PPOFX_DEVICE_CONTEXT DevCtx + ); + +NTSTATUS +StopDeviceIdleOnDeviceStart( + _In_ WDFDEVICE Device, + _In_ PPOFX_DEVICE_CONTEXT DevCtx + ); + +VOID +DevicePowerRequiredForS0Idle( + _In_ WDFDEVICE Device, + _In_ PPOFX_DEVICE_CONTEXT DevCtx + ); + +VOID +DevicePowerNotRequiredForS0Idle( + _In_ WDFDEVICE Device, + _In_ PPOFX_DEVICE_CONTEXT DevCtx + ); + +#define INITIALIZE_DEVICE_SETTINGS_FOR_S0_IDLE(_Device, _DevCtx) \ + InitializeDeviceSettingsForS0Idle(_Device, _DevCtx) + +#define STOP_DEVICE_IDLE_ON_DEVICE_START(_Status, _Device, _DevCtx, _ExitLabel)\ +{ \ + _Status = StopDeviceIdleOnDeviceStart(_Device, _DevCtx); \ + if (FALSE == NT_SUCCESS(_Status)) { \ + goto _ExitLabel; \ + } \ +} + +// +// For PDOs, if the device is restarted after being disabled, we need to +// prevent device idle until the power framework permits it. Therefore, we +// reset the StopIdleInvokedOnDeviceStart value here, so that a subsequent +// restart causes us to invoke WdfDeviceStopIdle to prevent device idling. +// +#define SELF_MANAGED_IO_FLUSH_FOR_S0_IDLE(_DevCtx) \ +{ \ + _DevCtx->StopIdleInvokedOnDeviceStart = FALSE; \ +} + +#define DEVICE_POWER_REQUIRED_FOR_S0_IDLE(_Device, _DevCtx) \ + DevicePowerRequiredForS0Idle(_Device,_DevCtx) + +#define DEVICE_POWER_NOT_REQUIRED_FOR_S0_IDLE(_Device, _DevCtx) \ + DevicePowerNotRequiredForS0Idle(_Device, _DevCtx) + +#else // PFH_S0IDLE_SUPPORTED + + +// +// The power framework library is compiled without S0-idle power management +// support +// +#define INITIALIZE_DEVICE_SETTINGS_FOR_S0_IDLE(_Device, _DevCtx) (0) +#define STOP_DEVICE_IDLE_ON_DEVICE_START(_Status, _Device, _DevCtx, _ExitLabel)\ + (0) +#define SELF_MANAGED_IO_FLUSH_FOR_S0_IDLE(_DevCtx) (0) + +// +// The current implementation does not support S0-idle. It assumes that the +// device always remains in D0 unless there is a system sleep transition, +// resource rebalance or device removal. Therefore, we call +// PoFxReportDevicePoweredOn immediately. +// +#define DEVICE_POWER_REQUIRED_FOR_S0_IDLE(_Device, _DevCtx) \ + PoFxReportDevicePoweredOn(_DevCtx->PoHandle) + +#define DEVICE_POWER_NOT_REQUIRED_FOR_S0_IDLE(_Device, _DevCtx) (0) + +#endif // PFH_S0IDLE_SUPPORTED + +#endif // _S0IDLE_H_ |
