/*++
*******************************************************************************
* Copyright (c) 1990-98 Digi International Inc., All Rights Reserved				*
* 																										*
* This software contains proprietary and confidential information of Digi		*
* International Inc.  By accepting transfer of this copy, Recipient agrees		*
* to retain this software in confidence, to prevent disclosure to others,		*
* and to make no use of this software other than that for which it was			*
* delivered.  This is an unpublished copyrighted work of Digi International	*
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly	*
* prohibited.																						*
* 																										*
* Restricted Rights Legend																		*
*																										*
* Use, duplication, or disclosure by the Government is subject to					*
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in			*
* Technical Data and Computer Software clause at DFARS 252.227-7031 or			*
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -				*
* Restricted Rights at 48 CFR 52.227-19, as applicable.								*
*																										*
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343				*
*																										*
*******************************************************************************

Module Name:

   dxbpower.c

Abstract:

   This module contains handler routines for IRP_MJ_POWER requests.

Author:

   Michael Zarns

--*/
#include "digidxb.h"


#if NT5_BUILD
/* --------------------------------------------------------------------------*/
/* -- Local data:                                                            */
/* --------------------------------------------------------------------------*/

#ifdef DBG
typedef struct POWER_MINOR_TABLE
{
	ULONG Minor;
	PCSTR Name;
} POWER_MINOR_TABLE, PPOWER_MINOR_TABLE;

const struct POWER_MINOR_TABLE DxbPowerIrps[] = 
{
	IRP_MN_SET_POWER,		"IRP_MN_SET_POWER",
	IRP_MN_QUERY_POWER,		"IRP_MN_QUERY_POWER",
	IRP_MN_WAIT_WAKE,		"IRP_MN_WAIT_WAKE",
	IRP_MN_POWER_SEQUENCE,	"IRP_MN_POWER_SEQUENCE"
};

#define DisplayPowerIrpName(Type) \
	GetIrpName( (ULONG)(Type), DxbPowerIrps, ARRAY_SIZE( DxbPowerIrps ) )

const struct POWER_MINOR_TABLE DxbPowerSystemStates[] =
{
	PowerSystemUnspecified,		"PowerSystemUnspecified",
	PowerSystemWorking,			"PowerSystemWorking",
	PowerSystemSleeping1,		"PowerSystemSleeping1",
	PowerSystemSleeping2,		"PowerSystemSleeping2",
	PowerSystemSleeping3,		"PowerSystemSleeping3",
	PowerSystemHibernate,		"PowerSystemHibernate",
	PowerSystemShutdown,		"PowerSystemShutdown",
    PowerSystemMaximum,         "PowerSystemMaximum"
};

#define DisplayPowerSystemState(Type) \
	GetIrpName( (ULONG)(Type), DxbPowerSystemStates, ARRAY_SIZE( DxbPowerSystemStates ) )

const struct POWER_MINOR_TABLE DxbPowerDeviceStates[] =
{
	PowerDeviceUnspecified,		"PowerDeviceUnspecified",
	PowerDeviceD0,				"PowerDeviceD0",
	PowerDeviceD1,				"PowerDeviceD1",
	PowerDeviceD2,				"PowerDeviceD2",
	PowerDeviceD3,				"PowerDeviceD3",
    PowerDeviceMaximum,         "PowerDeviceMaximum"
};

#define DisplayPowerDeviceState(Type) \
	GetIrpName( (ULONG)(Type), DxbPowerDeviceStates, ARRAY_SIZE( DxbPowerDeviceStates ) )

const struct POWER_MINOR_TABLE DxbPowerStates[] =
{
    PWRSTATE_ON,                "PWRSTATE_ON",
    PWRSTATE_OFF_PENDING,       "PWRSTATE_OFF_PENDING",
    PWRSTATE_OFF,               "PWRSTATE_OFF",
    PWRSTATE_OFF_IDLE,          "PWRSTATE_OFF_IDLE"
};

#define DisplayPowerState(Type) \
	GetIrpName( (ULONG)(Type), DxbPowerStates, ARRAY_SIZE( DxbPowerStates ) )
#endif


/* --------------------------------------------------------------------------*/
/* -- Implementation:                                                        */
/* --------------------------------------------------------------------------*/

PCSTR
GetIrpName( ULONG Minor, POWER_MINOR_TABLE const Table[], ULONG nEntries )
{
    static const CHAR Unknown[] = "Unknown";
    PCSTR Name = Unknown;
    ULONG i;

    for ( i = 0; i < nEntries; i++ )
    {
        if ( Minor == Table[i].Minor )
        {
            Name = Table[i].Name;
            break;
        }
    }

    return Name;  
}


/*---------------------------------------------------------------------------*/
/*  Name: DispatchPowerFdo                                                   */
/* --------------------------------------------------------------------------*/ 
/*    In: - pFuncDeviceObject, functional device object created by device    */
/*        - Irp, I/O request                                                 */   
/*---------------------------------------------------------------------------*/
/* InOut:                                                                    */
/*---------------------------------------------------------------------------*/
/*   Out:                                                                    */
/*---------------------------------------------------------------------------*/
/* Description: Handle IRP_MJ_POWER request                                  */
/*   uses the IRP's minor function code to dispatch a handler function (like */
/*   HandleSetPower for IRP_MN_SET_POWER)                                    */
/*---------------------------------------------------------------------------*/

NTSTATUS DispatchPowerFdo( IN PDEVICE_OBJECT pFuncDeviceObject, IN PIRP Irp )
{
  NTSTATUS                   Status;
  PIO_STACK_LOCATION         pIrpStack;
  UCHAR                      fcn;
  PDXB_DEVICE_EXTENSION      pExt;

  static NTSTATUS ( *fcntab[])(IN PDEVICE_OBJECT pFuncDeviceObject, IN PIRP Irp ) = 
  {
    DefaultPowerHandler,  // IRP_MN_WAIT_WAKE
    DefaultPowerHandler,  // IRP_MN_POWER_SEQUENCE
    HandleSetPower,       // IRP_MN_SET_POWER
    HandleQueryPower      // IRP_MN_QUERY_POWER
  };

  pExt = pFuncDeviceObject->DeviceExtension;

  pIrpStack = IoGetCurrentIrpStackLocation(Irp);
  ASSERT( pIrpStack->MajorFunction == IRP_MJ_POWER );

  fcn = pIrpStack->MinorFunction;
  if ( fcn >= ARRAY_SIZE( fcntab ) )
  {
    // unknown function
    return DefaultPowerHandler( pFuncDeviceObject, Irp ); // some function we don't know about
}

#ifdef DBG
  if ( fcn == IRP_MN_SET_POWER || fcn == IRP_MN_QUERY_POWER )
  {
    POWER_STATE_TYPE PowerStateType = pIrpStack->Parameters.Power.Type;

    DigiDump(DIGIPNP, ("DXB!DispatchPowerFdo(%s)", DisplayPowerIrpName(fcn)));

    if ( PowerStateType == SystemPowerState )
    {   
      DigiDump(DIGIPNP, (", SysPowState=%s\n", DisplayPowerSystemState(pIrpStack->Parameters.Power.State.SystemState) ));
    }
    else
    {   
      DigiDump(DIGIPNP, (", DevPowState=%s\n", DisplayPowerDeviceState(pIrpStack->Parameters.Power.State.DeviceState) ));
    }
  }
  else
  {   
    DigiDump(DIGIPNP, ("DXB!DispatchPowerFdo(%s)\n", DisplayPowerIrpName(fcn) ));
  }
#endif

  return (*fcntab[fcn])( pFuncDeviceObject, Irp );
}

 
/*---------------------------------------------------------------------------*/
/*  Name: DispatchPowerPdo                                                   */
/* --------------------------------------------------------------------------*/ 
/*    In:                                                                    */
/*---------------------------------------------------------------------------*/
/* InOut:                                                                    */
/*---------------------------------------------------------------------------*/
/*   Out:                                                                    */
/*---------------------------------------------------------------------------*/
/* Description:                                                              */
/*---------------------------------------------------------------------------*/
NTSTATUS DispatchPowerPdo( IN PDEVICE_OBJECT pFuncDeviceObject, IN PIRP Irp )
{
  NTSTATUS            Status = STATUS_SUCCESS;
  PIO_STACK_LOCATION  pIrpStack;
  UCHAR               fcn;
  POWER_STATE         PowerState;
  POWER_STATE_TYPE    PowerType;
  PDXB_CHILD_DEVICE   pChildExt = pFuncDeviceObject->DeviceExtension;
  pIrpStack = IoGetCurrentIrpStackLocation (Irp);
  
  PowerType = pIrpStack->Parameters.Power.Type;
  PowerState = pIrpStack->Parameters.Power.State;

  fcn = pIrpStack->MinorFunction;

  #ifdef DBG
  if ((fcn == IRP_MN_SET_POWER) || (fcn == IRP_MN_QUERY_POWER))
  {
    POWER_STATE_TYPE PowerStateType = pIrpStack->Parameters.Power.Type;

    DigiDump(DIGIPNP, ("DXB!DispatchPowerPdo(%s)", DisplayPowerIrpName(fcn)));

    if ( PowerStateType == SystemPowerState )
    {   
      DigiDump(DIGIPNP, (":, SysPowState=%s\n", DisplayPowerSystemState(pIrpStack->Parameters.Power.State.SystemState) ));
    }
    else
    {   
      DigiDump(DIGIPNP, (", DevPowState=%s\n", DisplayPowerDeviceState(pIrpStack->Parameters.Power.State.DeviceState) ));
    }
  }
  else
  {   
    DigiDump(DIGIPNP, ("DXB!DispatchPowerPdo(%s)\n", DisplayPowerIrpName(fcn) ));
  }
  #endif

  switch (pIrpStack->MinorFunction)
  {
    case IRP_MN_SET_POWER:
      switch (PowerType)
      {
        case DevicePowerState:
          if (pChildExt->PowerState > PowerState.DeviceState)
          {
            PoSetPowerState (pChildExt->pPhysicalDeviceObject, PowerType, PowerState);
            pChildExt->PowerState = PowerState.DeviceState;
          }
          else if (pChildExt->PowerState < PowerState.DeviceState)
          {
            //
            // Powering down.
            //
            PoSetPowerState (pChildExt->pPhysicalDeviceObject, PowerType, PowerState);
            pChildExt->PowerState = PowerState.DeviceState;
          }
          break;

        case SystemPowerState:
          Status = STATUS_SUCCESS;
          break;

        default:
          Status = STATUS_NOT_SUPPORTED;
          break;
      }
      break;

    case IRP_MN_QUERY_POWER:
      Status = STATUS_SUCCESS;
      break;

    case IRP_MN_WAIT_WAKE:
    case IRP_MN_POWER_SEQUENCE:
      Status = STATUS_NOT_SUPPORTED;
      break;

    default:
      Status = STATUS_NOT_SUPPORTED;
      break;
  }

  PoStartNextPowerIrp (Irp);
  CompleteRequest (Irp, Status ,0);
  
  return Status;
}

/*---------------------------------------------------------------------------*/
/*  Name: DefaultPowerHandler                                                */
/* --------------------------------------------------------------------------*/ 
/*    In: - pFuncDeviceObject, functional device object created by device    */
/*        - Irp, I/O request                                                 */   
/*---------------------------------------------------------------------------*/
/* InOut:                                                                    */
/*---------------------------------------------------------------------------*/
/*   Out:                                                                    */
/*---------------------------------------------------------------------------*/
/* Description: forwards a POWER IRP to the next driver using the            */
/*   special PoCallDriver function                                           */
/*---------------------------------------------------------------------------*/

NTSTATUS DefaultPowerHandler( IN PDEVICE_OBJECT pFuncDeviceObject, IN PIRP Irp )
{
  PDXB_DEVICE_EXTENSION pExt;
  
  DigiDump(DIGIPNP, ("DXB!DefaultPowerHandler\n"));

  PoStartNextPowerIrp(Irp);  // must be done while we own the IRP
  IoSkipCurrentIrpStackLocation(Irp);
  pExt = pFuncDeviceObject->DeviceExtension;

  return PoCallDriver(pExt->pNextDeviceObject, Irp);
}


/*---------------------------------------------------------------------------*/
/*  Name: HandleQueryPower                                                   */
/* --------------------------------------------------------------------------*/ 
/*    In: - pFuncDeviceObject, functional device object created by device    */
/*        - Irp, I/O request                                                 */   
/*---------------------------------------------------------------------------*/
/* InOut:                                                                    */
/*---------------------------------------------------------------------------*/
/*   Out:                                                                    */
/*---------------------------------------------------------------------------*/
/* Description: Handle IRP_MN_QUERY_POWER requests                           */
/*---------------------------------------------------------------------------*/

NTSTATUS HandleQueryPower(IN PDEVICE_OBJECT pFuncDeviceObject, IN PIRP Irp)
{
  PDXB_DEVICE_EXTENSION pExt;
  PIO_STACK_LOCATION pIrpStack;
  POWER_STATE_TYPE   Type;
  POWER_STATE        State;
  DEVICE_POWER_STATE DevPower;

  pIrpStack = IoGetCurrentIrpStackLocation( Irp );
  Type      = pIrpStack->Parameters.Power.Type;
  State     = pIrpStack->Parameters.Power.State;
  pExt      = pFuncDeviceObject->DeviceExtension;
  

  DigiDump(DIGIPNP, ("DXB!HandleQueryPower\n"));

  if (Type == SystemPowerState)
  {
      // This switch is necessary because of Microsoft bogus enumeration 
      switch (State.SystemState)
      {
      case PowerSystemUnspecified:
          break;

      case PowerSystemSleeping1:
      case PowerSystemSleeping2:
      case PowerSystemSleeping3:
      case PowerSystemHibernate:
      case PowerSystemShutdown:
          DevPower = PowerDeviceD3;
          break;

      case PowerSystemWorking:
      case PowerSystemMaximum:
          DevPower = PowerDeviceD0;
          break;

      default:
          // Do nothing (unknown SystemPowerState value)
          DigiDump(DIGIERROR, ("DXB!HandleQueryPower: Unknown SystemPowerState value=%d\n", State.SystemState));
          break;
      }
  }
  else
  {
    DevPower = State.DeviceState;
  }

  if (DevPower > PowerDeviceD0
      && pExt->PowerState != PWRSTATE_OFF
      && pExt->PowerState != PWRSTATE_OFF_PENDING)
  {
    DigiDump(DIGIPNP, ("DXB!PowerState: %s -> PWRSTATE_OFF_PENDING\n", DisplayPowerState(pExt->PowerState)));
    pExt->PowerState = PWRSTATE_OFF_PENDING;
  }
  
  return DefaultPowerHandler(pFuncDeviceObject, Irp);
}

/*---------------------------------------------------------------------------*/
/*  Name: HandleSetPower                                                     */
/* --------------------------------------------------------------------------*/ 
/*    In: - pFuncDeviceObject, functional device object created by device    */
/*        - Irp, I/O request                                                 */   
/*---------------------------------------------------------------------------*/
/* InOut:                                                                    */
/*---------------------------------------------------------------------------*/
/*   Out:                                                                    */
/*---------------------------------------------------------------------------*/
/* Description: Handle IRP_MN_SET_POWER requests                             */
/*---------------------------------------------------------------------------*/

NTSTATUS HandleSetPower( IN PDEVICE_OBJECT pFuncDeviceObject, IN PIRP Irp )
{
  PDXB_DEVICE_EXTENSION pExt;
  PIO_STACK_LOCATION pIrpStack;
  POWER_STATE_TYPE   Type;
  POWER_STATE        State;
  
  pIrpStack = IoGetCurrentIrpStackLocation( Irp );
  Type      = pIrpStack->Parameters.Power.Type;
  State     = pIrpStack->Parameters.Power.State;

  // If this IRP pertains to a system state, the device is put into a corresponding 
  // device state.  
  // If the system state is below SystemWorking, the device is set to state D3 (fully off)
  // If the system state is in SystemWorking, the device is set to state D0 (fully on)
    
  pExt = pFuncDeviceObject->DeviceExtension;
  
  if (Type == SystemPowerState)
  {
      // This switch is necessary because of Microsoft bogus enumeration 
      switch (State.SystemState)
      {
      case PowerSystemUnspecified:
          // Do nothing (because the system doesn't know it's state)
          DigiDump(DIGIPNP, ("DXB!HandleSetPower: Unspecified SystemPowerState\n"));
          if (pExt->PowerState == PWRSTATE_OFF_PENDING)
          {
            DigiDump(DIGIPNP, ("DXB!PowerState: PWRSTATE_OFF_PENDING -> PWRSTATE_ON\n"));
            pExt->PowerState = PWRSTATE_ON;
          }
          return DefaultPowerHandler(pFuncDeviceObject, Irp);
          break;

      case PowerSystemWorking:
      case PowerSystemSleeping1:
      case PowerSystemSleeping2:
      case PowerSystemSleeping3:
      case PowerSystemHibernate:
      case PowerSystemShutdown:
          if (State.SystemState < pExt->SysPower)
          {
            // Add power
            DigiDump(DIGIPNP, ("DXB!HandleSetPower: Add SystemPower\n"));
            return HandleAddSystemPower(pFuncDeviceObject, Irp);
          }
          else if (State.SystemState > pExt->SysPower)
          {
            // Remove power
            DigiDump(DIGIPNP, ("DXB!HandleSetPower: Remove SystemPower\n"));
            return HandleRemoveSystemPower(pFuncDeviceObject, Irp);
          }
          else
          {
            // Do nothing
            DigiDump(DIGIPNP, ("DXB!HandleSetPower: No SystemPowerState change\n"));
            if (pExt->PowerState == PWRSTATE_OFF_PENDING)
            {
              DigiDump(DIGIPNP, ("DXB!PowerState: PWRSTATE_OFF_PENDING -> POWERON\n"));
              pExt->PowerState = PWRSTATE_ON;
            }
            return DefaultPowerHandler(pFuncDeviceObject, Irp);
          }
          break;

      case PowerSystemMaximum:
          // Add power (or do nothing, if we're already powered up)
          if ((pExt->SysPower < State.SystemState) && (pExt->SysPower != PowerSystemWorking))
          {
            // Add power
            DigiDump(DIGIPNP, ("DXB!HandleSetPower: Add SystemPower\n"));
            return HandleAddSystemPower(pFuncDeviceObject, Irp);
          }
          else
          {
            // Do nothing
            DigiDump(DIGIPNP, ("DXB!HandleSetPower: No SystemPowerState change\n"));
            if (pExt->PowerState == PWRSTATE_OFF_PENDING)
            {
              DigiDump(DIGIPNP, ("DXB!PowerState: PWRSTATE_OFF_PENDING -> POWERON\n"));
              pExt->PowerState = PWRSTATE_ON;
            }
            return DefaultPowerHandler(pFuncDeviceObject, Irp);
          }
          break;

      default:
          // Do nothing (unknown SystemPowerState value)
          DigiDump(DIGIERROR, ("DXB!HandleSetPower: Unknown SystemPowerState value=%d\n", State.SystemState));
          return DefaultPowerHandler(pFuncDeviceObject, Irp);
          break;
      }
  }
  else
  {
      // This switch is necessary because of Microsoft bogus enumeration 
      switch (State.DeviceState)
      {
      case PowerDeviceUnspecified:
          // Do nothing (because the system doesn't know it's state)
          DigiDump(DIGIPNP, ("DXB!HandleSetPower: Unspecified DevicePowerState\n"));
          return DefaultPowerHandler(pFuncDeviceObject, Irp);
          break;

      case PowerDeviceD0:
      case PowerDeviceD1:
      case PowerDeviceD2:
      case PowerDeviceD3:
          if (State.DeviceState < pExt->DevPower)
          {
            // Add power
            DigiDump(DIGIPNP, ("DXB!HandleSetPower: Add DevicePower\n"));
            return HandleAddDevicePower(pFuncDeviceObject, Irp);
          }
          else if (State.SystemState > pExt->SysPower)
          {
            // Remove power
            DigiDump(DIGIPNP, ("DXB!HandleSetPower: Remove DevicePower\n"));
            return HandleRemoveDevicePower(pFuncDeviceObject, Irp);
          }
          else
          {
            // Do nothing
            DigiDump(DIGIPNP, ("DXB!HandleSetPower: No DevicePowerState change\n"));
            return DefaultPowerHandler(pFuncDeviceObject, Irp);
          }
          break;

      case PowerDeviceMaximum:
          // Add power (or do nothing, if we're already powered up)
          if ((pExt->DevPower < State.DeviceState) && (pExt->DevPower != PowerDeviceD0))
          {
            // Add power
            DigiDump(DIGIPNP, ("DXB!HandleSetPower: Add DevicePower\n"));
            return HandleAddSystemPower(pFuncDeviceObject, Irp);
          }
          else
          {
            // Do nothing
            DigiDump(DIGIPNP, ("DXB!HandleSetPower: No DevicePowerState change\n"));
            return DefaultPowerHandler(pFuncDeviceObject, Irp);
          }
          break;

      default:
          // Do nothing (unknown DevicePowerState value)
          DigiDump(DIGIERROR, ("DXB!HandleSetPower: Unknown DevicePowerState value=%d\n", State.DeviceState));
          return DefaultPowerHandler(pFuncDeviceObject, Irp);
          break;
      }
  }
}


/*---------------------------------------------------------------------------*/
/*  Name: HandleAddSystemPower                                               */
/* --------------------------------------------------------------------------*/ 
/*    In: - pFuncDeviceObject, functional device object created by device    */
/*        - Irp, I/O request                                                 */   
/*---------------------------------------------------------------------------*/
/* InOut:                                                                    */
/*---------------------------------------------------------------------------*/
/*   Out:                                                                    */
/*---------------------------------------------------------------------------*/
/* Description: Handle IRP_MN_SET_POWER/add SystemPower requests             */
/*---------------------------------------------------------------------------*/

NTSTATUS HandleAddSystemPower( IN PDEVICE_OBJECT pFuncDeviceObject, IN PIRP Irp )
{
    PDXB_DEVICE_EXTENSION pExt;

    // If this IRP pertains to a system state, the device is put into a corresponding device state.  
    // If the system state is below SystemWorking, the device is set to state D3 (fully off)
    // If the system state is in SystemWorking, the device is set to state D0 (fully on)

    pExt = pFuncDeviceObject->DeviceExtension;
  
    // If system going to a higher power state, pass the request down and do
    // device specific processing in the completion routine. Note that we do  
    // LockDevice an additional time to be sure we don't go away until
    // the completion routine gets called
    IoCopyCurrentIrpStackLocationToNext(Irp);
    IoSetCompletionRoutine(Irp, (PIO_COMPLETION_ROUTINE)HandleAddPowerCompletion,
                           NULL, TRUE, TRUE, TRUE);

    return PoCallDriver(pExt->pNextDeviceObject, Irp);
}


/*---------------------------------------------------------------------------*/
/*  Name: HandleRemoveSystemPower                                            */
/* --------------------------------------------------------------------------*/ 
/*    In: - pFuncDeviceObject, functional device object created by device    */
/*        - Irp, I/O request                                                 */   
/*---------------------------------------------------------------------------*/
/* InOut:                                                                    */
/*---------------------------------------------------------------------------*/
/*   Out:                                                                    */
/*---------------------------------------------------------------------------*/
/* Description: Handle IRP_MN_SET_POWER/remove SystemPower requests          */
/*---------------------------------------------------------------------------*/

NTSTATUS HandleRemoveSystemPower( IN PDEVICE_OBJECT pFuncDeviceObject, IN PIRP Irp )
{
    PDXB_DEVICE_EXTENSION pExt;
    PIO_STACK_LOCATION pIrpStack;
    POWER_STATE        State;
  
    pIrpStack = IoGetCurrentIrpStackLocation(Irp);
    State = pIrpStack->Parameters.Power.State;
    pExt = pFuncDeviceObject->DeviceExtension;
  
    // If system going to a lower power state, originate a device power IRP
    // to put our device into the correct new state.
    pExt->WakeSysPower = State.SystemState;
    SendDeviceSetPower(pFuncDeviceObject, PowerDeviceD3, TRUE);
    pExt->SysPower = State.SystemState;
    
    return DefaultPowerHandler(pFuncDeviceObject, Irp);
}


/*---------------------------------------------------------------------------*/
/*  Name: HandleAddDevicePower                                               */
/* --------------------------------------------------------------------------*/ 
/*    In: - pFuncDeviceObject, functional device object created by device    */
/*        - Irp, I/O request                                                 */   
/*---------------------------------------------------------------------------*/
/* InOut:                                                                    */
/*---------------------------------------------------------------------------*/
/*   Out:                                                                    */
/*---------------------------------------------------------------------------*/
/* Description: Handle IRP_MN_SET_POWER/add DevicePower requests             */
/*---------------------------------------------------------------------------*/

NTSTATUS HandleAddDevicePower( IN PDEVICE_OBJECT pFuncDeviceObject, IN PIRP Irp )
{
    PDXB_DEVICE_EXTENSION pExt;

    pExt = pFuncDeviceObject->DeviceExtension;

    IoCopyCurrentIrpStackLocationToNext(Irp);
    IoSetCompletionRoutine(Irp, (PIO_COMPLETION_ROUTINE)HandleAddPowerCompletion,
                           NULL, TRUE, TRUE, TRUE);

    return PoCallDriver(pExt->pNextDeviceObject, Irp);
}


/*---------------------------------------------------------------------------*/
/*  Name: HandleRemoveDevicePower                                            */
/* --------------------------------------------------------------------------*/ 
/*    In: - pFuncDeviceObject, functional device object created by device    */
/*        - Irp, I/O request                                                 */   
/*---------------------------------------------------------------------------*/
/* InOut:                                                                    */
/*---------------------------------------------------------------------------*/
/*   Out:                                                                    */
/*---------------------------------------------------------------------------*/
/* Description: Handle IRP_MN_SET_POWER/remove DevicePower requests          */
/*---------------------------------------------------------------------------*/

NTSTATUS HandleRemoveDevicePower( IN PDEVICE_OBJECT pFuncDeviceObject, IN PIRP Irp )
{
    PDXB_DEVICE_EXTENSION pExt = pFuncDeviceObject->DeviceExtension;
    PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation( Irp );
    POWER_STATE State;

    DxbPnpStopDevice(pExt, NULL, NULL);

    State = pIrpStack->Parameters.Power.State;

    SetDevicePowerState(pFuncDeviceObject, State.DeviceState);

    return DefaultPowerHandler(pFuncDeviceObject, Irp);
}


/*---------------------------------------------------------------------------*/
/*  Name: HandleAddPowerCompletion                                           */
/* --------------------------------------------------------------------------*/ 
/*    In: - pFuncDeviceObject, functional device object created by device    */
/*        - Irp, I/O request                                                 */   
/*---------------------------------------------------------------------------*/
/* InOut:                                                                    */
/*---------------------------------------------------------------------------*/
/*   Out:                                                                    */
/*---------------------------------------------------------------------------*/
/* Description: Completion routine for IRP_MN_SET_POWER requests to enter    */
/*   higher power states                                                     */
/*---------------------------------------------------------------------------*/

NTSTATUS HandleAddPowerCompletion( IN PDEVICE_OBJECT pFuncDeviceObject, IN PIRP Irp, IN PVOID NotUsed )
{
  NTSTATUS           Status;
  PIO_STACK_LOCATION pIrpStack;
  POWER_STATE_TYPE   Type;
  POWER_STATE        State;
  PDXB_DEVICE_EXTENSION pExt;

  DigiDump(DIGIPNP, ("DXB!HandleAddPowerCompletion: Start\n"));

  pExt = pFuncDeviceObject->DeviceExtension;
  
  if (Irp->PendingReturned)
  {
      IoMarkIrpPending(Irp);  // lower-level pended this IRP
  }

  Status = Irp->IoStatus.Status;
  if (!NT_SUCCESS( Status ) )
  {
    DigiDump(DIGIPNP, ("DXB!HandleAddPowerCompletion: Status=0x%lx not OK\n", Status));
    PoStartNextPowerIrp(Irp);
    return Status;      
  }

  pIrpStack = IoGetCurrentIrpStackLocation( Irp );

  Type  = pIrpStack->Parameters.Power.Type;
  State = pIrpStack->Parameters.Power.State;

  ASSERT(pIrpStack->MajorFunction == IRP_MJ_POWER);
  ASSERT(pIrpStack->MinorFunction == IRP_MN_SET_POWER);

  if ( Type == SystemPowerState )
  {
    // restoring power to the system
    DigiDump(DIGIPNP, ("DXB!HandleAddPowerCompletion: SystemPowerState IRP:0x%x\n", pFuncDeviceObject->CurrentIrp ));
  
    pExt = pFuncDeviceObject->DeviceExtension;

    Status = SendDeviceSetPower( pFuncDeviceObject, PowerDeviceD0, FALSE);
    pExt->SysPower = State.SystemState;
  }
  else
  {
    DigiDump(DIGIPNP, ("DXB!HandleAddPowerCompletion: DevicePowerState IRP:0x%x\n", pFuncDeviceObject->CurrentIrp ));

    if (NT_ERROR(DxbPnpStartDevice(pExt, NULL, NULL)))
    {	
        if (ChildrenRemoved(pExt))
         {
            // If any child devices were found - notify pnp system of the removal.
            IoInvalidateDeviceRelations(pExt->pPdo, BusRelations);
            DigiDump(DIGIPNP, ("DXB!HandleAddPowerCompletion: InvalidateDeviceRelations\n"));
        }                        
    }

    SetDevicePowerState(pFuncDeviceObject, State.DeviceState);
  }

  PoStartNextPowerIrp( Irp );

  DigiDump(DIGIPNP, ("DXB!HandleAddPowerCompletion: End, Status=0x%lx\n", Status));
 
  return Status;
}


/*---------------------------------------------------------------------------*/
/*  Name: SendDeviceSetPowerCompletion                                       */
/* --------------------------------------------------------------------------*/ 
/*    In:                                                                    */                                   
/*---------------------------------------------------------------------------*/
/* InOut:                                                                    */
/*---------------------------------------------------------------------------*/
/*   Out:                                                                    */
/*---------------------------------------------------------------------------*/
/* Description:                                                              */
/*---------------------------------------------------------------------------*/

VOID SendDeviceSetPowerCompletion( PDEVICE_OBJECT junk, UCHAR MinorFunction, POWER_STATE State,
                            POWERCOMPLETIONCONTEXT* context, PIO_STATUS_BLOCK pstatus)
{  
  DigiDump(DIGIPNP, ("DXB!SendDeviceSetPowerCompletion: Start\n"));

  // If SendDeviceSetPower is waiting, set the event it's waiting on.
  if (context->pEvent)
  {
    // satisfy wait
    context->Status = pstatus->Status;
    KeSetEvent(context->pEvent, EVENT_INCREMENT, FALSE);
  }
  else
  {
    ExFreePool(context);
  }

  DigiDump(DIGIPNP, ("DXB!SendDeviceSetPowerCompletion: End\n"));
}


/*---------------------------------------------------------------------------*/
/*  Name: SendDeviceSetPower                                                 */
/* --------------------------------------------------------------------------*/ 
/*    In: - pFuncDeviceObject, functional device object created by device    */
/*        - PowerState, Desired new device power state                       */
/*        - SystemContext                                                    */
/*---------------------------------------------------------------------------*/
/* InOut:                                                                    */
/*---------------------------------------------------------------------------*/
/*   Out:                                                                    */
/*---------------------------------------------------------------------------*/
/* Description:                                                              */
/*---------------------------------------------------------------------------*/

NTSTATUS SendDeviceSetPower( PDEVICE_OBJECT pFuncDeviceObject, DEVICE_POWER_STATE DevPower, BOOLEAN wait)
{
  NTSTATUS           Status;
  KEVENT             Event;
  POWER_STATE        State;
  PDXB_DEVICE_EXTENSION pExt;
  POWERCOMPLETIONCONTEXT *pComplContext;


  DigiDump(DIGIPNP, ("DXB!SendDeviceSetPower: Start\n"));
    
  State.DeviceState = DevPower;

  pExt = pFuncDeviceObject->DeviceExtension;

  pComplContext = (POWERCOMPLETIONCONTEXT*) ExAllocatePoolWithTag(NonPagedPool, sizeof(POWERCOMPLETIONCONTEXT), DefaultPoolTag);
  if (!pComplContext)
  {
    DigiDump(DIGIPNP, ("DXB!*SendDeviceSetPower: No resources\n"));
    return STATUS_INSUFFICIENT_RESOURCES;
  }
  RtlZeroMemory(pComplContext, sizeof(POWERCOMPLETIONCONTEXT));

  pComplContext->pFdo = pFuncDeviceObject;

  if (wait)
  {
    // wait for result
    ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
    KeInitializeEvent(&Event, NotificationEvent, FALSE);
    pComplContext->pEvent = &Event;
  }
  else
  {
    pComplContext->pEvent = NULL;
  }

  Status = PoRequestPowerIrp(pExt->pNextDeviceObject, 
                             IRP_MN_SET_POWER, 
                             State,
                             (PREQUEST_POWER_COMPLETE)SendDeviceSetPowerCompletion, 
                             pComplContext, 
                             NULL);

  if ( Status == STATUS_PENDING && wait)
  {
    // wait for power request to finish
    KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
    Status = pComplContext->Status;
    ExFreePool(pComplContext);
  }
 
  DigiDump(DIGIPNP, ("DXB!SendDeviceSetPower: End\n"));
  return Status;
}


/*---------------------------------------------------------------------------*/
/*  Name: SetDevicePowerState                                                */
/* --------------------------------------------------------------------------*/ 
/*    In: - pFuncDeviceObject, functional device object created by device    */
/*        - PowerState, Desired new device power state                       */
/*---------------------------------------------------------------------------*/
/* InOut:                                                                    */
/*---------------------------------------------------------------------------*/
/*   Out:                                                                    */
/*---------------------------------------------------------------------------*/
/* Description:                                                              */                                                  
/*---------------------------------------------------------------------------*/

VOID SetDevicePowerState(PDEVICE_OBJECT pFuncDeviceObject, DEVICE_POWER_STATE DevPower)
{
  PDXB_DEVICE_EXTENSION pExt = pFuncDeviceObject->DeviceExtension;
  DXB_POWER_STATE oldstate = pExt->PowerState;
  
  POWER_STATE PoOldPowerState;
  POWER_STATE PoNewPowerState;

  DigiDump(DIGIPNP, ("DXB!SetDevicePowerState: Start\n"));

  ASSERT(DevPower != pExt->DevPower); // only call for change in power state!
  pExt->DevPower = DevPower;

  if (DevPower > PowerDeviceD0)
  {
    // power is off
    DigiDump(DIGIPNP, ("DXB!PowerState: %s -> PWRSTATE_OFF\n", DisplayPowerState(oldstate)));
    pExt->PowerState = PWRSTATE_OFF;
  }
  else
  {
    // power is on
    DigiDump(DIGIPNP, ("DXB!PowerState: %s -> PWRSTATE_ON\n", DisplayPowerState(oldstate)));
    pExt->PowerState = PWRSTATE_ON;
  }

  PoNewPowerState.DeviceState = DevPower;
  
  PoOldPowerState = PoSetPowerState(pFuncDeviceObject, DevicePowerState, PoNewPowerState);

  DigiDump(DIGIPNP, ("DXB!SetDevicePowerState: (OldSysState=%s) OldDevState=%s, NewDevState=%s\n",
           DisplayPowerSystemState(PoOldPowerState.SystemState),
           DisplayPowerDeviceState(PoOldPowerState.DeviceState),
           DisplayPowerDeviceState(PoNewPowerState.DeviceState) ));

  DigiDump(DIGIPNP, ("DXB!SetDevicePowerState: End\n"));
}

/*---------------------------------------------------------------------------*/
/*  Name: CompleteRequest                                                    */
/* --------------------------------------------------------------------------*/ 
/*    In: - pIrp, I/O request in question                                    */
/*        - Status, Standard status code                                     */
/*        - Info, Additional information related to status code              */
/*---------------------------------------------------------------------------*/
/* InOut:                                                                    */
/*---------------------------------------------------------------------------*/
/*   Out:                                                                    */
/*---------------------------------------------------------------------------*/
/* Description: Mark I/O request complete                                    */
/*---------------------------------------------------------------------------*/

NTSTATUS CompleteRequest( IN PIRP Irp, IN NTSTATUS Status, IN ULONG Info)
{
  DigiDump(DIGIPNP, ("DXB!CompleteRequest: Status=0x%lx\n", Status));

  Irp->IoStatus.Status = Status;
  Irp->IoStatus.Information = Info;
  IoCompleteRequest( Irp, IO_NO_INCREMENT );

  return Status;
}

#endif // NT5_BUILD
