windows-nt/Source/XPSP1/NT/base/busdrv/acpi/driver/nt/devpower.c
2020-09-26 16:20:57 +08:00

6570 lines
155 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
dpower.c
Abstract:
This handles requests to have devices set themselves at specific power
levels
Author:
Stephane Plante (splante)
Environment:
Kernel mode only.
Revision History:
09-Oct-96 Initial Revision
20-Nov-96 Interrupt Vector support
31-Mar-97 Cleanup
17-Sep-97 Major Rewrite
06-Jan-98 Cleaned Up the SST code
--*/
#include "pch.h"
//
// This is the variable that indicates wether or not the DPC is running
//
BOOLEAN AcpiPowerDpcRunning;
//
// This is the variable that indicates wether or not the DPC has completed
// real work
//
BOOLEAN AcpiPowerWorkDone;
//
// This is the lock that is used to protect certain power resources and
// lists
//
KSPIN_LOCK AcpiPowerLock;
//
// This is the lock that is used *only* within this module to queue requests
// onto the Phase0 list *and* to modify the state of some global variables
//
KSPIN_LOCK AcpiPowerQueueLock;
//
// This is the list that the build dpc queue power requests onto until it
// has finished building all of the device extensions. Once the extensions
// are built, the contents of the list are moved onto the AcpiPowerQueueList
//
LIST_ENTRY AcpiPowerDelayedQueueList;
//
// This is the only list that routines outside of the DPC can queue reqests
// onto
//
LIST_ENTRY AcpiPowerQueueList;
//
// This is the list where we run the _STA to determine if the resources that
// we care about are still present
//
LIST_ENTRY AcpiPowerPhase0List;
//
// This is the list for the phase where we run PS1-PS3 and figure out
// which PowerResources need to be in the 'on' state
//
LIST_ENTRY AcpiPowerPhase1List;
//
// This is the list for when we process the System Requests. It turns out
// that we have to let all of the DeviceRequests through Phase1 before
// we can figure out which devices are on the hibernate path, and which
// arent
//
LIST_ENTRY AcpiPowerPhase2List;
//
// This is the list for the phase where we run ON or OFF
//
LIST_ENTRY AcpiPowerPhase3List;
//
// This is the list for the phase where we check to see if ON/OFF ran okay
//
LIST_ENTRY AcpiPowerPhase4List;
//
// This is the list for the phase where we run PSW or PSW
//
LIST_ENTRY AcpiPowerPhase5List;
//
// This is the list for the phase where we have WaitWake Irps pending
//
LIST_ENTRY AcpiPowerWaitWakeList;
//
// This is the list for the synchronize power requests
//
LIST_ENTRY AcpiPowerSynchronizeList;
//
// This is the list of Power Device Nodes objects
//
LIST_ENTRY AcpiPowerNodeList;
//
// This is what we use to queue up the DPC
//
KDPC AcpiPowerDpc;
//
// This is where we remember if the system is in steady state or if it is going
// into standby
//
BOOLEAN AcpiPowerLeavingS0;
//
// This is the list that we use to pre-allocate storage for requests
//
NPAGED_LOOKASIDE_LIST RequestLookAsideList;
//
// This is the list that we use to pre-allocate storage for object data
//
NPAGED_LOOKASIDE_LIST ObjectDataLookAsideList;
//
// This table is used to map DevicePowerStates from the ACPI format to some
// thing the system can handle
//
DEVICE_POWER_STATE DevicePowerStateTranslation[DEVICE_POWER_MAXIMUM] = {
PowerDeviceD0,
PowerDeviceD1,
PowerDeviceD2,
PowerDeviceD3
};
//
// This table is used to map SystemPowerStates from the ACPI format to some
// thing the system can handle
//
SYSTEM_POWER_STATE SystemPowerStateTranslation[SYSTEM_POWER_MAXIMUM] = {
PowerSystemWorking,
PowerSystemSleeping1,
PowerSystemSleeping2,
PowerSystemSleeping3,
PowerSystemHibernate,
PowerSystemShutdown
};
//
// This table is used to map SystemPowerStates from the NT format to the
// ACPI format
//
ULONG AcpiSystemStateTranslation[PowerSystemMaximum] = {
-1, // PowerSystemUnspecified
0, // PowerSystemWorking
1, // PowerSystemSleepingS1
2, // PowerSystemSleepingS2
3, // PowerSystemSleepingS3
4, // PowerSystemHibernate
5 // PowerSystemShutdown
};
//
// This is the table used to map functions in the Phase0 case WORK_DONE_STEP_0
//
PACPI_POWER_FUNCTION AcpiDevicePowerProcessPhase0Table1[AcpiPowerRequestMaximum+1] = {
ACPIDevicePowerProcessPhase0DeviceSubPhase1,
ACPIDevicePowerProcessPhase0SystemSubPhase1,
ACPIDevicePowerProcessForward,
ACPIDevicePowerProcessForward,
ACPIDevicePowerProcessForward,
ACPIDevicePowerProcessInvalid
};
//
// This is the table used to map functions in the Phase0 case WORK_DONE_STEP_1
//
PACPI_POWER_FUNCTION AcpiDevicePowerProcessPhase0Table2[AcpiPowerRequestMaximum+1] = {
ACPIDevicePowerProcessPhase0DeviceSubPhase2,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid
};
//
// This is the dispatch table for Phase 0
//
PACPI_POWER_FUNCTION *AcpiDevicePowerProcessPhase0Dispatch[] = {
NULL,
NULL,
NULL,
AcpiDevicePowerProcessPhase0Table1,
AcpiDevicePowerProcessPhase0Table2
};
//
// This is the table used to map functions in the Phase1 case WORK_DONE_STEP_0
//
PACPI_POWER_FUNCTION AcpiDevicePowerProcessPhase1Table1[AcpiPowerRequestMaximum+1] = {
ACPIDevicePowerProcessPhase1DeviceSubPhase1,
ACPIDevicePowerProcessForward,
ACPIDevicePowerProcessForward,
ACPIDevicePowerProcessForward,
ACPIDevicePowerProcessForward,
ACPIDevicePowerProcessInvalid
};
//
// This is the table used to map functions in the Phase1 case WORK_DONE_STEP_1
//
PACPI_POWER_FUNCTION AcpiDevicePowerProcessPhase1Table2[AcpiPowerRequestMaximum+1] = {
ACPIDevicePowerProcessPhase1DeviceSubPhase2,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid
};
//
// This is the table used to map functions in the Phase1 case WORK_DONE_STEP_2
//
PACPI_POWER_FUNCTION AcpiDevicePowerProcessPhase1Table3[AcpiPowerRequestMaximum+1] = {
ACPIDevicePowerProcessPhase1DeviceSubPhase3,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid
};
//
// This is the table used to map functions in the Phase1 case WORK_DONE_STEP_3
//
PACPI_POWER_FUNCTION AcpiDevicePowerProcessPhase1Table4[AcpiPowerRequestMaximum+1] = {
ACPIDevicePowerProcessPhase1DeviceSubPhase4,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid
};
//
// This is the dispatch table for Phase 1
//
PACPI_POWER_FUNCTION *AcpiDevicePowerProcessPhase1Dispatch[] = {
NULL,
NULL,
NULL,
AcpiDevicePowerProcessPhase1Table1,
AcpiDevicePowerProcessPhase1Table2,
AcpiDevicePowerProcessPhase1Table3,
AcpiDevicePowerProcessPhase1Table4
};
//
// This is the table used to map functions in the Phase2 case WORK_DONE_STEP_0
//
PACPI_POWER_FUNCTION AcpiDevicePowerProcessPhase2Table1[AcpiPowerRequestMaximum+1] = {
ACPIDevicePowerProcessForward,
ACPIDevicePowerProcessPhase2SystemSubPhase1,
ACPIDevicePowerProcessForward,
ACPIDevicePowerProcessForward,
ACPIDevicePowerProcessForward,
ACPIDevicePowerProcessInvalid
};
//
// This is the table used to map functions in the Phase2 case WORK_DONE_STEP_1
//
PACPI_POWER_FUNCTION AcpiDevicePowerProcessPhase2Table2[AcpiPowerRequestMaximum+1] = {
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessPhase2SystemSubPhase2,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid
};
//
// This is the table used to map functions in the Phase3 case WORK_DONE_STEP_2
//
PACPI_POWER_FUNCTION AcpiDevicePowerProcessPhase2Table3[AcpiPowerRequestMaximum+1] = {
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessPhase2SystemSubPhase3,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid
};
//
// This is the dispatch table for Phase 2
//
PACPI_POWER_FUNCTION *AcpiDevicePowerProcessPhase2Dispatch[] = {
NULL,
NULL,
NULL,
AcpiDevicePowerProcessPhase2Table1,
AcpiDevicePowerProcessPhase2Table2,
AcpiDevicePowerProcessPhase2Table3
};
//
// This is the table used to map functions in the Phase5 case WORK_DONE_STEP_0
//
PACPI_POWER_FUNCTION AcpiDevicePowerProcessPhase5Table1[AcpiPowerRequestMaximum+1] = {
ACPIDevicePowerProcessPhase5DeviceSubPhase1,
ACPIDevicePowerProcessPhase5SystemSubPhase1,
ACPIDevicePowerProcessForward,
ACPIDevicePowerProcessPhase5WarmEjectSubPhase1,
ACPIDevicePowerProcessForward,
ACPIDevicePowerProcessInvalid
};
//
// This is the table used to map functions in the Phase 5 case WORK_DONE_STEP_1
//
PACPI_POWER_FUNCTION AcpiDevicePowerProcessPhase5Table2[AcpiPowerRequestMaximum+1] = {
ACPIDevicePowerProcessPhase5DeviceSubPhase2,
ACPIDevicePowerProcessPhase5SystemSubPhase2,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessPhase5WarmEjectSubPhase2,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid
};
//
// This is the table used to map functions in the Phase 5 case WORK_DONE_STEP_2
//
PACPI_POWER_FUNCTION AcpiDevicePowerProcessPhase5Table3[AcpiPowerRequestMaximum+1] = {
ACPIDevicePowerProcessPhase5DeviceSubPhase3,
ACPIDevicePowerProcessPhase5SystemSubPhase3,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid
};
//
// This is the table used to map functions in the Phase 5 case WORK_DONE_STEP_3
//
PACPI_POWER_FUNCTION AcpiDevicePowerProcessPhase5Table4[AcpiPowerRequestMaximum+1] = {
ACPIDevicePowerProcessPhase5DeviceSubPhase4,
ACPIDevicePowerProcessPhase5SystemSubPhase4,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid
};
//
// This is the table used to map functions in the Phase 5 case WORK_DONE_STEP_4
//
PACPI_POWER_FUNCTION AcpiDevicePowerProcessPhase5Table5[AcpiPowerRequestMaximum+1] = {
ACPIDevicePowerProcessPhase5DeviceSubPhase5,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid
};
//
// This is the table used to map functions in the Phase 5 case WORK_DONE_STEP_5
//
PACPI_POWER_FUNCTION AcpiDevicePowerProcessPhase5Table6[AcpiPowerRequestMaximum+1] = {
ACPIDevicePowerProcessPhase5DeviceSubPhase6,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid,
ACPIDevicePowerProcessInvalid
};
//
// This is the dispatch table for Phase 5
//
PACPI_POWER_FUNCTION *AcpiDevicePowerProcessPhase5Dispatch[] = {
NULL,
NULL,
NULL,
AcpiDevicePowerProcessPhase5Table1,
AcpiDevicePowerProcessPhase5Table2,
AcpiDevicePowerProcessPhase5Table3,
AcpiDevicePowerProcessPhase5Table4,
AcpiDevicePowerProcessPhase5Table5,
AcpiDevicePowerProcessPhase5Table6
};
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,ACPIDevicePowerDetermineSupportedDeviceStates)
#endif
VOID
ACPIDeviceCancelWaitWakeIrp(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is called when the system wants to cancel any pending
WaitWake Irps
Note: This routine is called at DPC level
Arguments:
DeviceObject - The target device for which the irp was sent to
Irp - The irp to be cancelled
Return Value:
None
--*/
{
NTSTATUS status;
PACPI_POWER_CALLBACK callBack;
PACPI_POWER_REQUEST powerRequest;
PDEVICE_EXTENSION deviceExtension = ACPIInternalGetDeviceExtension(DeviceObject);
PLIST_ENTRY listEntry;
PVOID context;
ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL);
//
// Let the world know that we are getting a cancel routine
//
ACPIDevPrint( (
ACPI_PRINT_WARNING,
deviceExtension,
"(0x%08lx): ACPIDeviceCancelWaitWakeIrp - Start\n",
Irp
) );
//
// We need to grab the lock so that we look for the irp in the lists
// of pending WaitWake events. The cancel lock is already acquired
//
KeAcquireSpinLockAtDpcLevel( &AcpiPowerLock );
//
// Walk the list, looking for the Irp in question
//
listEntry = AcpiPowerWaitWakeList.Flink;
while (listEntry != &AcpiPowerWaitWakeList) {
//
// Crack the record, and get ready to look at the next item
//
powerRequest = CONTAINING_RECORD(
listEntry,
ACPI_POWER_REQUEST,
ListEntry
);
//
// Does the power request match the current target? We also know that
// for WaitWake requests, the context poitns to the Irp, so we make
// sure that those match as well.
//
if (powerRequest->DeviceExtension != deviceExtension ||
(PIRP) powerRequest->Context != Irp ) {
listEntry = listEntry->Flink;
continue;
}
ACPIDevPrint( (
ACPI_PRINT_POWER,
deviceExtension,
"(0x%08lx): ACPIDeviceCancelWaitWakeIrp - Match 0x%08lx\n",
Irp,
powerRequest
) );
//
// Remove the request from the WaitWakeList
//
RemoveEntryList( listEntry );
//
// Rebuild the GPE mask
//
ACPIWakeRemoveDevicesAndUpdate( NULL, NULL );
//
// Grab whatever information we feel we need from the request
//
powerRequest->Status = STATUS_CANCELLED;
callBack = powerRequest->CallBack;
context = powerRequest->Context;
//
// Release the power spinlock and the Cancel spinlock
//
KeReleaseSpinLockFromDpcLevel( &AcpiPowerLock );
IoReleaseCancelSpinLock( Irp->CancelIrql );
//
// Call the completion routine
//
(*callBack)(
deviceExtension,
Irp,
STATUS_CANCELLED
);
//
// Disable the device --- the CallBack *must* be invoked by this
// routine, so we don't need to do it ourselves
//
status = ACPIWakeEnableDisableAsync(
deviceExtension,
FALSE,
ACPIDeviceCancelWaitWakeIrpCallBack,
powerRequest
);
//
// We are done, so we can return now
//
return;
} // while (listEntry != &AcpiPowerWaitWakeList)
//
// In this case, the irp isn't in our queue. Display and assert for
// now
//
ACPIDevPrint( (
ACPI_PRINT_WARNING,
deviceExtension,
"(0x%08lx): ACPIDeviceCancelWaitWakeIrp - Not Found!\n",
Irp
) );
//
// We really shouldn't fall to this point,
//
ASSERT( FALSE );
//
// Release the spinlocks
//
KeReleaseSpinLockFromDpcLevel( &AcpiPowerLock );
IoReleaseCancelSpinLock( Irp->CancelIrql );
}
VOID EXPORT
ACPIDeviceCancelWaitWakeIrpCallBack(
IN PNSOBJ AcpiObject,
IN NTSTATUS Status,
IN POBJDATA ObjectData,
IN PVOID Context
)
/*++
Routine Description:
This routine is called after _PSW(Off) has been run as part of the
task of cancelling the irp. This routine is here so that we can free
the request and to allow us to keep track of things
Arguments:
AcpiObject - Points to the control method that was run
Status - Result of the method
ObjectData - Information about the result
Context - ACPI_POWER_REQUEST
Return Value:
NTSTATUS
--*/
{
PACPI_POWER_REQUEST powerRequest = (PACPI_POWER_REQUEST) Context;
PDEVICE_EXTENSION deviceExtension = powerRequest->DeviceExtension;
ACPIDevPrint( (
ACPI_PRINT_POWER,
deviceExtension,
"ACPIDeviceCancelWaitWakeIrpCallBack = 0x%08lx\n",
Status
) );
//
// free the request
//
ExFreeToNPagedLookasideList(
&RequestLookAsideList,
powerRequest
);
}
VOID
ACPIDeviceCompleteCommon(
IN PULONG OldWorkDone,
IN ULONG NewWorkDone
)
/*++
Routine Description:
Since the completion routines all have to do some bit of common work
to get the DPC firing again, this routine reduces the code duplication
Arguments:
OldWorkDone - Pointer to the old work done
NewWorkDone - The new amount of work that has been completed
NOTENOTE: There is an implicit assumption that the current value of
WorkDone in the request is WORK_DONE_PENDING
Return Value:
None
--*/
{
KIRQL oldIrql;
//
// Mark the request as being complete
//
InterlockedCompareExchange(
OldWorkDone,
NewWorkDone,
WORK_DONE_PENDING
);
//
// We need this lock to look at the following variables
//
KeAcquireSpinLock( &AcpiPowerQueueLock, &oldIrql );
//
// No matter what, work was done
//
AcpiPowerWorkDone = TRUE;
//
// Is the DPC already running?
//
if (!AcpiPowerDpcRunning) {
//
// Better make sure it does then
//
KeInsertQueueDpc( &AcpiPowerDpc, 0, 0 );
}
//
// Done with the lock
//
KeReleaseSpinLock( &AcpiPowerQueueLock, oldIrql );
}
VOID EXPORT
ACPIDeviceCompleteGenericPhase(
IN PNSOBJ AcpiObject,
IN NTSTATUS Status,
IN POBJDATA ObjectData,
IN PVOID Context
)
/*++
Routine Description:
This is the generic completion handler. If the interpreter has
successfully executed the method, it completes the request to the
next desired WORK_DONE, otherwise it fails the request.
Arguments:
AcpiObject - Points to the control method that was run
Status - Result of the method
ObjectData - Information about the result
Context - ACPI_POWER_REQUEST
Return Value:
NTSTATUS
--*/
{
DEVICE_POWER_STATE deviceState;
PACPI_POWER_REQUEST powerRequest = (PACPI_POWER_REQUEST) Context;
PDEVICE_EXTENSION deviceExtension = powerRequest->DeviceExtension;
UNREFERENCED_PARAMETER( AcpiObject );
UNREFERENCED_PARAMETER( ObjectData );
ACPIDevPrint( (
ACPI_PRINT_POWER,
deviceExtension,
"ACPIDeviceCompleteGenericPhase = 0x%08lx\n",
Status
) );
//
// Decide what state we should transition to next
//
if (!NT_SUCCESS(Status)) {
//
// Then complete the request as failed
//
powerRequest->Status = Status;
ACPIDeviceCompleteCommon( &(powerRequest->WorkDone), WORK_DONE_FAILURE);
} else {
//
// Get ready to go the next stage
//
ACPIDeviceCompleteCommon(
&(powerRequest->WorkDone),
powerRequest->NextWorkDone
);
}
}
VOID EXPORT
ACPIDeviceCompleteInterpreterRequest(
IN PVOID Context
)
/*++
Routine Description:
This routine is called after the interpreter has flushed its queue and
marked itself as no longer accepting requests.
Arguments:
Context - The context we told the interpreter to pass back to us
Return Value:
None
--*/
{
//
// This is just a wrapper for CompleteRequest (because the interpreter
// used different callbacks in this case)
//
ACPIDeviceCompleteGenericPhase(
NULL,
STATUS_SUCCESS,
NULL,
Context
);
}
VOID EXPORT
ACPIDeviceCompletePhase3Off(
IN PNSOBJ AcpiObject,
IN NTSTATUS Status,
IN POBJDATA ObjectData,
IN PVOID Context
)
/*++
Routine Description:
This routine is called after _OFF has been run on a Power Resource
Arguments:
AcpiObject - Points to the control method that was run
Status - Result of the method
ObjectData - Information about the result
Context - ACPI_POWER_DEVICE_NODE
Return Value:
NTSTATUS
--*/
{
KIRQL oldIrql;
PACPI_POWER_DEVICE_NODE powerNode = (PACPI_POWER_DEVICE_NODE) Context;
UNREFERENCED_PARAMETER( AcpiObject );
UNREFERENCED_PARAMETER( ObjectData );
UNREFERENCED_PARAMETER( Status );
ACPIPrint( (
ACPI_PRINT_POWER,
"ACPIDeviceCompletePhase3Off: PowerNode: 0x%08lx OFF = 0x%08lx\n",
powerNode,
Status
) );
//
// We need a spin lock for this
//
KeAcquireSpinLock( &AcpiPowerLock, &oldIrql );
//
// First step is to set the new flags for the node
//
if (NT_SUCCESS(Status)) {
ACPIInternalUpdateFlags( &(powerNode->Flags), DEVICE_NODE_ON, TRUE );
} else {
ACPIInternalUpdateFlags( &(powerNode->Flags), DEVICE_NODE_FAIL, FALSE );
}
//
// We can give up the lock now
//
KeReleaseSpinLock( &AcpiPowerLock, oldIrql );
//
// Done
//
ACPIDeviceCompleteCommon( &(powerNode->WorkDone), WORK_DONE_COMPLETE );
}
VOID EXPORT
ACPIDeviceCompletePhase3On(
IN PNSOBJ AcpiObject,
IN NTSTATUS Status,
IN POBJDATA ObjectData,
IN PVOID Context
)
/*++
Routine Description:
This routine is called after _ON has been run on a Power Resource
Arguments:
AcpiObject - Points to the control method that was run
Status - Result of the method
ObjectData - Information about the result
Context - ACPI_POWER_DEVICE_NODE
Return Value:
NTSTATUS
--*/
{
KIRQL oldIrql;
PACPI_POWER_DEVICE_NODE powerNode = (PACPI_POWER_DEVICE_NODE) Context;
UNREFERENCED_PARAMETER( AcpiObject );
UNREFERENCED_PARAMETER( ObjectData );
UNREFERENCED_PARAMETER( Status );
ACPIPrint( (
ACPI_PRINT_POWER,
"ACPIDeviceCompletePhase3On: PowerNode: 0x%08lx ON = 0x%08lx\n",
powerNode,
Status
) );
//
// We need a spin lock for this
//
KeAcquireSpinLock( &AcpiPowerLock, &oldIrql );
//
// First step is to set the new flags for the node
//
if (NT_SUCCESS(Status)) {
ACPIInternalUpdateFlags( &(powerNode->Flags), DEVICE_NODE_ON, FALSE );
} else {
ACPIInternalUpdateFlags( &(powerNode->Flags), DEVICE_NODE_FAIL, FALSE );
}
//
// We can give up the lock now
//
KeReleaseSpinLock( &AcpiPowerLock, oldIrql );
//
// Done
//
ACPIDeviceCompleteCommon( &(powerNode->WorkDone), WORK_DONE_COMPLETE );
}
VOID
ACPIDeviceCompleteRequest(
IN PACPI_POWER_REQUEST PowerRequest
)
/*++
Routine Description:
This routine invokes the callbacks on a given PowerRequest, dequeues the
request from any list that it is on, and does any other post-processing
that is required.
Note: this is where *all* of the various special handling should be done.
A prime example of something that should be done here is that we want
to return STATUS_SUCCESS to any Dx irp that are more off
Arguments:
None used
Return:
Void
--*/
{
KIRQL oldIrql;
PACPI_POWER_CALLBACK callBack = PowerRequest->CallBack;
PACPI_POWER_REQUEST nextRequest;
PDEVICE_EXTENSION deviceExtension = PowerRequest->DeviceExtension;
ACPIDevPrint( (
ACPI_PRINT_POWER,
deviceExtension,
"(0x%08lx): ACPIDeviceCompleteRequest = 0x%08lx\n",
PowerRequest,
PowerRequest->Status
) );
if (PowerRequest->RequestType == AcpiPowerRequestDevice ) {
if (deviceExtension->PowerInfo.PowerState != PowerDeviceUnspecified) {
DEVICE_POWER_STATE deviceState;
//
// If this is the first time we have seen the request, and it
// is a failure, then we should undo whatever it was we did
//
if (PowerRequest->FailedOnce == FALSE &&
!NT_SUCCESS(PowerRequest->Status) ) {
//
// Grab the queue Lock
//
KeAcquireSpinLock( &AcpiPowerQueueLock, &oldIrql );
//
// Transition back to the previous state
//
PowerRequest->u.DevicePowerRequest.DevicePowerState =
deviceExtension->PowerInfo.PowerState;
PowerRequest->FailedOnce = TRUE;
//
// Remove the Request from the current list
//
RemoveEntryList( &(PowerRequest->ListEntry) );
//
// Insert the request back in the Phase0 list
//
InsertTailList(
&(AcpiPowerQueueList),
&(PowerRequest->ListEntry)
);
//
// Work was done --- we reinserted the request into the queues
//
AcpiPowerWorkDone = TRUE;
//
// Make sure that the dpc is running, start it if neccessary.
//
if ( !AcpiPowerDpcRunning ) {
KeInsertQueueDpc( &AcpiPowerDpc, NULL, NULL );
}
//
// Done with the queue lock
//
KeReleaseSpinLock( &AcpiPowerQueueLock, oldIrql );
//
// we cannot continue
//
return;
}
//
// Are we turning the device more off?
//
deviceState = PowerRequest->u.DevicePowerRequest.DevicePowerState;
if (deviceExtension->PowerInfo.PowerState < deviceState ) {
//
// Yes, then no matter what, we succeeded
//
PowerRequest->Status = STATUS_SUCCESS;
}
}
}
//
// Invoke the callback, if there is any
//
if (callBack != NULL) {
(*callBack)(
deviceExtension,
PowerRequest->Context,
PowerRequest->Status
);
}
//
// Grab the queue Lock
//
KeAcquireSpinLock( &AcpiPowerQueueLock, &oldIrql );
//
// Remove the Request from all lists
//
RemoveEntryList( &(PowerRequest->ListEntry) );
RemoveEntryList( &(PowerRequest->SerialListEntry) );
//
// Should we queue up another request?
//
if (!IsListEmpty( &(deviceExtension->PowerInfo.PowerRequestListEntry) ) ) {
//
// No? Then make sure that the request gets processed
//
nextRequest = CONTAINING_RECORD(
deviceExtension->PowerInfo.PowerRequestListEntry.Flink,
ACPI_POWER_REQUEST,
SerialListEntry
);
InsertTailList(
&(AcpiPowerQueueList),
&(nextRequest->ListEntry)
);
//
// Remember this as the current request
//
deviceExtension->PowerInfo.CurrentPowerRequest = nextRequest;
} else {
deviceExtension->PowerInfo.CurrentPowerRequest = NULL;
}
//
// Done with the queue lock
//
KeReleaseSpinLock( &AcpiPowerQueueLock, oldIrql );
//
// Free the allocate memory
//
ExFreeToNPagedLookasideList(
&RequestLookAsideList,
PowerRequest
);
}
NTSTATUS
ACPIDeviceInitializePowerRequest(
IN PDEVICE_EXTENSION DeviceExtension,
IN POWER_STATE Power,
IN PACPI_POWER_CALLBACK CallBack,
IN PVOID CallBackContext,
IN POWER_ACTION PowerAction,
IN ACPI_POWER_REQUEST_TYPE RequestType,
IN ULONG Flags
)
/*++
Routine Description:
This is the actual worker function that fills in a PowerRequest
Arguments:
DeviceExtension - Target device
PowerState - Target S or D state
CallBack - routine to call when done
CallBackContext - context to pass when done
PowerAction - The reason we are doing this
RequestType - What kind of request we are looking at
Flags - Some flags that will let us control the behavior more
Return Value:
NTSTATUS
--*/
{
KIRQL oldIrql;
PACPI_POWER_REQUEST powerRequest;
//
// Allocate a powerRequest structure
//
powerRequest = ExAllocateFromNPagedLookasideList(
&RequestLookAsideList
);
if (powerRequest == NULL) {
//
// Call the completion routine
//
if (*CallBack != NULL) {
(*CallBack)(
DeviceExtension,
CallBackContext,
STATUS_INSUFFICIENT_RESOURCES
);
}
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Fill in the common parts of the structure powerRequest structure
//
RtlZeroMemory( powerRequest, sizeof(ACPI_POWER_REQUEST) );
powerRequest->Signature = ACPI_SIGNATURE;
powerRequest->CallBack = CallBack;
powerRequest->Context = CallBackContext;
powerRequest->DeviceExtension = DeviceExtension;
powerRequest->WorkDone = WORK_DONE_STEP_0;
powerRequest->Status = STATUS_SUCCESS;
powerRequest->RequestType = RequestType;
InitializeListHead( &(powerRequest->ListEntry) );
InitializeListHead( &(powerRequest->SerialListEntry) );
//
// At this point, we need the spinlock
//
KeAcquireSpinLock( &AcpiPowerQueueLock, &oldIrql );
//
// Fill in the request specific parts of the structure
//
switch (RequestType) {
case AcpiPowerRequestDevice: {
ULONG count;
count = InterlockedCompareExchange( &(DeviceExtension->HibernatePathCount), 0, 0);
if (count) {
//
// If we are on the hibernate path, then special rules apply
// We need to basically lock down all the power resources on the
// device.
//
if (PowerAction == PowerActionHibernate &&
Power.DeviceState == PowerDeviceD3) {
Flags |= DEVICE_REQUEST_LOCK_HIBER;
} else if (PowerAction != PowerActionHibernate &&
Power.DeviceState == PowerDeviceD0) {
Flags |= DEVICE_REQUEST_UNLOCK_HIBER;
}
}
powerRequest->u.DevicePowerRequest.DevicePowerState = Power.DeviceState;
powerRequest->u.DevicePowerRequest.Flags = Flags;
//
// If the transition is *to* a lower Dx state, then we need to run
// the function that lets the system that we are about to do this work
//
if (Power.DeviceState > DeviceExtension->PowerInfo.PowerState &&
DeviceExtension->DeviceObject != NULL) {
PoSetPowerState(
DeviceExtension->DeviceObject,
DevicePowerState,
Power
);
}
break;
}
case AcpiPowerRequestWaitWake: {
NTSTATUS status;
powerRequest->u.WaitWakeRequest.SystemPowerState = Power.SystemState;
powerRequest->u.WaitWakeRequest.Flags = Flags;
//
// Release the spinlock --- no longer required, enable the wakeup for the
// device and return
//
KeReleaseSpinLock( &AcpiPowerQueueLock, oldIrql );
status = ACPIWakeEnableDisableAsync(
DeviceExtension,
TRUE,
ACPIDeviceIrpWaitWakeRequestPending,
powerRequest
);
if (status == STATUS_PENDING) {
status = STATUS_MORE_PROCESSING_REQUIRED;
}
return status;
}
case AcpiPowerRequestSystem:
powerRequest->u.SystemPowerRequest.SystemPowerState = Power.SystemState;
powerRequest->u.SystemPowerRequest.SystemPowerAction = PowerAction;
break;
case AcpiPowerRequestWarmEject:
powerRequest->u.EjectPowerRequest.EjectPowerState = Power.SystemState;
powerRequest->u.EjectPowerRequest.Flags = Flags;
break;
case AcpiPowerRequestSynchronize:
powerRequest->u.SynchronizePowerRequest.Flags = Flags;
break;
}
//
// Should we even queue the request?
//
if (Flags & DEVICE_REQUEST_NO_QUEUE) {
goto ACPIDeviceInitializePowerRequestExit;
}
//
// Add the request to the right place in the lists. Note that this function
// must be called with the PowerQueueLock being held.
//
ACPIDeviceInternalQueueRequest(
DeviceExtension,
powerRequest,
Flags
);
ACPIDeviceInitializePowerRequestExit:
//
// Done with the spinlock
//
KeReleaseSpinLock( &AcpiPowerQueueLock, oldIrql );
//
// The request will not be completed immediately. Note that we return
// MORE_PROCESSING requird just in case this routine was called within
// the context of a completion routine. It is the caller's responsibility
// to turn this into a STATUS_PENDING
//
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
ACPIDeviceInternalDelayedDeviceRequest(
IN PDEVICE_EXTENSION DeviceExtension,
IN DEVICE_POWER_STATE DeviceState,
IN PACPI_POWER_CALLBACK CallBack,
IN PVOID CallBackContext
)
/*++
Routine Description:
This routine is called when a device extension wants to transition to
another Device State. This one differs from the
ACPIDeviceInternalDeviceRequest function in that the queue is only emptied
by the build device DPC when it has flushed the device list
Arguments:
DeviceExtension - The device which wants to transition
DeviceState - What the desired target state is
CallBack - The function to call when done
CallBackContext - The argument to pass to that function
Return Value:
NTSTATUS
--*/
{
NTSTATUS status;
POWER_STATE powerState;
//
// Let the user know what is going on
//
ACPIDevPrint( (
ACPI_PRINT_POWER,
DeviceExtension,
"(0x%08lx): ACPIDeviceInternalDelayedDeviceRequest - "
"Transition to D%d\n",
CallBackContext,
(DeviceState - PowerDeviceD0)
) );
//
// Cast the desired state
//
powerState.DeviceState = DeviceState;
//
// Queue the request
//
status = ACPIDeviceInitializePowerRequest(
DeviceExtension,
powerState,
CallBack,
CallBackContext,
PowerActionNone,
AcpiPowerRequestDevice,
(DEVICE_REQUEST_DELAYED | DEVICE_REQUEST_UNLOCK_DEVICE)
);
if (status == STATUS_MORE_PROCESSING_REQUIRED) {
status = STATUS_PENDING;
}
return status;
}
NTSTATUS
ACPIDeviceInternalDeviceRequest(
IN PDEVICE_EXTENSION DeviceExtension,
IN DEVICE_POWER_STATE DeviceState,
IN PACPI_POWER_CALLBACK CallBack,
IN PVOID CallBackContext,
IN ULONG Flags
)
/*++
Routine Description:
This routine is called when a device extension wants to transition to
another Device State
Arguments:
DeviceExtension - The device which wants to transition
DeviceState - What the desired target state is
CallBack - The function to call when done
CallBackContext - The argument to pass to that function
Flags - Flags (lock, unlock, etc)
Return Value:
NTSTATUS
--*/
{
NTSTATUS status;
POWER_STATE powerState;
//
// Let the user know what is going on
//
ACPIDevPrint( (
ACPI_PRINT_POWER,
DeviceExtension,
"(0x%08lx): ACPIDeviceInternalDeviceRequest - Transition to D%d\n",
CallBackContext,
(DeviceState - PowerDeviceD0)
) );
//
// Cast the desired state
//
powerState.DeviceState = DeviceState;
//
// Queue the request
//
status = ACPIDeviceInitializePowerRequest(
DeviceExtension,
powerState,
CallBack,
CallBackContext,
PowerActionNone,
AcpiPowerRequestDevice,
Flags
);
if (status == STATUS_MORE_PROCESSING_REQUIRED) {
status = STATUS_PENDING;
}
return status;
}
VOID
ACPIDeviceInternalQueueRequest(
IN PDEVICE_EXTENSION DeviceExtension,
IN PACPI_POWER_REQUEST PowerRequest,
IN ULONG Flags
)
/*++
Routine Description:
This routine is called with the AcpiPowerQueueLock being held. The routine
correctly adds the PowerRequest into the right list entries such that it
will get processed in the correct order
Arguments:
DeviceExtension - The device in question
PowerRequest - The request to queue
Flags - Useful information about the request
Return Value:
None
--*/
{
if (Flags & DEVICE_REQUEST_TO_SYNC_QUEUE) {
//
// add the request to the synchronize list
//
InsertHeadList(
&AcpiPowerSynchronizeList,
&(PowerRequest->ListEntry)
);
} else if (IsListEmpty( &(DeviceExtension->PowerInfo.PowerRequestListEntry) ) ) {
//
// We are going to add the request to both the device's serial list and
// the main power queue.
//
InsertTailList(
&(DeviceExtension->PowerInfo.PowerRequestListEntry),
&(PowerRequest->SerialListEntry)
);
if (Flags & DEVICE_REQUEST_DELAYED) {
InsertTailList(
&(AcpiPowerDelayedQueueList),
&(PowerRequest->ListEntry)
);
} else {
InsertTailList(
&(AcpiPowerQueueList),
&(PowerRequest->ListEntry)
);
}
} else {
//
// Serialize the request
//
InsertTailList(
&(DeviceExtension->PowerInfo.PowerRequestListEntry),
&(PowerRequest->SerialListEntry)
);
}
//
// Remember that Work *was* done
//
AcpiPowerWorkDone = TRUE;
//
// Make sure that the dpc is running, if it has to
//
if (!(Flags & DEVICE_REQUEST_DELAYED) && !AcpiPowerDpcRunning ) {
KeInsertQueueDpc( &AcpiPowerDpc, NULL, NULL );
}
//
// Done
//
return;
}
NTSTATUS
ACPIDeviceInternalSynchronizeRequest(
IN PDEVICE_EXTENSION DeviceExtension,
IN PACPI_POWER_CALLBACK CallBack,
IN PVOID CallBackContext,
IN ULONG Flags
)
/*++
Routine Description:
This routine is called when a device wants to make sure that the power
dpc is empty
Arguments:
DeviceExtension - The device which wants to know
CallBack - The function to call when done
CallBackContext - The argument to pass to that function
Flags - Flags (lock, unlock, etc)
Return Value:
NTSTATUS
--*/
{
NTSTATUS status;
POWER_STATE powerState;
//
// Let the user know what is going on
//
ACPIDevPrint( (
ACPI_PRINT_POWER,
DeviceExtension,
"(0x%08lx): ACPIDeviceInternalSynchronizeRequest\n"
) );
//
// We don't care about the state
//
powerState.DeviceState = PowerDeviceUnspecified;
//
// Queue the request
//
status = ACPIDeviceInitializePowerRequest(
DeviceExtension,
powerState,
CallBack,
CallBackContext,
PowerActionNone,
AcpiPowerRequestSynchronize,
(Flags | DEVICE_REQUEST_TO_SYNC_QUEUE)
);
if (status == STATUS_MORE_PROCESSING_REQUIRED) {
status = STATUS_PENDING;
}
return status;
}
VOID
ACPIDeviceIrpCompleteRequest(
IN PDEVICE_EXTENSION DeviceExtension,
IN PVOID Context,
IN NTSTATUS Status
)
/*++
Routine Description:
This is one of the completion routines for Irp-based device power
management requests
This routine will always complete the request with the given status.
Arguments:
DeviceExtension - Points to the DeviceExtension that was the target
Context - The Irp that was associated with the request
Status - The Result of the request
Return Value:
None
--*/
{
PIRP irp = (PIRP) Context;
LONG oldReferenceValue;
ACPIDevPrint( (
ACPI_PRINT_POWER,
DeviceExtension,
"(0x%08lx): ACPIDeviceIrpCompleteRequest = 0x%08lx\n",
irp,
Status
) );
//
// Start the next power request
//
PoStartNextPowerIrp( irp );
//
// Mark it pending (again) because it was pending already
//
IoMarkIrpPending( irp );
//
// Complete this irp
//
irp->IoStatus.Status = Status;
IoCompleteRequest( irp, IO_NO_INCREMENT );
//
// Remove our reference
//
ACPIInternalDecrementIrpReferenceCount( DeviceExtension );
}
VOID
ACPIDeviceIrpDelayedDeviceOffRequest(
IN PDEVICE_EXTENSION DeviceExtension,
IN PVOID Context,
IN NTSTATUS Status
)
/*++
Routine Description:
This is one of the completion routines for Irp-based device power
management requests
This routine completes the irp (on failure), or forwards it to
the DeviceObject below this one (on success)
Arguments:
DeviceExtension - Points to the DeviceExtension that was the target
Context - The Irp that was associated with the request
Status - The Result of the request
Return Value:
None
--*/
{
PIRP irp = (PIRP) Context;
LONG oldReferenceValue;
ACPIDevPrint( (
ACPI_PRINT_POWER,
DeviceExtension,
"(0x%08lx): ACPIDeviceIrpDelayedDeviceOffRequest = 0x%08lx\n",
irp,
Status
) );
if (!NT_SUCCESS(Status)) {
//
// Start the next power request
//
PoStartNextPowerIrp( irp );
//
// Complete this irp
//
irp->IoStatus.Status = Status;
IoCompleteRequest( irp, IO_NO_INCREMENT );
} else {
//
// We cannot call ForwardPowerIrp because that would blow away our
// completion routine
//
//
// Increment the OutstandingIrpCount since a completion routine
// counts for this purpose
//
InterlockedIncrement( (&DeviceExtension->OutstandingIrpCount) );
//
// Forward the power irp to target device
//
IoCopyCurrentIrpStackLocationToNext( irp );
//
// We want the completion routine to fire. We cannot call
// ACPIDispatchForwardPowerIrp here because we set this completion
// routine
//
IoSetCompletionRoutine(
irp,
ACPIDeviceIrpDeviceFilterRequest,
ACPIDeviceIrpCompleteRequest,
TRUE,
TRUE,
TRUE
);
//
// Start the next power irp
//
PoStartNextPowerIrp( irp );
//
// Let the person below us execute. Note: we can't block at
// any time within this code path.
//
ASSERT( DeviceExtension->TargetDeviceObject != NULL);
PoCallDriver( DeviceExtension->TargetDeviceObject, irp );
}
//
// Remove our reference
//
ACPIInternalDecrementIrpReferenceCount( DeviceExtension );
}
VOID
ACPIDeviceIrpDelayedDeviceOnRequest(
IN PDEVICE_EXTENSION DeviceExtension,
IN PVOID Context,
IN NTSTATUS Status
)
/*++
Routine Description:
This is one of the completion routines for Irp-based device power
management requests
This routine completes the irp (on failure), or forwards it to
the DeviceObject below this one (on success)
Arguments:
DeviceExtension - Points to the DeviceExtension that was the target
Context - The Irp that was associated with the request
Status - The Result of the request
Return Value:
None
--*/
{
PIRP irp = (PIRP) Context;
LONG oldReferenceValue;
ACPIDevPrint( (
ACPI_PRINT_POWER,
DeviceExtension,
"(0x%08lx): ACPIDeviceIrpDelayedDeviceOnRequest = 0x%08lx\n",
irp,
Status
) );
if (!NT_SUCCESS(Status)) {
//
// Start the next power request
//
PoStartNextPowerIrp( irp );
//
// Complete this irp
//
irp->IoStatus.Status = Status;
IoCompleteRequest( irp, IO_NO_INCREMENT );
} else {
//
// We cannot call ForwardPowerIrp because that would blow away our
// completion routine
//
//
// Increment the OutstandingIrpCount since a completion routine
// counts for this purpose
//
InterlockedIncrement( (&DeviceExtension->OutstandingIrpCount) );
//
// Forward the power irp to target device
//
IoCopyCurrentIrpStackLocationToNext( irp );
//
// We want the completion routine to fire. We cannot call
// ACPIDispatchForwardPowerIrp here because we set this completion
// routine
//
IoSetCompletionRoutine(
irp,
ACPIBuildRegOnRequest,
ACPIDeviceIrpCompleteRequest,
TRUE,
TRUE,
TRUE
);
//
// Let the person below us execute. Note: we can't block at
// any time within this code path.
//
ASSERT( DeviceExtension->TargetDeviceObject != NULL);
PoCallDriver( DeviceExtension->TargetDeviceObject, irp );
}
//
// Remove our reference
//
ACPIInternalDecrementIrpReferenceCount( DeviceExtension );
}
NTSTATUS
ACPIDeviceIrpDeviceFilterRequest(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PACPI_POWER_CALLBACK CallBack
)
/*++
Routine Description:
This routine is called when an Irp wishes to do D-level power management
Note: that we always pass the Irp back as the Context for the CallBack
Arguments:
DeviceObject - The target device object
Irp - The target irp
CallBack - The routine to call when done
Return Value:
NTSTATUS
--*/
{
BOOLEAN unlockDevice = FALSE;
DEVICE_POWER_STATE deviceState;
LONG oldReferenceValue;
NTSTATUS status;
PDEVICE_EXTENSION deviceExtension = ACPIInternalGetDeviceExtension(DeviceObject);
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation( Irp );
POWER_ACTION powerAction;
POWER_STATE powerState;
//
// Grab the requested device state
//
deviceState = irpStack->Parameters.Power.State.DeviceState;
powerAction = irpStack->Parameters.Power.ShutdownType;
//
// Let the user know what is going on
//
ACPIDevPrint( (
ACPI_PRINT_POWER,
deviceExtension,
"(0x%08lx): ACPIDeviceIrpDeviceFilterRequest - Transition to D%d\n",
Irp,
(deviceState - PowerDeviceD0)
) );
//
// Do we need to mark the irp as pending?
//
if (Irp->PendingReturned) {
IoMarkIrpPending( Irp );
}
//
// Lets us look at the current status code for the request. On error,
// we cannot call a completion routine because we would complete the
// irp at that point. Double-completing an irp is bad.
//
status = Irp->IoStatus.Status;
if (!NT_SUCCESS(status)) {
//
// Remove our reference
//
ACPIInternalDecrementIrpReferenceCount( deviceExtension );
return status;
}
//
// Cast the desired state
//
powerState.DeviceState = deviceState;
#if defined(ACPI_INTERNAL_LOCKING)
//
// Determine if we should unlock the device
//
if (powerAction == PowerActionShutdown ||
powerAction == PowerActionShutdownReset ||
powerAction == PowerActionShutdownOff) {
unlockDevice = TRUE;
}
#endif
//
// Queue the request --- this function will always return
// MORE_PROCESSING_REQUIRED instead of PENDING, so we don't have
// to mess with it
//
status = ACPIDeviceInitializePowerRequest(
deviceExtension,
powerState,
CallBack,
Irp,
powerAction,
AcpiPowerRequestDevice,
(unlockDevice ? DEVICE_REQUEST_UNLOCK_DEVICE : 0)
);
return status;
}
NTSTATUS
ACPIDeviceIrpDeviceRequest(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PACPI_POWER_CALLBACK CallBack
)
/*++
Routine Description:
This routine is called when an Irp wishes to do D-level power management
Note: that we always pass the Irp back as the Context for the CallBack
Arguments:
DeviceObject - The target device object
Irp - The target irp
CallBack - The routine to call when done
Return Value:
NTSTATUS
--*/
{
BOOLEAN unlockDevice = FALSE;
DEVICE_POWER_STATE deviceState;
NTSTATUS status;
PDEVICE_EXTENSION deviceExtension = ACPIInternalGetDeviceExtension(DeviceObject);
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation( Irp );
POWER_ACTION powerAction;
POWER_STATE powerState;
//
// Grab the requested device state and power action
//
deviceState = irpStack->Parameters.Power.State.DeviceState;
powerAction = irpStack->Parameters.Power.ShutdownType;
//
// Let the user know what is going on
//
ACPIDevPrint( (
ACPI_PRINT_POWER,
deviceExtension,
"(0x%08lx): ACPIDeviceIrpDeviceRequest - Transition to D%d\n",
Irp,
(deviceState - PowerDeviceD0)
) );
//
// Do we need to mark the irp as pending?
//
if (Irp->PendingReturned) {
IoMarkIrpPending( Irp );
}
//
// Lets us look at the current status code for the request. On error,
// we will just call the completion right now, and it is responsible
// for doing the 'right' thing
//
status = Irp->IoStatus.Status;
if (!NT_SUCCESS(status)) {
//
// Call the completion routine and return
//
if (*CallBack != NULL ) {
(*CallBack)(
deviceExtension,
Irp,
status
);
return status;
}
}
//
// Cast the desired state
//
powerState.DeviceState = deviceState;
#if defined(ACPI_INTERNAL_LOCKING)
//
// Determine if we should unlock the device
//
if (powerAction == PowerActionShutdown ||
powerAction == PowerActionShutdownReset ||
powerAction == PowerActionShutdownOff) {
unlockDevice = TRUE;
}
#endif
//
// Queue the request --- this function will always return
// MORE_PROCESSING_REQUIRED instead of PENDING, so we don't have
// to mess with it
//
status = ACPIDeviceInitializePowerRequest(
deviceExtension,
powerState,
CallBack,
Irp,
powerAction,
AcpiPowerRequestDevice,
(unlockDevice ? DEVICE_REQUEST_UNLOCK_DEVICE : 0)
);
return status;
}
VOID
ACPIDeviceIrpForwardRequest(
IN PDEVICE_EXTENSION DeviceExtension,
IN PVOID Context,
IN NTSTATUS Status
)
/*++
Routine Description:
This is one of the completion routines for Irp-based device power
management requests
This routine completes the irp (on failure), or forwards it to
the DeviceObject below this one (on success)
Arguments:
DeviceExtension - Points to the DeviceExtension that was the target
Context - The Irp that was associated with the request
Status - The Result of the request
Return Value:
None
--*/
{
PIRP irp = (PIRP) Context;
LONG oldReferenceValue;
ACPIDevPrint( (
ACPI_PRINT_POWER,
DeviceExtension,
"(0x%08lx): ACPIDeviceIrpForwardRequest = 0x%08lx\n",
irp,
Status
) );
if (!NT_SUCCESS(Status)) {
//
// Start the next power request
//
PoStartNextPowerIrp( irp );
//
// Complete this irp
//
irp->IoStatus.Status = Status;
IoCompleteRequest( irp, IO_NO_INCREMENT );
} else {
PDEVICE_OBJECT devObject;
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation( irp );
devObject = irpSp->DeviceObject;
//
// Forward the request
//
ACPIDispatchForwardPowerIrp(
devObject,
irp
);
}
//
// Remove our reference
//
ACPIInternalDecrementIrpReferenceCount( DeviceExtension );
}
NTSTATUS
ACPIDeviceIrpSystemRequest(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PACPI_POWER_CALLBACK CallBack
)
/*++
Routine Description:
This routine is called when an Irp wishes to do S-level power management
Note: that we always pass the Irp back as the Context for the CallBack
Arguments:
DeviceObject - The target device object
Irp - The target irp
CallBack - The routine to call when done
Return Value:
NTSTATUS
--*/
{
NTSTATUS status;
PDEVICE_EXTENSION deviceExtension = ACPIInternalGetDeviceExtension(DeviceObject);
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation( Irp );
POWER_ACTION powerAction;
POWER_STATE powerState;
SYSTEM_POWER_STATE systemState;
//
// Grab the requested system state and system action
//
systemState = irpStack->Parameters.Power.State.SystemState;
powerAction = irpStack->Parameters.Power.ShutdownType;
//
// Let the user know what is going on
//
ACPIDevPrint( (
ACPI_PRINT_POWER,
deviceExtension,
"(0x%08lx): ACPIDeviceIrpSystemRequest - Transition to S%d\n",
Irp,
ACPIDeviceMapACPIPowerState(systemState)
) );
//
// Do we need to mark the irp as pending?
//
if (Irp->PendingReturned) {
IoMarkIrpPending( Irp );
}
//
// Lets us look at the current status code for the request. On error,
// we will just call the completion right now, and it is responsible
// for doing the 'right' thing
//
status = Irp->IoStatus.Status;
if (!NT_SUCCESS(status)) {
//
// Call the completion routine and return
//
(*CallBack)(
deviceExtension,
Irp,
status
);
return status;
}
//
// Cast the desired state
//
powerState.SystemState = systemState;
//
// Queue the request --- this function will always return
// MORE_PROCESSING_REQUIRED instead of PENDING, so we don't have
// to mess with it
//
status = ACPIDeviceInitializePowerRequest(
deviceExtension,
powerState,
CallBack,
Irp,
powerAction,
AcpiPowerRequestSystem,
0
);
return status;
}
NTSTATUS
ACPIDeviceIrpWaitWakeRequest(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PACPI_POWER_CALLBACK CallBack
)
/*++
Routine Description:
This routine is called when an Irp wishes to do wake support
Note: that we always pass the Irp back as the Context for the CallBack
Note: this function is coded differently then the other DeviceIrpXXXRequest
functions --- there are no provisions made that this routine can
be called as a IoCompletionRoutine, although the arguments could
support it.
Arguments:
DeviceObject - The target device object
Irp - The target irp
CallBack - The routine to call when done
Return Value:
NTSTATUS
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_EXTENSION deviceExtension = ACPIInternalGetDeviceExtension(DeviceObject);
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation( Irp );
POWER_STATE powerState;
SYSTEM_POWER_STATE systemState;
//
// Grab the requested device state
//
systemState = irpStack->Parameters.WaitWake.PowerState;
//
// Let the user know what is going on
//
ACPIDevPrint( (
ACPI_PRINT_WAKE,
deviceExtension,
"(0x%08lx): ACPIDeviceIrpWaitWakeRequest - Wait Wake S%d\n",
Irp,
ACPIDeviceMapACPIPowerState(systemState)
) );
//
// Cast the desired state
//
powerState.SystemState = systemState;
//
// Queue the request --- this function will always return
// MORE_PROCESSING_REQUIRED instead of PENDING, so we don't have
// to mess with it
//
status = ACPIDeviceInitializePowerRequest(
deviceExtension,
powerState,
CallBack,
Irp,
PowerActionNone,
AcpiPowerRequestWaitWake,
DEVICE_REQUEST_NO_QUEUE
);
return status;
}
VOID
ACPIDeviceIrpWaitWakeRequestComplete(
IN PACPI_POWER_REQUEST PowerRequest
)
/*++
Routine Description:
This routine is called when the WaitWake Irp is finally complete and we
need to pass it back to whomever called us with it
Arguments:
PowerRequest - The request that was completed
Return Value:
NTSTATUS
--*/
{
KIRQL oldIrql;
PDEVICE_EXTENSION deviceExtension;
//
// Make sure that we own the power lock for this
//
KeAcquireSpinLock( &AcpiPowerQueueLock, &oldIrql );
//
// Remember the device extension
//
deviceExtension = PowerRequest->DeviceExtension;
//
// Make sure that the request can no longer be cancelled
//
if (PowerRequest->u.WaitWakeRequest.Flags & DEVICE_REQUEST_HAS_CANCEL) {
KIRQL cancelIrql;
PIRP irp = (PIRP) PowerRequest->Context;
IoAcquireCancelSpinLock( &cancelIrql );
IoSetCancelRoutine( irp, NULL );
PowerRequest->u.WaitWakeRequest.Flags &= ~DEVICE_REQUEST_HAS_CANCEL;
IoReleaseCancelSpinLock( cancelIrql );
}
//
// Add the request to the right place in the lists. Note this function
// must be called with the PowerQueueLock being held
//
ACPIDeviceInternalQueueRequest(
deviceExtension,
PowerRequest,
PowerRequest->u.WaitWakeRequest.Flags
);
//
// Done with spinlock
//
KeReleaseSpinLock( &AcpiPowerQueueLock, oldIrql );
}
VOID EXPORT
ACPIDeviceIrpWaitWakeRequestPending(
IN PNSOBJ AcpiObject,
IN NTSTATUS Status,
IN POBJDATA ObjectData,
IN PVOID Context
)
/*++
Routine Description:
This routine is called after _PSW has been run and we want to enable
the GPE associated with the current object
Arguments:
AcpiObject - Points to the control method that was run
Status - Result of the method
ObjectData - Information about the result
Context - ACPI_POWER_REQUEST
Return Value:
NTSTATUS
--*/
{
KIRQL oldIrql;
PACPI_POWER_REQUEST powerRequest = (PACPI_POWER_REQUEST) Context;
PDEVICE_EXTENSION deviceExtension = powerRequest->DeviceExtension;
PIRP irp = (PIRP) powerRequest->Context;
ACPIDevPrint( (
ACPI_PRINT_WAKE,
deviceExtension,
"(0x%08lx): ACPIDeviceIrpWaitWakeRequestPending= 0x%08lx\n",
powerRequest,
Status
) );
//
// Did we fail the request?
//
if (!NT_SUCCESS(Status)) {
powerRequest->Status = Status;
ACPIDeviceIrpWaitWakeRequestComplete( powerRequest );
return;
}
//
// At this point, we need the power spin lock and the cancel spinlock
//
IoAcquireCancelSpinLock( &oldIrql );
KeAcquireSpinLockAtDpcLevel( &AcpiPowerLock );
//
// Remember that we have this request outstanding
//
InsertTailList(
&(AcpiPowerWaitWakeList),
&(powerRequest->ListEntry)
);
//
// Has the irp been cancelled?
//
if (irp->Cancel) {
//
// Yes, so lets release release the power lock and call the
// cancel routine
//
KeReleaseSpinLockFromDpcLevel( &AcpiPowerLock );
ACPIDeviceCancelWaitWakeIrp(
deviceExtension->DeviceObject,
irp
);
//
// Return now --- the cancel routine should have taken care off
// everything else
//
return;
}
//
// Remember that this request has a cancel routine
//
powerRequest->u.WaitWakeRequest.Flags |= DEVICE_REQUEST_HAS_CANCEL;
//
// Update the Gpe Wake Bits
//
ACPIWakeRemoveDevicesAndUpdate( NULL, NULL );
//
// Mark the Irp as cancelable
//
IoSetCancelRoutine( irp, ACPIDeviceCancelWaitWakeIrp );
//
// Done with the spinlocks
//
KeReleaseSpinLockFromDpcLevel( &AcpiPowerLock );
IoReleaseCancelSpinLock( oldIrql );
} // ACPIDeviceIrpWaitWakeRequestPending
NTSTATUS
ACPIDeviceIrpWarmEjectRequest(
IN PDEVICE_EXTENSION DeviceExtension,
IN PIRP Irp,
IN PACPI_POWER_CALLBACK CallBack,
IN BOOLEAN UpdateHardwareProfile
)
/*++
Routine Description:
This routine is called when an Irp wishes to do S-level power management
Note: that we always pass the Irp back as the Context for the CallBack
Arguments:
DeviceExtension - Extension of the device with the _EJx methods to run
Irp - The target irp
CallBack - The routine to call when done
Flags - Update profiles, etc
Return Value:
NTSTATUS
--*/
{
NTSTATUS status;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation( Irp );
POWER_ACTION ejectAction;
POWER_STATE powerState;
SYSTEM_POWER_STATE ejectState;
//
// Grab the requested system state
//
ejectState = irpStack->Parameters.Power.State.SystemState;
//
// Let the user know what is going on
//
ACPIDevPrint( (
ACPI_PRINT_POWER,
DeviceExtension,
"(0x%08lx): ACPIDeviceIrpWarmEjectRequest - Transition to S%d\n",
Irp,
ACPIDeviceMapACPIPowerState(ejectState)
) );
//
// Do we need to mark the irp as pending?
//
if (Irp->PendingReturned) {
IoMarkIrpPending( Irp );
}
//
// Lets us look at the current status code for the request. On error,
// we will just call the completion right now, and it is responsible
// for doing the 'right' thing
//
status = Irp->IoStatus.Status;
if (!NT_SUCCESS(status)) {
//
// Call the completion routine and return
//
(*CallBack)(
DeviceExtension,
Irp,
status
);
return status;
}
//
// Cast the desired state
//
powerState.SystemState = ejectState;
//
// Queue the request --- this function will always return
// MORE_PROCESSING_REQUIRED instead of PENDING, so we don't have
// to mess with it
//
status = ACPIDeviceInitializePowerRequest(
DeviceExtension,
powerState,
CallBack,
Irp,
PowerActionNone,
AcpiPowerRequestWarmEject,
UpdateHardwareProfile ? DEVICE_REQUEST_UPDATE_HW_PROFILE : 0
);
return status;
}
#if 0
ULONG
ACPIDeviceMapACPIPowerState(
SYSTEM_POWER_STATE Level
)
/*++
Routine Description:
This isn't a routine. Its a macro. It returns a ULONG that corresponds
to ACPI based System Power State based on the NT SystemPower State
Arguments:
Level - The NT Based S state
Return Value:
ULONG
--*/
{
}
#endif
#if 0
DEVICE_POWER_STATE
ACPIDeviceMapPowerState(
ULONG Level
)
/*++
Routine Description:
This isn't a routine. Its a macro. It returns a DEVICE_POWER_STATE
that corresponds to the mapping provided in the ACPI spec
Arguments:
Level - The 0-based D level (0 == D0, 1 == D1, ..., 3 == D3)
Return Value:
DEVICE_POWER_STATE
--*/
{
}
#endif
#if 0
SYSTEM_POWER_STATE
ACPIDeviceMapSystemState(
ULONG Level
)
/*++
Routine Description:
This isn't a routine. Its a macro. It returns a SYSTEM_POWER_STATE that
corresponds to the mapping provided in the ACPI spec
Arguments:
Level - The 0-based S level (0 = Working, ..., 5 = Shutdown)
Return Value:
SYSTEM_POWER_STATE
--*/
{
}
#endif
NTSTATUS
ACPIDevicePowerDetermineSupportedDeviceStates(
IN PDEVICE_EXTENSION DeviceExtension,
IN PULONG SupportedPrStates,
IN PULONG SupportedPsStates
)
/*++
Routine Description:
This routine calculates the bit masks that reflect which D states are
supported via PRx methods and which D states are supported via PSx
methods
Arguments:
DeviceExtension - Device Extension to determine D-States
SupportedPrStates - Bit Mask of supported D-States via _PRx
SupportedPsStates - Bit Mask of supported D-States via _PSx
Return Value:
NTSTATUS
--*/
{
DEVICE_POWER_STATE index;
PNSOBJ object;
ULONG i;
ULONG prBitIndex = 0;
ULONG prNames[] = { PACKED_PR0, PACKED_PR1, PACKED_PR2 };
ULONG psBitIndex = 0;
ULONG psNames[] = { PACKED_PS0, PACKED_PS1, PACKED_PS2, PACKED_PS3 };
ULONG supportedIndex = 0;
PAGED_CODE();
ASSERT( DeviceExtension != NULL );
ASSERT( SupportedPrStates != NULL );
ASSERT( SupportedPsStates != NULL );
//
// Assume we support nothing
//
*SupportedPrStates = 0;
*SupportedPsStates = 0;
//
// This is another place that we want to be able to call this code even
// though there is no NameSpace Object associated with this extension.
// This special case code lets us avoid adding a check to GetNamedChild
//
if (DeviceExtension->Flags & DEV_PROP_NO_OBJECT) {
//
// Assume that we support 'PS' states 0 and 3
//
psBitIndex = ( 1 << PowerDeviceD0 ) + ( 1 << PowerDeviceD3 );
goto ACPIDevicePowerDetermineSupportedDeviceStatesExit;
}
//
// Look for all of the _PS methods
//
for (i = 0, index = PowerDeviceD0; index <= PowerDeviceD3; i++, index++) {
//
// Does the object exist?
//
object = ACPIAmliGetNamedChild(
DeviceExtension->AcpiObject,
psNames[i]
);
if (object != NULL) {
psBitIndex |= (1 << index);
}
}
//
// Look for all of the _PR methods
//
for (i = 0, index = PowerDeviceD0; index <= PowerDeviceD2; i++, index++) {
//
// Does the object exist?
//
object = ACPIAmliGetNamedChild(
DeviceExtension->AcpiObject,
prNames[i]
);
if (object != NULL) {
prBitIndex |= (1 << index);
//
// We always support D3 'passively'
//
prBitIndex |= (1 << PowerDeviceD3);
}
}
//
// The supported index is the union of which _PR and which _PS are
// present
supportedIndex = (prBitIndex | psBitIndex);
//
// If we didn't find anything, then there is nothing for us to do
//
if (!supportedIndex) {
//
// Done
//
return STATUS_SUCCESS;
}
//
// One of the rules that we have setup is that we must support D3 and
// D0 if we support any power states at all. Make sure that this is
// true.
//
if ( !(supportedIndex & (1 << PowerDeviceD0) ) ) {
ACPIDevPrint( (
ACPI_PRINT_CRITICAL,
DeviceExtension,
"does not support D0 power state!\n"
) );
KeBugCheckEx(
ACPI_BIOS_ERROR,
ACPI_REQUIRED_METHOD_NOT_PRESENT,
(ULONG_PTR) DeviceExtension,
(prBitIndex != 0 ? PACKED_PR0 : PACKED_PS0),
0
);
}
if ( !(supportedIndex & (1 << PowerDeviceD3) ) ) {
ACPIDevPrint( (
ACPI_PRINT_CRITICAL,
DeviceExtension,
"does not support D3 power state!\n"
) );
KeBugCheckEx(
ACPI_BIOS_ERROR,
ACPI_REQUIRED_METHOD_NOT_PRESENT,
(ULONG_PTR) DeviceExtension,
PACKED_PS3,
0
);
ACPIInternalError( ACPI_INTERNAL );
}
if ( prBitIndex != 0 && psBitIndex != 0 && prBitIndex != psBitIndex) {
ACPIDevPrint( (
ACPI_PRINT_CRITICAL,
DeviceExtension,
"has mismatch between power plane and power source information!\n"
) );
prBitIndex &= psBitIndex;
psBitIndex &= prBitIndex;
}
ACPIDevicePowerDetermineSupportedDeviceStatesExit:
//
// Give the answer of what we support
//
*SupportedPrStates = prBitIndex;
*SupportedPsStates = psBitIndex;
//
// Done
//
return STATUS_SUCCESS;
}
VOID
ACPIDevicePowerDpc(
IN PKDPC Dpc,
IN PVOID DpcContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
/*++
Routine Description:
This routine is where all of the Power-related work is done. It looks
at queued requests and processes them as appropriate.
Arguments:
None used
Return Value:
Void
--*/
{
LIST_ENTRY tempList;
NTSTATUS status;
UNREFERENCED_PARAMETER( Dpc );
UNREFERENCED_PARAMETER( DpcContext );
UNREFERENCED_PARAMETER( SystemArgument1 );
UNREFERENCED_PARAMETER( SystemArgument2 );
//
// First step is to acquire the DPC Lock, and check to see if another
// DPC is already running
//
KeAcquireSpinLockAtDpcLevel( &AcpiPowerQueueLock );
if (AcpiPowerDpcRunning) {
//
// The DPC is already running, so we need to exit now
//
KeReleaseSpinLockFromDpcLevel( &AcpiPowerQueueLock );
return;
}
//
// Remember that the DPC is now running
//
AcpiPowerDpcRunning = TRUE;
//
// Initialize the list that will hold the synchronize items
//
InitializeListHead( &tempList );
//
// We must try to do *some* work
//
do {
//
// Assume that we won't do any work
//
AcpiPowerWorkDone = FALSE;
//
// If there are items in the Queue list, move them to the Phase0 list
//
if (!IsListEmpty( &AcpiPowerQueueList ) ) {
ACPIInternalMovePowerList(
&AcpiPowerQueueList,
&AcpiPowerPhase0List
);
}
//
// We can release the spin lock now
//
KeReleaseSpinLockFromDpcLevel( &AcpiPowerQueueLock );
//
// If there are items in the Phase0 list, process the list
//
if (!IsListEmpty( &AcpiPowerPhase0List ) ) {
status = ACPIDevicePowerProcessGenericPhase(
&AcpiPowerPhase0List,
AcpiDevicePowerProcessPhase0Dispatch,
FALSE
);
if (NT_SUCCESS(status) && status != STATUS_PENDING) {
//
// This indicates that we have completed all the work
// on the Phase0 list, so we are ready to move all the
// items to the next list
//
ACPIInternalMovePowerList(
&AcpiPowerPhase0List,
&AcpiPowerPhase1List
);
}
}
//
// If there are items in Phase1 list, process the list
//
if (!IsListEmpty( &AcpiPowerPhase1List ) &&
IsListEmpty( &AcpiPowerPhase0List) ) {
status = ACPIDevicePowerProcessGenericPhase(
&AcpiPowerPhase1List,
AcpiDevicePowerProcessPhase1Dispatch,
FALSE
);
if (NT_SUCCESS(status) && status != STATUS_PENDING) {
//
// This indicates that we have completed all the work
// on the Phase1 list, so we are ready to move all the
// items to the next list
//
ACPIInternalMovePowerList(
&AcpiPowerPhase1List,
&AcpiPowerPhase2List
);
}
}
//
// If there are items in the Phase2 list, then process those
//
if (IsListEmpty( &AcpiPowerPhase0List) &&
IsListEmpty( &AcpiPowerPhase1List) &&
!IsListEmpty( &AcpiPowerPhase2List) ) {
status = ACPIDevicePowerProcessGenericPhase(
&AcpiPowerPhase2List,
AcpiDevicePowerProcessPhase2Dispatch,
FALSE
);
if (NT_SUCCESS(status) && status != STATUS_PENDING) {
//
// This indicates that we have completed all the work
// on the Phase1 list, so we are ready to move all the
// items to the next list
//
ACPIInternalMovePowerList(
&AcpiPowerPhase2List,
&AcpiPowerPhase3List
);
}
}
//
// We cannot do this step if the Phase1List or Phase2List are non-empty
//
if (IsListEmpty( &AcpiPowerPhase0List) &&
IsListEmpty( &AcpiPowerPhase1List) &&
IsListEmpty( &AcpiPowerPhase2List) &&
!IsListEmpty( &AcpiPowerPhase3List) ) {
status = ACPIDevicePowerProcessPhase3( );
if (NT_SUCCESS(status) && status != STATUS_PENDING) {
//
// This indicates that we have completed all the work
// on the Phase2 list, so we are ready to move all the
// itmes to the Phase3 list
//
ACPIInternalMovePowerList(
&AcpiPowerPhase3List,
&AcpiPowerPhase4List
);
}
}
//
// We can always empty the Phase4 list
//
if (!IsListEmpty( &AcpiPowerPhase4List ) ) {
status = ACPIDevicePowerProcessPhase4( );
if (NT_SUCCESS(status) && status != STATUS_PENDING) {
//
// This indicates that we have completed all the work
// on the Phase1 list, so we are ready to move all the
// items to the Phase2 list
//
ACPIInternalMovePowerList(
&AcpiPowerPhase4List,
&AcpiPowerPhase5List
);
}
}
//
// We can always empty the Phase5 list
//
if (!IsListEmpty( &AcpiPowerPhase5List) ) {
status = ACPIDevicePowerProcessGenericPhase(
&AcpiPowerPhase5List,
AcpiDevicePowerProcessPhase5Dispatch,
TRUE
);
}
//
// We need the lock again, since we are about to check to see if
// we have completed some work
//
KeAcquireSpinLockAtDpcLevel( &AcpiPowerQueueLock );
} while ( AcpiPowerWorkDone );
//
// The DPC is no longer running
//
AcpiPowerDpcRunning = FALSE;
//
// Have we flushed all of our queues?
//
if (IsListEmpty( &AcpiPowerPhase0List ) &&
IsListEmpty( &AcpiPowerPhase1List ) &&
IsListEmpty( &AcpiPowerPhase2List ) &&
IsListEmpty( &AcpiPowerPhase3List ) &&
IsListEmpty( &AcpiPowerPhase4List ) &&
IsListEmpty( &AcpiPowerPhase5List ) ) {
//
// Let the world know
//
ACPIPrint( (
ACPI_PRINT_POWER,
"ACPIDevicePowerDPC: Queues Empty. Terminating.\n"
) );
//
// Do we have a synchronization request?
//
if (!IsListEmpty( &AcpiPowerSynchronizeList ) ) {
//
// Move all the item from the Sync list to the temp list
//
ACPIInternalMovePowerList(
&AcpiPowerSynchronizeList,
&tempList
);
}
}
//
// We no longer need the lock
//
KeReleaseSpinLockFromDpcLevel( &AcpiPowerQueueLock );
//
// Do we have any work in the synchronize list?
//
if (!IsListEmpty( &tempList) ) {
ACPIDevicePowerProcessSynchronizeList( &tempList );
}
}
NTSTATUS
ACPIDevicePowerFlushQueue(
PDEVICE_EXTENSION DeviceExtension
)
/*++
Routine Description:
This routine will block until the Power queues have been flushed
Arguments:
DeviceExtension - The device extension which wants to flush
Return Value:
NTSTATUS
--*/
{
KEVENT event;
NTSTATUS status;
//
// Initialize the event that we will wait on
//
KeInitializeEvent( &event, SynchronizationEvent, FALSE );
//
// Now, push a request onto the stack such that when the power lists
// have been emptied, we unblock this thread
//
status = ACPIDeviceInternalSynchronizeRequest(
DeviceExtension,
ACPIDevicePowerNotifyEvent,
&event,
0
);
//
// Block until its done
//
if (status == STATUS_PENDING) {
KeWaitForSingleObject(
&event,
Executive,
KernelMode,
FALSE,
NULL
);
status = STATUS_SUCCESS;
}
//
// Let the world know
//
return status;
}
VOID
ACPIDevicePowerNotifyEvent(
IN PDEVICE_EXTENSION DeviceExtension,
IN PVOID Context,
IN NTSTATUS Status
)
/*++
Routine Description:
This routine is called when all the power queues are empty
Arguments:
DeviceExtension - The device that asked to be notified
Context - KEVENT
Status - The result of the operation
--*/
{
PKEVENT event = (PKEVENT) Context;
UNREFERENCED_PARAMETER( DeviceExtension );
UNREFERENCED_PARAMETER( Status );
//
// Set the event
//
KeSetEvent( event, IO_NO_INCREMENT, FALSE );
}
NTSTATUS
ACPIDevicePowerProcessForward(
IN PACPI_POWER_REQUEST PowerRequest
)
/*++
Routine Description:
This routine is called in liew of another PowerProcessPhaseXXX routine.
It is called because there is no real work to do in the current phase
on the selected request
Arguments:
PowerRequest - The request that we must process
Return Value:
NTSTATUS
--*/
{
InterlockedCompareExchange(
&(PowerRequest->WorkDone),
WORK_DONE_COMPLETE,
WORK_DONE_PENDING
);
//
// Remember that we have completed some work
//
KeAcquireSpinLockAtDpcLevel( &AcpiPowerQueueLock );
AcpiPowerWorkDone = TRUE;
KeReleaseSpinLockFromDpcLevel( &AcpiPowerQueueLock );
//
// We always succeed
//
return STATUS_SUCCESS;
}
NTSTATUS
ACPIDevicePowerProcessGenericPhase(
IN PLIST_ENTRY ListEntry,
IN PACPI_POWER_FUNCTION **DispatchTable,
IN BOOLEAN Complete
)
/*++
Routine Description:
This routine dispatches an item on the queue to the proper handler,
based on what type of request is present
Arguments:
ListEntry - The list we are currently walking
DispatchTable - Where to find which functions to call
Complete - Do we need complete the request when done?
Return Value:
NTSTATUS
- If any request is not marked as being complete, then STATUS_PENDING
is returned, otherwise, STATUS_SUCCESS is returned
--*/
{
BOOLEAN allWorkComplete = TRUE;
NTSTATUS status = STATUS_SUCCESS;
PACPI_POWER_FUNCTION *powerTable;
PACPI_POWER_REQUEST powerRequest;
PLIST_ENTRY currentEntry = ListEntry->Flink;
PLIST_ENTRY tempEntry;
ULONG workDone;
//
// Look at all the items in the list
//
while (currentEntry != ListEntry) {
//
// Turn this into a device request
//
powerRequest = CONTAINING_RECORD(
currentEntry,
ACPI_POWER_REQUEST,
ListEntry
);
//
// Set the temporary pointer to the next element
//
tempEntry = currentEntry->Flink;
//
// Check to see if we have any work to do on the request
//
workDone = InterlockedCompareExchange(
&(powerRequest->WorkDone),
WORK_DONE_PENDING,
WORK_DONE_PENDING
);
//
// Do we have a table associated with this level of workdone?
//
powerTable = DispatchTable[ workDone ];
if (powerTable != NULL) {
//
// Mark the request as pending
//
workDone = InterlockedCompareExchange(
&(powerRequest->WorkDone),
WORK_DONE_PENDING,
workDone
);
//
// Call the function
//
status = (powerTable[powerRequest->RequestType])( powerRequest );
//
// Did we succeed?
//
if (NT_SUCCESS(status)) {
//
// Go to the next request
//
continue;
}
//
// If we got an error before, then we must assume that we
// have completed the work request
//
workDone = WORK_DONE_COMPLETE;
}
//
// Grab the next entry
//
currentEntry = tempEntry;
//
// Check the status of the request
//
if (workDone != WORK_DONE_COMPLETE) {
allWorkComplete = FALSE;
}
//
// Do we need to complete the request or not?
//
if (workDone == WORK_DONE_FAILURE ||
(Complete == TRUE && workDone == WORK_DONE_COMPLETE)) {
//
// We are done with the request
//
ACPIDeviceCompleteRequest(
powerRequest
);
}
}
//
// Have we completed all of our work?
//
return (allWorkComplete ? STATUS_SUCCESS : STATUS_PENDING);
} // ACPIPowerProcessGenericPhase
NTSTATUS
ACPIDevicePowerProcessInvalid(
IN PACPI_POWER_REQUEST PowerRequest
)
/*++
Routine Description:
This routine is called in liew of another PowerProcessPhaseXXX routine.
It is called because the request is invalid
Arguments:
PowerRequest - The request that we must process
Return Value:
NTSTATUS
--*/
{
//
// Note the status of the request as having failed
//
PowerRequest->Status = STATUS_INVALID_PARAMETER_1;
//
// Complete the request
//
ACPIDeviceCompleteRequest( PowerRequest );
//
// Remember that we have completed some work
//
KeAcquireSpinLockAtDpcLevel( &AcpiPowerQueueLock );
AcpiPowerWorkDone = TRUE;
KeReleaseSpinLockFromDpcLevel( &AcpiPowerQueueLock );
//
// We always fail
//
return STATUS_INVALID_PARAMETER_1;
} // ACPIPowerProcessInvalid
NTSTATUS
ACPIDevicePowerProcessPhase0DeviceSubPhase1(
IN PACPI_POWER_REQUEST PowerRequest
)
/*++
Routine Description:
This routine looks for the _STA object and evalutes it. We will base
many things on wether or not the device is present
Arguments:
PowerRequest - The request that we are asked to process
Return Value:
NTSTATUS
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_EXTENSION deviceExtension = PowerRequest->DeviceExtension;
POBJDATA resultData = &(PowerRequest->ResultData);
//
// The next step is STEP_1
//
PowerRequest->NextWorkDone = WORK_DONE_STEP_1;
//
// Initialize the result data
//
RtlZeroMemory( resultData, sizeof(OBJDATA) );
//
// Get the device presence
//
status = ACPIGetDeviceHardwarePresenceAsync(
deviceExtension,
ACPIDeviceCompleteGenericPhase,
PowerRequest,
&(resultData->uipDataValue),
&(resultData->dwDataLen)
);
ACPIDevPrint( (
ACPI_PRINT_POWER,
deviceExtension,
"(0x%08lx): ACPIDevicePowerProcessPhase0DeviceSubPhase1 = 0x%08lx\n",
PowerRequest,
status
) );
if (status == STATUS_PENDING) {
return status;
}
//
// Call the completion routine by brute force. Note that at this
// point, we count everything as being SUCCESS
//
ACPIDeviceCompleteGenericPhase(
NULL,
status,
resultData,
PowerRequest
);
return STATUS_SUCCESS;
} // ACPIDevicePowerProcessPhase0DeviceSubPhase1
NTSTATUS
ACPIDevicePowerProcessPhase0DeviceSubPhase2(
IN PACPI_POWER_REQUEST PowerRequest
)
/*++
Routine Description:
This routine is called after the _STA method on a device has been run.
If the method was successfull, or not present, then we can continue to
process the request
Arguments:
PowerRequest - The request that we are asked to process
Return Value:
NTSTATUS
--*/
{
PDEVICE_EXTENSION deviceExtension = PowerRequest->DeviceExtension;
POBJDATA resultData = &(PowerRequest->ResultData);
ACPIDevPrint( (
ACPI_PRINT_POWER,
deviceExtension,
"(0x%08lx): ACPIDevicePowerProcessPhase0DeviceSubPhase2\n",
PowerRequest
) );
//
// If the bit isn't set as being present, then we must abort this
// request
//
if (!(resultData->uipDataValue & STA_STATUS_PRESENT) ) {
//
// The next work done phase is WORK_DONE_FAILURE. This allows the
// request to be completed right away. We will mark the status as
// success however, so that processing can continue
//
PowerRequest->NextWorkDone = WORK_DONE_FAILURE;
PowerRequest->Status = STATUS_SUCCESS;
} else {
//
// We are done with this work
//
PowerRequest->NextWorkDone = WORK_DONE_COMPLETE;
}
//
// Call the completion routine by brute force. Note that at this
// point, we count everything as being SUCCESS
//
ACPIDeviceCompleteGenericPhase(
NULL,
STATUS_SUCCESS,
NULL,
PowerRequest
);
return STATUS_SUCCESS;
} // ACPIDevicePowerProcessPhase0DeviceSubPhase2
NTSTATUS
ACPIDevicePowerProcessPhase0SystemSubPhase1(
IN PACPI_POWER_REQUEST PowerRequest
)
/*++
Routine Description:
This routine unpauses the interpreter (if so required)
Arguments:
PowerRequest - The request we are currently processing
Return Value:
NTSTATUS
--*/
{
PDEVICE_EXTENSION deviceExtension = PowerRequest->DeviceExtension;
SYSTEM_POWER_STATE systemState;
ACPIDevPrint( (
ACPI_PRINT_POWER,
deviceExtension,
"(0x%08lx): ACPIDevicePowerProcessPhase0SystemSubPhase1\n",
PowerRequest
) );
//
// We are done the first phase
//
PowerRequest->NextWorkDone = WORK_DONE_COMPLETE;
//
// Fetch the target system state
//
systemState = PowerRequest->u.SystemPowerRequest.SystemPowerState;
//
// If we are going to S0, then tell the interperter to resume
//
if (systemState == PowerSystemWorking) {
AMLIResumeInterpreter();
}
//
// Call the completion routine
//
ACPIDeviceCompleteInterpreterRequest(
PowerRequest
);
//
// We are successfull
//
return STATUS_SUCCESS;
} // ACPIDevicePowerProcessPhase0SystemSubPhase1
NTSTATUS
ACPIDevicePowerProcessPhase1DeviceSubPhase1(
IN PACPI_POWER_REQUEST PowerRequest
)
/*++
Routine Description:
Any device that is going to transition to the D3 state should have
have it resources disabled. This function detects if this is the
case and runs the _DIS object, if appropriate
Arguments:
PowerRequest - The current request structure that we must process
Return Value:
NTSTATUS
If we ignore errors, then this function can only return:
STATUS_SUCCESS
STATUS_PENDING
Otherwise, it can return any STATUS_XXX code it wants
--*/
{
DEVICE_POWER_STATE deviceState;
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_EXTENSION deviceExtension = PowerRequest->DeviceExtension;
PNSOBJ disObject = NULL;
ULONG flags;
//
// Get some data from the request
//
deviceState = PowerRequest->u.DevicePowerRequest.DevicePowerState;
flags = PowerRequest->u.DevicePowerRequest.Flags;
//
// We are going to need to fake the value from an _STA, so lets do
// that now
//
RtlZeroMemory( &(PowerRequest->ResultData), sizeof(OBJDATA) );
PowerRequest->ResultData.dwDataType = OBJTYPE_INTDATA;
PowerRequest->ResultData.uipDataValue = 0;
//
// Decide what the next subphase will be. The rule here is that if we
// are going to D0, then we can skip to Step 3, otherwise, we must go
// to Step 1. We also skip to step3 if we are on the hibernate path
//
if (deviceState == PowerDeviceD0 ||
(flags & DEVICE_REQUEST_LOCK_HIBER) ) {
PowerRequest->NextWorkDone = WORK_DONE_STEP_3;
goto ACPIDevicePowerProcessPhase1DeviceSubPhase1Exit;
} else if (deviceExtension->Flags & DEV_PROP_NO_OBJECT) {
PowerRequest->NextWorkDone = WORK_DONE_STEP_2;
goto ACPIDevicePowerProcessPhase1DeviceSubPhase1Exit;
} else {
PowerRequest->NextWorkDone = WORK_DONE_STEP_1;
if (deviceState != PowerDeviceD3) {
goto ACPIDevicePowerProcessPhase1DeviceSubPhase1Exit;
}
}
//
// See if the _DIS object exists
//
disObject = ACPIAmliGetNamedChild(
deviceExtension->AcpiObject,
PACKED_DIS
);
if (disObject != NULL) {
//
// Lets run that method
//
status = AMLIAsyncEvalObject(
disObject,
NULL,
0,
NULL,
ACPIDeviceCompleteGenericPhase,
PowerRequest
);
//
// If we got a pending back, then we should return now
//
if (status == STATUS_PENDING) {
return status;
}
}
ACPIDevicePowerProcessPhase1DeviceSubPhase1Exit:
//
// Call the completion routine by brute force. Note that at this
// point, we count everything as being SUCCESS
//
ACPIDeviceCompleteGenericPhase(
disObject,
status,
NULL,
PowerRequest
);
return STATUS_SUCCESS;
} // ACPIDevicePowerProcessPhase1DeviceSubPhase1
NTSTATUS
ACPIDevicePowerProcessPhase1DeviceSubPhase2(
IN PACPI_POWER_REQUEST PowerRequest
)
/*++
Routine Description:
This routine runs the _PS1, _PS2, or _PS3 control methods
Arguments:
PowerRequest - The current request structure that we must process
Return Value:
NTSTATUS
If we ignore errors, then this function can only return:
STATUS_SUCCESS
STATUS_PENDING
Otherwise, it can return any STATUS_XXX code it wants
--*/
{
DEVICE_POWER_STATE deviceState;
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_EXTENSION deviceExtension = PowerRequest->DeviceExtension;
PNSOBJ powerObject = NULL;
//
// The next phase that we will go to is Step_2
//
PowerRequest->NextWorkDone = WORK_DONE_STEP_2;
//
// Since we cannot get to this subphase when transitioning to D0, its
// safe to just look for the object to run
//
deviceState = PowerRequest->u.DevicePowerRequest.DevicePowerState;
powerObject = deviceExtension->PowerInfo.PowerObject[ deviceState ];
//
// If there is an object, then run the control method
//
if (powerObject != NULL) {
status = AMLIAsyncEvalObject(
powerObject,
NULL,
0,
NULL,
ACPIDeviceCompleteGenericPhase,
PowerRequest
);
ACPIDevPrint( (
ACPI_PRINT_POWER,
deviceExtension,
"(0x%08lx): ACPIDevicePowerProcessPhase1DeviceSubPhase2 "
"= 0x%08lx\n",
PowerRequest,
status
) );
//
// If we cannot complete the work ourselves, we must stop now
//
if (status == STATUS_PENDING) {
return status;
}
}
//
// Call the completion routine by brute force.
//
ACPIDeviceCompleteGenericPhase(
powerObject,
status,
NULL,
PowerRequest
);
return STATUS_SUCCESS;
} // ACPIPowerProcessPhase1DeviceSubPhase2
NTSTATUS
ACPIDevicePowerProcessPhase1DeviceSubPhase3(
IN PACPI_POWER_REQUEST PowerRequest
)
/*++
Routine Description:
This routine runs the _STA of the device to make sure that it has in
fact been turned off
Arguments:
PowerRequest - The current request structure that we must process
Return Value:
NTSTATUS
If we ignore errors, then this function can only return:
STATUS_SUCCESS
STATUS_PENDING
Otherwise, it can return any STATUS_XXX code it wants
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_EXTENSION deviceExtension = PowerRequest->DeviceExtension;
POBJDATA resultData = &(PowerRequest->ResultData);
PNSOBJ staObject = NULL;
PNSOBJ acpiObject = NULL;
//
// The next stage is STEP_3
//
PowerRequest->NextWorkDone = WORK_DONE_STEP_3;
//
// We already have space allocate for the result of the _STA. Make
// that there is no garbage present
//
RtlZeroMemory( resultData, sizeof(OBJDATA) );
//
// Is there an _STA object present on this device?
//
if (deviceExtension->Flags & DEV_PROP_DOCK) {
ASSERT( deviceExtension->Dock.CorrospondingAcpiDevice );
acpiObject = deviceExtension->Dock.CorrospondingAcpiDevice->AcpiObject;
} else {
acpiObject = deviceExtension->AcpiObject;
}
staObject = ACPIAmliGetNamedChild(
acpiObject,
PACKED_STA
);
if (staObject != NULL) {
status = AMLIAsyncEvalObject(
staObject,
resultData,
0,
NULL,
ACPIDeviceCompleteGenericPhase,
PowerRequest
);
ACPIDevPrint( (
ACPI_PRINT_POWER,
deviceExtension,
"(0x%08lx): ACPIDevicePowerProcessPhase1DeviceSubPhase3 "
"= 0x%08lx\n",
PowerRequest,
status
) );
} else {
//
// Lets fake the data. Note that in this case we will pretend that
// the value is 0x0, even though the spec says that the default
// is (ULONG) - 1. The reason we are doing this is that in this
// case we want to approximate the behaviour of the real _STA...
//
resultData->dwDataType = OBJTYPE_INTDATA;
resultData->uipDataValue = STA_STATUS_PRESENT;
status = STATUS_SUCCESS;
}
//
// Do we have to call the completion routine ourselves?
//
if (status != STATUS_PENDING) {
ACPIDeviceCompleteGenericPhase(
staObject,
status,
NULL,
PowerRequest
);
}
//
// Always return success
//
return STATUS_SUCCESS;
} // ACPIDevicePowerProcessPhase1DeviceSubPhase3
NTSTATUS
ACPIDevicePowerProcessPhase1DeviceSubPhase4(
IN PACPI_POWER_REQUEST PowerRequest
)
/*++
Routine Description:
This function determines which device nodes need to be looked at. The
generic rule is that we need to remember which nodes belong to a device
that is either starting or stopping to use that node. Generally, these
are the nodes in the current power state and the nodes in the desired
power state
Arguments:
PowerRequest - The current request structure that we must process
Return Value:
NTSTATUS
If we ignore errors, then this function can only return:
STATUS_SUCCESS
STATUS_PENDING
Otherwise, it can return any STATUS_XXX code it wants
--*/
{
DEVICE_POWER_STATE deviceState;
KIRQL oldIrql;
PACPI_DEVICE_POWER_NODE deviceNode = NULL;
PDEVICE_EXTENSION deviceExtension = PowerRequest->DeviceExtension;
POBJDATA resultData = &(PowerRequest->ResultData);
ULONG flags;
//
// Clear the result
//
AMLIFreeDataBuffs( resultData, 1 );
RtlZeroMemory( resultData, sizeof(OBJDATA) );
//
// We cannot walk any data structures without holding a lock
//
KeAcquireSpinLock( &AcpiPowerLock, &oldIrql );
//
// First step is to find the list of nodes which are in use by this
// device
//
deviceState = deviceExtension->PowerInfo.PowerState;
if (deviceState >= PowerDeviceD0 && deviceState <= PowerDeviceD2) {
//
// In this case, we have to look at the current and the desired
// device states only
//
deviceNode = deviceExtension->PowerInfo.PowerNode[ deviceState ];
//
// Next step is to look at all the nodes and mark the power objects
// as requiring an update
//
while (deviceNode != NULL) {
InterlockedExchange(
&(deviceNode->PowerNode->WorkDone),
WORK_DONE_STEP_0
);
deviceNode = deviceNode->Next;
}
//
// Now, we need to find the list of nodes which are going to be used
//
deviceState = PowerRequest->u.DevicePowerRequest.DevicePowerState;
if (deviceState >= PowerDeviceD0 && deviceState <= PowerDeviceD2) {
deviceNode = deviceExtension->PowerInfo.PowerNode[ deviceState ];
}
//
// Next step is to look at all the nodes and mark the power objects
// as requiring an update
//
while (deviceNode != NULL) {
InterlockedExchange(
&(deviceNode->PowerNode->WorkDone),
WORK_DONE_STEP_0
);
deviceNode = deviceNode->Next;
}
} else {
//
// In this case, we have to look at all possible Device states
//
for (deviceState = PowerDeviceD0;
deviceState < PowerDeviceD3;
deviceState++) {
deviceNode = deviceExtension->PowerInfo.PowerNode[ deviceState ];
//
// Next step is to look at all the nodes and mark the power objects
// as requiring an update
//
while (deviceNode != NULL) {
InterlockedExchange(
&(deviceNode->PowerNode->WorkDone),
WORK_DONE_STEP_0
);
deviceNode = deviceNode->Next;
}
}
//
// This is the device state that we will go to
//
deviceState = PowerRequest->u.DevicePowerRequest.DevicePowerState;
}
//
// If this is a request on the hibernate path, the mark all the nodes
// for the D0 as being required Hibernate nodes
//
flags = PowerRequest->u.DevicePowerRequest.Flags;
if (flags & DEVICE_REQUEST_LOCK_HIBER) {
deviceNode = deviceExtension->PowerInfo.PowerNode[ PowerDeviceD0 ];
//
// Next step is to look at all the nodes and mark the power objects
// as requiring an update
//
while (deviceNode != NULL) {
ACPIInternalUpdateFlags(
&(deviceNode->PowerNode->Flags),
(DEVICE_NODE_HIBERNATE_PATH | DEVICE_NODE_OVERRIDE_ON),
FALSE
);
ACPIInternalUpdateFlags(
&(deviceNode->PowerNode->Flags),
DEVICE_NODE_OVERRIDE_OFF,
TRUE
);
InterlockedExchange(
&(deviceNode->PowerNode->WorkDone),
WORK_DONE_STEP_0
);
deviceNode = deviceNode->Next;
}
} else if (flags & DEVICE_REQUEST_UNLOCK_HIBER) {
deviceNode = deviceExtension->PowerInfo.PowerNode[ PowerDeviceD0 ];
//
// Next step is to look at all the nodes and mark the power objects
// as requiring an update
//
while (deviceNode != NULL) {
ACPIInternalUpdateFlags(
&(deviceNode->PowerNode->Flags),
(DEVICE_NODE_HIBERNATE_PATH | DEVICE_NODE_OVERRIDE_ON),
TRUE
);
InterlockedExchange(
&(deviceNode->PowerNode->WorkDone),
WORK_DONE_STEP_0
);
deviceNode = deviceNode->Next;
}
}
//
// Remember the desired state
//
deviceExtension->PowerInfo.DesiredPowerState = deviceState;
//
// Also, consider that the device is now in an unknown state ---
// if we fail something, the is the power state that we will be left
// at
//
deviceExtension->PowerInfo.PowerState = PowerDeviceUnspecified;
//
// We no longer need the PowerLock
//
KeReleaseSpinLock( &AcpiPowerLock, oldIrql );
//
// Done
//
ACPIDeviceCompleteCommon( &(PowerRequest->WorkDone), WORK_DONE_COMPLETE );
return STATUS_SUCCESS;
}
NTSTATUS
ACPIDevicePowerProcessPhase2SystemSubPhase1(
IN PACPI_POWER_REQUEST PowerRequest
)
/*++
Routine Description:
This routine updates the PowerObject references so that we can run
_ON or _OFF methods as needed
This also cause _WAK() to be run on the system
Arguments:
PowerRequest - The current request structure that we must process
Return Value:
NTSTATUS
If we ignore errors, then this function can only return:
STATUS_SUCCESS
STATUS_PENDING
Otherwise, it can return any STATUS_XXX code it wants
--*/
{
BOOLEAN restart = FALSE;
NTSTATUS status = STATUS_SUCCESS;
OBJDATA objData;
PACPI_DEVICE_POWER_NODE deviceNode = NULL;
PACPI_POWER_DEVICE_NODE powerNode = NULL;
PDEVICE_EXTENSION deviceExtension = PowerRequest->DeviceExtension;
PLIST_ENTRY deviceList;
PLIST_ENTRY powerList;
PNSOBJ sleepObject = NULL;
POWER_ACTION systemAction;
SYSTEM_POWER_STATE systemState;
SYSTEM_POWER_STATE wakeFromState;
ULONG hibernateCount;
//
// The next stage after this one is STEP_1
//
PowerRequest->NextWorkDone = WORK_DONE_STEP_1;
//
// Get the desired system state
//
systemState = PowerRequest->u.SystemPowerRequest.SystemPowerState;
systemAction = PowerRequest->u.SystemPowerRequest.SystemPowerAction;
//
// Is the system restarting?
//
restart = ( (systemState == PowerSystemShutdown) &&
(systemAction == PowerActionShutdownReset) );
//
// We need to hold this lock before we can walk this list
//
KeAcquireSpinLockAtDpcLevel( &AcpiPowerLock );
//
// Get the first power node
//
powerList = AcpiPowerNodeList.Flink;
//
// Walk the list and see which devices need to be turned on or
// turned off
//
while (powerList != &AcpiPowerNodeList) {
//
// Obtain the power node from the listEntry
//
powerNode = CONTAINING_RECORD(
powerList,
ACPI_POWER_DEVICE_NODE,
ListEntry
);
//
// Next node
//
powerList = powerList->Flink;
//
// We need to walk the list of device nodes to see if any of
// the devices are in the hibernate path.
//
deviceList = powerNode->DevicePowerListHead.Flink;
while (deviceList != &(powerNode->DevicePowerListHead) ) {
//
// Obtain the devicenode from the list pointer
//
deviceNode = CONTAINING_RECORD(
deviceList,
ACPI_DEVICE_POWER_NODE,
DevicePowerListEntry
);
//
// Point to the next node
//
deviceList = deviceList->Flink;
//
// Grab the associated device extension
//
deviceExtension = deviceNode->DeviceExtension;
//
// Does the node belong on the hibernate path
//
hibernateCount = InterlockedCompareExchange(
&(deviceExtension->HibernatePathCount),
0,
0
);
if (hibernateCount) {
break;
}
}
//
// Mark the node as being in the hibernate path, or not, as the
// case might be
//
ACPIInternalUpdateFlags(
&(powerNode->Flags),
DEVICE_NODE_HIBERNATE_PATH,
(BOOLEAN) !hibernateCount
);
//
// First check is to see if the node is on the hibernate path and
// this is a hibernate request, or if the system is restarting
//
if ( (hibernateCount && systemState == PowerSystemHibernate) ||
(restart == TRUE) ) {
if (powerNode->Flags & DEVICE_NODE_OVERRIDE_OFF) {
//
// make sure that the Override Off flag is disabled
//
ACPIInternalUpdateFlags(
&(powerNode->Flags),
DEVICE_NODE_OVERRIDE_OFF,
TRUE
);
//
// Mark the node as requiring an update
//
InterlockedExchange(
&(powerNode->WorkDone),
WORK_DONE_STEP_0
);
}
} else {
//
// Does the node support the indicates system state?
//
if (powerNode->SystemLevel < systemState) {
//
// No --- we must disable it, but if we cannot always be on.
//
if ( !(powerNode->Flags & DEVICE_NODE_ALWAYS_ON) ) {
ACPIInternalUpdateFlags(
&(powerNode->Flags),
DEVICE_NODE_OVERRIDE_OFF,
FALSE
);
}
//
// Mark the node as requiring an update
//
InterlockedExchange(
&(powerNode->WorkDone),
WORK_DONE_STEP_0
);
} else if (powerNode->Flags & DEVICE_NODE_OVERRIDE_OFF) {
//
// Disable this flag
//
ACPIInternalUpdateFlags(
&(powerNode->Flags),
DEVICE_NODE_OVERRIDE_OFF,
TRUE
);
//
// Mark the node as requiring an update
//
InterlockedExchange(
&(powerNode->WorkDone),
WORK_DONE_STEP_0
);
}
}
}
//
// Set the WakeFromState while we still hold the power lock.
//
wakeFromState = AcpiMostRecentSleepState;
//
// We don't need to hold lock anymore
//
KeReleaseSpinLockFromDpcLevel( &AcpiPowerLock );
//
// We can only do the following if we are transitioning to the S0 state
//
if (systemState == PowerSystemWorking) {
//
// Always run the _WAK method (this clears the PTS(S5) if that is
// the last thing we did, otherwise it is the proper action to take
//
sleepObject = ACPIAmliGetNamedChild(
PowerRequest->DeviceExtension->AcpiObject->pnsParent,
PACKED_WAK
);
//
// We only try to evaluate a method if we found an object
//
if (sleepObject != NULL) {
//
// Remember that AMLI doesn't use our definitions, so we will
// have to normalize the S value
//
RtlZeroMemory( &objData, sizeof(OBJDATA) );
objData.dwDataType = OBJTYPE_INTDATA;
objData.uipDataValue = ACPIDeviceMapACPIPowerState(
wakeFromState
);
//
// Safely run the control method
//
status = AMLIAsyncEvalObject(
sleepObject,
NULL,
1,
&objData,
ACPIDeviceCompleteGenericPhase,
PowerRequest
);
//
// If we got STATUS_PENDING, then we cannot do any more work here.
//
if (status == STATUS_PENDING) {
return status;
}
}
}
//
// Always call the completion routine
//
ACPIDeviceCompleteGenericPhase(
sleepObject,
status,
NULL,
PowerRequest
);
//
// Never return anything other then STATUS_SUCCESS
//
return STATUS_SUCCESS;
} // ACPIPowerProcessPhase2SystemSubPhase1
NTSTATUS
ACPIDevicePowerProcessPhase2SystemSubPhase2(
IN PACPI_POWER_REQUEST PowerRequest
)
/*++
Routine Description:
This phase is called after the _WAK method has been run
Arguments:
PowerRequest - The current request structure that we must process
Return Value:
NTSTATUS
If we ignore errors, then this function can only return:
STATUS_SUCCESS
STATUS_PENDING
Otherwise, it can return any STATUS_XXX code it wants
--*/
{
NTSTATUS status = STATUS_SUCCESS;
SYSTEM_POWER_STATE systemState;
//
// The next stage is STEP_2
//
PowerRequest->NextWorkDone = WORK_DONE_STEP_2;
//
// We need to make sure that the IRQ arbiter has been restored
// if we are making an S0 transition
//
systemState = PowerRequest->u.SystemPowerRequest.SystemPowerState;
if (systemState == PowerSystemWorking) {
//
// Restore the IRQ arbiter
//
status = IrqArbRestoreIrqRouting(
ACPIDeviceCompleteGenericPhase,
(PVOID) PowerRequest
);
if (status == STATUS_PENDING) {
//
// Do not do any more work here
//
return status;
}
}
//
// Call the next completion routine
//
ACPIDeviceCompleteGenericPhase(
NULL,
status,
NULL,
PowerRequest
);
//
// Always return success
//
return STATUS_SUCCESS;
} // ACPIDevicePowerProcessPhase2SystemSubPhase2
NTSTATUS
ACPIDevicePowerProcessPhase2SystemSubPhase3(
IN PACPI_POWER_REQUEST PowerRequest
)
/*++
Routine Description:
This phase is used to see if we need to re-run the _PSW for all the
devices. We need to do this when we restore from the hibernate state
Arguments:
PowerRequest - The current request structure that we must process
Return Value:
NTSTATUS
If we ignore errors, then this function can only return:
STATUS_SUCCESS
STATUS_PENDING
Otherwise, it can return any STATUS_XXX code it wants
--*/
{
NTSTATUS status = STATUS_SUCCESS;
SYSTEM_POWER_STATE systemState;
SYSTEM_POWER_STATE wakeFromState;
//
// The next stage is COMPLETE
//
PowerRequest->NextWorkDone = WORK_DONE_COMPLETE;
//
// If we just transitioned from Hibernate, then we must re-enable all
// the wake devices
//
systemState = PowerRequest->u.SystemPowerRequest.SystemPowerState;
//
// Grab the current most recent sleep state and make sure to hold the
// locks while doing so
//
KeAcquireSpinLockAtDpcLevel( &AcpiPowerLock );
wakeFromState = AcpiMostRecentSleepState;
KeReleaseSpinLockFromDpcLevel( &AcpiPowerLock );
if (systemState == PowerSystemWorking &&
wakeFromState == PowerSystemHibernate) {
//
// Restore the IRQ arbiter
//
status = ACPIWakeRestoreEnables(
ACPIWakeRestoreEnablesCompletion,
PowerRequest
);
if (status == STATUS_PENDING) {
//
// Do not do any more work here
//
return status;
}
}
//
// Call the next completion routine
//
ACPIDeviceCompleteGenericPhase(
NULL,
status,
NULL,
PowerRequest
);
//
// Always return success
//
return STATUS_SUCCESS;
} // ACPIDevicePowerProcessPhase2SystemSubPhase3
NTSTATUS
ACPIDevicePowerProcessPhase3(
VOID
)
/*++
Routine Description:
This routine ensures that the Power Resources are in sync
Arguments:
NONE
Return Value:
NTSTATUS
- If any request is not marked as being complete, then STATUS_PENDING
is returned, otherwise, STATUS_SUCCESS is returned
--*/
{
BOOLEAN returnPending = FALSE;
NTSTATUS status = STATUS_SUCCESS;
PACPI_DEVICE_POWER_NODE deviceNode;
PACPI_POWER_DEVICE_NODE powerNode;
PDEVICE_EXTENSION deviceExtension;
PLIST_ENTRY deviceList;
PLIST_ENTRY powerList;
ULONG useCounts;
ULONG wakeCount;
ULONG workDone;
//
// Grab the PowerLock that we need for this
//
KeAcquireSpinLockAtDpcLevel( &AcpiPowerLock );
//
// Grab the first node in the PowerNode list
//
powerList = AcpiPowerNodeList.Flink;
//
// Walk the list forward to device what to turn on
//
while (powerList != &AcpiPowerNodeList) {
//
// Look at the current power node
//
powerNode = CONTAINING_RECORD(
powerList,
ACPI_POWER_DEVICE_NODE,
ListEntry
);
//
// Next item in the list
//
powerList = powerList->Flink;
//
// Have we marked the node has having some potential work that
// needs to be done?
//
workDone = InterlockedCompareExchange(
&(powerNode->WorkDone),
WORK_DONE_STEP_1,
WORK_DONE_STEP_0
);
//
// If we don't have any work to do, then loop back to the start
//
if (workDone != WORK_DONE_STEP_0) {
continue;
}
//
// We need to walk the list of device nodes to see if
// any of the devices are in need of this power resource
//
useCounts = 0;
deviceList = powerNode->DevicePowerListHead.Flink;
while (deviceList != &(powerNode->DevicePowerListHead) ) {
//
// Obtain the deviceNode from the list pointer
//
deviceNode = CONTAINING_RECORD(
deviceList,
ACPI_DEVICE_POWER_NODE,
DevicePowerListEntry
);
//
// Point to the next node
//
deviceList = deviceList->Flink;
//
// Grab the associated device extension
//
deviceExtension = deviceNode->DeviceExtension;
//
// Grab the number of wake counts on the node
//
wakeCount = InterlockedCompareExchange(
&(deviceExtension->PowerInfo.WakeSupportCount),
0,
0
);
//
// Does the device node belong to the desired state? The
// other valid state is if the node is required to wake the
// device and we have functionality enabled.
//
if (deviceExtension->PowerInfo.DesiredPowerState ==
deviceNode->AssociatedDeviceState ||
(wakeCount && deviceNode->WakePowerResource) ) {
useCounts++;
}
}
//
// Set the number of use counts in the PowerResource
//
InterlockedExchange(
&(powerNode->UseCounts),
useCounts
);
//
// See if the override bits are set properly
//
if ( (powerNode->Flags & DEVICE_NODE_TURN_OFF) ) {
//
// Do not run anything
//
continue;
}
if ( !(powerNode->Flags & DEVICE_NODE_TURN_ON) &&
useCounts == 0 ) {
//
// Do not run anything
//
continue;
}
//
// We are going to do some work on this node, so mark it as
// such, so that we don't accidently run the _OFF method for
// this device
//
workDone = InterlockedCompareExchange(
&(powerNode->WorkDone),
WORK_DONE_PENDING,
WORK_DONE_STEP_1
);
//
// We cannot hold the spin lock while we eval the method
//
KeReleaseSpinLockFromDpcLevel( &AcpiPowerLock );
//
// Evaluate the method to turn this node on
//
status = AMLIAsyncEvalObject(
powerNode->PowerOnObject,
NULL,
0,
NULL,
ACPIDeviceCompletePhase3On,
powerNode
);
//
// Let the world know
//
ACPIPrint( (
ACPI_PRINT_POWER,
"ACPIDevicePowerProcessPhase3: PowerNode: 0x%08lx ON = 0x%08lx\n",
powerNode,
status
) );
if (status != STATUS_PENDING) {
//
// Fake a call to the callback
//
ACPIDeviceCompletePhase3On(
powerNode->PowerOnObject,
status,
NULL,
powerNode
);
} else {
//
// Remember that a function returned Pending
//
returnPending = TRUE;
}
//
// Reacquire the spinlock so that we can loop again
//
KeAcquireSpinLockAtDpcLevel( &AcpiPowerLock );
} // while
//
// Grab the blink so that we can start walking the list backward
//
powerList = AcpiPowerNodeList.Blink;
//
// Walk the list backward
//
while (powerList != &AcpiPowerNodeList) {
//
// Look at the current power node
//
powerNode = CONTAINING_RECORD(
powerList,
ACPI_POWER_DEVICE_NODE,
ListEntry
);
//
// Next item in the list
//
powerList = powerList->Blink;
//
// Have we marked the node has having some potential work that
// needs to be done?
//
workDone = InterlockedCompareExchange(
&(powerNode->WorkDone),
WORK_DONE_PENDING,
WORK_DONE_STEP_1
);
//
// To do work on this node, we need to see WORK_DONE_STEP_1
//
if (workDone != WORK_DONE_STEP_1) {
//
// While we are here, we can check to see if the request is
// complete --- if it isn't then we must return STATUS_PENDING
//
if (workDone != WORK_DONE_COMPLETE) {
returnPending = TRUE;
}
continue;
}
//
// Release the spinlock since we cannot own it while we call
// the interpreter
//
KeReleaseSpinLockFromDpcLevel( &AcpiPowerLock );
//
// If we are here, we *must* run the _OFF method
//
status = AMLIAsyncEvalObject(
powerNode->PowerOffObject,
NULL,
0,
NULL,
ACPIDeviceCompletePhase3Off,
powerNode
);
//
// Let the world know
//
ACPIPrint( (
ACPI_PRINT_POWER,
"ACPIDevicePowerProcessPhase3: PowerNode: 0x%08lx OFF = 0x%08lx\n",
powerNode,
status
) );
if (status != STATUS_PENDING) {
//
// Fake a call to the callback
//
ACPIDeviceCompletePhase3Off(
powerNode->PowerOffObject,
status,
NULL,
powerNode
);
} else {
//
// Remember that a function returned Pending
//
returnPending = TRUE;
}
//
// Reacquire the spinlock so that we can loop again
//
KeAcquireSpinLockAtDpcLevel( &AcpiPowerLock );
}
//
// We no longer need the spin lock
//
KeReleaseSpinLockFromDpcLevel( &AcpiPowerLock );
//
// Do we need to return status pending?
//
return (returnPending ? STATUS_PENDING : STATUS_SUCCESS);
} // ACPIPowerProcessPhase3
NTSTATUS
ACPIDevicePowerProcessPhase4(
VOID
)
/*++
Routine Description:
This routine looks at the all the PowerNodes again and determines wether
or not to fail a given request by wether or not a powernode failed to
go to the desired state
Arguments:
None
Return Value:
NTSTATUS
--*/
{
PACPI_DEVICE_POWER_NODE deviceNode;
PACPI_POWER_DEVICE_NODE powerNode;
PACPI_POWER_REQUEST powerRequest;
PDEVICE_EXTENSION deviceExtension;
PLIST_ENTRY listEntry = AcpiPowerPhase4List.Flink;
PLIST_ENTRY nodeList;
PLIST_ENTRY requestList;
//
// Now, we have to look at all the power nodes, and clear the fail flags
// This has to be done under spinlock protection
//
KeAcquireSpinLockAtDpcLevel( &AcpiPowerLock );
listEntry = AcpiPowerNodeList.Flink;
while (listEntry != &AcpiPowerNodeList) {
powerNode = CONTAINING_RECORD(
listEntry,
ACPI_POWER_DEVICE_NODE,
ListEntry
);
listEntry = listEntry->Flink;
if (powerNode->Flags & DEVICE_NODE_FAIL) {
//
// Clear the failure flag
//
ACPIInternalUpdateFlags(
&(powerNode->Flags),
DEVICE_NODE_FAIL,
TRUE
);
//
// Loop for all the device extensions
//
nodeList = powerNode->DevicePowerListHead.Flink;
while (nodeList != &(powerNode->DevicePowerListHead)) {
deviceNode = CONTAINING_RECORD(
nodeList,
ACPI_DEVICE_POWER_NODE,
DevicePowerListEntry
);
nodeList = nodeList->Flink;
//
// We must do the next part not under spinlock
//
KeReleaseSpinLockFromDpcLevel( &AcpiPowerLock );
//
// Grab the device extension associated with this node
//
deviceExtension = deviceNode->DeviceExtension;
//
// Loop on all the requests
//
requestList = AcpiPowerPhase4List.Flink;
while (requestList != &AcpiPowerPhase4List) {
powerRequest = CONTAINING_RECORD(
requestList,
ACPI_POWER_REQUEST,
ListEntry
);
requestList = requestList->Flink;
//
// Do we have a match?
//
if (powerRequest->DeviceExtension != deviceExtension) {
//
// No? Then continue
//
continue;
}
//
// Yes? Then fail the request
//
powerRequest->Status = STATUS_ACPI_POWER_REQUEST_FAILED;
ACPIDeviceCompleteRequest( powerRequest );
}
//
// Reacquire the lock
//
KeAcquireSpinLockAtDpcLevel( &AcpiPowerLock );
}
}
}
KeReleaseSpinLockFromDpcLevel( &AcpiPowerLock );
//
// Always return success
//
return STATUS_SUCCESS;
}
NTSTATUS
ACPIDevicePowerProcessPhase5DeviceSubPhase1(
IN PACPI_POWER_REQUEST PowerRequest
)
/*++
Routine Description:
This routine runs the _PS0 control method
Arguments:
PowerRequest - The current request structure that we must process
Return Value:
NTSTATUS
If we ignore errors, then this function can only return:
STATUS_SUCCESS
STATUS_PENDING
Otherwise, it can return any STATUS_XXX code it wants
--*/
{
BOOLEAN nodeOkay = TRUE;
DEVICE_POWER_STATE deviceState;
KIRQL oldIrql;
NTSTATUS status = STATUS_SUCCESS;
PACPI_DEVICE_POWER_NODE deviceNode = NULL;
PACPI_POWER_DEVICE_NODE powerNode = NULL;
PACPI_POWER_INFO powerInfo;
PDEVICE_EXTENSION deviceExtension = PowerRequest->DeviceExtension;
PNSOBJ powerObject = NULL;
//
// What is our desired device state?
//
deviceState = PowerRequest->u.DevicePowerRequest.DevicePowerState;
//
// Grab the power Information structure
//
powerInfo = &(deviceExtension->PowerInfo);
//
// Decide what the next subphase will be. The rule here is that if we
// are not going to D0, then we can skip to STEP_2, otherwise, we must go
// to STEP_1
//
if (deviceState != PowerDeviceD0) {
PowerRequest->NextWorkDone = WORK_DONE_STEP_2;
} else {
PowerRequest->NextWorkDone = WORK_DONE_STEP_1;
//
// We cannot walk any data structures without holding a lock
//
KeAcquireSpinLock( &AcpiPowerLock, &oldIrql );
//
// Look at the device nodes for D0
//
deviceNode = powerInfo->PowerNode[PowerDeviceD0];
//
// Next step is to look at all the nodes and mark the power objects
// as requiring an update
//
while (deviceNode != NULL) {
//
// Grab the associated power node
//
powerNode = deviceNode->PowerNode;
//
// Make sure that the power node is in the ON state
//
if ( !(powerNode->Flags & DEVICE_NODE_ON) ) {
nodeOkay = FALSE;
break;
}
//
// Look at the next node
//
deviceNode = deviceNode->Next;
}
//
// We are done with the lock
//
KeReleaseSpinLock( &AcpiPowerLock, oldIrql );
//
// Are all the nodes in the correct state?
//
if (!nodeOkay) {
status = STATUS_UNSUCCESSFUL;
} else {
//
// Otherwise, see if there is a _PS0 method to run
//
powerObject = powerInfo->PowerObject[ deviceState ];
//
// If there is an object, then run the control method
//
if (powerObject != NULL) {
status = AMLIAsyncEvalObject(
powerObject,
NULL,
0,
NULL,
ACPIDeviceCompleteGenericPhase,
PowerRequest
);
}
ACPIDevPrint( (
ACPI_PRINT_POWER,
deviceExtension,
"(0x%08lx): ACPIDevicePowerProcessPhase5DeviceSubPhase1 "
"= 0x%08lx\n",
PowerRequest,
status
) );
//
// If we cannot complete the work ourselves, we must stop now
//
if (status == STATUS_PENDING) {
return status;
} else {
status = STATUS_SUCCESS;
}
}
}
//
// Call the completion routine by brute force. Note that at this
// point, we count everything as being SUCCESS
//
ACPIDeviceCompleteGenericPhase(
powerObject,
status,
NULL,
PowerRequest
);
return STATUS_SUCCESS;
} // ACPIPowerProcessPhase5DeviceSubPhase1
NTSTATUS
ACPIDevicePowerProcessPhase5DeviceSubPhase2(
IN PACPI_POWER_REQUEST PowerRequest
)
/*++
Routine Description:
This routine runs the _SRS control method
Note: that we only come down this path if we are transitioning to D0
Arguments:
PowerRequest - The current request structure that we must process
Return Value:
NTSTATUS
If we ignore errors, then this function can only return:
STATUS_SUCCESS
STATUS_PENDING
Otherwise, it can return any STATUS_XXX code it wants
--*/
{
DEVICE_POWER_STATE deviceState =
PowerRequest->u.DevicePowerRequest.DevicePowerState;
KIRQL oldIrql;
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_EXTENSION deviceExtension = PowerRequest->DeviceExtension;
PNSOBJ srsObject = NULL;
//
// The next phase is STEP_2
//
PowerRequest->NextWorkDone = WORK_DONE_STEP_2;
if (!(deviceExtension->Flags & DEV_PROP_NO_OBJECT)) {
//
// Is there an _SRS object present on this device?
//
srsObject = ACPIAmliGetNamedChild(
deviceExtension->AcpiObject,
PACKED_SRS
);
}
if (srsObject != NULL) {
//
// We must hold this lock while we run the Control Method.
//
// Note: Because the interpreter will make a copy of the data
// arguments passed to it, we only need to hold the lock as long
// as it takes for the interpreter to return
//
KeAcquireSpinLock( &AcpiDeviceTreeLock, &oldIrql );
if (deviceExtension->PnpResourceList != NULL) {
//
// Evalute the method
//
status = AMLIAsyncEvalObject(
srsObject,
NULL,
1,
deviceExtension->PnpResourceList,
ACPIDeviceCompleteGenericPhase,
PowerRequest
);
ACPIDevPrint( (
ACPI_PRINT_POWER,
deviceExtension,
"(0x%08lx): ACPIDevicePowerProcessPhase5DeviceSubPhase2 "
"= 0x%08lx\n",
PowerRequest,
status
) );
}
//
// Mo longer need the lock
//
KeReleaseSpinLock( &AcpiDeviceTreeLock, oldIrql );
if (status == STATUS_PENDING) {
return status;
}
} else {
//
// Consider the request successfull
//
status = STATUS_SUCCESS;
}
//
// Call the completion routine brute force
//
ACPIDeviceCompleteGenericPhase(
srsObject,
status,
NULL,
PowerRequest
);
//
// Always return success
//
return STATUS_SUCCESS;
} // ACPIDevicePowerProcessPhase5DeviceSubPhase2
NTSTATUS
ACPIDevicePowerProcessPhase5DeviceSubPhase3(
IN PACPI_POWER_REQUEST PowerRequest
)
/*++
Routine Description:
This routine enables or disables the lock on the device
Arguments:
PowerRequest - The current request structure that we must process
Return Value:
NTSTATUS
If we ignore errors, then this function can only return:
STATUS_SUCCESS
STATUS_PENDING
Otherwise, it can return any STATUS_XXX code it wants
--*/
{
DEVICE_POWER_STATE deviceState;
NTSTATUS status = STATUS_SUCCESS;
OBJDATA objData;
PDEVICE_EXTENSION deviceExtension = PowerRequest->DeviceExtension;
PNSOBJ lckObject = NULL;
ULONG flags;
ACPIDevPrint( (
ACPI_PRINT_POWER,
deviceExtension,
"(%#08lx): ACPIDevicePowerProcessPhase5DeviceSubPhase4\n",
PowerRequest
) );
//
// What is our desired device state and action?
//
deviceState = PowerRequest->u.DevicePowerRequest.DevicePowerState;
flags = PowerRequest->u.DevicePowerRequest.Flags;
//
// If we aren't going to D0, then skip to the end
//
if (deviceState != PowerDeviceD0) {
//
// The next stage is STEP_5
//
PowerRequest->NextWorkDone = WORK_DONE_STEP_5;
} else {
//
// The next stage is STEP_3
//
PowerRequest->NextWorkDone = WORK_DONE_STEP_3;
}
if (deviceExtension->Flags & DEV_PROP_NO_OBJECT) {
goto ACPIDevicePowerProcessPhase5DeviceSubPhase3Exit;
}
//
// Is there an _LCK object present on this device?
//
lckObject = ACPIAmliGetNamedChild(
deviceExtension->AcpiObject,
PACKED_LCK
);
if (lckObject == NULL) {
goto ACPIDevicePowerProcessPhase5DeviceSubPhase3Exit;
}
//
// Initialize the argument that we will pass to the function
//
RtlZeroMemory( &objData, sizeof(OBJDATA) );
objData.dwDataType = OBJTYPE_INTDATA;
//
// Look at the flags and see if we should lock or unlock the device
//
if (flags & DEVICE_REQUEST_LOCK_DEVICE) {
objData.uipDataValue = 1; // Lock the device
} else if (flags & DEVICE_REQUEST_UNLOCK_DEVICE) {
objData.uipDataValue = 0; // Unlock the device
} else {
goto ACPIDevicePowerProcessPhase5DeviceSubPhase3Exit;
}
//
// Run the control method now
//
status = AMLIAsyncEvalObject(
lckObject,
NULL,
1,
&objData,
ACPIDeviceCompleteGenericPhase,
PowerRequest
);
ACPIDevPrint( (
ACPI_PRINT_POWER,
deviceExtension,
"(0x%08lx): ACPIDevicePowerProcessPhase5DeviceSubPhase3 "
"= 0x%08lx\n",
PowerRequest,
status
) );
ACPIDevicePowerProcessPhase5DeviceSubPhase3Exit:
//
// Do we have to call the completion routine ourselves?
//
if (status != STATUS_PENDING) {
ACPIDeviceCompleteGenericPhase(
lckObject,
status,
NULL,
PowerRequest
);
}
//
// Always return success
//
return STATUS_SUCCESS;
} // ACPIDevicePowerProcessPhase5DeviceSubPhase3
NTSTATUS
ACPIDevicePowerProcessPhase5DeviceSubPhase4(
IN PACPI_POWER_REQUEST PowerRequest
)
/*++
Routine Description:
This routine runs the _STA control method
Note: that we only come down this path if we are transitioning to D0
Arguments:
PowerRequest - The current request structure that we must process
Return Value:
NTSTATUS
If we ignore errors, then this function can only return:
STATUS_SUCCESS
STATUS_PENDING
Otherwise, it can return any STATUS_XXX code it wants
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_EXTENSION deviceExtension = PowerRequest->DeviceExtension;
POBJDATA resultData = &(PowerRequest->ResultData);
//
// The next phase is STEP_4
//
PowerRequest->NextWorkDone = WORK_DONE_STEP_4;
//
// Make sure to initialize the structure. Since we are using the
// objdata structure in request, we need to make sure that it will
// look like something that the interpreter will understand
//
RtlZeroMemory( resultData, sizeof(OBJDATA) );
//
// Get the status of the device
//
status = ACPIGetDeviceHardwarePresenceAsync(
deviceExtension,
ACPIDeviceCompleteGenericPhase,
PowerRequest,
&(resultData->uipDataValue),
&(resultData->dwDataLen)
);
ACPIDevPrint( (
ACPI_PRINT_POWER,
deviceExtension,
"(0x%08lx): ACPIDevicePowerProcessPhase5DeviceSubPhase4 "
"= 0x%08lx\n",
PowerRequest,
status
) );
if (status == STATUS_PENDING) {
return status;
}
//
// Call the completion routine ourselves
//
ACPIDeviceCompleteGenericPhase(
NULL,
status,
NULL,
PowerRequest
);
//
// Always return success
//
return STATUS_SUCCESS;
} // ACPIDevicePowerProcessPhase5DeviceSubPhase4
NTSTATUS
ACPIDevicePowerProcessPhase5DeviceSubPhase5(
IN PACPI_POWER_REQUEST PowerRequest
)
/*++
Routine Description:
This is the where we look at the device state.
Arguments:
PowerRequest - The request we are currently handling
Return Value:
NTSTATUS
--*/
{
PDEVICE_EXTENSION deviceExtension = PowerRequest->DeviceExtension;
POBJDATA resultData = &(PowerRequest->ResultData);
ACPIDevPrint( (
ACPI_PRINT_POWER,
deviceExtension,
"(0x%08lx): ACPIDevicePowerProcessPhase5DeviceSubPhase5\n",
PowerRequest
) );
//
// The next phase is STEP_5
//
PowerRequest->NextWorkDone = WORK_DONE_STEP_5;
//
// First things first --- we just ran _STA (or faked it), so we
// must check the return data
//
if (!(resultData->uipDataValue & STA_STATUS_PRESENT) ||
!(resultData->uipDataValue & STA_STATUS_WORKING_OK) ||
( !(resultData->uipDataValue & STA_STATUS_ENABLED) &&
!(deviceExtension->Flags & DEV_TYPE_FILTER) ) ) {
//
// This device is not working
//
PowerRequest->Status = STATUS_INVALID_DEVICE_STATE;
ACPIDeviceCompleteCommon(
&(PowerRequest->WorkDone),
WORK_DONE_FAILURE
);
return STATUS_SUCCESS;
}
//
// We don't clear the result or do anything on the resultData structure
// because we only used some of its storage --- the entire structure
// is not valid. However, just to be safe, we will zero everything out
//
RtlZeroMemory( resultData, sizeof(OBJDATA) );
//
// Call the completion routine by brute force. Note that at this
// point, we count everything as being SUCCESS
//
ACPIDeviceCompleteGenericPhase(
NULL,
STATUS_SUCCESS,
NULL,
PowerRequest
);
//
// Always return success
//
return STATUS_SUCCESS;
} // ACPIDevicePowerProcessPhase5DeviceSubPhase5
NTSTATUS
ACPIDevicePowerProcessPhase5DeviceSubPhase6(
IN PACPI_POWER_REQUEST PowerRequest
)
/*++
Routine Description:
This is the final routine in the device path. This routines
determines if everything is okay and updates the system book-keeping.
Arguments:
PowerRequest - The request we are currently handling
Return Value:
NTSTATUS
--*/
{
PDEVICE_OBJECT deviceObject;
PDEVICE_EXTENSION deviceExtension = PowerRequest->DeviceExtension;
POBJDATA resultData = &(PowerRequest->ResultData);
POWER_STATE state;
ACPIDevPrint( (
ACPI_PRINT_POWER,
deviceExtension,
"(0x%08lx): ACPIDevicePowerProcessPhase5DeviceSubPhase6\n",
PowerRequest
) );
//
// We need a spinlock to touch these values
//
KeAcquireSpinLockAtDpcLevel( &AcpiPowerLock );
//
// Update the current PowerState with the requested PowerState
//
deviceExtension->PowerInfo.PowerState =
deviceExtension->PowerInfo.DesiredPowerState;
//
// We also need to store the new device state so that we can notify
// the system
//
state.DeviceState = deviceExtension->PowerInfo.PowerState;
//
// Remember the device object
//
deviceObject = deviceExtension->DeviceObject;
//
// Just release the spin lock
//
KeReleaseSpinLockFromDpcLevel( &AcpiPowerLock );
//
// If this deviceExtension has an associated deviceObject, then
// we had better tell the system about which state we are in
//
if (deviceObject != NULL) {
//
// Notify the system
//
PoSetPowerState(
deviceObject,
DevicePowerState,
state
);
}
//
// Make sure that we set the current status in the PowerRequest
// to indicate what happened
//
PowerRequest->Status = STATUS_SUCCESS;
//
// We are done
//
ACPIDeviceCompleteCommon( &(PowerRequest->WorkDone), WORK_DONE_COMPLETE );
//
// Always return success
//
return STATUS_SUCCESS;
} // ACPIDevicePowerProcessPhase5DeviceSubPhase6
NTSTATUS
ACPIDevicePowerProcessPhase5SystemSubPhase1(
IN PACPI_POWER_REQUEST PowerRequest
)
/*++
Routine Description:
This routine runs the _PTS, or _WAK method
Arguments:
PowerRequest - The current request structure that we must process
Return Value:
NTSTATUS
If we ignore errors, then this function can only return:
STATUS_SUCCESS
STATUS_PENDING
Otherwise, it can return any STATUS_XXX code it wants
--*/
{
NTSTATUS status = STATUS_SUCCESS;
OBJDATA objData;
PACPI_POWER_INFO powerInfo;
PDEVICE_EXTENSION deviceExtension = PowerRequest->DeviceExtension;
PNSOBJ sleepObject = NULL;
SYSTEM_POWER_STATE systemState =
PowerRequest->u.SystemPowerRequest.SystemPowerState;
//
// The next phase is STEP_1
//
PowerRequest->NextWorkDone = WORK_DONE_STEP_1;
//
// If we are going back to the working state, then don't run any _PTS
// code
//
if (systemState != PowerSystemWorking) {
//
// First step it to initialize the objData so that we can remember
// what arguments we want to pass to the AML Interpreter
//
RtlZeroMemory( &objData, sizeof(OBJDATA) );
objData.dwDataType = OBJTYPE_INTDATA;
//
// Obtain the correct NameSpace object to run
//
sleepObject = ACPIAmliGetNamedChild(
deviceExtension->AcpiObject->pnsParent,
PACKED_PTS
);
//
// We only try to evaluate a method if we found an object
//
if (sleepObject != NULL) {
//
// Remember that AMLI doesn't use our definitions, so we will
// have to normalize the S value
//
objData.uipDataValue = ACPIDeviceMapACPIPowerState( systemState );
//
// Safely run the control method
//
status = AMLIAsyncEvalObject(
sleepObject,
NULL,
1,
&objData,
ACPIDeviceCompleteGenericPhase,
PowerRequest
);
//
// If we got STATUS_PENDING, then we cannot do any more work here.
//
if (status == STATUS_PENDING) {
return status;
}
}
}
//
// Call the completion routine
//
ACPIDeviceCompleteGenericPhase(
sleepObject,
status,
NULL,
PowerRequest
);
//
// We are successfull
//
return STATUS_SUCCESS;
} // ACPIPowerProcessPhase5SystemSubPhase1
NTSTATUS
ACPIDevicePowerProcessPhase5SystemSubPhase2(
IN PACPI_POWER_REQUEST PowerRequest
)
/*++
Routine Description:
This routine runs the _SST method
Arguments:
PowerRequest - The current request structure that we must process
Return Value:
NTSTATUS
If we ignore errors, then this function can only return:
STATUS_SUCCESS
STATUS_PENDING
Otherwise, it can return any STATUS_XXX code it wants
--*/
{
NTSTATUS status = STATUS_SUCCESS;
OBJDATA objData;
PACPI_POWER_INFO powerInfo;
PDEVICE_EXTENSION deviceExtension = PowerRequest->DeviceExtension;
PNSOBJ sstObject = NULL;
SYSTEM_POWER_STATE systemState =
PowerRequest->u.SystemPowerRequest.SystemPowerState;
//
// The next phase is STEP_2
//
PowerRequest->NextWorkDone = WORK_DONE_STEP_2;
//
// First step it to initialize the objData so that we can remember
// what arguments we want to pass to the AML Interpreter
//
RtlZeroMemory( &objData, sizeof(OBJDATA) );
objData.dwDataType = OBJTYPE_INTDATA;
//
// Obtain the correct NameSpace object to run
//
sstObject = ACPIAmliGetNamedChild(
deviceExtension->AcpiObject->pnsParent,
PACKED_SI
);
if (sstObject != NULL) {
sstObject = ACPIAmliGetNamedChild(
sstObject,
PACKED_SST
);
}
//
// We only try to evaluate a method if we found an object
//
if (sstObject != NULL) {
switch (systemState) {
case PowerSystemWorking:
objData.uipDataValue = 1;
break;
case PowerSystemHibernate:
objData.uipDataValue = 4;
break;
case PowerSystemSleeping1:
case PowerSystemSleeping2:
case PowerSystemSleeping3:
objData.uipDataValue = 3;
break;
default:
objData.uipDataValue = 0;
}
//
// Safely run the control method
//
status = AMLIAsyncEvalObject(
sstObject,
NULL,
1,
&objData,
ACPIDeviceCompleteGenericPhase,
PowerRequest
);
//
// If we got STATUS_PENDING, then we cannot do any more work here.
//
if (status == STATUS_PENDING) {
return status;
}
} else {
//
// Consider the request successfull
//
status = STATUS_SUCCESS;
}
//
// Call the completion routine
//
ACPIDeviceCompleteGenericPhase(
sstObject,
status,
NULL,
PowerRequest
);
//
// We are successfull
//
return STATUS_SUCCESS;
} // ACPIPowerProcessPhase5SystemSubPhase2
NTSTATUS
ACPIDevicePowerProcessPhase5SystemSubPhase3(
IN PACPI_POWER_REQUEST PowerRequest
)
/*++
Routine Description:
This routine will pause the interpreter if requird
Arguments:
PowerRequest - The request we are currently processing
Return Value:
NTSTATUS
--*/
{
NTSTATUS status;
PDEVICE_EXTENSION deviceExtension = PowerRequest->DeviceExtension;
SYSTEM_POWER_STATE systemState;
ACPIDevPrint( (
ACPI_PRINT_POWER,
deviceExtension,
"(0x%08lx): ACPIDevicePowerProcessPhase5SystemSubPhase3\n",
PowerRequest
) );
//
// The next phase is STEP_3
//
PowerRequest->NextWorkDone = WORK_DONE_STEP_3;
//
// Fetch the target system state
//
systemState = PowerRequest->u.SystemPowerRequest.SystemPowerState;
//
// If we are going to a system state other than S0, then we need to pause
// the interpreter. After this call completes, no one can execute a control
// method
//
if (systemState != PowerSystemWorking) {
status = AMLIPauseInterpreter(
ACPIDeviceCompleteInterpreterRequest,
PowerRequest
);
if (status == STATUS_PENDING) {
return status;
}
}
//
// Call the completion routine
//
ACPIDeviceCompleteInterpreterRequest(
PowerRequest
);
//
// We are successfull
//
return STATUS_SUCCESS;
} // ACPIDevicePowerProcessPhase5SystemSubPhase3
NTSTATUS
ACPIDevicePowerProcessPhase5SystemSubPhase4(
IN PACPI_POWER_REQUEST PowerRequest
)
/*++
Routine Description:
This is the final routine in the system path. It updates the bookkeeping
Arguments:
PowerRequest - The request we are currently processing
Return Value:
NTSTATUS
--*/
{
KIRQL oldIrql;
PDEVICE_EXTENSION deviceExtension = PowerRequest->DeviceExtension;
PDEVICE_OBJECT deviceObject;
POWER_STATE state;
SYSTEM_POWER_STATE systemState;
ACPIDevPrint( (
ACPI_PRINT_POWER,
deviceExtension,
"(0x%08lx): ACPIDevicePowerProcessPhase5SystemSubPhase4\n",
PowerRequest
) );
//
// Fetch the target system state
//
systemState = PowerRequest->u.SystemPowerRequest.SystemPowerState;
//
// Grab the spinlock
//
IoAcquireCancelSpinLock( &oldIrql );
KeAcquireSpinLockAtDpcLevel( &AcpiPowerLock );
//
// Remember this as being our most recent sleeping state
//
AcpiMostRecentSleepState = systemState;
//
// Update the Gpe Wake Bits
//
ACPIWakeRemoveDevicesAndUpdate( NULL, NULL );
//
// Fetch the associated device object
//
deviceObject = deviceExtension->DeviceObject;
//
// We are done with the lock
//
KeReleaseSpinLockFromDpcLevel( &AcpiPowerLock );
IoReleaseCancelSpinLock( oldIrql );
//
// Is there an ACPI device object?
//
if (deviceObject != NULL) {
//
// Notify the system of the new S state
//
state.SystemState = systemState;
PoSetPowerState(
deviceObject,
SystemPowerState,
state
);
}
//
// Make sure that we set the current status in the PowerRequest
// to indicate what happened.
//
PowerRequest->Status = STATUS_SUCCESS;
//
// Finally, we mark the power request has having had all of its works
// done
//
ACPIDeviceCompleteCommon( &(PowerRequest->WorkDone), WORK_DONE_COMPLETE );
return STATUS_SUCCESS;
} // ACPIDevicePowerProcessPhase5SystemSubPhase4
NTSTATUS
ACPIDevicePowerProcessPhase5WarmEjectSubPhase1(
IN PACPI_POWER_REQUEST PowerRequest
)
/*++
Routine Description:
This is the method that runs the _EJx method appropriate for this
device
Arguments:
PowerRequest - The request we are currently processing
Return Value:
NTSTATUS
--*/
{
NTSTATUS status = STATUS_SUCCESS;
OBJDATA objData;
PACPI_POWER_INFO powerInfo;
PDEVICE_EXTENSION deviceExtension = PowerRequest->DeviceExtension;
PNSOBJ ejectObject = NULL;
SYSTEM_POWER_STATE ejectState =
PowerRequest->u.EjectPowerRequest.EjectPowerState;
ULONG ejectNames[] = { 0, 0, PACKED_EJ1, PACKED_EJ2,
PACKED_EJ3, PACKED_EJ4, 0 };
ULONG flags;
//
// The next phase is STEP_1 if we have profile work to do, otherwise we're
// done.
//
flags = PowerRequest->u.EjectPowerRequest.Flags;
PowerRequest->NextWorkDone = (flags & DEVICE_REQUEST_UPDATE_HW_PROFILE) ?
WORK_DONE_STEP_1 :
WORK_DONE_COMPLETE;
//
// Obtain the correct NameSpace object to run
//
ejectObject = ACPIAmliGetNamedChild(
deviceExtension->AcpiObject,
ejectNames[ejectState]
);
//
// If we didn't find the object, then something terrible happened
// and we cannot continue
//
if (ejectObject == NULL) {
ACPIInternalError( ACPI_DEVPOWER );
}
//
// Kiss the device goodbye
//
status = ACPIGetNothingEvalIntegerAsync(
deviceExtension,
ejectNames[ejectState],
1,
ACPIDeviceCompleteGenericPhase,
PowerRequest
);
ACPIDevPrint( (
ACPI_PRINT_POWER,
deviceExtension,
"(%0x%08lx) : ACPIDevicePowerProcessPhase5WarmEjectSubPhase1 = %08lx\n",
PowerRequest,
status
) );
if (status == STATUS_PENDING) {
return status;
}
//
// Call the completion routine by brute force. Note that at this
// point, we count everything as being SUCCESS
//
ACPIDeviceCompleteGenericPhase(
ejectObject,
status,
NULL,
PowerRequest
);
return STATUS_SUCCESS;
}
NTSTATUS
ACPIDevicePowerProcessPhase5WarmEjectSubPhase2(
IN PACPI_POWER_REQUEST PowerRequest
)
/*++
Routine Description:
This is the method that runs the _DCK method appropriate for this
device
Arguments:
PowerRequest - The request we are currently processing
Return Value:
NTSTATUS
--*/
{
NTSTATUS status = STATUS_SUCCESS;
OBJDATA objData;
PACPI_POWER_INFO powerInfo;
PDEVICE_EXTENSION deviceExtension = PowerRequest->DeviceExtension;
PDEVICE_EXTENSION dockExtension;
PNSOBJ dckObject = NULL;
//
// The next phase is WORK_DONE_COMPLETE
//
PowerRequest->NextWorkDone = WORK_DONE_COMPLETE;
//
// Obtain the correct NameSpace object to run
//
dckObject = ACPIAmliGetNamedChild(
deviceExtension->AcpiObject,
PACKED_DCK
);
//
// We might not find the _DCK method if this isn't a dock.
//
if (dckObject != NULL) {
dockExtension = ACPIDockFindCorrespondingDock( deviceExtension );
if (dockExtension &&
(dockExtension->Dock.IsolationState == IS_ISOLATION_DROPPED)) {
//
// Kiss the dock connect goodbye. Note that we don't even care
// about the return value because of the spec says that if it
// is called with 0, it should be ignored
//
dockExtension->Dock.IsolationState = IS_ISOLATED;
KdDisableDebugger();
status = ACPIGetNothingEvalIntegerAsync(
deviceExtension,
PACKED_DCK,
0,
ACPIDeviceCompleteGenericPhase,
PowerRequest
);
KdEnableDebugger();
ACPIDevPrint( (
ACPI_PRINT_POWER,
deviceExtension,
"(%0x%08lx) : ACPIDevicePowerProcessPhase5WarmEjectSubPhase2 = %08lx\n",
PowerRequest,
status
) );
if (status == STATUS_PENDING) {
return status;
}
}
}
//
// Call the completion routine by brute force. Note that at this
// point, we count everything as being SUCCESS
//
ACPIDeviceCompleteGenericPhase(
dckObject,
status,
NULL,
PowerRequest
);
return STATUS_SUCCESS;
}
NTSTATUS
ACPIDevicePowerProcessSynchronizeList(
IN PLIST_ENTRY ListEntry
)
/*++
Routine Description:
This routine completes all of the synchronize requests...
Arguments:
ListEntry - The list we are currently walking
Return Value:
NTSTATUS
- If any request is not marked as being complete, then STATUS_PENDING
is returned, otherwise, STATUS_SUCCESS is returned
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PACPI_POWER_REQUEST powerRequest;
PLIST_ENTRY currentEntry = ListEntry->Flink;
PLIST_ENTRY tempEntry;
//
// Look at all the items in the list
//
while (currentEntry != ListEntry) {
//
// Turn this into a device request
//
powerRequest = CONTAINING_RECORD(
currentEntry,
ACPI_POWER_REQUEST,
ListEntry
);
//
// Set the temporary pointer to the next element
//
tempEntry = currentEntry->Flink;
//
// We are done with the request
//
ACPIDeviceCompleteRequest(
powerRequest
);
//
// Grab the next entry
//
currentEntry = tempEntry;
}
//
// Have we completed all of our work?
//
return (STATUS_SUCCESS);
} // ACPIDevicePowerProcessSynchronizeList