www.pudn.com > 9054Src.rar > Power.c


/******************************************************************************* 
 * Copyright (c) 2006 PLX Technology, Inc. 
 * 
 * PLX Technology Inc. licenses this software under specific terms and 
 * conditions.  Use of any of the software or derviatives thereof in any 
 * product without a PLX Technology chip is strictly prohibited. 
 * 
 * PLX Technology, Inc. provides this software AS IS, WITHOUT ANY WARRANTY, 
 * EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY WARRANTY OF 
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  PLX makes no guarantee 
 * or representations regarding the use of, or the results of the use of, 
 * the software and documentation in terms of correctness, accuracy, 
 * reliability, currentness, or otherwise; and you rely on the software, 
 * documentation and results solely at your own risk. 
 * 
 * IN NO EVENT SHALL PLX BE LIABLE FOR ANY LOSS OF USE, LOSS OF BUSINESS, 
 * LOSS OF PROFITS, INDIRECT, INCIDENTAL, SPECIAL OR CONSEQUENTIAL DAMAGES 
 * OF ANY KIND.  IN NO EVENT SHALL PLX'S TOTAL LIABILITY EXCEED THE SUM 
 * PAID TO PLX FOR THE PRODUCT LICENSED HEREUNDER. 
 * 
 ******************************************************************************/ 
 
/****************************************************************************** 
 * 
 * File Name: 
 * 
 *      Power.c 
 * 
 * Description: 
 * 
 *      Power Management functions 
 * 
 * Revision History: 
 * 
 *      02-01-06 : PCI SDK v4.40 
 * 
 ******************************************************************************/ 
 
 
#include "ApiFunctions.h" 
#include "PciSupport.h" 
#include "PlugPlay.h" 
#include "Power.h" 
#include "SupportFunc.h" 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  Dispatch_Power 
 * 
 * Description:  Handles power requests 
 * 
 ******************************************************************************/ 
NTSTATUS 
Dispatch_Power( 
    PDEVICE_OBJECT fdo, 
    PIRP           pIrp 
    ) 
{ 
    NTSTATUS           status; 
    PIO_STACK_LOCATION stack; 
 
 
    PlxLockDevice( 
        fdo->DeviceExtension 
        ); 
 
    DebugPrintf(("Received POWER Message (IRP=0x%p) ==> ", pIrp)); 
 
    stack = 
        IoGetCurrentIrpStackLocation( 
            pIrp 
            ); 
 
    switch (stack->MinorFunction) 
    { 
        case IRP_MN_WAIT_WAKE: 
            DebugPrintf_NoInfo(("IRP_MN_WAIT_WAKE\n")); 
            status = 
                DefaultPowerHandler( 
                    fdo, 
                    pIrp 
                    ); 
            break; 
 
        case IRP_MN_POWER_SEQUENCE: 
            DebugPrintf_NoInfo(("IRP_MN_POWER_SEQUENCE\n")); 
            status = 
                DefaultPowerHandler( 
                    fdo, 
                    pIrp 
                    ); 
            break; 
 
        case IRP_MN_SET_POWER: 
            DebugPrintf_NoInfo(("IRP_MN_SET_POWER\n")); 
            if (stack->Parameters.Power.Type == SystemPowerState) 
            { 
                DebugPrintf(( 
                    "SystemContext = 0x%08x  SystemPowerState = 0x%08x\n", 
                    stack->Parameters.Power.SystemContext, 
                    stack->Parameters.Power.State.SystemState 
                    )); 
            } 
            else 
            { 
                DebugPrintf(( 
                    "SystemContext = 0x%08x  DevicePowerState = 0x%08x\n", 
                    stack->Parameters.Power.SystemContext, 
                    stack->Parameters.Power.State.DeviceState 
                    )); 
            } 
 
            status = 
                HandleSetPower( 
                    fdo, 
                    pIrp 
                    ); 
            break; 
 
        case IRP_MN_QUERY_POWER: 
            DebugPrintf_NoInfo(("IRP_MN_QUERY_POWER\n")); 
            DebugPrintf(( 
                "SystemContext = 0x%08x\n", 
                stack->Parameters.Power.SystemContext 
                )); 
 
            status = 
                HandleQueryPower( 
                    fdo, 
                    pIrp 
                    ); 
            break; 
 
        default: 
            DebugPrintf_NoInfo(( 
                "Unsupported IRP_MN_Xxx (0x%08x)\n", 
                stack->MinorFunction 
                )); 
 
            status = 
                DefaultPowerHandler( 
                    fdo, 
                    pIrp 
                    ); 
            break; 
    } 
 
    PlxUnlockDevice( 
        fdo->DeviceExtension 
        ); 
 
    return status; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  DefaultPowerHandler 
 * 
 * Description:  Handle defaults power requests 
 * 
 ******************************************************************************/ 
NTSTATUS 
DefaultPowerHandler( 
    PDEVICE_OBJECT fdo, 
    PIRP           pIrp 
    ) 
{ 
    DebugPrintf(("Forwarded IRP to next lower driver\n")); 
 
    // Must be done while we own the IRP 
    PoStartNextPowerIrp( 
        pIrp 
        ); 
 
    IoSkipCurrentIrpStackLocation( 
        pIrp 
        ); 
 
    return PoCallDriver( 
               ((DEVICE_EXTENSION *)fdo->DeviceExtension)->pLowerDeviceObject, 
               pIrp 
               ); 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  HandleSetPower 
 * 
 * Description:  Handles IRP_MN_SET_POWER IRP 
 * 
 ******************************************************************************/ 
NTSTATUS 
HandleSetPower( 
    PDEVICE_OBJECT fdo, 
    PIRP           pIrp 
    ) 
{ 
    U32                 context; 
    NTSTATUS            status; 
    POWER_STATE         state; 
    POWER_STATE_TYPE    type; 
    DEVICE_EXTENSION   *pdx; 
    PIO_STACK_LOCATION  stack; 
    DEVICE_POWER_STATE  devstate; 
 
 
    pdx     = fdo->DeviceExtension; 
    stack   = IoGetCurrentIrpStackLocation(pIrp); 
    context = stack->Parameters.Power.SystemContext; 
    type    = stack->Parameters.Power.Type; 
    state   = stack->Parameters.Power.State; 
 
    /* If this IRP pertains to a system state, we want to put ourselves into 
     * a corresponding device state. The rule here is very simple: if the 
     * system is below SystemWorking, me must go to D3. If the system is 
     * in SystemWorking, we're allowed to pick our own state (so we pick 
     * D0). We can't just change our power state now, though: we must send 
     * ourselves a separate POWER IRP with the selected device state. 
     */ 
    if (type == SystemPowerState) 
    { 
        if (state.SystemState <= PowerSystemWorking) 
            devstate = PowerDeviceD0; 
        else 
            devstate = PowerDeviceD3; 
    } 
    else 
        devstate = state.DeviceState; 
 
    // If we're adding power, first pass the IRP down. Queue the 
    // requisite device IRP in the completion routine after the 
    // lower layers have restored power. 
 
    if (devstate < pdx->PowerState) 
    { 
        // Adding more power 
        IoCopyCurrentIrpStackLocationToNext( 
            pIrp 
            ); 
 
        IoSetCompletionRoutine( 
            pIrp, 
            (PIO_COMPLETION_ROUTINE) OnFinishPowerUp, 
            NULL, 
            TRUE, 
            TRUE, 
            TRUE 
            ); 
 
        return PoCallDriver( 
                   pdx->pLowerDeviceObject, 
                   pIrp 
                   ); 
    } 
    else if (devstate > pdx->PowerState) 
    { 
        /* If we're removing power, first do the device specific part. 
         * Then send the request down the stack. In the case of a system 
         * power request, send ourselves a device power IRP to power down first. 
         * In the case of a device power request, use PoSetPowerState to tell 
         * the Power Manager what we're about to do and then actually depower 
         * the hardware. 
         */ 
 
        // Removing power 
        if (type == SystemPowerState) 
        { 
            // Change in system state 
            status = 
                SendDeviceSetPower( 
                    fdo, 
                    devstate, 
                    context 
                    ); 
 
            if (!NT_SUCCESS(status)) 
            { 
                // Couldn't set device power state 
                PoStartNextPowerIrp( 
                    pIrp 
                    ); 
 
                return PlxCompleteIrpWithInformation( 
                           pIrp, 
                           status, 
                           0 
                           ); 
            } 
        } 
        else 
        { 
            // Change in device state 
            PoSetPowerState( 
                fdo, 
                type, 
                state 
                ); // before we power down 
 
            SetPowerState( 
                fdo, 
                devstate 
                ); 
        } 
 
        // Pass request down 
        return DefaultPowerHandler( 
                   fdo, 
                   pIrp 
                   ); 
    } 
 
    // Pass request down 
    return DefaultPowerHandler( 
               fdo, 
               pIrp 
               ); 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  HandleQueryPower 
 * 
 * Description:  Handle the IRP_MN_QUERY_POWER power IRP 
 * 
 ******************************************************************************/ 
NTSTATUS 
HandleQueryPower( 
    PDEVICE_OBJECT fdo, 
    PIRP           pIrp 
    ) 
{ 
    PIO_STACK_LOCATION  stack; 
    DEVICE_POWER_STATE  devstate; 
 
 
    stack = 
        IoGetCurrentIrpStackLocation( 
            pIrp 
            ); 
 
    switch (stack->Parameters.Power.Type) 
    { 
        case SystemPowerState: 
            DebugPrintf(("Query System Power State\n")); 
 
            if (stack->Parameters.Power.State.SystemState <= PowerSystemWorking) 
                devstate = PowerDeviceD0; 
            else 
                devstate = PowerDeviceD3; 
            break; 
 
        case DevicePowerState: 
            DebugPrintf(("Query Device Power State\n")); 
 
            devstate = stack->Parameters.Power.State.DeviceState; 
 
            if (PlxIsPowerLevelSupported( 
                    fdo->DeviceExtension, 
                    devstate - PowerDeviceD0 + D0 
                    ) == FALSE) 
            { 
                // Unsupported power level, we must fail the IRP 
                DebugPrintf(( 
                    "WARNING - HandleQueryPower() unsupported power level\n" 
                    )); 
 
                return PlxCompleteIrpWithInformation( 
                           pIrp, 
                           STATUS_POWER_STATE_INVALID, 
                           0 
                           ); 
            } 
            break; 
 
        default: 
            DebugPrintf(("Unknown query power state type\n")); 
            break; 
    } 
 
    // Pass request down 
    return DefaultPowerHandler( 
               fdo, 
               pIrp 
               ); 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  OnFinishPowerUp 
 * 
 * Description:  Completion routine for IRP_MN_SET_POWER 
 * 
 ******************************************************************************/ 
NTSTATUS 
OnFinishPowerUp( 
    PDEVICE_OBJECT fdo, 
    PIRP           pIrp, 
    PVOID          junk 
    ) 
{ 
    NTSTATUS           status; 
    POWER_STATE        state; 
    POWER_STATE_TYPE   type; 
    PIO_STACK_LOCATION stack; 
    DEVICE_POWER_STATE devstate; 
 
 
    DebugPrintf(("in OnFinishPowerUp\n")); 
 
    if (pIrp->PendingReturned) 
    { 
        // Lower-level pended this IRP 
        IoMarkIrpPending( 
            pIrp 
            ); 
    } 
 
    status = pIrp->IoStatus.Status; 
    if (!NT_SUCCESS(status)) 
    { 
        DebugPrintf(("OnFinishPowerUp: IRP failed\n")); 
        return status; 
    } 
 
    stack = IoGetCurrentIrpStackLocation(pIrp); 
    type  = stack->Parameters.Power.Type; 
    state = stack->Parameters.Power.State; 
 
    /* If we just handled a request to restore power to the system, we 
     * want to send ourselves a device power IRP. But don't do it unless 
     * there's actually a request pending right now (we may immediately get 
     * another system IRP to go to a different sleep state than we were in 
     * to start with). 
     */ 
    if (type == SystemPowerState) 
    { 
        // Restoring power to the system 
        if (state.SystemState <= PowerSystemWorking) 
            devstate = PowerDeviceD0; 
        else 
            devstate = PowerDeviceD3; 
 
        status = 
            SendDeviceSetPower( 
                fdo, 
                devstate, 
                stack->Parameters.Power.SystemContext 
                ); 
    } 
    else 
    { 
        /* For a device re-power request, first restore power to the actual 
         * device and then call PoSetPowerState to tell the Power Manager we 
         * did so. 
         */ 
 
        // Restoring power to the device 
        SetPowerState( 
            fdo, 
            state.DeviceState 
            ); 
 
        PoSetPowerState( 
            fdo, 
            type, 
            state 
            ); // after we power up 
    } 
 
    PoStartNextPowerIrp( 
        pIrp 
        ); 
 
    return status; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  OnPowerRequestComplete 
 * 
 * Description:  Completion routine for SendSelfSetPowerRequest 
 * 
 ******************************************************************************/ 
VOID 
OnPowerRequestComplete( 
    PDEVICE_OBJECT   DeviceObject, 
    U8               MinorFunction, 
    POWER_STATE      PowerState, 
    PVOID            context, 
    PIO_STATUS_BLOCK ioStatus 
    ) 
{ 
    DebugPrintf(("in OnPowerRequestComplete\n")); 
 
    // Set event 
    if ((PKEVENT)context != NULL) 
    { 
        KeSetEvent( 
            (PKEVENT)context, 
            1, 
            FALSE 
            ); 
    } 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  SendDeviceSetPower 
 * 
 * Description:  Send ourselves an IRP_MN_SET_POWER request 
 * 
 ******************************************************************************/ 
NTSTATUS 
SendDeviceSetPower( 
    PDEVICE_OBJECT     fdo, 
    DEVICE_POWER_STATE state, 
    U32                context 
    ) 
{ 
    PIRP               pIrp; 
    KEVENT             event; 
    NTSTATUS           status; 
    PIO_STACK_LOCATION stack; 
 
 
    DebugPrintf(("in SendDeviceSetPower\n")); 
 
    // Skip request if we are already at the desired Power level 
    if (state == ((DEVICE_EXTENSION *)fdo->DeviceExtension)->PowerState) 
        return STATUS_SUCCESS; 
 
    pIrp = 
        IoAllocateIrp( 
            fdo->StackSize, 
            FALSE 
            ); 
 
    if (pIrp == NULL) 
    { 
        DebugPrintf(("ERROR - Unable to allocate IRP for Power request\n")); 
        return STATUS_INSUFFICIENT_RESOURCES; 
    } 
 
    // IRP status must be initialized 
    pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED; 
 
    stack                                     = IoGetNextIrpStackLocation(pIrp); 
    stack->MajorFunction                      = IRP_MJ_POWER; 
    stack->MinorFunction                      = IRP_MN_SET_POWER; 
    stack->Parameters.Power.SystemContext     = context; 
    stack->Parameters.Power.Type              = DevicePowerState; 
    stack->Parameters.Power.State.DeviceState = state; 
 
    KeInitializeEvent( 
        &event, 
        NotificationEvent, 
        FALSE 
        ); 
 
    IoSetCompletionRoutine( 
        pIrp, 
        (PIO_COMPLETION_ROUTINE) OnRequestComplete, 
        (PVOID) &event, 
        TRUE, 
        TRUE, 
        TRUE 
        ); 
 
    status = 
        PoCallDriver( 
            fdo, 
            pIrp 
            ); 
 
    if (status == STATUS_PENDING) 
    { 
        // Wait for completion 
        KeWaitForSingleObject( 
            &event, 
            Executive, 
            KernelMode, 
            FALSE, 
            NULL 
            ); 
 
        status = pIrp->IoStatus.Status; 
    } 
 
    IoFreeIrp( 
        pIrp 
        ); 
 
    return status; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  SetPowerState 
 * 
 * Description:  Set the new power state 
 * 
 ******************************************************************************/ 
VOID 
SetPowerState( 
    PDEVICE_OBJECT     fdo, 
    DEVICE_POWER_STATE state 
    ) 
{ 
    POWER_INFO pPowerInfo; 
 
 
    pPowerInfo.pdx   = fdo->DeviceExtension; 
    pPowerInfo.state = state; 
 
    EmpowerDevice( 
        &pPowerInfo 
        ); 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  EmpowerDevice 
 * 
 * Description:  Empower device 
 * 
 ******************************************************************************/ 
BOOLEAN 
EmpowerDevice( 
    POWER_INFO *pPowerInfo 
    ) 
{ 
    // If already in the requested state, do nothing 
    if (pPowerInfo->state == pPowerInfo->pdx->PowerState) 
        return TRUE; 
 
    DebugPrintf(("Putting device into state ==> ")); 
 
    switch (pPowerInfo->state) 
    { 
        case D0Uninitialized: 
            DebugPrintf_NoInfo(("D0Uninitialized\n")); 
            break; 
 
        case D0: 
            DebugPrintf_NoInfo(("D0\n")); 
            break; 
 
        case D1: 
            DebugPrintf_NoInfo(("D1\n")); 
            break; 
 
        case D2: 
            DebugPrintf_NoInfo(("D2\n")); 
            break; 
 
        case D3Hot: 
            DebugPrintf_NoInfo(("D3Hot\n")); 
            break; 
 
        case D3Cold: 
            DebugPrintf_NoInfo(("D3Cold\n")); 
            break; 
    } 
 
    /* 
        A real device would interpret the new state relative to the old state 
        and save (or restore) some context information, send control information 
        to the device, etc. This fake device just records the power state in the 
        device extension. 
    */ 
 
    // Set the new power state 
    PlxPciPowerLevelSet( 
        pPowerInfo->pdx, 
        pPowerInfo->state - PowerDeviceD0 + D0 
        ); 
 
    // Record the new power state 
    pPowerInfo->pdx->PowerState = pPowerInfo->state; 
 
    return TRUE; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  SendSelfSetPowerRequest 
 * 
 * Description:  Change the power level of the device. Note that this is not 
 *               the way Microsoft recommends. 
 * 
 ******************************************************************************/ 
NTSTATUS 
SendSelfSetPowerRequest( 
    PDEVICE_OBJECT     fdo, 
    DEVICE_POWER_STATE state 
    ) 
{ 
    NTSTATUS          status; 
    POWER_STATE       power; 
    DEVICE_EXTENSION *pdx; 
 
 
    DebugPrintf(("in SendSelfSetPowerRequest\n")); 
 
    // Get the device extension 
    if (fdo->DeviceExtension == NULL) 
    { 
        DebugPrintf(("ERROR - NULL extension\n")); 
        return STATUS_NOT_SUPPORTED; 
    } 
 
    pdx = fdo->DeviceExtension; 
 
    // Compare new and old power states 
    if (state == pdx->PowerState) 
        return STATUS_SUCCESS; 
 
    // PoSetPowerState needs a POWER_STATE instead of a DEVICE_POWER_STATE 
    power.DeviceState = state; 
 
    if (state > pdx->PowerState) 
    { 
        // Removing power, process first 
        PoSetPowerState( 
            fdo, 
            DevicePowerState, 
            power 
            ); 
 
        SetPowerState( 
            fdo, 
            state 
            ); 
    } 
 
    /* 
        Normally, here we would create an IRP and send it down to the PCI bus 
        driver. However, this crashes Win98. So, we do not do it. Note that 
        the consequence of that is that the PCI bus driver does not know that 
        we are changing the power level of the device. 
    */ 
 
    if (state < pdx->PowerState) 
    { 
        // adding power, process request after 
        SetPowerState( 
            fdo, 
            state 
            ); 
 
        PoSetPowerState( 
            fdo, 
            DevicePowerState, 
            power 
            ); 
    } 
 
    return STATUS_SUCCESS; 
}