/***************************************************************************

Copyright (c) 2000 Microsoft Corporation

Module Name:

        Dot4Usb.sys - Lower Filter Driver for Dot4.sys for USB connected
                        IEEE 1284.4 devices.

File Name:

        Power.c

Abstract:

        Power management functions

Environment:

        Kernel mode only

Notes:

        THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
        KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
        IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
        PURPOSE.

        Copyright (c) 2000 Microsoft Corporation.  All Rights Reserved.

Revision History:

        01/18/2000 : created

ToDo in this file:

        - code cleanup and documentation
        - code review

Author(s):

        Joby Lafky (JobyL)
        Doug Fritz (DFritz)

****************************************************************************/

#include "pch.h"

VOID
SetPowerIrpCompletion(IN PDEVICE_OBJECT   DeviceObject,
                      IN UCHAR            MinorFunction,
                      IN POWER_STATE      PowerState,
                      IN PVOID            Context,
                      IN PIO_STATUS_BLOCK IoStatus);
NTSTATUS
PowerD0Completion(IN PDEVICE_OBJECT   DeviceObject,
                  IN PIRP             Irp,
                  IN PVOID            Context);


NTSTATUS 
DispatchPower(
    IN PDEVICE_OBJECT DevObj,
    IN PIRP           Irp
    )
{
    PDEVICE_EXTENSION       devExt = DevObj->DeviceExtension;
    PIO_STACK_LOCATION      irpSp = IoGetCurrentIrpStackLocation( Irp );
    NTSTATUS                status;
    POWER_STATE             powerState;
    POWER_STATE             newState;
    POWER_STATE             oldState;
    BOOLEAN                 passRequest  = TRUE;

    TR_VERBOSE(("DispatchPower, MinorFunction = %x", (ULONG)irpSp->MinorFunction));

    //
    // Acquire RemoveLock to prevent us from being Removed
    //
    status = IoAcquireRemoveLock( &devExt->RemoveLock, Irp );
    if( !NT_SUCCESS(status) ) 
    {
        // couldn't aquire RemoveLock - we're in the process of being removed - abort
        PoStartNextPowerIrp( Irp );
        Irp->IoStatus.Status = status;
        IoCompleteRequest( Irp, IO_NO_INCREMENT );
        return status;
    }


    powerState = irpSp->Parameters.Power.State;

    switch (irpSp->MinorFunction) 
    {

    case IRP_MN_SET_POWER:

        switch(irpSp->Parameters.Power.Type) 
        {

        case SystemPowerState:
            // save the current system state
            devExt->SystemPowerState = powerState.SystemState;

            // map the new system state to a new device state
            if(powerState.SystemState != PowerSystemWorking)
            {
                newState.DeviceState = PowerDeviceD3;
            }
            else
            {
                newState.DeviceState = PowerDeviceD0;
            }

            if(devExt->DevicePowerState != newState.DeviceState)
            {
                // save the current power Irp for sending down later
                devExt->CurrentPowerIrp = Irp;

                // send a power Irp to set new device state
                status = PoRequestPowerIrp(devExt->Pdo,
                                           IRP_MN_SET_POWER,
                                           newState,
                                           SetPowerIrpCompletion,
                                           (PVOID) devExt,
                                           NULL);
                
                // this will get passed down in the completion routine
                passRequest  = FALSE;
            }

            break;

        case DevicePowerState:

            // Update the current device state.
            oldState.DeviceState = devExt->DevicePowerState;
            devExt->DevicePowerState = powerState.DeviceState;

            // powering up
            if(oldState.DeviceState > PowerDeviceD0 &&
               powerState.DeviceState == PowerDeviceD0)
            {
                // we need to know when this completes and our device is at the proper state
                IoCopyCurrentIrpStackLocationToNext(Irp);

                IoSetCompletionRoutine(Irp,
                                       PowerD0Completion,
                                       devExt,
                                       TRUE,
                                       TRUE,
                                       TRUE);

                status = PoCallDriver(devExt->LowerDevObj, Irp);

                // we already passed this one down
                passRequest = FALSE;

            }
            else
            {
                // powering down, jsut set a flag and pass the request down
                if(devExt->PnpState == STATE_STARTED) 
                {
                    devExt->PnpState = STATE_SUSPENDED;
                }

                passRequest = TRUE;
            }

            break;
        }
    }


    if(passRequest)
    {
        //
        // Send the IRP down the driver stack,
        //
        IoCopyCurrentIrpStackLocationToNext( Irp );

        PoStartNextPowerIrp(Irp);

        // release lock
        IoReleaseRemoveLock( &devExt->RemoveLock, Irp );

        status = PoCallDriver( devExt->LowerDevObj, Irp );        
    }

    return status;
}

VOID
SetPowerIrpCompletion(IN PDEVICE_OBJECT   DeviceObject,
                      IN UCHAR            MinorFunction,
                      IN POWER_STATE      PowerState,
                      IN PVOID            Context,
                      IN PIO_STATUS_BLOCK IoStatus)
{
    PDEVICE_EXTENSION       devExt;
    PIRP                    irp;
    NTSTATUS                ntStatus;

    UNREFERENCED_PARAMETER( DeviceObject );
    UNREFERENCED_PARAMETER( MinorFunction );
    UNREFERENCED_PARAMETER( PowerState );
    UNREFERENCED_PARAMETER( IoStatus );

    devExt = (PDEVICE_EXTENSION) Context;

    // get the current power irp
    irp = devExt->CurrentPowerIrp;

    devExt->CurrentPowerIrp = NULL;

    // the requested DevicePowerState Irp has completed, so send the system power Irp down
    PoStartNextPowerIrp(irp);

    IoCopyCurrentIrpStackLocationToNext(irp);

    // mark the Irp pending
    IoMarkIrpPending(irp);

    // release the lock
    IoReleaseRemoveLock( &devExt->RemoveLock, irp );

    ntStatus = PoCallDriver(devExt->LowerDevObj, irp);
}

NTSTATUS
PowerD0Completion(IN PDEVICE_OBJECT   DeviceObject,
                  IN PIRP             Irp,
                  IN PVOID            Context)
{
    PDEVICE_EXTENSION       devExt;
    NTSTATUS                ntStatus;

    UNREFERENCED_PARAMETER( DeviceObject );

    devExt = (PDEVICE_EXTENSION) Context;

    // the device is powered up, set out state
    if(devExt->PnpState == STATE_SUSPENDED) 
    {
        devExt->PnpState = STATE_STARTED;
    }


    ntStatus = Irp->IoStatus.Status;

    // release the lock
    IoReleaseRemoveLock( &devExt->RemoveLock, Irp );

    PoStartNextPowerIrp(Irp);

    return ntStatus;
}