/*****************************************************************************
* Copyright (c) 1990-2000 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 File    : AsynPower.c
 *
 * Purpose        : Power Irp Handler routines
 *
 *----------------------------------------------------------------------
 */
#include "digiasyn.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 AsynPowerIrps[] = 
{
	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), AsynPowerIrps, ARRAY_SIZE( AsynPowerIrps ) )

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

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

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

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

const struct POWER_MINOR_TABLE AsynPowerStates[] =
{
    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), AsynPowerStates, ARRAY_SIZE( AsynPowerStates ) )
#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: AsynPowerDispatch                                                  */
/* --------------------------------------------------------------------------*/ 
/*    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 AsynPowerDispatch( IN PDEVICE_OBJECT pFuncDeviceObject, IN PIRP Irp )
{
  NTSTATUS               Status;
  PIO_STACK_LOCATION     pIrpStack;
  UCHAR                  fcn;
  PASYN_PORT_EXT         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, ("ASYN!AsynPowerDispatch(%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, ("ASYN!AsynPowerDispatch(%s)\n", DisplayPowerIrpName(fcn) ));
  }
#endif

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

 
/*---------------------------------------------------------------------------*/
/*  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 )
{
  PASYN_PORT_EXT pExt;
  
  DigiDump(DIGIPNP, ("ASYN!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)
{
  PASYN_PORT_EXT     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, ("ASYN!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, ("ASYN!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, ("ASYN!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 )
{
  PASYN_PORT_EXT     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, ("ASYN!HandleSetPower: Unspecified SystemPowerState\n"));
          if (pExt->PowerState == PWRSTATE_OFF_PENDING)
          {
            DigiDump(DIGIPNP, ("ASYN!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, ("ASYN!HandleSetPower: Add SystemPower\n"));
            return HandleAddSystemPower(pFuncDeviceObject, Irp);
          }
          else if (State.SystemState > pExt->SysPower)
          {
            // Remove power
            DigiDump(DIGIPNP, ("ASYN!HandleSetPower: Remove SystemPower\n"));
            return HandleRemoveSystemPower(pFuncDeviceObject, Irp);
          }
          else
          {
            // Do nothing
            DigiDump(DIGIPNP, ("ASYN!HandleSetPower: No SystemPowerState change\n"));
            if (pExt->PowerState == PWRSTATE_OFF_PENDING)
            {
              DigiDump(DIGIPNP, ("ASYN!PowerState: PWRSTATE_OFF_PENDING -> PWRSTATE_ON\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, ("ASYN!HandleSetPower: Add SystemPower\n"));
            return HandleAddSystemPower(pFuncDeviceObject, Irp);
          }
          else
          {
            // Do nothing
            DigiDump(DIGIPNP, ("ASYN!HandleSetPower: No SystemPowerState change\n"));
            if (pExt->PowerState == PWRSTATE_OFF_PENDING)
            {
              DigiDump(DIGIPNP, ("ASYN!PowerState: PWRSTATE_OFF_PENDING -> PWRSTATE_ON\n"));
              pExt->PowerState = PWRSTATE_ON;
            }
            return DefaultPowerHandler(pFuncDeviceObject, Irp);
          }
          break;

      default:
          // Do nothing (unknown SystemPowerState value)
          DigiDump(DIGIERROR, ("ASYN!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, ("ASYN!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, ("ASYN!HandleSetPower: Add DevicePower\n"));
            return HandleAddDevicePower(pFuncDeviceObject, Irp);
          }
          else if (State.SystemState > pExt->SysPower)
          {
            // Remove power
            DigiDump(DIGIPNP, ("ASYN!HandleSetPower: Remove DevicePower\n"));
            return HandleRemoveDevicePower(pFuncDeviceObject, Irp);
          }
          else
          {
            // Do nothing
            DigiDump(DIGIPNP, ("ASYN!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, ("ASYN!HandleSetPower: Add DevicePower\n"));
            return HandleAddSystemPower(pFuncDeviceObject, Irp);
          }
          else
          {
            // Do nothing
            DigiDump(DIGIPNP, ("ASYN!HandleSetPower: No DevicePowerState change\n"));
            return DefaultPowerHandler(pFuncDeviceObject, Irp);
          }
          break;

      default:
          // Do nothing (unknown DevicePowerState value)
          DigiDump(DIGIERROR, ("ASYN!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 )
{
    PASYN_PORT_EXT 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 )
{
    PASYN_PORT_EXT     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 )
{
    PASYN_PORT_EXT 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 )
{
    PASYN_PORT_EXT pExt = pFuncDeviceObject->DeviceExtension;
    PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation( Irp );
    POWER_STATE State;

    SetPowerD3(pExt);

    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;
  PASYN_PORT_EXT     pExt;

  DigiDump(DIGIPNP, ("ASYN!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, ("ASYN!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, ("ASYN!HandleAddPowerCompletion: SystemPowerState IRP:0x%x\n", pFuncDeviceObject->CurrentIrp ));
  
    pExt = pFuncDeviceObject->DeviceExtension;

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

    SetPowerD0(pExt);

    SetDevicePowerState(pFuncDeviceObject, State.DeviceState);
  }

  PoStartNextPowerIrp( Irp );

  DigiDump(DIGIPNP, ("ASYN!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, ("ASYN!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, ("ASYN!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;
  PASYN_PORT_EXT     pExt;
  POWERCOMPLETIONCONTEXT *pComplContext;


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

  pExt = pFuncDeviceObject->DeviceExtension;

  pComplContext = (POWERCOMPLETIONCONTEXT*) ExAllocatePoolWithTag(NonPagedPool, sizeof(POWERCOMPLETIONCONTEXT), DefaultPoolTag);
  if (!pComplContext)
  {
    DigiDump(DIGIPNP, ("ASYN!*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, ("ASYN!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)
{
  PASYN_PORT_EXT pExt = pFuncDeviceObject->DeviceExtension;
  ASYN_POWER_STATE oldstate = pExt->PowerState;
  
  POWER_STATE PoOldPowerState;
  POWER_STATE PoNewPowerState;

  DigiDump(DIGIPNP, ("ASYN!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, ("ASYN!PowerState: %s -> PWRSTATE_OFF\n", DisplayPowerState(oldstate)));
    pExt->PowerState = PWRSTATE_OFF;
  }
  else
  {
    // power is on
    DigiDump(DIGIPNP, ("ASYN!PowerState: %s -> PWRSTATE_ON\n", DisplayPowerState(oldstate)));
    pExt->PowerState = PWRSTATE_ON;
  }

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

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

  DigiDump(DIGIPNP, ("ASYN!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, ("ASYN!CompleteRequest: Status=0x%lx\n", Status ));

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

  return Status;
}


VOID SetPowerD3( IN PASYN_PORT_EXT pExt )
{
	PORT_STATE currentState = pExt->State;
   
   DigiDump(DIGIPOWER, ("ASYN!SetPowerD3 Port: %d State: %d\n",pExt->iSdxbPort,pExt->State));

	ASSERT( KeGetCurrentIrql() == PASSIVE_LEVEL );
  	// Cancel IRPs in progress because adapter is being powered-down
	( void )AsynPnpStopDevice( pExt, NULL, TRUE );
	switch ( currentState )
	{
		// Update Device State to guarantee that any requests will be 
	   // rejected while the port is in the 'powered down' state.
	   case OpenState: 
         if ( pExt->pPendingIrp != NULL )
			{
				// A close IRP was received while the port was being powered down. It can be completed now...
				{
					// Complete the pending Create or Close IRP
					PIRP			pIrp;
					NTSTATUS		Status = STATUS_SUCCESS;               

					DigiAcquireSpinLock( &pExt->Lock );
					pIrp = pExt->pPendingIrp;
					pExt->pPendingIrp = NULL;   
					DigiReleaseSpinLock( &pExt->Lock );

					if ( pIrp != NULL )
					{   
						DigiDump( DIGICREATE|DIGICLOSE|DIGIFLOW, ("Port %i completing %p with status=0x%x \n",pExt->iSdxbPort,pIrp,Status));
						pIrp->IoStatus.Information = 0; 
						AsynIoCompleteRequest( pIrp, Status, NULL );
					}      
			   	pExt->State = IdleState_PowerD3;
				}      
				                     
			}                  
			else
		   	pExt->State = OpenState_PowerD3;
                  
		         
	      break;
	   case IdleState:
	   	pExt->State = IdleState_PowerD3;
	      break;
	   default:
			break;				
	}
	ASSERT( KeGetCurrentIrql() == PASSIVE_LEVEL );
   DigiDump(DIGIPOWER, ("Leaving ASYN!SetPowerD3 Port: %d State: %d\n",pExt->iSdxbPort,pExt->State));
}


VOID SetPowerD0( IN PASYN_PORT_EXT pPortExt )
{

   NTSTATUS Status;
	
	Status = DsOpenDevice( &pPortExt->DxbDevice, &pPortExt->DsHandle);
   DigiDump(DIGIPOWER, ("ASYN!SetPowerD0: Port %d DsOpenDevice Status=0x%x\n", pPortExt->iSdxbPort,Status));
   if (NT_ERROR(Status))
   {
		AsynLogDsOpenDeviceError( Status, &pPortExt->DxbDevice );
      goto Exit;
	}
   
   switch ( pPortExt->State )
   {
   	case OpenState_PowerD3:
      {
		   ULONG       ErrorCode;

		   Status = DsOpen( pPortExt->DsHandle, "SERIAL", (HANDLE)pPortExt, &pPortExt->iSdxbPort,
								sizeof (pPortExt->iSdxbPort), &pPortExt->DsPortHandle, &ErrorCode);
		   DigiDump(DIGIPOWER|DIGICREATE, ("ASYN!SetPowerD0: Port %d DsOpen Status=0x%x\n", pPortExt->iSdxbPort,Status));
			if (NT_ERROR( Status ))			                        
			{
				AsynLogDsOpenFailed( &pPortExt->PortName );
				goto OpenErr;            
			}                        

         // Start both RxEngines
         Start_HighPriority_RxEngine( pPortExt );
			Start_LowPriority_RxEngine( pPortExt );
         if ( pPortExt->hRxEngine_LowPriority == NULL || pPortExt->hRxEngine_HighPriority == NULL )
         {
				// Log an error         
				AsynLogRxEngineFailed( &pPortExt->PortName );
				Status = STATUS_UNSUCCESSFUL;
				goto OpenErr;
			}         	
         
         // Restore SDXB Port settings
		   Status = AsynInitializeSdxbPort( pPortExt, TRUE );
		   if ( NT_ERROR( Status ))
         {
		   	goto OpenErr;
			}            
         pPortExt->State = OpenState;
         break;
		OpenErr:
			AsynCloseSdxbPort( pPortExt );
         break;
		}         
		case OpeningState_PowerD3:
		{
		   ULONG       ErrorCode;
		   Status = DsOpen( pPortExt->DsHandle, "SERIAL", (HANDLE)pPortExt, &pPortExt->iSdxbPort,
								sizeof (pPortExt->iSdxbPort), &pPortExt->DsPortHandle, &ErrorCode);
		   DigiDump(DIGIPOWER|DIGICREATE, ("ASYN!SetPowerD0: Port %d DsOpen Status=0x%x\n", pPortExt->iSdxbPort,Status));
			if (NT_ERROR( Status ))			                        
			{
         	// Open failed: log an error and complete the Create IRP
				PIRP pIrp = pPortExt->pPendingIrp;         
				AsynLogDsOpenFailed( &pPortExt->PortName );
            if ( pIrp != NULL )
				{            
            	pPortExt->pPendingIrp = NULL;
					pIrp->IoStatus.Information = 0;
					AsynIoCompleteRequest( pIrp, Status, NULL );
	            pPortExt->State = IdleState;
               goto OpeningExit;
				}    
			}            
			// Create IRP will be completed later          
			pPortExt->State = OpenInProgressState;
			if (NT_ERROR( AsynResetPortSettings( pPortExt )))
				AsynCloseSdxbPort( pPortExt );
		OpeningExit:
			break;      
		}      			             
      case IdleState_PowerD3:
		{      
      	pPortExt->State = IdleState;
         break;
		}         
		default:
      	break;         
	}   
Exit:   
	if ( NT_ERROR( Status ))   
	{   
		pPortExt->DeviceStateFlags |= PNP_DEVICE_FAILED;      
		IoInvalidateDeviceState( pPortExt->pPdo );
	}      
	pPortExt->PnpState &= ~PNPSTATE_STOPPED;
	DigiDump(DIGIPOWER, ("ASYN!SetPowerD0: Port %d State %x PnpState %x\n", pPortExt->iSdxbPort,pPortExt->State,pPortExt->PnpState));
}

#endif // NT5_BUILD
