/*++

Copyright (c) 1998-2000    Microsoft Corporation

Module Name:

    power.c

Abstract:

    DummyDma driver - the power management related processing.

    Just pass the power management IRPs down to the next driver 
    in the stack.

    For information on how to handle power management IRPs, see 
    the other samples or read the DDK documentation.

Environment:

    Kernel mode

Revision History:

    Feb-1999    :  Peter Lee (Compaq Computer Corporation)
    - Add power management code.
    
--*/

#include "dummydma.h"

#ifdef ALLOC_PRAGMA
#pragma alloc_text (PAGE, DummyDmaDispatchPower)
#pragma alloc_text (PAGE, DummyDmaPassDownToNextPowerDriver)
#endif


PCHAR
PowerMinorFunctionString (
    UCHAR MinorFunction
);


NTSTATUS
DummyDmaDispatchPower (
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )
/*++

Routine Description:

    The power dispatch routine.
    
Arguments:

   DeviceObject - pointer to a device object.

   Irp - pointer to an I/O Request Packet.

Return Value:

      NT status code

--*/
{
    PIO_STACK_LOCATION  stack;
    PDUMMYDMA_DEVICE_EXTENSION        DeviceExtension =
        (PDUMMYDMA_DEVICE_EXTENSION) DeviceObject->DeviceExtension;
    NTSTATUS            status;

    PAGED_CODE ();
    
    stack   = IoGetCurrentIrpStackLocation(Irp);

    DD_DebugPrint((2, "%s FDO 0x%x PDO 0x%x IRP 0x%x\n", 
                  PowerMinorFunctionString(stack->MinorFunction),
                  DeviceExtension->Self, DeviceExtension->PDO, Irp));
                  
    //
    // We don't queue power Irps, we'll only check if the
    // device was removed, otherwise we'll take appropriate 
    // action and send it to the next lower driver. In general 
    // drivers should not cause long delays while handling power 
    // IRPs. If a driver cannot handle a power IRP in a brief time, 
    // it should return STATUS_PENDING and queue all incoming 
    // IRPs until the IRP completes. 
    // 
    
    if (DeviceExtension->DevicePnPState == Deleted) {
    
        //
        // Even if a driver fails the IRP, it must nevertheless call 
        // PoStartNextPowerIrp to inform the Power Manager that it 
        // is ready to handle another power IRP. 
        //

        PoStartNextPowerIrp (Irp);
        status = STATUS_DELETE_PENDING;
        Irp->IoStatus.Information = 0;
        Irp->IoStatus.Status = status;
        IoCompleteRequest (Irp, IO_NO_INCREMENT);
        return status;
    } 
    
    DummyDmaIoIncrement (DeviceExtension);

    //
    // Check the request type.
    //
    
    switch  (stack->MinorFunction)  {
        case IRP_MN_SET_POWER   :
        
            //
            // The Power Manager sends this IRP for one of the 
            // following reasons: 
            // 1) To notify drivers of a change to the system power state.
            // 2) To change the power state of a device for which 
            //    the Power Manager is performing idle detection.
            // A driver sends IRP_MN_SET_POWER to change the power 
            // state of its device if it's a power policy owner for the
            // device.
            //
                
        case IRP_MN_WAIT_WAKE   :
            //
            // The minor power IRP code IRP_MN_WAIT_WAKE provides 
            // for waking a device or waking the system. Drivers 
            // of devices that can wake themselves or the system 
            // send IRP_MN_WAIT_WAKE. The system sends IRP_MN_WAIT_WAKE
            // only to devices that always wake the system, such as 
            // the power-on switch.
            //
                    
        case IRP_MN_POWER_SEQUENCE   :        
            //
            // A driver sends this IRP as an optimization to determine 
            // whether its device actually entered a specific power state. 
            // This IRP is optional. Power Manager cannot send this IRP.
            //

        case IRP_MN_QUERY_POWER   :
            //
            // The Power Manager sends a power IRP with the minor 
            // IRP code IRP_MN_QUERY_POWER to determine whether it 
            // can safely change to the specified system power state 
            // (S1-S5) and to allow drivers to prepare for such a change. 
            // If a driver can put its device in the requested state, 
            // it sets status to STATUS_SUCCESS and passes the IRP down. 
            //

        default:
            //
            // Pass it down
            //
            status = DummyDmaPassDownToNextPowerDriver(DeviceObject, Irp);

            DummyDmaIoDecrement(DeviceExtension);          

            break;
    }
    
    return status;
}



NTSTATUS    
DummyDmaPassDownToNextPowerDriver  (
    IN  PDEVICE_OBJECT  DeviceObject,
    IN OUT  PIRP        Irp
    )   

/*++

Routine Description:

    If a driver does not support a particular power IRP, 
    it must nevertheless pass the IRP down the device stack 
    to the next-lower driver. A driver further down the stack 
    might be prepared to handle the IRP and must have the 
    opportunity to do so.    
    
Arguments:

   DeviceObject - pointer to a device object.

   Irp - pointer to an I/O Request Packet.

Return Value:

   NT status code

--*/
{
    NTSTATUS            status;
    PDUMMYDMA_DEVICE_EXTENSION        DeviceExtension =
        (PDUMMYDMA_DEVICE_EXTENSION)DeviceObject->DeviceExtension;

    PAGED_CODE ();

    IoCopyCurrentIrpStackLocationToNext(Irp);

    // 
    // Drivers must call PoStartNextPowerIrp while the current 
    // IRP stack location points to the current driver. 
    // This routine can be called from the DispatchPower routine 
    // or from the IoCompletion routine. However, PoStartNextPowerIrp 
    // must be called before IoCompleteRequest, IoSkipCurrentIrpStackLocation, 
    // and PoCallDriver. Calling the routines in the other order might 
    // cause a system deadlock.
    //

    PoStartNextPowerIrp(Irp);

    //
    // Drivers must use PoCallDriver, rather than IoCallDriver, 
    // to pass power IRPs. PoCallDriver allows the Power Manager
    // to ensure that power IRPs are properly synchronized throughout 
    // the system. 
    //
    
    status = PoCallDriver(DeviceExtension->NextLowerDriver, Irp);

    if (!NT_SUCCESS(status)) {
        DD_DebugPrint((0, "Lower driver fails a power irp\n"));
    }

    return status;
    

}


PCHAR
PowerMinorFunctionString (
    UCHAR MinorFunction
)
{
    switch (MinorFunction)
    {
        case IRP_MN_SET_POWER:
            return "IRP_MN_SET_POWER";
        case IRP_MN_QUERY_POWER:
            return "IRP_MN_QUERY_POWER";
        case IRP_MN_POWER_SEQUENCE:
            return "IRP_MN_POWER_SEQUENCE";
        case IRP_MN_WAIT_WAKE:
            return "IRP_MN_WAIT_WAKE";
            
        default:
            return "IRP_MN_?????";
    }
}

