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

1870 lines
43 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

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

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
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;
}