2168 lines
55 KiB
C
2168 lines
55 KiB
C
/*++
|
||
|
||
Copyright (c) 1998 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
syspower.c
|
||
|
||
Abstract:
|
||
|
||
Contains all the code that deals with the system having to determine
|
||
System Power State to Device Power State mappings
|
||
|
||
Author:
|
||
|
||
Stephane Plante (splante)
|
||
|
||
Environment:
|
||
|
||
Kernel mode only.
|
||
|
||
Revision History:
|
||
|
||
October 29th, 1998
|
||
|
||
--*/
|
||
|
||
#include "pch.h"
|
||
|
||
//
|
||
// Quick Lookup table to map S-States to SxD methods
|
||
//
|
||
ULONG AcpiSxDMethodTable[] = {
|
||
PACKED_SWD,
|
||
PACKED_S0D,
|
||
PACKED_S1D,
|
||
PACKED_S2D,
|
||
PACKED_S3D,
|
||
PACKED_S4D,
|
||
PACKED_S5D
|
||
};
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE,ACPISystemPowerGetSxD)
|
||
#pragma alloc_text(PAGE,ACPISystemPowerProcessRootMapping)
|
||
#pragma alloc_text(PAGE,ACPISystemPowerProcessSxD)
|
||
#pragma alloc_text(PAGE,ACPISystemPowerQueryDeviceCapabilities)
|
||
#pragma alloc_text(PAGE,ACPISystemPowerUpdateWakeCapabilities)
|
||
#endif
|
||
|
||
NTSTATUS
|
||
ACPISystemPowerDetermineSupportedDeviceStates(
|
||
IN PDEVICE_EXTENSION DeviceExtension,
|
||
IN SYSTEM_POWER_STATE SystemState,
|
||
OUT ULONG *SupportedDeviceStates
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This recursive routine looks at all the children of the current
|
||
device extension and determines what device states might be supported
|
||
at the specified system state. This is accomplished by looking at the
|
||
_SxD methods and looking at the power plane information
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - The device whose children we want to know
|
||
information about
|
||
SystemState - The system state we want to know about
|
||
SupportedDeviceStates - Set bits represent supported D-states
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
DEVICE_POWER_STATE deviceState;
|
||
EXTENSIONLIST_ENUMDATA eled;
|
||
KIRQL oldIrql;
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
PDEVICE_EXTENSION childExtension;
|
||
SYSTEM_POWER_STATE prSystemState;
|
||
|
||
ASSERT(
|
||
SystemState >= PowerSystemWorking &&
|
||
SystemState <= PowerSystemShutdown
|
||
);
|
||
ASSERT( SupportedDeviceStates != NULL );
|
||
|
||
//
|
||
// Setup the data structure that we will use to walk the device extension
|
||
// tree
|
||
//
|
||
ACPIExtListSetupEnum(
|
||
&eled,
|
||
&(DeviceExtension->ChildDeviceList),
|
||
&AcpiDeviceTreeLock,
|
||
SiblingDeviceList,
|
||
WALKSCHEME_REFERENCE_ENTRIES
|
||
);
|
||
|
||
//
|
||
// Look at all children of the current device extension
|
||
//
|
||
for (childExtension = ACPIExtListStartEnum( &eled );
|
||
ACPIExtListTestElement( &eled, (BOOLEAN) NT_SUCCESS(status) );
|
||
childExtension = ACPIExtListEnumNext( &eled) ) {
|
||
|
||
//
|
||
// Recurse first
|
||
//
|
||
status = ACPISystemPowerDetermineSupportedDeviceStates(
|
||
childExtension,
|
||
SystemState,
|
||
SupportedDeviceStates
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
continue;
|
||
|
||
}
|
||
|
||
//
|
||
// Get the _SxD mapping for the device
|
||
//
|
||
status = ACPISystemPowerGetSxD(
|
||
childExtension,
|
||
SystemState,
|
||
&deviceState
|
||
);
|
||
if (NT_SUCCESS( status ) ) {
|
||
|
||
//
|
||
// We support this D-state
|
||
//
|
||
*SupportedDeviceStates |= (1 << deviceState );
|
||
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_SXD,
|
||
childExtension,
|
||
" S%x->D%x\n",
|
||
(SystemState - 1),
|
||
(deviceState - 1)
|
||
) );
|
||
|
||
//
|
||
// Don't bother looking at the _PRx methods
|
||
//
|
||
continue;
|
||
|
||
} else if (status != STATUS_OBJECT_NAME_NOT_FOUND) {
|
||
|
||
//
|
||
// If we hit another error, then we should continue now
|
||
// Note that continuing will cause us to terminate the loop
|
||
//
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_FAILURE,
|
||
childExtension,
|
||
" - ACPISystemPowerdetermineSupportedDeviceStates = %08lx\n",
|
||
status
|
||
) );
|
||
continue;
|
||
|
||
} else {
|
||
|
||
//
|
||
// If we got here, then that means that the childExtension doesn't
|
||
// have a _SxD method, which is okay. We reset the status so that
|
||
// the loop test will succeed, or at least won't fail because there
|
||
// wasn't an _SxD method.
|
||
//
|
||
status = STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
//
|
||
// We are going to play with the power nodes, so we must be holding
|
||
// the power lock
|
||
//
|
||
KeAcquireSpinLock( &AcpiPowerLock, &oldIrql );
|
||
|
||
//
|
||
// Look at all the device states that might be supported via
|
||
// the _PR methods
|
||
//
|
||
for (deviceState = PowerDeviceD0;
|
||
deviceState <= PowerDeviceD2;
|
||
deviceState++) {
|
||
|
||
prSystemState = ACPISystemPowerDetermineSupportedSystemState(
|
||
childExtension,
|
||
deviceState
|
||
);
|
||
if (prSystemState >= SystemState) {
|
||
|
||
//
|
||
// This d-state maps to a deeper S-state than what we
|
||
// are looking for, so we should be implicitly supporting
|
||
// this d-state for the current S-state
|
||
//
|
||
*SupportedDeviceStates |= (1 << deviceState);
|
||
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_SXD,
|
||
childExtension,
|
||
" PR%x maps to S%x, so S%x->D%x\n",
|
||
(deviceState - 1),
|
||
(prSystemState - 1),
|
||
(SystemState - 1),
|
||
(deviceState - 1)
|
||
) );
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Done with the lock
|
||
//
|
||
KeReleaseSpinLock( &AcpiPowerLock, oldIrql );
|
||
|
||
}
|
||
|
||
//
|
||
// Done
|
||
//
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
DEVICE_POWER_STATE
|
||
ACPISystemPowerDetermineSupportedDeviceWakeState(
|
||
IN PDEVICE_EXTENSION DeviceExtension
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine looks at the PowerInformation structure and determines
|
||
the D-State that is supported by the wake state
|
||
|
||
As a rule of thumb, if the S-State is not supported, then we
|
||
return PowerDeviceUnspecified
|
||
|
||
Note: The parent is holding the AcpiPowerLock
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - The extension that we wish to check
|
||
|
||
Return Value:
|
||
|
||
DEVICE_POWER_STATE
|
||
|
||
--*/
|
||
{
|
||
DEVICE_POWER_STATE deviceState = PowerDeviceMaximum;
|
||
PACPI_DEVICE_POWER_NODE deviceNode;
|
||
|
||
deviceNode = DeviceExtension->PowerInfo.PowerNode[PowerDeviceUnspecified];
|
||
while (deviceNode != NULL) {
|
||
|
||
//
|
||
// Does the current device node support a lower device then the
|
||
// current maximum device state?
|
||
//
|
||
if (deviceNode->AssociatedDeviceState < deviceState) {
|
||
|
||
//
|
||
// Yes, so this is the new maximum system state
|
||
//
|
||
deviceState = deviceNode->AssociatedDeviceState;
|
||
|
||
}
|
||
deviceNode = deviceNode->Next;
|
||
|
||
}
|
||
|
||
//
|
||
// PowerSystemMaximum is not a valid entry. So if that is what we would
|
||
// return, then change that to return PowerSystemUnspecified
|
||
//
|
||
if (deviceState == PowerDeviceMaximum) {
|
||
|
||
deviceState = PowerDeviceUnspecified;
|
||
|
||
}
|
||
return deviceState;
|
||
}
|
||
|
||
SYSTEM_POWER_STATE
|
||
ACPISystemPowerDetermineSupportedSystemState(
|
||
IN PDEVICE_EXTENSION DeviceExtension,
|
||
IN DEVICE_POWER_STATE DeviceState
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine looks at the PowerInformation structure and determines
|
||
the S-State that is supported by the D-state
|
||
|
||
As a rule of thumb, if the D-State is not supported, then we
|
||
return PowerSystemUnspecified
|
||
|
||
Note: The parent is holding the AcpiPowerLock
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - The extension that we wish to check
|
||
DeviceState - The state that we wish to sanity check
|
||
|
||
Return Value:
|
||
|
||
SYSTEM_POWER_STATE
|
||
|
||
--*/
|
||
{
|
||
PACPI_DEVICE_POWER_NODE deviceNode;
|
||
SYSTEM_POWER_STATE systemState = PowerSystemMaximum;
|
||
|
||
if (DeviceState == PowerDeviceD3) {
|
||
|
||
goto ACPISystemPowerDetermineSupportedSystemStateExit;
|
||
|
||
}
|
||
|
||
deviceNode = DeviceExtension->PowerInfo.PowerNode[DeviceState];
|
||
while (deviceNode != NULL) {
|
||
|
||
//
|
||
// Does the current device node support a lower system then the
|
||
// current maximum system state?
|
||
//
|
||
if (deviceNode->SystemState < systemState) {
|
||
|
||
//
|
||
// Yes, so this is the new maximum system state
|
||
//
|
||
systemState = deviceNode->SystemState;
|
||
|
||
}
|
||
deviceNode = deviceNode->Next;
|
||
|
||
}
|
||
|
||
ACPISystemPowerDetermineSupportedSystemStateExit:
|
||
//
|
||
// PowerSystemMaximum is not a valid entry. So if that is what we would
|
||
// return, then change that to return PowerSystemUnspecified
|
||
//
|
||
if (systemState == PowerSystemMaximum) {
|
||
|
||
systemState = PowerSystemUnspecified;
|
||
|
||
}
|
||
return systemState;
|
||
}
|
||
|
||
NTSTATUS
|
||
ACPISystemPowerGetSxD(
|
||
IN PDEVICE_EXTENSION DeviceExtension,
|
||
IN SYSTEM_POWER_STATE SystemState,
|
||
OUT DEVICE_POWER_STATE *DeviceState
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the worker function that is called when we want to run an
|
||
SxD method. We give the function an S-State, and we get back a
|
||
D-State.
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - The device to run the SxD on
|
||
SystemState - The S-state to determine the D-State for
|
||
DeviceState - Where we store the answer
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
ULONG value;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Assume that we don't find an answer
|
||
//
|
||
*DeviceState = PowerDeviceUnspecified;
|
||
|
||
//
|
||
// We want this code to run even though there is no namespace object
|
||
// for the device. Since we don't want to add a check to GetNamedChild
|
||
// that checks for null, we need to handle this special case here
|
||
//
|
||
if ( (DeviceExtension->Flags & DEV_PROP_NO_OBJECT) ||
|
||
(DeviceExtension->Flags & DEV_PROP_FAILED_INIT) ) {
|
||
|
||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||
|
||
}
|
||
|
||
//
|
||
// Evaluate the control method
|
||
//
|
||
status = ACPIGetIntegerSync(
|
||
DeviceExtension,
|
||
AcpiSxDMethodTable[SystemState],
|
||
&value,
|
||
NULL
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// Convert this number to a D-State
|
||
//
|
||
*DeviceState = ACPIDeviceMapPowerState( value );
|
||
|
||
} else if (status == STATUS_OBJECT_NAME_NOT_FOUND) {
|
||
|
||
//
|
||
// HACKHACK --- Program Management wants us to force the PCI Root Bus
|
||
// mappings for S1 to be D1. So look for a device node that has
|
||
// both the PCI flag and the HID flag set, and if so, return that
|
||
// we support D1
|
||
//
|
||
if (SystemState == PowerSystemSleeping1 &&
|
||
(DeviceExtension->Flags & DEV_MASK_HID) &&
|
||
(DeviceExtension->Flags & DEV_CAP_PCI) ) {
|
||
|
||
*DeviceState = PowerDeviceD1;
|
||
status = STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
#if DBG
|
||
} else {
|
||
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_CRITICAL,
|
||
DeviceExtension,
|
||
"ACPISystemPowerGetSxD: Cannot run _S%cD - 0x%08lx\n",
|
||
(SystemState == 0 ? 'w' : '0' + (UCHAR) (SystemState - 1) ),
|
||
status
|
||
) );
|
||
#endif
|
||
|
||
}
|
||
|
||
//
|
||
// Done
|
||
//
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
ACPISystemPowerInitializeRootMapping(
|
||
IN PDEVICE_EXTENSION DeviceExtension,
|
||
IN PDEVICE_CAPABILITIES DeviceCapabilities
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is responsible for initializing the S->D mapping for the
|
||
root device extension
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - Pointer to the root device extension
|
||
DeviceCapabilitites - DeviceCapabilitites
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
BOOLEAN sxdFound;
|
||
DEVICE_POWER_STATE deviceMap[PowerSystemMaximum];
|
||
KIRQL oldIrql;
|
||
NTSTATUS status;
|
||
SYSTEM_POWER_STATE sysIndex;
|
||
|
||
//
|
||
// Can we actually do any real work here?
|
||
//
|
||
if ( (DeviceExtension->Flags & DEV_PROP_BUILT_POWER_TABLE) ||
|
||
(DeviceExtension->DeviceState != Started) ) {
|
||
|
||
goto ACPISystemPowerInitializeRootMappingExit;
|
||
|
||
}
|
||
|
||
//
|
||
// Initialize the root mapping
|
||
//
|
||
RtlZeroMemory( deviceMap, sizeof(DEVICE_POWER_STATE) * PowerSystemMaximum );
|
||
|
||
//
|
||
// Copy the mapping from the device extension. See the comment at the
|
||
// end as to why we don't grab a spinlock
|
||
//
|
||
IoCopyDeviceCapabilitiesMapping(
|
||
DeviceExtension->PowerInfo.DevicePowerMatrix,
|
||
deviceMap
|
||
);
|
||
|
||
//
|
||
// Make sure that S0->D0
|
||
//
|
||
deviceMap[PowerSystemWorking] = PowerDeviceD0;
|
||
|
||
//
|
||
// Special case the fact that someone one might want to have the
|
||
// HAL return a different template. If the capabilities that we got
|
||
// handed have some values in them, have them override our defaults
|
||
//
|
||
for (sysIndex = PowerSystemSleeping1;
|
||
sysIndex <= PowerSystemShutdown;
|
||
sysIndex++) {
|
||
|
||
if (DeviceCapabilities->DeviceState[sysIndex] != PowerDeviceUnspecified) {
|
||
|
||
deviceMap[sysIndex] = DeviceCapabilities->DeviceState[sysIndex];
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Porcess the SxD methods if there are any
|
||
//
|
||
status = ACPISystemPowerProcessSxD(
|
||
DeviceExtension,
|
||
deviceMap,
|
||
&sxdFound
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_CRITICAL,
|
||
DeviceExtension,
|
||
"- ACPISystemPowerProcessSxD = %08lx\n",
|
||
status
|
||
) );
|
||
return status;
|
||
|
||
}
|
||
|
||
//
|
||
// Make sure that the Shutdown case doesn't map to PowerDeviceUnspecified
|
||
// If it does, then it should really map to PowerDeviceD3
|
||
//
|
||
if (deviceMap[PowerSystemShutdown] == PowerDeviceUnspecified) {
|
||
|
||
deviceMap[PowerSystemShutdown] = PowerDeviceD3;
|
||
|
||
}
|
||
|
||
//
|
||
// Look at all the children capabilities to help us decide the root
|
||
// mapping
|
||
//
|
||
status = ACPISystemPowerProcessRootMapping(
|
||
DeviceExtension,
|
||
deviceMap
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_CRITICAL,
|
||
DeviceExtension,
|
||
" - ACPISystemPowerProcessRootMapping = %08lx\n",
|
||
status
|
||
) );
|
||
goto ACPISystemPowerInitializeRootMappingExit;
|
||
|
||
}
|
||
|
||
//
|
||
// If we have reached this point, then we have build the SxD table
|
||
// and never need to do so again
|
||
//
|
||
ACPIInternalUpdateFlags(
|
||
&(DeviceExtension->Flags),
|
||
DEV_PROP_BUILT_POWER_TABLE,
|
||
FALSE
|
||
);
|
||
|
||
#if DBG
|
||
//
|
||
// We haven't updated the device extension yet, so we can still do this
|
||
// at this point in the game
|
||
//
|
||
ACPIDebugDeviceCapabilities(
|
||
DeviceExtension,
|
||
DeviceCapabilities,
|
||
"Initial"
|
||
);
|
||
ACPIDebugPowerCapabilities( DeviceExtension, "Before Update" );
|
||
#endif
|
||
|
||
//
|
||
// Copy the mapping to the device extension
|
||
//
|
||
KeAcquireSpinLock( &AcpiPowerLock, &oldIrql );
|
||
IoCopyDeviceCapabilitiesMapping(
|
||
deviceMap,
|
||
DeviceExtension->PowerInfo.DevicePowerMatrix
|
||
);
|
||
KeReleaseSpinLock( &AcpiPowerLock, oldIrql );
|
||
|
||
#if DBG
|
||
ACPIDebugPowerCapabilities( DeviceExtension, "After Update" );
|
||
#endif
|
||
|
||
ACPISystemPowerInitializeRootMappingExit:
|
||
//
|
||
// Hmm.. I'm tempted to grab a spinlock here, but since we cannot
|
||
// updating the capabilities for this device, I think it is safe
|
||
// to not do so. We need to grab the spinlock when setting these
|
||
// values so that we can sync with the power code
|
||
//
|
||
|
||
//
|
||
// Copy the power capabilities to their final location
|
||
//
|
||
IoCopyDeviceCapabilitiesMapping(
|
||
DeviceExtension->PowerInfo.DevicePowerMatrix,
|
||
DeviceCapabilities->DeviceState
|
||
);
|
||
#if DBG
|
||
ACPIDebugDeviceCapabilities(DeviceExtension, DeviceCapabilities, "Done" );
|
||
#endif
|
||
|
||
//
|
||
// Done
|
||
//
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
ACPISystemPowerProcessRootMapping(
|
||
IN PDEVICE_EXTENSION DeviceExtension,
|
||
IN DEVICE_POWER_STATE DeviceMap[PowerSystemMaximum]
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called by the FDO to figure out what the minimal set
|
||
of capabilities for each s state are. These then become the root
|
||
capabilitites
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - The root device extension
|
||
DeviceMap - The current mapping
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
DEVICE_POWER_STATE deviceState;
|
||
KIRQL oldIrql;
|
||
NTSTATUS status;
|
||
SYSTEM_POWER_STATE systemState;
|
||
ULONG supportedDeviceStates;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Loop on all the system supported states
|
||
//
|
||
for (systemState = PowerSystemSleeping1;
|
||
systemState <= PowerSystemShutdown;
|
||
systemState++) {
|
||
|
||
//
|
||
// Do we support this state?
|
||
//
|
||
if (!(AcpiSupportedSystemStates & (1 << systemState) ) ) {
|
||
|
||
continue;
|
||
|
||
}
|
||
|
||
//
|
||
// We always support the D3 state
|
||
//
|
||
supportedDeviceStates = (1 << PowerDeviceD3);
|
||
|
||
//
|
||
// Determine the supported Device states for this System state
|
||
//
|
||
status = ACPISystemPowerDetermineSupportedDeviceStates(
|
||
DeviceExtension,
|
||
systemState,
|
||
&supportedDeviceStates
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_WARNING,
|
||
DeviceExtension,
|
||
"Cannot determine D state for S%x - %08lx\n",
|
||
(systemState - 1),
|
||
status
|
||
) );
|
||
DeviceMap[systemState] = PowerDeviceD3;
|
||
continue;
|
||
|
||
}
|
||
|
||
//
|
||
// Starting from the device states that we currently are set to
|
||
// (which we would have gotten by running the _SxD method on the
|
||
// \_SB), look to see if we can use a lower D-state instead.
|
||
//
|
||
// Note: It is *VERY* important to remember that *ALL* devices can
|
||
// support D3, so the following loop will *always* terminate in the
|
||
// D3 case.
|
||
//
|
||
for (deviceState = DeviceMap[systemState];
|
||
deviceState <= PowerDeviceD3;
|
||
deviceState++) {
|
||
|
||
//
|
||
// Is this a supported device state?
|
||
//
|
||
if (!(supportedDeviceStates & (1 << deviceState) ) ) {
|
||
|
||
//
|
||
// no? then look at the next one
|
||
//
|
||
continue;
|
||
|
||
}
|
||
|
||
//
|
||
// This is the D-state that we need to use
|
||
//
|
||
DeviceMap[systemState] = deviceState;
|
||
break;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Always return success
|
||
//
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
ACPISystemPowerProcessSxD(
|
||
IN PDEVICE_EXTENSION DeviceExtension,
|
||
IN DEVICE_POWER_STATE CurrentMapping[PowerSystemMaximum],
|
||
IN PBOOLEAN MatchFound
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine updates the current S-to-D mapping with the information
|
||
in the ACPI namespace. If it finds any _SxD routines, then it tells the
|
||
caller
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - Device to check
|
||
CurrentMapping - The current mapping to modify
|
||
MatchFound - Where to indicate if we have found a match or not
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
DEVICE_POWER_STATE dState;
|
||
NTSTATUS status;
|
||
SYSTEM_POWER_STATE sState;
|
||
|
||
PAGED_CODE();
|
||
ASSERT( MatchFound != NULL );
|
||
|
||
//
|
||
// Assume no match
|
||
//
|
||
*MatchFound = FALSE;
|
||
|
||
//
|
||
// Loop for all the S-States that we care about
|
||
//
|
||
for (sState = PowerSystemWorking; sState < PowerSystemMaximum; sState++) {
|
||
|
||
//
|
||
// Does the system support this S-State?
|
||
//
|
||
if (!(AcpiSupportedSystemStates & (1 << sState)) ) {
|
||
|
||
//
|
||
// This S-state is not supported by the system. Mark it as such
|
||
//
|
||
CurrentMapping[sState] = PowerDeviceUnspecified;
|
||
continue;
|
||
|
||
}
|
||
|
||
//
|
||
// Evaluate the control method
|
||
//
|
||
status = ACPISystemPowerGetSxD( DeviceExtension, sState, &dState );
|
||
if (status == STATUS_OBJECT_NAME_NOT_FOUND) {
|
||
|
||
//
|
||
// Not a critical error
|
||
//
|
||
continue;
|
||
|
||
}
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_CRITICAL,
|
||
DeviceExtension,
|
||
"ACPISystemPowerProcessSxD: Cannot Evaluate _SxD - 0x%08lx\n",
|
||
status
|
||
) );
|
||
continue;
|
||
|
||
}
|
||
|
||
//
|
||
// Match found
|
||
//
|
||
*MatchFound = TRUE;
|
||
|
||
//
|
||
// Is this value greater then the number within the table?
|
||
//
|
||
if (dState > CurrentMapping[sState]) {
|
||
|
||
//
|
||
// Yes, so we have a new mapping
|
||
//
|
||
CurrentMapping[sState] = dState;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Done
|
||
//
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
ACPISystemPowerQueryDeviceCapabilities(
|
||
IN PDEVICE_EXTENSION DeviceExtension,
|
||
IN PDEVICE_CAPABILITIES DeviceCapabilities
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Any routine that needs to know the device capabilities will call this
|
||
function for the power capabilities
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - The extension whose capabilities we want
|
||
DeviceCapabilities - Where to store the capabilities
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
#if DBG
|
||
BOOLEAN dumpAtEnd = FALSE;
|
||
#endif
|
||
DEVICE_CAPABILITIES parentCapabilities;
|
||
NTSTATUS status;
|
||
PDEVICE_CAPABILITIES baseCapabilities;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// We only need to do this once
|
||
//
|
||
if (!(DeviceExtension->Flags & DEV_PROP_BUILT_POWER_TABLE) ) {
|
||
|
||
#if DBG
|
||
ACPIDebugDeviceCapabilities(
|
||
DeviceExtension,
|
||
DeviceCapabilities,
|
||
"From PDO"
|
||
);
|
||
#endif
|
||
|
||
//
|
||
// Our next action depends on wether or not we are a filter (only)
|
||
// or a PDO
|
||
//
|
||
if ( (DeviceExtension->Flags & DEV_TYPE_FILTER) &&
|
||
!(DeviceExtension->Flags & DEV_TYPE_PDO) ) {
|
||
|
||
//
|
||
// In this case, our base capabilities are the ones that have
|
||
// already been passed to us
|
||
//
|
||
baseCapabilities = DeviceCapabilities;
|
||
|
||
} else {
|
||
|
||
//
|
||
// We must get the capabilities of the parent device
|
||
//
|
||
status = ACPIInternalGetDeviceCapabilities(
|
||
DeviceExtension->ParentExtension->DeviceObject,
|
||
&parentCapabilities
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_CRITICAL,
|
||
DeviceExtension,
|
||
" - Could not get parent caps - %08lx\n",
|
||
status
|
||
) );
|
||
return status;
|
||
|
||
}
|
||
|
||
//
|
||
// our base capabilities are the one that we just fetched
|
||
//
|
||
baseCapabilities = &parentCapabilities;
|
||
|
||
#if DBG
|
||
ACPIDebugDeviceCapabilities(
|
||
DeviceExtension,
|
||
baseCapabilities,
|
||
"From Parent"
|
||
);
|
||
#endif
|
||
|
||
}
|
||
|
||
#if DBG
|
||
ACPIDebugPowerCapabilities( DeviceExtension, "Before Update" );
|
||
#endif
|
||
|
||
//
|
||
// Update our capabilities with those of our parent
|
||
//
|
||
status = ACPISystemPowerUpdateDeviceCapabilities(
|
||
DeviceExtension,
|
||
baseCapabilities,
|
||
DeviceCapabilities
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_CRITICAL,
|
||
DeviceExtension,
|
||
" - Could not update caps - %08lx\n",
|
||
status
|
||
) );
|
||
|
||
//
|
||
// If this is a pdo, then this is a fatal error
|
||
//
|
||
if ( (DeviceExtension->Flags & DEV_TYPE_PDO) ) {
|
||
|
||
ACPIInternalError( ACPI_SYSPOWER );
|
||
|
||
}
|
||
return status;
|
||
|
||
}
|
||
#if DBG
|
||
ACPIDebugPowerCapabilities( DeviceExtension, "After Update" );
|
||
dumpAtEnd = TRUE;
|
||
#endif
|
||
|
||
//
|
||
// Never do this again
|
||
//
|
||
ACPIInternalUpdateFlags(
|
||
&(DeviceExtension->Flags),
|
||
DEV_PROP_BUILT_POWER_TABLE,
|
||
FALSE
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// Hmm.. I'm tempted to grab a spinlock here, but since we cannot
|
||
// updating the capabilities for this device, I think it is safe
|
||
// to not do so. We need to grab the spinlock when setting these
|
||
// values so that we can sync with the power code
|
||
//
|
||
|
||
//
|
||
// Okay, at this point, we think the device extension's capabilities
|
||
// are appropriate for the stack at hand. Let's copy them over
|
||
//
|
||
IoCopyDeviceCapabilitiesMapping(
|
||
DeviceExtension->PowerInfo.DevicePowerMatrix,
|
||
DeviceCapabilities->DeviceState
|
||
);
|
||
|
||
//
|
||
// then set those capabilities as well.
|
||
//
|
||
DeviceCapabilities->SystemWake = DeviceExtension->PowerInfo.SystemWakeLevel;
|
||
DeviceCapabilities->DeviceWake = DeviceExtension->PowerInfo.DeviceWakeLevel;
|
||
|
||
//
|
||
// Set the other capabilities
|
||
//
|
||
DeviceCapabilities->DeviceD1 = DeviceExtension->PowerInfo.SupportDeviceD1;
|
||
DeviceCapabilities->DeviceD2 = DeviceExtension->PowerInfo.SupportDeviceD2;
|
||
DeviceCapabilities->WakeFromD0 = DeviceExtension->PowerInfo.SupportWakeFromD0;
|
||
DeviceCapabilities->WakeFromD1 = DeviceExtension->PowerInfo.SupportWakeFromD1;
|
||
DeviceCapabilities->WakeFromD2 = DeviceExtension->PowerInfo.SupportWakeFromD2;
|
||
DeviceCapabilities->WakeFromD3 = DeviceExtension->PowerInfo.SupportWakeFromD3;
|
||
|
||
#if DBG
|
||
if (dumpAtEnd) {
|
||
|
||
ACPIDebugDeviceCapabilities(
|
||
DeviceExtension,
|
||
DeviceCapabilities,
|
||
"Done"
|
||
);
|
||
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Done
|
||
//
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
ACPISystemPowerUpdateDeviceCapabilities(
|
||
IN PDEVICE_EXTENSION DeviceExtension,
|
||
IN PDEVICE_CAPABILITIES BaseCapabilities,
|
||
IN PDEVICE_CAPABILITIES DeviceCapabilities
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine updates the DevicePowerMatrix of the device extension with
|
||
the current S to D mapping for the device.
|
||
|
||
The BaseCapabilities are used as the template. That is, they provide
|
||
values that we then modify.
|
||
|
||
The DeviceCapabilities are the actual capabilities that are returned
|
||
to the OS. Note that it is possible for the BaseCapabilities to the be
|
||
same pointer as the DeviceCapabilities (if its a Filter).
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - The device whose capabilities we want
|
||
BaseCapabilities - The base values
|
||
DeviceCapabilities - The device capabilities
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
BOOLEAN matchFound;
|
||
DEVICE_POWER_STATE currentDState;
|
||
DEVICE_POWER_STATE currentMapping[PowerSystemMaximum];
|
||
DEVICE_POWER_STATE devIndex;
|
||
DEVICE_POWER_STATE deviceWakeLevel = PowerDeviceUnspecified;
|
||
DEVICE_POWER_STATE filterWakeLevel = PowerDeviceUnspecified;
|
||
KIRQL oldIrql;
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
SYSTEM_POWER_STATE sysIndex;
|
||
SYSTEM_POWER_STATE supportedState;
|
||
SYSTEM_POWER_STATE systemWakeLevel = PowerSystemUnspecified;
|
||
ULONG interestingBits;
|
||
ULONG mask;
|
||
ULONG supported = 0;
|
||
ULONG supportedPr = 0;
|
||
ULONG supportedPs = 0;
|
||
ULONG supportedWake = 0;
|
||
|
||
//
|
||
// We should remember what the capabilities of the device. We need
|
||
// to remember because we will be modifying these capabilities in
|
||
// the next call (if required)
|
||
//
|
||
IoCopyDeviceCapabilitiesMapping(
|
||
BaseCapabilities->DeviceState,
|
||
currentMapping
|
||
);
|
||
|
||
//
|
||
// Sanity checks
|
||
//
|
||
if (currentMapping[PowerSystemWorking] != PowerDeviceD0) {
|
||
|
||
#if DBG
|
||
ACPIDebugDeviceCapabilities(
|
||
DeviceExtension,
|
||
BaseCapabilities,
|
||
"PowerSystemWorking != PowerDeviceD0"
|
||
);
|
||
#endif
|
||
// ASSERT( currentMapping[PowerSystemWorking] == PowerDeviceD0 );
|
||
currentMapping[PowerSystemWorking] = PowerDeviceD0;
|
||
|
||
}
|
||
|
||
//
|
||
// Get the D-States that are supported by this extension
|
||
//
|
||
status = ACPIDevicePowerDetermineSupportedDeviceStates(
|
||
DeviceExtension,
|
||
&supportedPr,
|
||
&supportedPs
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// Hmm...
|
||
//
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_CRITICAL,
|
||
DeviceExtension,
|
||
"ACPIDevicePowerDetermineSupportedDeviceStates = 0x%08lx\n",
|
||
status
|
||
) );
|
||
return status;
|
||
|
||
}
|
||
|
||
//
|
||
// The supported index is the union of which _PR and which _PS are
|
||
// present
|
||
supported = (supportedPr | supportedPs);
|
||
|
||
//
|
||
// At this point, if there are no supported bits, then we should check
|
||
// the device capabilities and what our parent supports
|
||
//
|
||
if (!supported) {
|
||
|
||
//
|
||
// Do some special checkin if we are a filter. We can only do the
|
||
// following if the caps indicate that there is a D0 or D3 support
|
||
//
|
||
if ( (DeviceExtension->Flags & DEV_TYPE_FILTER) &&
|
||
!(DeviceExtension->Flags & DEV_TYPE_PDO) &&
|
||
!(DeviceCapabilities->DeviceD1) &&
|
||
!(DeviceCapabilities->DeviceD2) ) {
|
||
|
||
//
|
||
// This is a filter, and we don't know any of its power caps, so
|
||
// the thing to do (because of Video) is to decide to not touch
|
||
// the mapping
|
||
//
|
||
goto ACPISystemPowerUpdateDeviceCapabilitiesExit;
|
||
|
||
}
|
||
|
||
//
|
||
// Assume that we support D0 and D3
|
||
//
|
||
supported = (1 << PowerDeviceD0) | (1 << PowerDeviceD3);
|
||
|
||
//
|
||
// Do we support D1?
|
||
//
|
||
if (DeviceCapabilities->DeviceD1) {
|
||
|
||
supported |= (1 << PowerDeviceD1);
|
||
|
||
}
|
||
|
||
//
|
||
// Do we support D2?
|
||
//
|
||
if (DeviceCapabilities->DeviceD2) {
|
||
|
||
supported |= (1 << PowerDeviceD2);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// We also need to update the Wake Capabilities. We do this so
|
||
// that we get the correct SystemWakeLevel based on the information
|
||
// present
|
||
//
|
||
status = ACPISystemPowerUpdateWakeCapabilities(
|
||
DeviceExtension,
|
||
BaseCapabilities,
|
||
DeviceCapabilities,
|
||
currentMapping,
|
||
&supportedWake,
|
||
&systemWakeLevel,
|
||
&deviceWakeLevel,
|
||
&filterWakeLevel
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_CRITICAL,
|
||
DeviceExtension,
|
||
"ACPISystemPowerUpdateWakeCapabilities = 0x%08lx\n",
|
||
status
|
||
) );
|
||
return status;
|
||
|
||
}
|
||
|
||
//
|
||
// Now, we must look at the base capabilities and determine
|
||
// if we need to modify them
|
||
//
|
||
for (sysIndex = PowerSystemSleeping1; sysIndex <= PowerSystemShutdown; sysIndex++) {
|
||
|
||
//
|
||
// Does the system support this S-State?
|
||
//
|
||
if (!(AcpiSupportedSystemStates & (1 << sysIndex) ) ) {
|
||
|
||
continue;
|
||
|
||
}
|
||
|
||
//
|
||
// See if there is an _SxD for this state
|
||
//
|
||
status = ACPISystemPowerGetSxD( DeviceExtension, sysIndex, &devIndex );
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// We have found a match. Is it better then the current mapping?
|
||
//
|
||
if (devIndex > currentMapping[sysIndex]) {
|
||
|
||
//
|
||
// Yes, so we have a new mapping
|
||
//
|
||
currentMapping[sysIndex] = devIndex;
|
||
|
||
}
|
||
continue;
|
||
|
||
} else if (status != STATUS_OBJECT_NAME_NOT_FOUND) {
|
||
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_CRITICAL,
|
||
DeviceExtension,
|
||
"ACPISystemPowerUpdateDeviceCapabilities: Cannot Evalutate "
|
||
"_SxD - 0x%08lx\n",
|
||
status
|
||
) );
|
||
|
||
}
|
||
|
||
//
|
||
// What is the base d-state for the current mapping
|
||
//
|
||
currentDState = currentMapping[sysIndex];
|
||
|
||
//
|
||
// Remember that we didn't find a match
|
||
//
|
||
matchFound = FALSE;
|
||
|
||
//
|
||
// Calculate the interesting pr bits. Do this by ignoring any bit
|
||
// less then the one indicated by the current mapping
|
||
//
|
||
mask = (1 << currentDState) - 1;
|
||
interestingBits = supported & ~mask;
|
||
|
||
//
|
||
// While there are interesting bits, look to see if they are
|
||
// available for the current state
|
||
//
|
||
while (interestingBits) {
|
||
|
||
//
|
||
// Determine what the highest possible D state that we can
|
||
// have on this device. Clear what we are looking at from
|
||
// the interesting bits
|
||
//
|
||
devIndex = (DEVICE_POWER_STATE) RtlFindLeastSignificantBit(
|
||
(ULONGLONG) interestingBits
|
||
);
|
||
mask = (1 << devIndex);
|
||
interestingBits &= ~mask;
|
||
|
||
//
|
||
// If this S-state is less than the wake level of the device
|
||
// then we should try to find a D-state that we can wake from
|
||
//
|
||
if (sysIndex <= systemWakeLevel) {
|
||
|
||
//
|
||
// If we can wake from a deeper state, then lets consider
|
||
// those bits
|
||
//
|
||
if ( (supportedWake & interestingBits) ) {
|
||
|
||
continue;
|
||
|
||
}
|
||
|
||
//
|
||
// Don't consider anything deeper than the deviceWake,
|
||
// although this should be taken care in the supportedWake
|
||
// test
|
||
//
|
||
if (devIndex == filterWakeLevel) {
|
||
|
||
matchFound = TRUE;
|
||
currentMapping[sysIndex] = devIndex;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// If our only choice is D3, than we automatically match that
|
||
// since all S states can map to D3.
|
||
//
|
||
if (devIndex == PowerDeviceD3) {
|
||
|
||
matchFound = TRUE;
|
||
currentMapping[sysIndex] = devIndex;
|
||
break;
|
||
|
||
}
|
||
|
||
//
|
||
// If we are looking at a _PR entry, then we need to determine
|
||
// if the power plane actually supports this S state
|
||
//
|
||
if (supportedPr == 0) {
|
||
|
||
//
|
||
// We are looking at a _PS entry, and automatically match
|
||
// those
|
||
//
|
||
matchFound = TRUE;
|
||
currentMapping[sysIndex] = devIndex;
|
||
break;
|
||
|
||
}
|
||
|
||
//
|
||
// We must holding a spinlock for the following
|
||
//
|
||
KeAcquireSpinLock( &AcpiPowerLock, &oldIrql );
|
||
|
||
//
|
||
// What system state does this pr state support. If the
|
||
// If the function does not support the D state, then Power
|
||
// SystemUnspecified is returned. The only time that we
|
||
// expect this value is when devIndex == PowerDeviceD3
|
||
//
|
||
supportedState = ACPISystemPowerDetermineSupportedSystemState(
|
||
DeviceExtension,
|
||
devIndex
|
||
);
|
||
if (supportedState == PowerSystemUnspecified) {
|
||
|
||
//
|
||
// Paranoia
|
||
//
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_CRITICAL,
|
||
DeviceExtension,
|
||
"D%x returned PowerSystemUnspecified!\n",
|
||
(devIndex - 1)
|
||
) );
|
||
KeBugCheckEx(
|
||
ACPI_BIOS_ERROR,
|
||
ACPI_CANNOT_MAP_SYSTEM_TO_DEVICE_STATES,
|
||
(ULONG_PTR) DeviceExtension,
|
||
0,
|
||
devIndex
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// Done with the power lock
|
||
//
|
||
KeReleaseSpinLock( &AcpiPowerLock, oldIrql );
|
||
|
||
//
|
||
// The only way to match is if the return value from
|
||
// ACPISystemPowerDetermineSupportedSystemState returns an S
|
||
// state greater than or equal to the one that we are currently
|
||
// processing.
|
||
//
|
||
if (supportedState >= sysIndex) {
|
||
|
||
matchFound = TRUE;
|
||
currentMapping[sysIndex] = devIndex;
|
||
break;
|
||
|
||
}
|
||
|
||
} // while
|
||
|
||
//
|
||
// If we didn't find a match at this point, that should be fatal
|
||
//
|
||
if (!matchFound) {
|
||
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_CRITICAL,
|
||
DeviceExtension,
|
||
"No match found for S%x\n",
|
||
(sysIndex - 1)
|
||
) );
|
||
KeBugCheckEx(
|
||
ACPI_BIOS_ERROR,
|
||
ACPI_CANNOT_MAP_SYSTEM_TO_DEVICE_STATES,
|
||
(ULONG_PTR) DeviceExtension,
|
||
1,
|
||
sysIndex
|
||
);
|
||
|
||
}
|
||
|
||
} // for
|
||
|
||
ACPISystemPowerUpdateDeviceCapabilitiesExit:
|
||
|
||
//
|
||
// Now, we re-run the wake capabilities to make sure that we get the correct
|
||
// device wake level
|
||
//
|
||
status = ACPISystemPowerUpdateWakeCapabilities(
|
||
DeviceExtension,
|
||
BaseCapabilities,
|
||
DeviceCapabilities,
|
||
currentMapping,
|
||
&supportedWake,
|
||
&systemWakeLevel,
|
||
&deviceWakeLevel,
|
||
&filterWakeLevel
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_CRITICAL,
|
||
DeviceExtension,
|
||
"ACPISystemPowerUpdateWakeCapabilities = 0x%08lx\n",
|
||
status
|
||
) );
|
||
return status;
|
||
|
||
}
|
||
|
||
//
|
||
// We must holding a spinlock for the following
|
||
//
|
||
KeAcquireSpinLock( &AcpiPowerLock, &oldIrql );
|
||
|
||
//
|
||
// Copy the mapping back onto the device
|
||
//
|
||
IoCopyDeviceCapabilitiesMapping(
|
||
currentMapping,
|
||
DeviceExtension->PowerInfo.DevicePowerMatrix
|
||
);
|
||
|
||
//
|
||
// Remember the system wake level, device wake level, and what
|
||
// the various support Wake and Power states are
|
||
//
|
||
DeviceExtension->PowerInfo.DeviceWakeLevel = deviceWakeLevel;
|
||
DeviceExtension->PowerInfo.SystemWakeLevel = systemWakeLevel;
|
||
DeviceExtension->PowerInfo.SupportDeviceD1 = ( ( supported & ( 1 << PowerDeviceD1 ) ) != 0);
|
||
DeviceExtension->PowerInfo.SupportDeviceD2 = ( ( supported & ( 1 << PowerDeviceD2 ) ) != 0);
|
||
DeviceExtension->PowerInfo.SupportWakeFromD0 = ( ( supportedWake & ( 1 << PowerDeviceD0 ) ) != 0);
|
||
DeviceExtension->PowerInfo.SupportWakeFromD1 = ( ( supportedWake & ( 1 << PowerDeviceD1 ) ) != 0);
|
||
DeviceExtension->PowerInfo.SupportWakeFromD2 = ( ( supportedWake & ( 1 << PowerDeviceD2 ) ) != 0);
|
||
DeviceExtension->PowerInfo.SupportWakeFromD3 = ( ( supportedWake & ( 1 << PowerDeviceD3 ) ) != 0);
|
||
|
||
//
|
||
// Done with the power lock
|
||
//
|
||
KeReleaseSpinLock( &AcpiPowerLock, oldIrql );
|
||
|
||
//
|
||
// Again, because we allowed device extension with no name space objects
|
||
// to use this function, we must make sure not to set the ACPI_POWER
|
||
// property unless they have a name space object
|
||
//
|
||
if (!(DeviceExtension->Flags & DEV_PROP_NO_OBJECT)) {
|
||
|
||
//
|
||
// Set the ACPI Power Management bits
|
||
//
|
||
ACPIInternalUpdateFlags(
|
||
&(DeviceExtension->Flags),
|
||
DEV_PROP_ACPI_POWER,
|
||
FALSE
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// Done
|
||
//
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
ACPISystemPowerUpdateWakeCapabilities(
|
||
IN PDEVICE_EXTENSION DeviceExtension,
|
||
IN PDEVICE_CAPABILITIES BaseCapabilities,
|
||
IN PDEVICE_CAPABILITIES DeviceCapabilities,
|
||
IN DEVICE_POWER_STATE CurrentMapping[PowerSystemMaximum],
|
||
IN ULONG *SupportedWake,
|
||
IN SYSTEM_POWER_STATE *SystemWakeLevel,
|
||
IN DEVICE_POWER_STATE *DeviceWakeLevel,
|
||
IN DEVICE_POWER_STATE *FilterWakeLevel
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine calculates the Wake Capabilities of the device based on
|
||
the present capabilities
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - The device whose capabilities we want
|
||
BaseCapabilities - The base values
|
||
ParentCapabilities - The capabilities for the device
|
||
CurrentMapping - The current S->D mapping
|
||
SupportedWake - BitMap of the supported Wake states
|
||
SystemWakeLevel - The S-State that we can wake up from
|
||
DeviceWakeLevel - The D-State that we can wake up from
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
|
||
PAGED_CODE();
|
||
|
||
if ( (DeviceExtension->Flags & DEV_TYPE_FILTER) &&
|
||
!(DeviceExtension->Flags & DEV_TYPE_PDO) ) {
|
||
|
||
return ACPISystemPowerUpdateWakeCapabilitiesForFilters(
|
||
DeviceExtension,
|
||
BaseCapabilities,
|
||
DeviceCapabilities,
|
||
CurrentMapping,
|
||
SupportedWake,
|
||
SystemWakeLevel,
|
||
DeviceWakeLevel,
|
||
FilterWakeLevel
|
||
);
|
||
|
||
} else {
|
||
|
||
if (FilterWakeLevel != NULL) {
|
||
|
||
*FilterWakeLevel = PowerDeviceUnspecified;
|
||
|
||
}
|
||
|
||
return ACPISystemPowerUpdateWakeCapabilitiesForPDOs(
|
||
DeviceExtension,
|
||
BaseCapabilities,
|
||
DeviceCapabilities,
|
||
CurrentMapping,
|
||
SupportedWake,
|
||
SystemWakeLevel,
|
||
DeviceWakeLevel,
|
||
FilterWakeLevel
|
||
);
|
||
}
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
ACPISystemPowerUpdateWakeCapabilitiesForFilters(
|
||
IN PDEVICE_EXTENSION DeviceExtension,
|
||
IN PDEVICE_CAPABILITIES BaseCapabilities,
|
||
IN PDEVICE_CAPABILITIES DeviceCapabilities,
|
||
IN DEVICE_POWER_STATE CurrentMapping[PowerSystemMaximum],
|
||
IN ULONG *SupportedWake,
|
||
IN SYSTEM_POWER_STATE *SystemWakeLevel,
|
||
IN DEVICE_POWER_STATE *DeviceWakeLevel,
|
||
IN DEVICE_POWER_STATE *FilterWakeLevel
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine calculates the Wake Capabilities of the device based on
|
||
the present capabilities. This version of the function uses the
|
||
devices states that the device can wake from to determine what the
|
||
appropriate system level is.
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - The device whose capabilities we want
|
||
BaseCapabilities - The base values
|
||
DeviceCapabilities - The capabilities for the device
|
||
CurrentMapping - The current S->D mapping
|
||
|
||
SupportedWake - BitMap of the supported Wake states
|
||
SystemWakeLevel - The S-State that we can wake up from
|
||
DeviceWakeLevel - The D-State that we can wake up from
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
BOOLEAN noPdoWakeSupport = FALSE;
|
||
BOOLEAN foundDState = FALSE;
|
||
DEVICE_POWER_STATE deviceWake;
|
||
DEVICE_POWER_STATE deviceTempWake;
|
||
KIRQL oldIrql;
|
||
NTSTATUS status;
|
||
PACPI_POWER_INFO powerInfo;
|
||
SYSTEM_POWER_STATE systemWake;
|
||
SYSTEM_POWER_STATE tempWake;
|
||
|
||
UNREFERENCED_PARAMETER( BaseCapabilities );
|
||
|
||
//
|
||
// Use the capabilities from the Device
|
||
//
|
||
deviceWake = DeviceCapabilities->DeviceWake;
|
||
systemWake = DeviceCapabilities->SystemWake;
|
||
|
||
//
|
||
// Does the device support wake from D0? D1? D2? D3?
|
||
//
|
||
if (DeviceCapabilities->WakeFromD0) {
|
||
|
||
*SupportedWake |= (1 << PowerDeviceD0 );
|
||
|
||
}
|
||
if (DeviceCapabilities->WakeFromD1) {
|
||
|
||
*SupportedWake |= (1 << PowerDeviceD1 );
|
||
|
||
}
|
||
if (DeviceCapabilities->WakeFromD2) {
|
||
|
||
*SupportedWake |= (1 << PowerDeviceD2 );
|
||
|
||
}
|
||
if (DeviceCapabilities->WakeFromD3) {
|
||
|
||
*SupportedWake |= (1 << PowerDeviceD3 );
|
||
|
||
}
|
||
|
||
//
|
||
// If we don't support any wake states in the PDO (ie: DeviceWake or
|
||
// SystemWake is 0) then we should remember that for future considerations
|
||
//
|
||
if (deviceWake == PowerDeviceUnspecified ||
|
||
systemWake == PowerSystemUnspecified) {
|
||
|
||
noPdoWakeSupport = TRUE;
|
||
deviceWake = PowerDeviceUnspecified;
|
||
systemWake = PowerSystemUnspecified;
|
||
|
||
}
|
||
|
||
//
|
||
// If we support the device wake (ie: there is a _PRW), then we
|
||
// should take the minimum of the systemWake we got from the parent
|
||
// and the value that is stored in the _PRW
|
||
//
|
||
if ( (DeviceExtension->Flags & DEV_CAP_WAKE) ) {
|
||
|
||
//
|
||
// Need power lock for the following.
|
||
//
|
||
KeAcquireSpinLock( &AcpiPowerLock, &oldIrql );
|
||
|
||
//
|
||
// Remember the current system wake level
|
||
//
|
||
tempWake = DeviceExtension->PowerInfo.SystemWakeLevel;
|
||
|
||
//
|
||
// See what D-state (if any) that the power plane information
|
||
// maps to
|
||
//
|
||
deviceTempWake = ACPISystemPowerDetermineSupportedDeviceWakeState(
|
||
DeviceExtension
|
||
);
|
||
|
||
KeReleaseSpinLock( &AcpiPowerLock, oldIrql );
|
||
|
||
//
|
||
// Take the minimum
|
||
//
|
||
if (tempWake < systemWake || noPdoWakeSupport) {
|
||
|
||
systemWake = tempWake;
|
||
|
||
}
|
||
|
||
//
|
||
// Did the PRW have useful information for us?
|
||
//
|
||
if (deviceTempWake != PowerDeviceUnspecified) {
|
||
|
||
//
|
||
// Note that in this case, they are basically overriding all
|
||
// other supported wake up states, so the thing to do is only
|
||
// remember this wake level
|
||
//
|
||
foundDState = TRUE;
|
||
deviceWake = deviceTempWake;
|
||
|
||
}
|
||
|
||
//
|
||
// See if there is a device wake specified for this S-state?
|
||
//
|
||
status = ACPISystemPowerGetSxD(
|
||
DeviceExtension,
|
||
tempWake,
|
||
&deviceTempWake
|
||
);
|
||
if (status == STATUS_OBJECT_NAME_NOT_FOUND) {
|
||
|
||
status = ACPISystemPowerGetSxD(
|
||
DeviceExtension,
|
||
systemWake,
|
||
&deviceTempWake
|
||
);
|
||
|
||
}
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// Note that in this case, they are basically overriding all other
|
||
// supported Wake up states, so the thing to do is only remember
|
||
// this wake level
|
||
//
|
||
foundDState = TRUE;
|
||
deviceWake = deviceTempWake;
|
||
|
||
}
|
||
|
||
if (!foundDState) {
|
||
|
||
//
|
||
// Crossreference the system wake level with the matrix
|
||
// Need spinlock to do this
|
||
//
|
||
deviceWake = CurrentMapping[systemWake];
|
||
|
||
//
|
||
// If this value isn't known, then we guess that it can
|
||
// from D3. In other words, unless they have made some
|
||
// explicity mechanism to tell which D-state to wake from,
|
||
// assume that we can do it from D3
|
||
//
|
||
if (deviceWake == PowerDeviceUnspecified) {
|
||
|
||
deviceWake = PowerDeviceD3;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// We should only check to see if the D-state is a wakeable state
|
||
// in the parent only if the parent claims to support wake
|
||
//
|
||
if (!noPdoWakeSupport) {
|
||
|
||
//
|
||
// The logic behind the following is that if we are a filter, even
|
||
// if we support device wake (that is the _PRW is in the PCI device
|
||
// itself, not for the root PCI bus), than we still need to make sure
|
||
// that the D-State that we mapped to is one that is supported by
|
||
// the hardware.
|
||
//
|
||
for (;deviceWake < PowerDeviceMaximum; deviceWake++) {
|
||
|
||
//
|
||
// If we we support this wake state, then we can stop
|
||
//
|
||
if (*SupportedWake & (1 << deviceWake) ) {
|
||
|
||
break;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// If we got here, and the D-state is PowerDeviceMaximum, then we
|
||
// don't really support wake on the device
|
||
//
|
||
if (deviceWake == PowerDeviceMaximum ||
|
||
deviceWake == PowerDeviceUnspecified) {
|
||
|
||
deviceWake = PowerDeviceUnspecified;
|
||
systemWake = PowerSystemUnspecified;
|
||
*SupportedWake = 0;
|
||
|
||
} else {
|
||
|
||
//
|
||
// In this situation, we will end up only supporting this wake state
|
||
//
|
||
*SupportedWake = (1 << deviceWake );
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// See if there is a device wake specified for this S-state
|
||
//
|
||
status = ACPISystemPowerGetSxD(
|
||
DeviceExtension,
|
||
systemWake,
|
||
&deviceTempWake
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// Find the best supported wake level
|
||
//
|
||
for (;deviceTempWake > PowerDeviceUnspecified; deviceTempWake--) {
|
||
|
||
if ( (*SupportedWake & (1 << deviceTempWake) ) ) {
|
||
|
||
deviceWake = deviceTempWake;
|
||
break;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Make sure that the system wake level is a valid one
|
||
//
|
||
for (; systemWake > PowerSystemUnspecified; systemWake--) {
|
||
|
||
//
|
||
// Since S-States that we don't support map to
|
||
// PowerDeviceUnspecified, we cannot consider any of those S
|
||
// states in this test. We also cannot consider them for other
|
||
// obvious reasons as well
|
||
//*
|
||
if (!(AcpiSupportedSystemStates & (1 << systemWake) ) ||
|
||
(CurrentMapping[systemWake] == PowerDeviceUnspecified) ) {
|
||
|
||
continue;
|
||
|
||
}
|
||
|
||
//
|
||
// Does this S-state support the given S-State?
|
||
//
|
||
if (CurrentMapping[systemWake] <= deviceWake) {
|
||
|
||
break;
|
||
|
||
}
|
||
|
||
//
|
||
// Does the device state for the current system wake mapping
|
||
// allow wake-from sleep?
|
||
//
|
||
if (*SupportedWake & (1 << CurrentMapping[systemWake]) ) {
|
||
|
||
//
|
||
// Yes? then we had better update our idea of what the
|
||
// device wake state should be...
|
||
//
|
||
deviceWake = CurrentMapping[systemWake];
|
||
break;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// If we got into a situation were we cannot find a single S-state
|
||
// that we can wake from, then we must make sure that the device
|
||
// wake is null
|
||
//
|
||
if (systemWake == PowerSystemUnspecified) {
|
||
|
||
//
|
||
// Remember that the device wake and supported wake states
|
||
// are null
|
||
//
|
||
deviceWake = PowerDeviceUnspecified;
|
||
*SupportedWake = 0;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Return the proper device wake and system wake values
|
||
//
|
||
if (SystemWakeLevel != NULL) {
|
||
|
||
*SystemWakeLevel = systemWake;
|
||
|
||
}
|
||
if (DeviceWakeLevel != NULL) {
|
||
|
||
*DeviceWakeLevel = deviceWake;
|
||
|
||
}
|
||
if (FilterWakeLevel != NULL) {
|
||
|
||
*FilterWakeLevel = deviceWake;
|
||
|
||
}
|
||
|
||
//
|
||
// Done
|
||
//
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
ACPISystemPowerUpdateWakeCapabilitiesForPDOs(
|
||
IN PDEVICE_EXTENSION DeviceExtension,
|
||
IN PDEVICE_CAPABILITIES BaseCapabilities,
|
||
IN PDEVICE_CAPABILITIES DeviceCapabilities,
|
||
IN DEVICE_POWER_STATE CurrentMapping[PowerSystemMaximum],
|
||
IN ULONG *SupportedWake,
|
||
IN SYSTEM_POWER_STATE *SystemWakeLevel,
|
||
IN DEVICE_POWER_STATE *DeviceWakeLevel,
|
||
IN DEVICE_POWER_STATE *FilterWakeLevel
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine calculates the Wake Capabilities of the device based on
|
||
the present capabilities. This version of the function uses the
|
||
system state that the device can wake from to determine what the
|
||
appropriate device level is.
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - The device whose capabilities we want
|
||
BaseCapabilities - The base values
|
||
DeviceCapabilities - The capabilities for the device
|
||
CurrentMapping - The current S->D mapping
|
||
SupportedWake - BitMap of the supported Wake states
|
||
SystemWakeLevel - The S-State that we can wake up from
|
||
DeviceWakeLevel - The D-State that we can wake up from
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
BOOLEAN foundDState = FALSE;
|
||
DEVICE_POWER_STATE deviceWake;
|
||
DEVICE_POWER_STATE deviceTempWake;
|
||
DEVICE_POWER_STATE filterWake = PowerDeviceUnspecified;
|
||
KIRQL oldIrql;
|
||
NTSTATUS status;
|
||
SYSTEM_POWER_STATE systemWake;
|
||
|
||
UNREFERENCED_PARAMETER( DeviceCapabilities );
|
||
UNREFERENCED_PARAMETER( BaseCapabilities );
|
||
|
||
//
|
||
// Use the capabilities of the device
|
||
//
|
||
if (!(DeviceExtension->Flags & DEV_CAP_WAKE) ) {
|
||
|
||
deviceWake = PowerDeviceUnspecified;
|
||
systemWake = PowerSystemUnspecified;
|
||
goto ACPISystemPowerUpdateWakeCapabilitiesForPDOsExit;
|
||
|
||
}
|
||
|
||
//
|
||
// Hold the lock for the following
|
||
//
|
||
KeAcquireSpinLock( &AcpiPowerLock, &oldIrql );
|
||
|
||
//
|
||
// Use the wake level that we know about. If this wakelevel
|
||
// isn't supported, than there is a bios error
|
||
//
|
||
systemWake = DeviceExtension->PowerInfo.SystemWakeLevel;
|
||
deviceTempWake = ACPISystemPowerDetermineSupportedDeviceWakeState(
|
||
DeviceExtension
|
||
);
|
||
|
||
//
|
||
// Done with the lock
|
||
//
|
||
KeReleaseSpinLock( &AcpiPowerLock, oldIrql );
|
||
|
||
//
|
||
// Sanity check
|
||
//
|
||
if (!(AcpiSupportedSystemStates & (1 << systemWake) ) ) {
|
||
|
||
#if 0
|
||
if (!(AcpiOverrideAttributes & ACPI_OVERRIDE_MP_SLEEP) ) {
|
||
|
||
KeBugCheckEx(
|
||
ACPI_BIOS_ERROR,
|
||
ACPI_CANNOT_MAP_SYSTEM_TO_DEVICE_STATES,
|
||
(ULONG_PTR) DeviceExtension,
|
||
2,
|
||
systemWake
|
||
);
|
||
|
||
}
|
||
#endif
|
||
|
||
deviceWake = PowerDeviceUnspecified;
|
||
systemWake = PowerSystemUnspecified;
|
||
goto ACPISystemPowerUpdateWakeCapabilitiesForPDOsExit;
|
||
|
||
}
|
||
|
||
if (deviceTempWake != PowerDeviceUnspecified) {
|
||
|
||
//
|
||
// Note that in this case, they are basically overriding all
|
||
// other supported wake up states, so the thing to do is only
|
||
// remember this wake level
|
||
//
|
||
foundDState = TRUE;
|
||
deviceWake = deviceTempWake;
|
||
filterWake = deviceTempWake;
|
||
*SupportedWake = (1 << deviceWake );
|
||
|
||
}
|
||
|
||
//
|
||
// See if there is an SxD method that will give us a hint
|
||
//
|
||
status = ACPISystemPowerGetSxD(
|
||
DeviceExtension,
|
||
systemWake,
|
||
&deviceTempWake
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// Note that in this case, they are basically overriding all other
|
||
// supported Wake up states, so the thing to do is only remember
|
||
// this wake level
|
||
deviceWake = deviceTempWake;
|
||
filterWake = deviceTempWake;
|
||
foundDState = TRUE;
|
||
|
||
}
|
||
|
||
if (!foundDState) {
|
||
|
||
//
|
||
// Crossreference the system wake level with the matrix
|
||
// Need spinlock to do this
|
||
//
|
||
deviceWake = CurrentMapping[systemWake];
|
||
|
||
//
|
||
// If this value isn't known, then we guess that it can
|
||
// from D3. In other words, unless they have made some
|
||
// explicity mechanism to tell which D-state to wake from,
|
||
// assume that we can do it from D3
|
||
//
|
||
if (deviceWake == PowerDeviceUnspecified) {
|
||
|
||
deviceWake = PowerDeviceD3;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
ACPISystemPowerUpdateWakeCapabilitiesForPDOsExit:
|
||
|
||
//
|
||
// Set the return values
|
||
//
|
||
if (deviceWake != PowerDeviceUnspecified) {
|
||
|
||
*SupportedWake = (1 << deviceWake );
|
||
|
||
} else {
|
||
|
||
*SupportedWake = 0;
|
||
}
|
||
|
||
if (SystemWakeLevel != NULL) {
|
||
|
||
*SystemWakeLevel = systemWake;
|
||
|
||
}
|
||
if (DeviceWakeLevel != NULL) {
|
||
|
||
*DeviceWakeLevel = deviceWake;
|
||
|
||
}
|
||
if (FilterWakeLevel != NULL) {
|
||
|
||
*FilterWakeLevel = filterWake;
|
||
|
||
}
|
||
|
||
//
|
||
// Done
|
||
//
|
||
return STATUS_SUCCESS;
|
||
}
|