1870 lines
43 KiB
C
1870 lines
43 KiB
C
/*++
|
||
|
||
Copyright (c) 1997 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
wake.c
|
||
|
||
Abstract:
|
||
|
||
Handles wake code for the entire ACPI subsystem
|
||
|
||
Author:
|
||
|
||
splante (splante)
|
||
|
||
Environment:
|
||
|
||
Kernel mode only.
|
||
|
||
Revision History:
|
||
|
||
06-18-97: Initial Revision
|
||
11-24-97: Rewrite
|
||
|
||
--*/
|
||
|
||
#include "pch.h"
|
||
#pragma hdrstop
|
||
#define INITGUID
|
||
#include <initguid.h>
|
||
#include <pciintrf.h>
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE,ACPIWakeEnableDisableSync)
|
||
#endif
|
||
|
||
//
|
||
// This request is used by the synchronous mechanism when it calls the
|
||
// asynchronous one
|
||
//
|
||
typedef struct _ACPI_WAKE_PSW_SYNC_CONTEXT {
|
||
KEVENT Event;
|
||
NTSTATUS Status;
|
||
} ACPI_WAKE_PSW_SYNC_CONTEXT, *PACPI_WAKE_PSW_SYNC_CONTEXT;
|
||
|
||
//
|
||
// This is a lookaside list of contexts
|
||
//
|
||
NPAGED_LOOKASIDE_LIST PswContextLookAsideList;
|
||
|
||
//
|
||
// Pointer to the PCI PME interface, which we will need (maybe)
|
||
//
|
||
PPCI_PME_INTERFACE PciPmeInterface;
|
||
|
||
//
|
||
// Have we loaded the PCI PME Interface?
|
||
//
|
||
BOOLEAN PciPmeInterfaceInstantiated;
|
||
|
||
//
|
||
// We need to access this piece of data here
|
||
//
|
||
extern PACPIInformation AcpiInformation;
|
||
|
||
VOID
|
||
ACPIWakeCompleteRequestQueue(
|
||
IN PLIST_ENTRY RequestList,
|
||
IN NTSTATUS Status
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine takes a LIST_ENTRY of requests to be completed and completes
|
||
all of them. This is to minimize code duplication.
|
||
|
||
Arguments:
|
||
|
||
RequestList - List Entry to process
|
||
Status - Status to complete the requests with
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
PLIST_ENTRY listEntry;
|
||
PACPI_POWER_REQUEST powerRequest;
|
||
|
||
//
|
||
// walk the list
|
||
//
|
||
listEntry = RequestList->Flink;
|
||
while (listEntry != RequestList) {
|
||
|
||
//
|
||
// Crack the request
|
||
//
|
||
powerRequest = CONTAINING_RECORD(
|
||
listEntry,
|
||
ACPI_POWER_REQUEST,
|
||
ListEntry
|
||
);
|
||
listEntry = listEntry->Flink;
|
||
|
||
//
|
||
// Complete this power request
|
||
//
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_WAKE,
|
||
powerRequest->DeviceExtension,
|
||
"ACPIWakeCompleteRequestQueue - Completing 0x%08lx - %08lx\n",
|
||
powerRequest,
|
||
Status
|
||
) );
|
||
powerRequest->Status = Status;
|
||
ACPIDeviceIrpWaitWakeRequestComplete( powerRequest );
|
||
|
||
}
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
ACPIWakeDisableAsync(
|
||
IN PDEVICE_EXTENSION DeviceExtension,
|
||
IN PLIST_ENTRY RequestList,
|
||
IN PFNACB CallBack,
|
||
IN PVOID Context
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine decrements the number of outstanding wake events on the
|
||
supplied DeviceExtension by the number of items in the request list.
|
||
If the reference goes to 0, then we run _PSW(Off) to disable wake support
|
||
on the device
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - Device for which we to deference the wake count
|
||
RequestList - The list of requests, for which the ref count will
|
||
be decreased
|
||
CallBack - Function to call when we are done
|
||
Context - Argument to the function
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
BOOLEAN runPsw = FALSE;
|
||
KIRQL oldIrql;
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
OBJDATA pswData;
|
||
PACPI_WAKE_PSW_CONTEXT pswContext;
|
||
PLIST_ENTRY listEntry = RequestList->Flink;
|
||
PNSOBJ pswObject = NULL;
|
||
ULONG count = 0;
|
||
|
||
//
|
||
// Walk the list, counting the number of items within it
|
||
//
|
||
while (listEntry != RequestList) {
|
||
|
||
count++;
|
||
listEntry = listEntry->Flink;
|
||
|
||
}
|
||
|
||
//
|
||
// Grab the spinlock
|
||
//
|
||
KeAcquireSpinLock( &AcpiPowerLock, &oldIrql );
|
||
|
||
//
|
||
// Let the world know what happened
|
||
//
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_WAKE,
|
||
DeviceExtension,
|
||
"ACPIWakeDisableAsync - ReferenceCount: %lx - %lx = %lx\n",
|
||
DeviceExtension->PowerInfo.WakeSupportCount,
|
||
count,
|
||
(DeviceExtension->PowerInfo.WakeSupportCount - count)
|
||
) );
|
||
|
||
//
|
||
// Update the number of references on the device
|
||
//
|
||
ASSERT( DeviceExtension->PowerInfo.WakeSupportCount <= count );
|
||
DeviceExtension->PowerInfo.WakeSupportCount -= count;
|
||
|
||
//
|
||
// Grab the pswObject
|
||
//
|
||
pswObject = DeviceExtension->PowerInfo.PowerObject[PowerDeviceUnspecified];
|
||
if (pswObject == NULL) {
|
||
|
||
goto ACPIWakeDisableAsyncExit;
|
||
|
||
}
|
||
|
||
//
|
||
// Are there no references left on the device?
|
||
//
|
||
if (DeviceExtension->PowerInfo.WakeSupportCount != 0) {
|
||
|
||
//
|
||
// If we own the PME pin for this device, then make sure that
|
||
// we clear the status pin and keep the PME signal enabled
|
||
//
|
||
if (DeviceExtension->Flags & DEV_PROP_HAS_PME ) {
|
||
|
||
ACPIWakeEnableDisablePciDevice(
|
||
DeviceExtension,
|
||
TRUE
|
||
);
|
||
|
||
}
|
||
goto ACPIWakeDisableAsyncExit;
|
||
|
||
}
|
||
|
||
//
|
||
// Allocate the _PSW context that we need to signify that there is
|
||
// a pending _PSW on this device
|
||
//
|
||
pswContext = ExAllocateFromNPagedLookasideList(
|
||
&PswContextLookAsideList
|
||
);
|
||
if (pswContext == NULL) {
|
||
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto ACPIWakeDisableAsyncExit;
|
||
|
||
}
|
||
|
||
//
|
||
// Initialize the context
|
||
//
|
||
pswContext->Enable = FALSE;
|
||
pswContext->CallBack = CallBack;
|
||
pswContext->Context = Context;
|
||
pswContext->DeviceExtension = DeviceExtension;
|
||
pswContext->Count = count;
|
||
|
||
//
|
||
// Check to see if we are simply going to queue the context up, or
|
||
// call the interpreter
|
||
//
|
||
if (IsListEmpty( &(DeviceExtension->PowerInfo.WakeSupportList) ) ) {
|
||
|
||
runPsw = TRUE;
|
||
|
||
}
|
||
|
||
//
|
||
// List is non-empty, so we just queue up the context
|
||
//
|
||
InsertTailList(
|
||
&(DeviceExtension->PowerInfo.WakeSupportList),
|
||
&(pswContext->ListEntry)
|
||
);
|
||
|
||
//
|
||
// Release the lock
|
||
//
|
||
KeReleaseSpinLock( &AcpiPowerLock, oldIrql );
|
||
|
||
//
|
||
// Should we run the method?
|
||
//
|
||
if (runPsw) {
|
||
|
||
//
|
||
// If we own the PCI PME pin for this device, the make sure to clear the
|
||
// status and disable it --- we enable the PME pin after we have
|
||
// turned on the _PSW, and we disable the PME pin before we turn off
|
||
// the _PSW
|
||
//
|
||
if ( (DeviceExtension->Flags & DEV_PROP_HAS_PME)) {
|
||
|
||
ACPIWakeEnableDisablePciDevice(
|
||
DeviceExtension,
|
||
FALSE
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// Initialize the arguments
|
||
//
|
||
RtlZeroMemory( &pswData, sizeof(OBJDATA) );
|
||
pswData.dwDataType = OBJTYPE_INTDATA;
|
||
pswData.uipDataValue = 0;
|
||
|
||
//
|
||
// Run the control method
|
||
//
|
||
status = AMLIAsyncEvalObject(
|
||
pswObject,
|
||
NULL,
|
||
1,
|
||
&pswData,
|
||
ACPIWakeEnableDisableAsyncCallBack,
|
||
pswContext
|
||
);
|
||
|
||
//
|
||
// What Happened
|
||
//
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_WAKE,
|
||
DeviceExtension,
|
||
"ACPIWakeDisableAsync = 0x%08lx (P)\n",
|
||
status
|
||
) );
|
||
|
||
|
||
if (status != STATUS_PENDING) {
|
||
|
||
ACPIWakeEnableDisableAsyncCallBack(
|
||
pswObject,
|
||
status,
|
||
NULL,
|
||
pswContext
|
||
);
|
||
|
||
}
|
||
return STATUS_PENDING;
|
||
|
||
} else {
|
||
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_WAKE,
|
||
DeviceExtension,
|
||
"ACPIWakeEnableDisableAsync = 0x%08lx (Q)\n",
|
||
STATUS_PENDING
|
||
) );
|
||
|
||
//
|
||
// we queued the request up, so we must return pending
|
||
//
|
||
return STATUS_PENDING;
|
||
|
||
}
|
||
|
||
ACPIWakeDisableAsyncExit:
|
||
|
||
//
|
||
// Release the lock
|
||
//
|
||
KeReleaseSpinLock( &AcpiPowerLock, oldIrql );
|
||
|
||
//
|
||
// What happened
|
||
//
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_WAKE,
|
||
DeviceExtension,
|
||
"ACPIWakeDisableAsync = 0x%08lx\n",
|
||
status
|
||
) );
|
||
|
||
//
|
||
// Call the specified callback ourselves
|
||
//
|
||
(*CallBack)(
|
||
pswObject,
|
||
status,
|
||
NULL,
|
||
Context
|
||
);
|
||
return STATUS_PENDING;
|
||
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
ACPIWakeEmptyRequestQueue(
|
||
IN PDEVICE_EXTENSION DeviceExtension
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine looks at the current list of Wake Request irps and
|
||
completes the ones that are waiting on the specified device
|
||
|
||
Note: this code assumes that if we clear the irps out but we don't
|
||
run _PSW(O), that nothing bad will happen if that GPE fires
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - Device for which we want no wake requests
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
KIRQL oldIrql;
|
||
LIST_ENTRY powerList;
|
||
|
||
//
|
||
// We will store the list of matching requests onto this list, so we
|
||
// must initialize it
|
||
//
|
||
InitializeListHead( &powerList );
|
||
|
||
//
|
||
// We need to hold both the Cancel and the Power lock while we remove
|
||
// things from the PowerQueue list
|
||
//
|
||
IoAcquireCancelSpinLock( &oldIrql );
|
||
KeAcquireSpinLockAtDpcLevel( &AcpiPowerLock );
|
||
ACPIWakeRemoveDevicesAndUpdate( DeviceExtension, &powerList );
|
||
KeReleaseSpinLockFromDpcLevel( &AcpiPowerLock );
|
||
IoReleaseCancelSpinLock( oldIrql );
|
||
|
||
//
|
||
// Complete the requests
|
||
//
|
||
ACPIWakeCompleteRequestQueue( &powerList, STATUS_NO_SUCH_DEVICE );
|
||
|
||
//
|
||
// Done
|
||
//
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
ACPIWakeEnableDisableAsync(
|
||
IN PDEVICE_EXTENSION DeviceExtension,
|
||
IN BOOLEAN Enable,
|
||
IN PFNACB CallBack,
|
||
IN PVOID Context
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Given a Device Extension, updates the count of outstanding PSW on the
|
||
device. If there is a 0-1 transition, then we must run _PSW(1). If there
|
||
is a 1-0 transition, then we must run _PSW(0)
|
||
|
||
NB: The CallBack will always be invoked
|
||
|
||
Arguments:
|
||
|
||
|
||
DeviceExtension - Object to look at
|
||
Enable - Increment or Decrement
|
||
CallBack - Function to run after running _PSW()
|
||
Context - Argument to pass to _PSW
|
||
|
||
Return Value:
|
||
|
||
Status
|
||
|
||
--*/
|
||
{
|
||
BOOLEAN runPsw = FALSE;
|
||
KIRQL oldIrql;
|
||
OBJDATA pswData;
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
PACPI_WAKE_PSW_CONTEXT pswContext;
|
||
PNSOBJ pswObject = NULL;
|
||
|
||
//
|
||
// Acquire the Spinlock
|
||
//
|
||
KeAcquireSpinLock( &AcpiPowerLock, &oldIrql );
|
||
|
||
//
|
||
// Update the number of references on the device
|
||
//
|
||
if (Enable) {
|
||
|
||
DeviceExtension->PowerInfo.WakeSupportCount++;
|
||
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_WAKE,
|
||
DeviceExtension,
|
||
"ACPIWakeEnableDisableAsync - Count: %d (+)\n",
|
||
DeviceExtension->PowerInfo.WakeSupportCount
|
||
) );
|
||
|
||
//
|
||
// Did we transition to one wake?
|
||
//
|
||
if (DeviceExtension->PowerInfo.WakeSupportCount != 1) {
|
||
|
||
//
|
||
// If we own the PME pin for this device, then make sure that
|
||
// we clear the status pin and keep the PME signal enabled
|
||
//
|
||
if (DeviceExtension->Flags & DEV_PROP_HAS_PME ) {
|
||
|
||
ACPIWakeEnableDisablePciDevice(
|
||
DeviceExtension,
|
||
TRUE
|
||
);
|
||
|
||
}
|
||
goto ACPIWakeEnableDisableAsyncExit;
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
ASSERT( DeviceExtension->PowerInfo.WakeSupportCount );
|
||
DeviceExtension->PowerInfo.WakeSupportCount--;
|
||
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_WAKE,
|
||
DeviceExtension,
|
||
"ACPIWakeEnableDisableAsync - Count: %d (-)\n",
|
||
DeviceExtension->PowerInfo.WakeSupportCount
|
||
) );
|
||
|
||
//
|
||
// Did we transition to zero wake?
|
||
//
|
||
if (DeviceExtension->PowerInfo.WakeSupportCount != 0) {
|
||
|
||
//
|
||
// If we own the PME pin for this device, then make sure that
|
||
// we clear the status pin and keep the PME signal enabled
|
||
//
|
||
if (DeviceExtension->Flags & DEV_PROP_HAS_PME ) {
|
||
|
||
ACPIWakeEnableDisablePciDevice(
|
||
DeviceExtension,
|
||
TRUE
|
||
);
|
||
|
||
}
|
||
goto ACPIWakeEnableDisableAsyncExit;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Grab the pswObject
|
||
//
|
||
pswObject = DeviceExtension->PowerInfo.PowerObject[PowerDeviceUnspecified];
|
||
if (pswObject == NULL) {
|
||
|
||
//
|
||
// If we got here, that means that there isn't a _PSW to be run and
|
||
// that we should make sure that if we own the PME pin, that we should
|
||
// set it.
|
||
//
|
||
if (DeviceExtension->Flags & DEV_PROP_HAS_PME) {
|
||
|
||
ACPIWakeEnableDisablePciDevice(
|
||
DeviceExtension,
|
||
TRUE
|
||
);
|
||
|
||
}
|
||
goto ACPIWakeEnableDisableAsyncExit;
|
||
|
||
}
|
||
|
||
//
|
||
// Allocate the _PSW context that we need to signify that there is
|
||
// a pending _PSW on this device
|
||
//
|
||
pswContext = ExAllocateFromNPagedLookasideList(
|
||
&PswContextLookAsideList
|
||
);
|
||
if (pswContext == NULL) {
|
||
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto ACPIWakeEnableDisableAsyncExit;
|
||
|
||
}
|
||
|
||
//
|
||
// Initialize the context
|
||
//
|
||
pswContext->Enable = Enable;
|
||
pswContext->CallBack = CallBack;
|
||
pswContext->Context = Context;
|
||
pswContext->DeviceExtension = DeviceExtension;
|
||
pswContext->Count = 1;
|
||
|
||
//
|
||
// Check to see if we are simply going to queue the context up, or
|
||
// call the interpreter
|
||
//
|
||
if (IsListEmpty( &(DeviceExtension->PowerInfo.WakeSupportList) ) ) {
|
||
|
||
runPsw = TRUE;
|
||
|
||
}
|
||
|
||
//
|
||
// List is non-empty, so we just queue up the context
|
||
//
|
||
InsertTailList(
|
||
&(DeviceExtension->PowerInfo.WakeSupportList),
|
||
&(pswContext->ListEntry)
|
||
);
|
||
|
||
//
|
||
// Release the lock
|
||
//
|
||
KeReleaseSpinLock( &AcpiPowerLock, oldIrql );
|
||
|
||
//
|
||
// Should we run the method?
|
||
//
|
||
if (runPsw) {
|
||
|
||
//
|
||
// If we own the PCI PME pin for this device, the make sure to clear the
|
||
// status and disable it --- we enable the PME pin after we have
|
||
// turned on the _PSW, and we disable the PME pin before we turn off
|
||
// the _PSW
|
||
//
|
||
if ( (DeviceExtension->Flags & DEV_PROP_HAS_PME) &&
|
||
pswContext->Enable == FALSE) {
|
||
|
||
ACPIWakeEnableDisablePciDevice(
|
||
DeviceExtension,
|
||
FALSE
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// Initialize the arguments
|
||
//
|
||
RtlZeroMemory( &pswData, sizeof(OBJDATA) );
|
||
pswData.dwDataType = OBJTYPE_INTDATA;
|
||
pswData.uipDataValue = (Enable ? 1 : 0);
|
||
|
||
//
|
||
// Run the control method
|
||
//
|
||
status = AMLIAsyncEvalObject(
|
||
pswObject,
|
||
NULL,
|
||
1,
|
||
&pswData,
|
||
ACPIWakeEnableDisableAsyncCallBack,
|
||
pswContext
|
||
);
|
||
|
||
//
|
||
// What Happened
|
||
//
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_WAKE,
|
||
DeviceExtension,
|
||
"ACPIWakeEnableDisableAsync = 0x%08lx (P)\n",
|
||
status
|
||
) );
|
||
|
||
if (status != STATUS_PENDING) {
|
||
|
||
ACPIWakeEnableDisableAsyncCallBack(
|
||
pswObject,
|
||
status,
|
||
NULL,
|
||
pswContext
|
||
);
|
||
|
||
}
|
||
return STATUS_PENDING;
|
||
|
||
} else {
|
||
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_WAKE,
|
||
DeviceExtension,
|
||
"ACPIWakeEnableDisableAsync = 0x%08lx (Q)\n",
|
||
STATUS_PENDING
|
||
) );
|
||
|
||
//
|
||
// we queued the request up, so we must return pending
|
||
//
|
||
return STATUS_PENDING;
|
||
|
||
}
|
||
|
||
ACPIWakeEnableDisableAsyncExit:
|
||
|
||
//
|
||
// Release the lock
|
||
//
|
||
KeReleaseSpinLock( &AcpiPowerLock, oldIrql );
|
||
|
||
//
|
||
// What happened
|
||
//
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_WAKE,
|
||
DeviceExtension,
|
||
"ACPIWakeEnableDisableAsync = 0x%08lx\n",
|
||
status
|
||
) );
|
||
|
||
//
|
||
// Call the specified callback ourselves
|
||
//
|
||
(*CallBack)(
|
||
pswObject,
|
||
status,
|
||
NULL,
|
||
Context
|
||
);
|
||
return STATUS_PENDING;
|
||
|
||
}
|
||
|
||
VOID
|
||
EXPORT
|
||
ACPIWakeEnableDisableAsyncCallBack(
|
||
IN PNSOBJ AcpiObject,
|
||
IN NTSTATUS Status,
|
||
IN POBJDATA ObjData,
|
||
IN PVOID Context
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called after a _PSW method has been run on a device.
|
||
|
||
This routine is responsible for seeing if there are any more delayed
|
||
_PSW requests on the same device, and if so, run them.
|
||
|
||
Arguments:
|
||
|
||
AcpiObject - The method object that was run
|
||
Status - The result of the eval
|
||
ObjData - Not used
|
||
Context - PACPI_WAKE_PSW_CONTEXT
|
||
|
||
Return value:
|
||
|
||
VOID
|
||
|
||
--*/
|
||
{
|
||
BOOLEAN runPsw = FALSE;
|
||
KIRQL oldIrql;
|
||
PACPI_WAKE_PSW_CONTEXT pswContext = (PACPI_WAKE_PSW_CONTEXT) Context;
|
||
PACPI_WAKE_PSW_CONTEXT nextContext;
|
||
PDEVICE_EXTENSION deviceExtension = pswContext->DeviceExtension;
|
||
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_WAKE,
|
||
deviceExtension,
|
||
"ACPIWakeEnableDisableAsyncCallBack = %08lx (C)\n",
|
||
Status
|
||
) );
|
||
|
||
//
|
||
// Acquire the spinlock
|
||
//
|
||
KeAcquireSpinLock( &AcpiPowerLock, &oldIrql );
|
||
|
||
//
|
||
// Remove the specified entry from the list
|
||
//
|
||
RemoveEntryList( &(pswContext->ListEntry) );
|
||
|
||
//
|
||
// If we failed the request, then we don't really know the status of the
|
||
// _PSW on the device. Lets assume that it doesn't change and undo
|
||
// whatever change we did to get here
|
||
//
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_WAKE,
|
||
deviceExtension,
|
||
"ACPIWakeEnableDisableAsyncCallBack - RefCount: %lx %s %lx = %lx\n",
|
||
deviceExtension->PowerInfo.WakeSupportCount,
|
||
(pswContext->Enable ? "-" : "+"),
|
||
pswContext->Count,
|
||
(pswContext->Enable ? deviceExtension->PowerInfo.WakeSupportCount -
|
||
pswContext->Count : deviceExtension->PowerInfo.WakeSupportCount +
|
||
pswContext->Count)
|
||
) );
|
||
|
||
|
||
if (pswContext->Enable) {
|
||
|
||
deviceExtension->PowerInfo.WakeSupportCount -= pswContext->Count;
|
||
|
||
} else {
|
||
|
||
deviceExtension->PowerInfo.WakeSupportCount += pswContext->Count;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// If we own the PCI PME pin for this device, the make sure to clear the
|
||
// status and either enable it --- we enable the PME pin after we have
|
||
// turned on the _PSW, and we disable the PME pin before we turn off
|
||
// the _PSW
|
||
//
|
||
if ( (deviceExtension->Flags & DEV_PROP_HAS_PME) &&
|
||
pswContext->Enable == TRUE) {
|
||
|
||
ACPIWakeEnableDisablePciDevice(
|
||
deviceExtension,
|
||
pswContext->Enable
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// Are the any items on the list?
|
||
//
|
||
if (!IsListEmpty( &(deviceExtension->PowerInfo.WakeSupportList) ) ) {
|
||
|
||
runPsw = TRUE;
|
||
nextContext = CONTAINING_RECORD(
|
||
deviceExtension->PowerInfo.WakeSupportList.Flink,
|
||
ACPI_WAKE_PSW_CONTEXT,
|
||
ListEntry
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// We can release the lock now
|
||
//
|
||
KeReleaseSpinLock( &AcpiPowerLock, oldIrql );
|
||
|
||
//
|
||
// Call the callback on the completed item
|
||
//
|
||
(*pswContext->CallBack)(
|
||
AcpiObject,
|
||
Status,
|
||
ObjData,
|
||
(pswContext->Context)
|
||
);
|
||
|
||
//
|
||
// Free the completed context
|
||
//
|
||
ExFreeToNPagedLookasideList(
|
||
&PswContextLookAsideList,
|
||
pswContext
|
||
);
|
||
|
||
//
|
||
// Do we have to run a method?
|
||
//
|
||
if (runPsw) {
|
||
|
||
NTSTATUS status;
|
||
OBJDATA pswData;
|
||
|
||
RtlZeroMemory( &pswData, sizeof(OBJDATA) );
|
||
pswData.dwDataType = OBJTYPE_INTDATA;
|
||
pswData.uipDataValue = (nextContext->Enable ? 1 : 0);
|
||
|
||
//
|
||
// If we own the PCI PME pin for this device, the make sure to clear the
|
||
// status and disable it --- we enable the PME pin after we have
|
||
// turned on the _PSW, and we disable the PME pin before we turn off
|
||
// the _PSW
|
||
//
|
||
if ( (deviceExtension->Flags & DEV_PROP_HAS_PME) &&
|
||
nextContext->Enable == FALSE) {
|
||
|
||
ACPIWakeEnableDisablePciDevice(
|
||
deviceExtension,
|
||
FALSE
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// Call the interpreter
|
||
//
|
||
status = AMLIAsyncEvalObject(
|
||
AcpiObject,
|
||
NULL,
|
||
1,
|
||
&pswData,
|
||
ACPIWakeEnableDisableAsyncCallBack,
|
||
nextContext
|
||
);
|
||
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_WAKE,
|
||
nextContext->DeviceExtension,
|
||
"ACPIWakeEnableDisableAsyncCallBack = 0x%08lx (M)\n",
|
||
status
|
||
) );
|
||
|
||
if (status != STATUS_PENDING) {
|
||
|
||
//
|
||
// Ugh - Recursive
|
||
//
|
||
ACPIWakeEnableDisableAsyncCallBack(
|
||
AcpiObject,
|
||
status,
|
||
NULL,
|
||
nextContext
|
||
);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
VOID
|
||
ACPIWakeEnableDisablePciDevice(
|
||
IN PDEVICE_EXTENSION DeviceExtension,
|
||
IN BOOLEAN Enable
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is what is actually called to enable or disable the
|
||
PCI PME pin for a device
|
||
|
||
N.B. The AcpiPowerLock must be owned
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - The device extension that is a filter on top of the
|
||
pdo from the PCI device
|
||
Enable - True to enable PME, false otherwise
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
KIRQL oldIrql;
|
||
|
||
|
||
//
|
||
// Is there an interface present?
|
||
//
|
||
if (!PciPmeInterfaceInstantiated) {
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
//
|
||
// Prevent the device from going away while we make this call
|
||
//
|
||
KeAcquireSpinLock( &AcpiDeviceTreeLock, &oldIrql );
|
||
|
||
//
|
||
// Check to see if there is a device object...
|
||
//
|
||
if (!DeviceExtension->PhysicalDeviceObject) {
|
||
|
||
KeReleaseSpinLock( &AcpiDeviceTreeLock, oldIrql );
|
||
return;
|
||
|
||
}
|
||
|
||
PciPmeInterface->UpdateEnable(
|
||
DeviceExtension->PhysicalDeviceObject,
|
||
Enable
|
||
);
|
||
|
||
//
|
||
// Done with the lock
|
||
//
|
||
KeReleaseSpinLock( &AcpiDeviceTreeLock, oldIrql );
|
||
}
|
||
|
||
NTSTATUS
|
||
ACPIWakeEnableDisableSync(
|
||
IN PDEVICE_EXTENSION DeviceExtension,
|
||
IN BOOLEAN Enable
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Given a DeviceExtension, enables or disables the device wake support
|
||
from the device
|
||
|
||
NB: This routine can only be called at passive level
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - The device we care about
|
||
Enable - True if we are to enable, false otherwise
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
ACPI_WAKE_PSW_SYNC_CONTEXT syncContext;
|
||
NTSTATUS status;
|
||
|
||
PAGED_CODE();
|
||
|
||
ASSERT( DeviceExtension != NULL &&
|
||
DeviceExtension->Signature == ACPI_SIGNATURE );
|
||
|
||
//
|
||
// Initialize the event
|
||
//
|
||
KeInitializeEvent( &syncContext.Event, NotificationEvent, FALSE );
|
||
|
||
//
|
||
// Call the async procedure
|
||
//
|
||
status = ACPIWakeEnableDisableAsync(
|
||
DeviceExtension,
|
||
Enable,
|
||
ACPIWakeEnableDisableSyncCallBack,
|
||
&syncContext
|
||
);
|
||
if (status == STATUS_PENDING) {
|
||
|
||
KeWaitForSingleObject(
|
||
&syncContext.Event,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL
|
||
);
|
||
status = syncContext.Status;
|
||
|
||
}
|
||
|
||
//
|
||
// Done
|
||
//
|
||
return status;
|
||
}
|
||
|
||
VOID
|
||
EXPORT
|
||
ACPIWakeEnableDisableSyncCallBack(
|
||
IN PNSOBJ AcpiObject,
|
||
IN NTSTATUS Status,
|
||
IN POBJDATA ObjData,
|
||
IN PVOID Context
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The Async part of the EnableDisable request has been completed
|
||
|
||
Arguments:
|
||
|
||
AcpiObject - The object that was executed
|
||
Status - The result of the operation
|
||
ObjData - Not used
|
||
Context - ACPI_WAKE_PSW_SYNC_CONTEXT
|
||
|
||
Return Value:
|
||
|
||
VOID
|
||
|
||
--*/
|
||
{
|
||
PACPI_WAKE_PSW_SYNC_CONTEXT pswContext = (PACPI_WAKE_PSW_SYNC_CONTEXT) Context;
|
||
|
||
//
|
||
// Set the real status
|
||
//
|
||
pswContext->Status = Status;
|
||
|
||
//
|
||
// Set the event
|
||
//
|
||
KeSetEvent( &(pswContext->Event), IO_NO_INCREMENT, FALSE );
|
||
}
|
||
|
||
VOID
|
||
ACPIWakeEnableWakeEvents(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called just before the system is put to sleep.
|
||
|
||
The purpose of this routine is re-allow all wake and run-time events
|
||
in the GpeCurEnable to be correctly set. After the machine wakes up,
|
||
the machine will check that register to see if any events triggered the
|
||
wakeup
|
||
|
||
NB: This routine is called with interrupts off.
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
KIRQL oldIrql;
|
||
ULONG gpeRegister = 0;
|
||
|
||
//
|
||
// This function is called when interrupts are disabled, so in theory,
|
||
// all the following should be safe. However, better safe than sorry.
|
||
//
|
||
KeAcquireSpinLock( &GpeTableLock, &oldIrql );
|
||
|
||
//
|
||
// Remember that on the way back up, we will entering the S0 state
|
||
//
|
||
AcpiPowerLeavingS0 = FALSE;
|
||
|
||
//
|
||
// Update all the registers
|
||
//
|
||
for (gpeRegister = 0; gpeRegister < AcpiInformation->GpeSize; gpeRegister++) {
|
||
|
||
//
|
||
// In any case, make sure that our current enable mask includes all
|
||
// the wake registers, but doesn't include any of the pending
|
||
// events
|
||
//
|
||
GpeCurEnable[gpeRegister] |= (GpeWakeEnable[gpeRegister] &
|
||
~GpePending[gpeRegister]);
|
||
|
||
}
|
||
|
||
//
|
||
// Set the wake events only
|
||
//
|
||
ACPIGpeEnableWakeEvents();
|
||
|
||
//
|
||
// Done with the table lock
|
||
//
|
||
KeReleaseSpinLock( &GpeTableLock, oldIrql );
|
||
}
|
||
|
||
NTSTATUS
|
||
ACPIWakeInitializePciDevice(
|
||
IN PDEVICE_OBJECT DeviceObject
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called when a filter is started to determine if the PCI
|
||
device is capable of generating a PME
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - The device object to initialize
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
BOOLEAN pmeSupported;
|
||
BOOLEAN pmeStatus;
|
||
BOOLEAN pmeEnable;
|
||
KIRQL oldIrql;
|
||
PDEVICE_EXTENSION deviceExtension = ACPIInternalGetDeviceExtension(DeviceObject);
|
||
|
||
//
|
||
// We don't have to worry if the device doesn't support wake methods
|
||
// directly
|
||
//
|
||
if (!(deviceExtension->Flags & DEV_CAP_WAKE) ) {
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
//
|
||
// Need to grab the power lock to do the following
|
||
//
|
||
KeAcquireSpinLock( &AcpiPowerLock, &oldIrql );
|
||
|
||
//
|
||
// Do we have an interface to call?
|
||
//
|
||
if (PciPmeInterfaceInstantiated == FALSE) {
|
||
|
||
goto ACPIWakeInitializePciDeviceExit;
|
||
|
||
}
|
||
|
||
//
|
||
// Get the status of PME for this device
|
||
//
|
||
PciPmeInterface->GetPmeInformation(
|
||
deviceExtension->PhysicalDeviceObject,
|
||
&pmeSupported,
|
||
&pmeStatus,
|
||
&pmeEnable
|
||
);
|
||
|
||
//
|
||
// if the device supports pme, then we own it...
|
||
//
|
||
if (pmeSupported == TRUE) {
|
||
|
||
//
|
||
// We own the PME pin for this device
|
||
//
|
||
ACPIInternalUpdateFlags(
|
||
&(deviceExtension->Flags),
|
||
(DEV_PROP_HAS_PME),
|
||
FALSE
|
||
);
|
||
|
||
//
|
||
// Check to see if we should disable PME or disable the PME status
|
||
//
|
||
if (pmeEnable) {
|
||
|
||
//
|
||
// Calling this also clears the PME status pin
|
||
//
|
||
PciPmeInterface->UpdateEnable(
|
||
deviceExtension->PhysicalDeviceObject,
|
||
FALSE
|
||
);
|
||
|
||
} else if (pmeStatus) {
|
||
|
||
//
|
||
// Clear the PME status
|
||
//
|
||
PciPmeInterface->ClearPmeStatus(
|
||
deviceExtension->PhysicalDeviceObject
|
||
);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
ACPIWakeInitializePciDeviceExit:
|
||
//
|
||
// Done with lock
|
||
//
|
||
KeReleaseSpinLock( &AcpiPowerLock, oldIrql );
|
||
|
||
//
|
||
// Done
|
||
//
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
ACPIWakeInitializePmeRouting(
|
||
IN PDEVICE_OBJECT DeviceObject
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will ask the PCI driver for its PME interface
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - The ACPI PDO for a PCI root bus
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
KIRQL oldIrql;
|
||
NTSTATUS status;
|
||
IO_STACK_LOCATION irpSp;
|
||
PPCI_PME_INTERFACE interface;
|
||
PULONG dummy;
|
||
|
||
//
|
||
// Allocate some memory for the interface
|
||
//
|
||
interface = ExAllocatePoolWithTag(
|
||
NonPagedPool,
|
||
sizeof(PCI_PME_INTERFACE),
|
||
ACPI_ARBITER_POOLTAG
|
||
);
|
||
if (interface == NULL) {
|
||
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
}
|
||
|
||
//
|
||
// Initialize the stack location
|
||
//
|
||
RtlZeroMemory( &irpSp, sizeof(IO_STACK_LOCATION) );
|
||
irpSp.MajorFunction = IRP_MJ_PNP;
|
||
irpSp.MinorFunction = IRP_MN_QUERY_INTERFACE;
|
||
irpSp.Parameters.QueryInterface.InterfaceType = (LPGUID) &GUID_PCI_PME_INTERFACE;
|
||
irpSp.Parameters.QueryInterface.Version = PCI_PME_INTRF_STANDARD_VER;
|
||
irpSp.Parameters.QueryInterface.Size = sizeof (PCI_PME_INTERFACE);
|
||
irpSp.Parameters.QueryInterface.Interface = (PINTERFACE) interface;
|
||
irpSp.Parameters.QueryInterface.InterfaceSpecificData = NULL;
|
||
|
||
//
|
||
// Send the request along
|
||
//
|
||
status = ACPIInternalSendSynchronousIrp(
|
||
DeviceObject,
|
||
&irpSp,
|
||
&dummy
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
PDEVICE_EXTENSION deviceExtension = ACPIInternalGetDeviceExtension( DeviceObject );
|
||
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_CRITICAL,
|
||
deviceExtension,
|
||
" - ACPIWakeInitializePmeRouting = %08lx\n",
|
||
status
|
||
) );
|
||
|
||
//
|
||
// Free the memory and return
|
||
//
|
||
ExFreePool( interface );
|
||
return status;
|
||
|
||
}
|
||
|
||
//
|
||
// Do this under spinlock protection
|
||
//
|
||
KeAcquireSpinLock( &AcpiPowerLock, &oldIrql );
|
||
if (PciPmeInterfaceInstantiated == FALSE) {
|
||
|
||
//
|
||
// Keep a global pointer to the interface
|
||
//
|
||
PciPmeInterfaceInstantiated = TRUE;
|
||
PciPmeInterface = interface;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Someone else got here before us, so we need to make sure
|
||
// that we free the extra memory
|
||
//
|
||
ExFreePool (interface );
|
||
|
||
}
|
||
KeReleaseSpinLock( &AcpiPowerLock, oldIrql );
|
||
|
||
//
|
||
// Done
|
||
//
|
||
return status;
|
||
}
|
||
|
||
VOID
|
||
ACPIWakeRemoveDevicesAndUpdate(
|
||
IN PDEVICE_EXTENSION TargetExtension,
|
||
OUT PLIST_ENTRY ListHead
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine finds the all of the WaitWake requests associated with
|
||
TargetDevice and return them on ListHead. This is done in a 'safe' way
|
||
|
||
NB: Caller must hold the AcpiPowerLock and Cancel Lock!
|
||
|
||
Arguments:
|
||
|
||
TargetExtension - The target extension that we are looking for
|
||
ListHead - Where to store the list of matched devices
|
||
|
||
Return Value:
|
||
|
||
NONE
|
||
|
||
--*/
|
||
{
|
||
PACPI_POWER_REQUEST powerRequest;
|
||
PDEVICE_EXTENSION deviceExtension;
|
||
PLIST_ENTRY listEntry;
|
||
SYSTEM_POWER_STATE sleepState;
|
||
ULONG gpeRegister;
|
||
ULONG gpeMask;
|
||
ULONG byteIndex;
|
||
|
||
ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
|
||
|
||
//
|
||
// We need to synchronize with the ProcessGPE code because we are going
|
||
// to touch one of the GPE Masks
|
||
//
|
||
KeAcquireSpinLockAtDpcLevel( &GpeTableLock );
|
||
|
||
//
|
||
// The first step is to disable all the wake vectors
|
||
//
|
||
for (gpeRegister = 0; gpeRegister < AcpiInformation->GpeSize; gpeRegister++) {
|
||
|
||
//
|
||
// Remove the wake vectors from the real-time vectors.
|
||
// Note that since we are going to be writting the GPE Enable vector
|
||
// later on in the process, it seems pointless to actually write them
|
||
// now as well
|
||
//
|
||
GpeCurEnable[gpeRegister] &= (GpeSpecialHandler[gpeRegister] |
|
||
~(GpeWakeEnable[gpeRegister] | GpeWakeHandler[gpeRegister]));
|
||
|
||
}
|
||
|
||
//
|
||
// Next step is to reset the wake mask
|
||
//
|
||
RtlZeroMemory( GpeWakeEnable, AcpiInformation->GpeSize * sizeof(UCHAR) );
|
||
|
||
|
||
//
|
||
// Look at the first element in the wake list
|
||
//
|
||
listEntry = AcpiPowerWaitWakeList.Flink;
|
||
|
||
//
|
||
// Loop for all elements in the list
|
||
//
|
||
while (listEntry != &AcpiPowerWaitWakeList) {
|
||
|
||
//
|
||
// Grab the irp from the listEntry
|
||
//
|
||
powerRequest = CONTAINING_RECORD(
|
||
listEntry,
|
||
ACPI_POWER_REQUEST,
|
||
ListEntry
|
||
);
|
||
|
||
//
|
||
// Point to the next request
|
||
//
|
||
listEntry = listEntry->Flink;
|
||
|
||
//
|
||
// Obtain the device extension for the request
|
||
//
|
||
deviceExtension = powerRequest->DeviceExtension;
|
||
|
||
//
|
||
// If this device is to be removed, then remove it
|
||
//
|
||
if (deviceExtension == TargetExtension) {
|
||
|
||
//
|
||
// Remove the request from the list and move it to the next
|
||
// list. Mark the irp as no longer cancelable.
|
||
//
|
||
IoSetCancelRoutine( (PIRP) powerRequest->Context, NULL );
|
||
RemoveEntryList( &powerRequest->ListEntry );
|
||
InsertTailList( ListHead, &powerRequest->ListEntry );
|
||
|
||
} else {
|
||
|
||
//
|
||
// If the wake level of the bit indicates that it isn't supported
|
||
// in the current sleep state, then don't enable it... Note that
|
||
// this doesn't solve the problem where two devices share the
|
||
// same vector, one can wake the computer from S2, one from S3 and
|
||
// we are going to S3. In this case, we don't have the smarts to
|
||
// un-run the _PSW from the S2 device
|
||
//
|
||
sleepState = powerRequest->u.WaitWakeRequest.SystemPowerState;
|
||
if (sleepState < AcpiMostRecentSleepState) {
|
||
|
||
continue;
|
||
|
||
}
|
||
|
||
//
|
||
// Get the byteIndex for this GPE
|
||
//
|
||
byteIndex = ACPIGpeIndexToByteIndex(
|
||
deviceExtension->PowerInfo.WakeBit
|
||
);
|
||
|
||
//
|
||
// Drivers cannot register on wake vectors
|
||
//
|
||
if (GpeMap[byteIndex]) {
|
||
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_WAKE,
|
||
deviceExtension,
|
||
"ACPIWakeRemoveDeviceAndUpdate - %x cannot be used as a"
|
||
"wake pin.\n",
|
||
deviceExtension->PowerInfo.WakeBit
|
||
) );
|
||
continue;
|
||
|
||
}
|
||
|
||
//
|
||
// Calculate the entry and offset. Assume that the Parameter is
|
||
// at most a UCHAR
|
||
//
|
||
gpeRegister = ACPIGpeIndexToGpeRegister(
|
||
deviceExtension->PowerInfo.WakeBit
|
||
);
|
||
gpeMask = 1 << ( (UCHAR) deviceExtension->PowerInfo.WakeBit % 8);
|
||
|
||
//
|
||
// This GPE is being used as a wake event
|
||
//
|
||
if (!(GpeWakeEnable[gpeRegister] & gpeMask)) {
|
||
|
||
//
|
||
// This is a wake pin
|
||
//
|
||
GpeWakeEnable[gpeRegister] |= gpeMask;
|
||
|
||
//
|
||
// Prevent machine stupity and try to clear the Status bit
|
||
//
|
||
ACPIWriteGpeStatusRegister( gpeRegister, (UCHAR) gpeMask );
|
||
|
||
//
|
||
// Do we have a control method associated with this GPE?
|
||
//
|
||
if (!(GpeEnable[gpeRegister] & gpeMask)) {
|
||
|
||
//
|
||
// Is this GPE already enabled?
|
||
//
|
||
if (GpeCurEnable[gpeRegister] & gpeMask) {
|
||
|
||
continue;
|
||
|
||
}
|
||
|
||
//
|
||
// Not enabled -- then there is no control method for this
|
||
// GPE, consider this to be a level vector.
|
||
//
|
||
GpeIsLevel[gpeRegister] |= gpeMask;
|
||
GpeCurEnable[gpeRegister] |= gpeMask;
|
||
|
||
} else if (!(GpeSpecialHandler[gpeRegister] & gpeMask) ) {
|
||
|
||
//
|
||
// In this case, the GPE *does* have a control method
|
||
// associated with it. Remember that.
|
||
//
|
||
GpeWakeHandler[gpeRegister] |= gpeMask;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Update all the registers
|
||
//
|
||
for (gpeRegister = 0; gpeRegister < AcpiInformation->GpeSize; gpeRegister++) {
|
||
|
||
if (AcpiPowerLeavingS0) {
|
||
|
||
//
|
||
// If we are leaving S0, then make sure to remove *all* the
|
||
// wake events that we know about from the current enable mask.
|
||
// If any wake events are currently pending, that will cause us
|
||
// to continue processing them, but hopefully will not lead us
|
||
// to renable them
|
||
//
|
||
GpeCurEnable[gpeRegister] &= ~GpeWakeEnable[gpeRegister];
|
||
|
||
} else {
|
||
|
||
//
|
||
// If we are re-entering S0, then we need to renable all the wake
|
||
// events, except the ones that we are already processing
|
||
//
|
||
GpeCurEnable[gpeRegister] |= (GpeWakeEnable[gpeRegister] &
|
||
~GpePending[gpeRegister]);
|
||
|
||
}
|
||
|
||
//
|
||
// Now that we have calculate what the proper register should be,
|
||
// write it back to the hardware
|
||
//
|
||
ACPIWriteGpeEnableRegister( gpeRegister, GpeCurEnable[gpeRegister] );
|
||
|
||
}
|
||
|
||
//
|
||
// Done with the spinlock
|
||
//
|
||
KeReleaseSpinLockFromDpcLevel( &GpeTableLock );
|
||
|
||
//
|
||
// Done
|
||
//
|
||
return;
|
||
}
|
||
|
||
NTSTATUS
|
||
ACPIWakeRestoreEnables(
|
||
IN PACPI_BUILD_CALLBACK CallBack,
|
||
IN PVOID CallBackContext
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine re-runs through the list of WAIT-WAKE irps and runs the _PSW
|
||
method for each of those irps again. The reason that this is done is to
|
||
restore the state of the hardware to what the OS thinks the state is.
|
||
|
||
Arguments:
|
||
|
||
CallBack - The function to call when done
|
||
CallBackContext - The context to pass to that function
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
|
||
ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL);
|
||
|
||
//
|
||
// We need to hold the device tree lock
|
||
//
|
||
KeAcquireSpinLockAtDpcLevel( &AcpiDeviceTreeLock );
|
||
|
||
//
|
||
// Call the build routines that we have already tested and running to
|
||
// cause them to walk the device extension tree and run the appropriate
|
||
// control methods
|
||
//
|
||
status = ACPIBuildRunMethodRequest(
|
||
RootDeviceExtension,
|
||
CallBack,
|
||
CallBackContext,
|
||
PACKED_PSW,
|
||
(RUN_REQUEST_CHECK_STATUS | RUN_REQUEST_RECURSIVE |
|
||
RUN_REQUEST_CHECK_WAKE_COUNT),
|
||
TRUE
|
||
);
|
||
|
||
//
|
||
// Done with the device tree lock
|
||
//
|
||
KeReleaseSpinLockFromDpcLevel( &AcpiDeviceTreeLock );
|
||
|
||
//
|
||
// Done
|
||
//
|
||
return status;
|
||
}
|
||
|
||
VOID
|
||
ACPIWakeRestoreEnablesCompletion(
|
||
IN PDEVICE_EXTENSION DeviceExtension,
|
||
IN PVOID Context,
|
||
IN NTSTATUS Status
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called after we have finished running all the _PSWs in the
|
||
system
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - The device that just completed the enables
|
||
Context - PACPI_POWER_REQUEST
|
||
Status - What the status of the operation was
|
||
|
||
--*/
|
||
{
|
||
UNREFERENCED_PARAMETER( DeviceExtension);
|
||
|
||
//
|
||
// Restart the device power management engine
|
||
//
|
||
ACPIDeviceCompleteGenericPhase(
|
||
NULL,
|
||
Status,
|
||
NULL,
|
||
Context
|
||
);
|
||
}
|
||
|
||
NTSTATUS
|
||
ACPIWakeWaitIrp(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the routine that is called when the system wants to be notified
|
||
of this device waking the system.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - The device object which is supposed to wake the system
|
||
Irp - The request
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
PDEVICE_EXTENSION deviceExtension = ACPIInternalGetDeviceExtension(DeviceObject);
|
||
PIO_STACK_LOCATION irpStack;
|
||
|
||
//
|
||
// The first step is to decide if this object can actually support
|
||
// a wake.
|
||
//
|
||
if ( !(deviceExtension->Flags & DEV_CAP_WAKE) ) {
|
||
|
||
//
|
||
// We do not support wake
|
||
//
|
||
return ACPIDispatchForwardOrFailPowerIrp( DeviceObject, Irp );
|
||
|
||
}
|
||
|
||
//
|
||
// Get the stack parameters
|
||
//
|
||
irpStack = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
//
|
||
// We must make sure that we are at the correct system level
|
||
// to support this functionality
|
||
//
|
||
if (deviceExtension->PowerInfo.SystemWakeLevel <
|
||
irpStack->Parameters.WaitWake.PowerState) {
|
||
|
||
//
|
||
// The system level is not the one we are currently at
|
||
//
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_WAKE,
|
||
deviceExtension,
|
||
"(0x%08lx): ACPIWakeWaitIrp ->S%d < Irp->S%d\n",
|
||
Irp,
|
||
deviceExtension->PowerInfo.SystemWakeLevel - 1,
|
||
irpStack->Parameters.WaitWake.PowerState - 1
|
||
) );
|
||
|
||
//
|
||
// Fail the Irp
|
||
//
|
||
Irp->IoStatus.Status = status = STATUS_INVALID_DEVICE_STATE;
|
||
PoStartNextPowerIrp( Irp );
|
||
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
||
return status;
|
||
|
||
}
|
||
|
||
//
|
||
// We must make sure that the device is in the proper device level
|
||
// to support this functionality
|
||
//
|
||
if (deviceExtension->PowerInfo.DeviceWakeLevel <
|
||
deviceExtension->PowerInfo.PowerState) {
|
||
|
||
//
|
||
// We are too much powered off to wake the computer
|
||
//
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_WAKE,
|
||
deviceExtension,
|
||
"(0x%08lx): ACPIWakeWaitIrp Device->D%d Max->D%d\n",
|
||
Irp,
|
||
deviceExtension->PowerInfo.DeviceWakeLevel - 1,
|
||
deviceExtension->PowerInfo.PowerState - 1
|
||
) );
|
||
|
||
//
|
||
// Fail the irp
|
||
//
|
||
Irp->IoStatus.Status = status = STATUS_INVALID_DEVICE_STATE;
|
||
PoStartNextPowerIrp( Irp );
|
||
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
||
return status;
|
||
|
||
}
|
||
|
||
//
|
||
// At this point, we are definately going to run the completion routine
|
||
// so, we mark the irp as pending and increment the reference count
|
||
//
|
||
IoMarkIrpPending( Irp );
|
||
InterlockedIncrement( &deviceExtension->OutstandingIrpCount );
|
||
|
||
//
|
||
// Feed the request to the device power management subsystem. Note that
|
||
// this function is supposed to invoke the completion request no matter
|
||
// what happens.
|
||
//
|
||
status = ACPIDeviceIrpWaitWakeRequest(
|
||
DeviceObject,
|
||
Irp,
|
||
ACPIDeviceIrpCompleteRequest
|
||
);
|
||
if (status == STATUS_MORE_PROCESSING_REQUIRED) {
|
||
|
||
status = STATUS_PENDING;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Remove our reference
|
||
//
|
||
ACPIInternalDecrementIrpReferenceCount( deviceExtension );
|
||
|
||
}
|
||
return status;
|
||
}
|
||
|