text: /*++
  
  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;
  }
  