You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2167 lines
58 KiB
2167 lines
58 KiB
/*++
|
|
|
|
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;
|
|
}
|