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;
}