///////////////////////////////////////////////////////////////////////////////
//
//    (C) Copyright 1995 - 2005 OSR Open Systems Resources, Inc.
//    All Rights Reserved
//
//    This sofware is supplied for instructional purposes only.
//
//    OSR Open Systems Resources, Inc. (OSR) expressly disclaims any warranty
//    for this software.  THIS SOFTWARE IS PROVIDED  "AS IS" WITHOUT WARRANTY
//    OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
//    THE IMPLIED WARRANTIES OF MECHANTABILITY OR FITNESS FOR A PARTICULAR
//    PURPOSE.  THE ENTIRE RISK ARISING FROM THE USE OF THIS SOFTWARE REMAINS
//    WITH YOU.  OSR's entire liability and your exclusive remedy shall not
//    exceed the price paid for this material.  In no event shall OSR or its
//    suppliers be liable for any damages whatsoever (including, without
//    limitation, damages for loss of business profit, business interruption,
//    loss of business information, or any other pecuniary loss) arising out
//    of the use or inability to use this software, even if OSR has been
//    advised of the possibility of such damages.  Because some states/
//    jurisdictions do not allow the exclusion or limitation of liability for
//    consequential or incidental damages, the above limitation may not apply
//    to you.
//
//    OSR Open Systems Resources, Inc.
//    105 Route 101A Suite 19
//    Amherst, NH 03031  (603) 595-6500 FAX: (603) 595-6503
//    email bugs to: bugs@osr.com
//
//
//    MODULE:
//
//        RedirectPnP.C -- Redirect driver V2 pnp functions
//
//    ABSTRACT:
//
//      This module handles Power management calls for the FDO. 
//
//    AUTHOR(S):
//
//        OSR Open Systems Resources, Inc.
// 
//    REVISION:   
//
//        This is a WDM compatible version of the original NT V4
//        REDIRECT driver.
//
///////////////////////////////////////////////////////////////////////////////

#include "redirect.h"


#if DBG
PUCHAR PrintSystemPowerState(SYSTEM_POWER_STATE State)
{
    PUCHAR string;

    switch(State) {

        case PowerSystemUnspecified: // = 0,
            string = (PUCHAR) "PowerSystemUnspecified";
            break;

        case PowerSystemWorking: //,
            string = (PUCHAR) "PowerSystemWorking";
            break;

        case PowerSystemSleeping1: //,
            string = (PUCHAR) "PowerSystemSleeping1";
            break;

        case PowerSystemSleeping2: //,
            string = (PUCHAR) "PowerSystemSleeping2";
            break;

        case PowerSystemSleeping3: //,
            string = (PUCHAR) "PowerSystemSleeping3";
            break;

        case PowerSystemHibernate: //,
            string = (PUCHAR) "PowerSystemHibernate";
            break;

        case PowerSystemShutdown: //,
            string = (PUCHAR) "PowerSystemShutdown";
            break;

        case PowerSystemMaximum: //
            string = (PUCHAR) "PowerSystemMaximum";
            break;

        default:
            string = (PUCHAR) "Unknown Power State ?????? Error";
            break;


    }

    return string;
}

PUCHAR PrintPowerAction(POWER_ACTION Action)
{
    PUCHAR string;

    switch(Action) {

        case PowerActionNone: // = 0,
            string = (PUCHAR) "PowerActionNone";
            break;

        case PowerActionReserved: //,
            string = (PUCHAR) "PowerActionReserved";
            break;

        case PowerActionSleep: //,
            string = (PUCHAR) "PowerActionSleep";
            break;

        case PowerActionHibernate: //,
            string = (PUCHAR) "PowerActionHibernate";
            break;

        case PowerActionShutdown: //,
            string = (PUCHAR) "PowerActionShutdown";
            break;

        case PowerActionShutdownReset: //,
            string = (PUCHAR) "PowerActionShutdownReset";
            break;

        case PowerActionShutdownOff: //,
            string = (PUCHAR) "PowerActionShutdownOff";
            break;

        case PowerActionWarmEject: //
            string = (PUCHAR) "PowerActionWarmEject";
            break;

        default:
            string = (PUCHAR) "Unknown Power Action ???? Error";
            break;

    }

    return string;
}

PUCHAR PrintDevicePowerState(DEVICE_POWER_STATE State)
{
    PUCHAR string;

    switch(State) {

        case PowerDeviceUnspecified: // = 0,
            string = (PUCHAR) "PowerDeviceUnspecified";
            break;

        case PowerDeviceD0: //,
            string = (PUCHAR) "PowerDeviceD0";
            break;

        case PowerDeviceD1: //,
            string = (PUCHAR) "PowerDeviceD1";
            break;

        case PowerDeviceD2: //,
            string = (PUCHAR) "PowerDeviceD2";
            break;

        case PowerDeviceD3: //,
            string = (PUCHAR) "PowerDeviceD3";
            break;

        case PowerDeviceMaximum: //
            string = (PUCHAR) "PowerDeviceMaximum";
            break;

        default:
            string = (PUCHAR) "Unknown Device Power State ???? Error";
            break;

    }

    return string;
}

PUCHAR PrintPowerStateType(POWER_STATE_TYPE Type)
{
    PUCHAR string;

    switch(Type) {
        case SystemPowerState:// = 0,
            string = (PUCHAR) "SystemPowerState";
            break;

        case DevicePowerState: //
            string = (PUCHAR) "DevicePowerState";
            break;

        default:
            string = (PUCHAR) "Unknown POWER_STATE_TYPE";
            break;

    }

    return string;
}
#endif // DBG

///////////////////////////////////////////////////////////////////////////////
//
//  RedirectPower
//
//      Handles power Irps sent to the FDO.
//      This driver is the power policy owner for its device stack.
//
//  INPUTS:
//
//      DeviceObject - Pointer to the device object.
//      Irp          - Pointer to the irp.
//
//  OUTPUTS:
//
//      None.
//
//  RETURNS:
//
//      STATUS_SUCCESS if okay, an error otherwise.
//
//  IRQL:
//
//      This routine is called at IRQL PASSIVE_LEVEL if the DO_POWER_PAGABLE
//      flag is set in the device object, otherwise its called at any IRQL
//      <= DISPATCH_LEVEL.
//
//  NOTES:
//
///////////////////////////////////////////////////////////////////////////////
NTSTATUS RedirectPower(IN PDEVICE_OBJECT DeviceObject,
                      IN PIRP           Irp)
{
    NTSTATUS            status;
    POWER_STATE         powerState;
    POWER_STATE_TYPE    powerType;
    PIO_STACK_LOCATION  stack;
    BOOLEAN             needCallback = FALSE;
    PENCRYPT_DEVICE_EXT fdoData;
    
#if DBG
    DbgPrint(DRIVER_NAME "RedirectPower Entered.\n");
#endif

    //
    // Get our device extension from the device object
    //
    fdoData = (PENCRYPT_DEVICE_EXT)DeviceObject->DeviceExtension;

    //
    // Get the information that we need out of the irp.
    //
    stack = IoGetCurrentIrpStackLocation (Irp);
    powerType = stack->Parameters.Power.Type;
    powerState = stack->Parameters.Power.State;

    //
    // What do we have.....
    //
    switch (stack->MinorFunction) {

        case IRP_MN_SET_POWER:
#if DBG
            DbgPrint(DRIVER_NAME "Setting %s state to %s\n",
                         PrintPowerStateType(powerType),
                         ((powerType == SystemPowerState) ? 
                            PrintSystemPowerState(powerState.SystemState) :
                                PrintDevicePowerState(powerState.DeviceState)));
#endif

            //
            // We always set success for SET_POWER
            //
            Irp->IoStatus.Status = STATUS_SUCCESS;

            switch (powerType) {

                case DevicePowerState:
                    
                    if (fdoData->DevicePowerState < powerState.DeviceState) {

                        //
                        // Powering down. Power down Irps are handled as they
                        // travel down the stack. This means that we set the 
                        // new device power state and update our state in the 
                        // device extension now. 
                        //

                        PoSetPowerState (fdoData->DeviceObject, 
                                         powerType, 
                                         powerState);
                        
                        fdoData->DevicePowerState = powerState.DeviceState;

                    } else if (fdoData->DevicePowerState == 
                                    powerState.DeviceState) {

                        //
                        // Power is staying the same, don't need 
                        // to do anything here.
                        //

                    } else {

                        //
                        // Powering Up. This Irp will be handled when it's 
                        // coming back up the stack.  This is a special case as 
                        // we do not necessarilly need to mark the IRP pending, 
                        // but we can just propagate the pending flag in the 
                        // completion routine.
                        //

                        //
                        // Setup the next stack location
                        //
                        IoCopyCurrentIrpStackLocationToNext(Irp);
        
                        //
                        // Setup our completion routine
                        //
                        IoSetCompletionRoutine(Irp, 
                                               RedirectFDOPowerComplete,
                                               NULL, 
                                               TRUE, 
                                               TRUE, 
                                               TRUE);
        
                        //
                        // Pass the power IRP down
                        //
                        return PoCallDriver (fdoData->DeviceToSendIrpsTo, Irp);

                    }
                    break;

                case SystemPowerState:

                    //
                    // Need a callback here.
                    // In the completion routine we'll choose an appropriate 
                    // device state and request a DevicePowerState Irp.
                    //
                    needCallback = TRUE;
                    
                    break;
            }

            break;

        case IRP_MN_QUERY_POWER:
#if DBG
            DbgPrint(DRIVER_NAME "Querying %s state %s\n",
                         PrintPowerStateType(powerType),
                         ((powerType == SystemPowerState) ? 
                            PrintSystemPowerState(powerState.SystemState) :
                                PrintDevicePowerState(powerState.DeviceState)));
#endif

            //
            // We always accept the query
            //
            Irp->IoStatus.Status = STATUS_SUCCESS;

            switch (powerType) {

                case DevicePowerState:

                    //
                    // No callback needed here.
                    //
                    needCallback = FALSE;

                    break;

                case SystemPowerState:


                    //
                    // Need a callback to request the device query power IRP
                    //
                    needCallback = TRUE;
                    
                    break;
            }

            break;     

        case IRP_MN_WAIT_WAKE:

            //
            // Fall through...
            //

        default:

            needCallback = FALSE;

            break;
    }

    //
    // See if we set the needCallback flag.  If we did, we need to know
    // when this Power Irp completes.   
    //
    if (needCallback) {

        //
        // Mark the Irp as pending
        //
        IoMarkIrpPending(Irp);      
        
        //
        // Setup the next stack location
        //
        IoCopyCurrentIrpStackLocationToNext(Irp);
        
        //
        // Setup our completion routine
        //
        IoSetCompletionRoutine(Irp, 
                               RedirectFDOPowerComplete,
                               NULL, 
                               TRUE, 
                               TRUE, 
                               TRUE);
        
        //
        // Pass the power IRP down
        //
        PoCallDriver (fdoData->DeviceToSendIrpsTo, Irp);
        
        //
        // the Irp is pending...
        //
        status = STATUS_PENDING;

    } else {

        //
        // We're not interested in this irp, so we'll start
        // the next one and pass this one down.
        //
        
        PoStartNextPowerIrp (Irp);

        IoSkipCurrentIrpStackLocation(Irp);

        status =  PoCallDriver (fdoData->DeviceToSendIrpsTo, Irp);
        
    }
    return status;
}

///////////////////////////////////////////////////////////////////////////////
//
//  RedirectFDOPowerComplete
//
//      Handles power Irps completions
//
//  INPUTS:
//
//      DeviceObject - Pointer to the device object.
//      Irp          - Pointer to the irp.
//      Context      - Completion context
//
//  OUTPUTS:
//
//      None.
//
//  RETURNS:
//
//      STATUS_MORE_PROCESSING_REQUIRED if IRP to be retained, some
//      other status otherwise.
//
//  IRQL:
//
//    This routine is called at any IRQL <= DISPATCH_LEVEL in an 
//    arbitrary context.
//
//  NOTES:
//
///////////////////////////////////////////////////////////////////////////////
NTSTATUS RedirectFDOPowerComplete(IN PDEVICE_OBJECT DeviceObject,
                                 IN PIRP           Irp,
                                 IN PVOID          Context)
{
    POWER_STATE         powerState;
    POWER_STATE_TYPE    powerType;
    PIO_STACK_LOCATION  stack;
    PENCRYPT_DEVICE_EXT fdoData;
    NTSTATUS            status = STATUS_SUCCESS;
    
    UNREFERENCED_PARAMETER (Context);

#if DBG
    DbgPrint(DRIVER_NAME "Entered RedirectFDOPowerComplete\n");
#endif

    //
    // If another device in the stack failed this request, there is no reason
    // to go on.  Just let I/O processing continue.
    //
    if (!NT_SUCCESS(Irp->IoStatus.Status)) {

        PoStartNextPowerIrp(Irp);
        return STATUS_SUCCESS;
    }

    //
    // Get our device extension out of the device object
    //
    fdoData = (PENCRYPT_DEVICE_EXT) DeviceObject->DeviceExtension;

    //
    // Get the information that we need out of the irp.
    //
    stack = IoGetCurrentIrpStackLocation (Irp);
    powerType = stack->Parameters.Power.Type;
    powerState = stack->Parameters.Power.State;
    
    //
    // what did we find completed....
    //

    switch (stack->MinorFunction) {

        case IRP_MN_SET_POWER:

            switch (powerType) {
        
                case DevicePowerState:

                    //
                    // Device Power State change....
                    //

                    //
                    // We need to propagate the pending flag for this case
                    // as we did not mark the IRP pending in the dispatch
                    // routine for this case.
                    //
                    if(Irp->PendingReturned) {
                        IoMarkIrpPending(Irp);
                    }

                    //
                    // We should be powering up here. Power downs or power stay 
                    // the same situations were handled in the power dispatch 
                    // routine.
                    //
                    ASSERT(fdoData->DevicePowerState > powerState.DeviceState);

                    //
                    // Now we can set our new (higher) power state
                    //
                    PoSetPowerState (fdoData->DeviceObject, 
                                     powerType, 
                                     powerState);

                    fdoData->DevicePowerState = powerState.DeviceState;

                    //
                    // No need to reclaim the IRP here, so we will 
                    // just return STATUS_SUCCESS
                    //
                    status = STATUS_SUCCESS;

                    break;

                case SystemPowerState:
                    
                    //
                    // System Power State change. Note that if we were doing 
                    // "true" power management we'd choose an appropriate 
                    // DeviceState based on the capabilities of the devices in 
                    // the stack. Here we just choose D0 for powering up and D3 
                    // for powering down
                    //
                   
                    if (powerState.SystemState > fdoData->SystemPowerState) {
#if DBG
                        DbgPrint(DRIVER_NAME "Powering Down\n");
#endif

                        //
                        // Reset the powerState variable and use it 
                        // for the power IRP request.
                        //
                        powerState.DeviceState = PowerDeviceD3;

                    } else if(powerState.SystemState < 
                                fdoData->SystemPowerState) {
#if DBG
                        DbgPrint(DRIVER_NAME "Powering Up\n");
#endif

                        //
                        // Reset the powerState variable and use it 
                        // for the power IRP request.
                        //
                        powerState.DeviceState = PowerDeviceD0;
                    } else {
#if DBG
                        DbgPrint(DRIVER_NAME "Power Unchanged\n");
#endif
                    }

                    //
                    // Save the system IRP.  We don't have to synchronize 
                    // access to this pointer in the device extension as we are 
                    // guaranteed to only get one system power IRP through our 
                    // driver at a time.
                    //
                    fdoData->SystemIrp = Irp;

                    //
                    // Request a Device _SET_POWER Irp. We're the power
                    // policy owner so we'll set our device power state when we
                    // receive this DevicePower Irp that we're requesting. 
                    // System power Irps are notifications, DON'T change your 
                    // device power state while processing them!
                    //
                    PoRequestPowerIrp(fdoData->DeviceToSendIrpsTo,
                                      IRP_MN_SET_POWER,
                                      powerState,
                                      RedirectFDOPowerTransitionPoRequestComplete,
                                      fdoData, 
                                      NULL // no return Irp
                                      ); 
                    
                    //
                    // Inform the IO subsystem that we are 
                    // not done with this IRP 
                    //
                    status = STATUS_MORE_PROCESSING_REQUIRED; 
                    break;

                default:
                    break;

            }
            break;

        case IRP_MN_QUERY_POWER:

            // 
            // We handle device queries within the power dispatch routine
            //
            ASSERT(powerType == SystemPowerState);
        
            //
            // System Power State change. Note that if we were doing "true"
            // power management we'd choose an appropriate DeviceState based
            // on the capabilities of the devices in the stack. Here we just 
            // choose
            // D0 for powering up and D3 for powering down
            //
           
            //
            // We should never get a query for the same or greater power state
            //
            ASSERT(powerState.SystemState > fdoData->SystemPowerState);

            //
            // We have minimal power handling here, so we always
            // use PowerDeviceD3 for the device query.
            //
            powerState.DeviceState = PowerDeviceD3;

            //
            // Save the system IRP
            //
            fdoData->SystemIrp = Irp;

            //
            // Request a Device _QUERY_POWER Irp. 
            //
            PoRequestPowerIrp(fdoData->DeviceToSendIrpsTo,
                              IRP_MN_QUERY_POWER,
                              powerState,
                              RedirectFDOPowerTransitionPoRequestComplete,
                              fdoData, 
                              NULL // no return Irp
                              ); 
            
            //
            // Inform the IO subsystem that we are not done with this IRP 
            //
            status = STATUS_MORE_PROCESSING_REQUIRED; 

            break;

        default:
            break;
    }

    if(status != STATUS_MORE_PROCESSING_REQUIRED) {
        //
        // We're done processing this power Irp, start the next one
        //
        PoStartNextPowerIrp (Irp);
    }
    return status;
}


///////////////////////////////////////////////////////////////////////////////
//
//  RedirectFDOPowerTransitionPoRequestComplete
//
//      This callback is called when all devices in the stack have 
//      completed the DevicePowerState _SET_POWER Irp that we requested. 
//      Its purpose is to start the next SystemPower IRP and complete the 
//      current SystemPower IRP 
//
//  INPUTS:
//
//      DeviceObject        - not used.
//      MinorFunction       - Power minor function
//      DevicePowerState    - current device power state
//      Context             - Pointer to the device context.
//      IoStatus            - Status block
//
//  OUTPUTS:
//
//      None.
//
//  RETURNS:
//
//      None.
//
//  IRQL:
//
//    This routine is called at any IRQL <= DISPATCH_LEVEL.
//
//  NOTES:
//
//    This is a power request completion routine, not an I/O 
//    completion routine.
//
///////////////////////////////////////////////////////////////////////////////
VOID RedirectFDOPowerTransitionPoRequestComplete (
                                    IN PDEVICE_OBJECT   DeviceObject,
                                    IN UCHAR            MinorFunction,
                                    IN POWER_STATE      DevicePowerState,
                                    IN PVOID            Context, 
                                    IN PIO_STATUS_BLOCK IoStatus)
{
    PENCRYPT_DEVICE_EXT fdoData = (PENCRYPT_DEVICE_EXT)Context;
    PIRP                SystemStateIrp = fdoData->SystemIrp;
    PIO_STACK_LOCATION  stack;

    UNREFERENCED_PARAMETER (DeviceObject);
    UNREFERENCED_PARAMETER (DevicePowerState);

#if DBG
    DbgPrint(DRIVER_NAME "Entered RedirectFDOPowerTransitionPoRequestComplete\n");
#endif

    stack = IoGetCurrentIrpStackLocation (SystemStateIrp);

    //
    // At this point here's what we've done (in order):
    //
    // 1) Received a SystemPowerState IRP
    // 2) Decided on an appropriate device power state and requested a
    //    DevicePowerState IRP
    // 3) Processed the DevicePowerState IRP, setting our new 
    //    device power state for _SET_POWER IRPs
    //
    // We've now completed the minimal amount of steps necessary to handle
    // a power transition so start the next system irp and complete the
    // current one
    //

    switch (MinorFunction) {

        case IRP_MN_SET_POWER:
            //
            // The SET_POWER IRP is not supposed to be failable so, it
            // should always succeed.
            //
            ASSERT(NT_SUCCESS(IoStatus->Status));

            //
            // Record the new system state here if all was successful
            //
            if(NT_SUCCESS(IoStatus->Status)) {
                fdoData->SystemPowerState = 
                    stack->Parameters.Power.State.SystemState;
            }

            SystemStateIrp->IoStatus.Status = IoStatus->Status;

            PoStartNextPowerIrp(SystemStateIrp);
                    
            IoCompleteRequest(SystemStateIrp, IO_NO_INCREMENT);

            break;

        case IRP_MN_QUERY_POWER:
            //
            // We never fail _QUERY_POWER IRPs in this driver so, it
            // will only fail in a driver below us.
            //

            SystemStateIrp->IoStatus.Status = IoStatus->Status;

            PoStartNextPowerIrp(SystemStateIrp);
                    
            IoCompleteRequest(SystemStateIrp, IO_NO_INCREMENT);
            break;

        default:
            break;
    }

    return;
}
