/*****************************************************************************
* Copyright (c) 1990-99 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    : power.c
 *
 * Purpose        : Power Irp Handler routines
 *
 *----------------------------------------------------------------------
 */
#include "header.h"
#if NT5_BUILD

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

#ifndef	ARRAY_SIZE
#define ARRAY_SIZE( a ) 	(( sizeof(a) / sizeof(a)[0] ) )
#endif

//__inline PCSTR
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;  
}

STATIC const struct POWER_MINOR_TABLE PowerIrps[] = 
{
	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"
};


PCSTR GetPowerIrpName( ULONG Minor )
{
	return GetIrpName( Minor, PowerIrps, ARRAY_SIZE( PowerIrps )) ;
}


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

#define GetPowerSystemState(Type) \
	GetIrpName( (ULONG)(Type), PowerSystemStates, ARRAY_SIZE( PowerSystemStates ) )

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

#define GetPowerDeviceState(Type) \
	GetIrpName( (ULONG)(Type), PowerDeviceStates, ARRAY_SIZE( PowerDeviceStates ) )

STATIC NTSTATUS
PowerForwardIrp( IN PDEVICE_OBJECT pDevice, IN OUT PIRP pIrp )
{
	ASSERT( pDevice );
	ASSERT( pIrp );
	PoStartNextPowerIrp( pIrp );
	IoSkipCurrentIrpStackLocation( pIrp );
	return PoCallDriver( pDevice, pIrp );
}

STATIC NTSTATUS
PowerIrpCompletion( IN PDEVICE_OBJECT pDevice, IN PIRP pIrp, IN PKEVENT pEvent )
{
	KeSetEvent( pEvent, IO_NO_INCREMENT, FALSE );
	return STATUS_MORE_PROCESSING_REQUIRED;
}

STATIC NTSTATUS
PowerForwardIrpWait( IN PDEVICE_OBJECT pDevice, IN OUT PIRP pIrp )
{
	KEVENT Event;
	NTSTATUS Status;
	ASSERT( pDevice );
	ASSERT( pIrp );

	KeInitializeEvent( &Event, SynchronizationEvent, FALSE );

	IoCopyCurrentIrpStackLocationToNext( pIrp );
	IoSetCompletionRoutine( pIrp, PowerIrpCompletion, &Event, TRUE, TRUE, TRUE );

	Status = PoCallDriver( pDevice, pIrp );
	if ( Status == STATUS_PENDING )
	{
		KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL );
		Status = pIrp->IoStatus.Status;
	}
	return Status;
}

void __inline CompletePowerIrp( IN PIRP Irp, NTSTATUS Status )
{
	PoStartNextPowerIrp( Irp );
   Irp->IoStatus.Status = Status;
   IoCompleteRequest( Irp, IO_NO_INCREMENT );
}

STATIC VOID
PowerComplete( 
	IN PDEVICE_OBJECT   pDevice,
	IN UCHAR            MinorFunction,
	IN POWER_STATE      PowerState,
	IN PVOID            Context,
	IN PIO_STATUS_BLOCK IoStatus )
{
	const PIRP pSystemPowerIrp = Context;
	PowerForwardIrp( pDevice, pSystemPowerIrp );
}

void SetPowerD3( IN PPORT_EXT pExt )
{
	PORT_STATE currentState = pExt->State;
   
  	// 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: 
	   	pExt->State = OpenState_PowerD3;
	      break;
	   case IdleState:
	   	pExt->State = IdleState_PowerD3;
	      break;
	   default:
			break;				
	}
}

void SetPowerD0( IN PPORT_EXT pExt )
{

   UNICODE_STRING DxbDevice = {0};
   NTSTATUS Status;
	
	Status = DsOpenDevice( &pExt->DxbDevice, &pExt->DsHandle);
   DigiDump(DIGIPOWER, ("ASYN!Open: DsOpen Status=0x%x\n", Status));
   if (NT_ERROR(Status))
   {
   	AsynLogError(gpDriverObject, ASYN_DSTREAMS_OPEN_ERROR, DxbDevice.Buffer, Status);
      goto Exit;
	}
   
   switch ( pExt->State )
   {
   	case OpenState_PowerD3:
      {
		   ULONG       ErrorCode;

		   Status = DsOpen( pExt->DsHandle, SERIAL_STREAM_DEVNAME, (HANDLE)pExt, &pExt->Port,
		                    sizeof (pExt->Port), &pExt->DsPortHandle, &ErrorCode);
		   DigiDump(DIGIOPEN, ("ASYN!Open: DsOpen Status=0x%x\n", Status));
         pExt->State = OpenState;
		   if (Status != STATUS_SUCCESS)
         	goto Exit;
         
			pExt->SdxbValues.Tout = 0;
			pExt->SyncOutSequence = 0;
			pExt->SyncInSequence = 0;
			pExt->TxBytesPending = 0;
			pExt->SdxbValues.Estat = 0;

			Status = AsynInitSDXBDevice( pExt, NULL ); 
         	goto Exit;
         break;
		}         
      case IdleState_PowerD3:
		{      
      	pExt->State = IdleState;
         break;
		}         
		default:
      	break;         
	}   
   
Exit:   
	pExt->PnpState &= ~PNPSTATE_STOPPED;
   KeAcquireSpinLock(&pExt->PortLock, &pExt->PortIrql);
	AsynSchedulerLoop( pExt );
   KeReleaseSpinLock( &pExt->PortLock, pExt->PortIrql);
}

NTSTATUS
AsynSetPower( IN PPORT_EXT pExt, IN PIRP pIrp, IN PIO_STACK_LOCATION pIos )
/*++

Description:

	Handle an IRP_MN_SET_POWER Irp for the FDO.  PowerDeviceD0 is fully powered, others
	are sleep mode.  For sleepmodes, call our stop handler.  For powered mode, start
	the port.

	Do NOT pass in the pIrp and pIos pointers to Pnp* routines!  They don't know 
	how to handle Power Irps!

Parameters:

	pExt
		Device extension for our FDO which describes the port.
	
	pIrp
		Request.

	pIos
		Parameters passed along with Irp.

--*/
{
	NTSTATUS Status = STATUS_SUCCESS;
   
	switch ( pIos->Parameters.Power.Type )
	{
		case SystemPowerState:
			DigiDump( DIGIPOWER, ( "DigiAsyn:   SystemPowerState, %s\n",GetPowerSystemState(pIos->Parameters.Power.State.SystemState) ) );
			switch ( pIos->Parameters.Power.State.SystemState )
			{
				case PowerSystemWorking:
            {
            	if ( pExt->PowerState != PowerDeviceD0 )
					{
               	// Transition to the power on state
						POWER_STATE NewState = { 0 };
                  ASSERT( pExt->PowerState == PowerDeviceD3 );	// We only use two power state.
                  
						NewState.DeviceState = pExt->PowerState = PowerDeviceD0;
						Status = PoRequestPowerIrp( pExt->pNextDeviceObject, IRP_MN_SET_POWER, NewState, PowerComplete, pIrp, NULL );
					}
               else
               {
						PoStartNextPowerIrp( pIrp );
	               Status = PowerForwardIrp( pExt->pNextDeviceObject, pIrp );
					}                  
					break;
				}               
				case PowerSystemSleeping1:
				case PowerSystemSleeping2:
				case PowerSystemSleeping3:
				case PowerSystemHibernate:
				{
            	if ( pExt->PowerState != PowerDeviceD3 )
               {
               	// Transition to the power off state
						POWER_STATE NewState = { 0 };
                  ASSERT( pExt->PowerState == PowerDeviceD0 );	// We only use two power state.
                  
						NewState.DeviceState = pExt->PowerState = PowerDeviceD3;
						Status = PoRequestPowerIrp( pExt->pNextDeviceObject, IRP_MN_SET_POWER, NewState, PowerComplete, pIrp, NULL );
					}           
               else
               {
						PoStartNextPowerIrp( pIrp );
	               Status = PowerForwardIrp( pExt->pNextDeviceObject, pIrp );
					}                  
					break;
				}

				case PowerSystemShutdown:
		         Status = PowerForwardIrp( pExt->pNextDeviceObject, pIrp );
					break;

				default:
					DigiDump( DIGIERRORS, ( "DigiAsyn: *** PowerSetFdo: Invalid PowerSystem state 0x%x! ***\n",pIos->Parameters.Power.State.SystemState ) );
      		   Status = PowerForwardIrp( pExt->pNextDeviceObject, pIrp );
					break;
			}
			break;

		case DevicePowerState:
			DigiDump( DIGIPOWER, ( "DigiAsyn:    DevicePowerState, %s\n",GetPowerDeviceState(pIos->Parameters.Power.State.DeviceState) ) );
			switch ( pIos->Parameters.Power.State.DeviceState )
         {
         	case PowerDeviceD0:
      			(void)PowerForwardIrpWait( pExt->pNextDeviceObject, pIrp );
               SetPowerD0( pExt );
               CompletePowerIrp( pIrp, Status );
            	break;
               
				case PowerDeviceD1:
            case PowerDeviceD2:
            case PowerDeviceD3:
					SetPowerD3( pExt );
		         Status = PowerForwardIrp( pExt->pNextDeviceObject, pIrp );
            	break;
               
				default:
		         Status = PowerForwardIrp( pExt->pNextDeviceObject, pIrp );
            	break;                   
         }
			break;
         
			break;

		default:
			DigiDump( DIGIERRORS, ( "DigiAsyn: *** PowerSetFdo: Invalid Power.Type 0x%x! ***\n",pIos->Parameters.Power.Type ) );
			break;
	}
	return STATUS_SUCCESS;
}

NTSTATUS
AsynPowerDispatch( IN PDEVICE_OBJECT pDevice, IN PIRP pIrp )

{
	
   PPORT_EXT pExt = pDevice->DeviceExtension;
   PIO_STACK_LOCATION  pIos = IoGetCurrentIrpStackLocation(pIrp);
	ULONG const Minor = pIos->MinorFunction;
	NTSTATUS Status = STATUS_SUCCESS;
   
	DigiDump( DIGIPOWER, ( "DigiAsyn!PowerDispatch: %s, pExt 0x%08x, pIrp 0x%08x currentIrql=%x\n",GetPowerIrpName(Minor), pExt, pIrp, KeGetCurrentIrql() ) );

	switch ( Minor )
	{
		case IRP_MN_SET_POWER:			
			Status = AsynSetPower( pExt, pIrp, pIos );
			break;

		case IRP_MN_QUERY_POWER:
			switch ( pIos->Parameters.Power.Type )
			{
				case SystemPowerState:
					DigiDump( DIGIPOWER, ( "DigiAsyn: QueryPower: SystemPowerState, %s\n",GetPowerSystemState(pIos->Parameters.Power.State.SystemState) ) );
					break;

				case DevicePowerState:
					DigiDump( DIGIPOWER, ( "DigiAsyn: QueryPower: DevicePowerState, %s\n",GetPowerDeviceState(pIos->Parameters.Power.State.DeviceState) ) );
					break;
			}
         Status = PowerForwardIrp( pExt->pNextDeviceObject, pIrp );
			break;

		case IRP_MN_WAIT_WAKE:
		case IRP_MN_POWER_SEQUENCE:
		default:
			DigiDump( DIGIPOWER|DIGIERRORS, ( "DigiAsyn: Power: Passing unhandled Power Irp %s on to PDO!\n",GetPowerIrpName( Minor ) ) );
         Status = PowerForwardIrp( pExt->pNextDeviceObject, pIrp );
			break;
	}

	if ( !NT_SUCCESS( Status ) )
	{
		DigiDump( DIGIPOWER, ( "DigiAsyn: PowerFdo: %s, returning status 0x%x\n",GetPowerIrpName(Minor), Status ) );
	}
	return Status;

}
#endif