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