windows-nt/Source/XPSP1/NT/base/busdrv/acpi/driver/nt/gpe.c

1032 lines
22 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
gpe.c
Abstract:
This module is how the ACPI driver interfaces with GPE Events
Author:
Stephane Plante (splante)
Environment:
NT Kernel Mode Driver Only
--*/
#include "pch.h"
//
// Global tables for GPE handling (Both GP0 and GP1)
//
PUCHAR GpeEnable = NULL;
PUCHAR GpeCurEnable = NULL;
PUCHAR GpeWakeEnable = NULL;
PUCHAR GpeIsLevel = NULL;
PUCHAR GpeHandlerType = NULL;
PUCHAR GpeWakeHandler = NULL;
PUCHAR GpeSpecialHandler = NULL;
PUCHAR GpePending = NULL;
PUCHAR GpeRunMethod = NULL;
PUCHAR GpeComplete = NULL;
PUCHAR GpeSavedWakeMask = NULL;
PUCHAR GpeSavedWakeStatus = NULL;
PUCHAR GpeMap = NULL;
//
// Lock to protect all GPE related information
//
KSPIN_LOCK GpeTableLock;
VOID
ACPIGpeBuildEventMasks(
VOID
)
/*++
Routine Description:
This routine looks at all the General Purpose Event sources and
builds up a mask of which events should be enabled, which events
are special, and which events are wake up events
Arguments:
None
Return Value:
None
--*/
{
BOOLEAN convertedToNumber;
KIRQL oldIrql;
NTSTATUS status;
PNSOBJ gpeObject;
PNSOBJ gpeMethod;
ULONG nameSeg;
ULONG gpeIndex;
//
// NOTENOTE --- Check to make sure sure that the following sequence
// of acquiring locks is correct
//
KeAcquireSpinLock( &AcpiDeviceTreeLock, &oldIrql );
KeAcquireSpinLockAtDpcLevel( &GpeTableLock );
//
// First things first, we need to look at the \_GPE branch of the
// tree to see which control methods, exist, if any
//
status = AMLIGetNameSpaceObject("\\_GPE", NULL, &gpeObject, 0);
if (!NT_SUCCESS(status)) {
ACPIPrint( (
ACPI_PRINT_WARNING,
"ACPIGpeBuildEventMasks - Could not find \\_GPE object %x\n",
status
) );
goto ACPIGpeBuildEventMasksExit;
}
//
// Get the first child of the GPE root --- we will need to look
// at all the methods under the object
//
gpeMethod = NSGETFIRSTCHILD(gpeObject);
//
// Use a for loop instead of a while loop to keep down the
// number of nested statements
//
for (;gpeMethod; gpeMethod = NSGETNEXTSIBLING(gpeMethod) ) {
//
// Make sure that we are dealing with a method
//
if (NSGETOBJTYPE(gpeMethod) != OBJTYPE_METHOD) {
continue;
}
//
// The name of the object contains the index that we want
// to associated with the object. We need to convert the string
// representation into a numerical representation
//
// The encoding is as follows:
// Object Name = _LXY [for example]
// Object->dwNameSeg = yxL_
// gpeIndex = (nameSeg >> 8) & 0xFF00 [the x]
// gpeIndex += (nameSeg >> 24) & 0xFF [the y]
//
nameSeg = gpeMethod->dwNameSeg;
gpeIndex = ( (nameSeg & 0x00FF0000) >> 8);
gpeIndex |= ( (nameSeg & 0xFF000000) >> 24);
nameSeg = ( (nameSeg & 0x0000FF00) >> 8);
convertedToNumber = ACPIInternalConvertToNumber(
(UCHAR) ( (gpeIndex & 0x00FF) ),
(UCHAR) ( (gpeIndex & 0xFF00) >> 8),
&gpeIndex
);
if (!convertedToNumber) {
continue;
}
//
// Set the proper bits to remember this GPE
// Note: we pass convertedToNumber as the argument
// since we don't particularly care what it returns
//
if ( (UCHAR) nameSeg == 'L') {
//
// Install the event as level triggered
//
ACPIGpeInstallRemoveIndex(
gpeIndex,
ACPI_GPE_LEVEL_INSTALL,
ACPI_GPE_CONTROL_METHOD,
&convertedToNumber
);
} else if ( (UCHAR) nameSeg == 'E') {
//
// Install the Edge triggered GPE
//
ACPIGpeInstallRemoveIndex(
gpeIndex,
ACPI_GPE_EDGE_INSTALL,
ACPI_GPE_CONTROL_METHOD,
&convertedToNumber
);
}
} // for (...)
ACPIGpeBuildEventMasksExit:
//
// We also need to look at all the vector objects and re-enable those
//
ACPIVectorBuildVectorMasks();
//
// At this point, we should re-enable the registers that should be
// enabled
//
ACPIGpeEnableDisableEvents( TRUE );
//
// Done
//
KeReleaseSpinLockFromDpcLevel( &GpeTableLock );
KeReleaseSpinLock( &AcpiDeviceTreeLock, oldIrql );
}
VOID
ACPIGpeBuildWakeMasks(
IN PDEVICE_EXTENSION DeviceExtension
)
/*++
Routine Description:
This recursive routine walks the entire device extension space and
tries to find device extension whose _PRW are special
This routine is called with device tree and gpe table lock spinlocks
owned
Argument:
DeviceExtension - The device whose children we need to examine
Return Value:
None
--*/
{
EXTENSIONLIST_ENUMDATA eled;
PDEVICE_EXTENSION childExtension;
ULONG gpeRegister;
ULONG gpeMask;
//
// Setup the data structures that we will use to walk the device
// extension tree
//
ACPIExtListSetupEnum(
&eled,
&(DeviceExtension->ChildDeviceList),
NULL,
SiblingDeviceList,
WALKSCHEME_NO_PROTECTION
);
//
// Look at all children of the current device extension
//
for (childExtension = ACPIExtListStartEnum( &eled );
ACPIExtListTestElement( &eled, TRUE);
childExtension = ACPIExtListEnumNext( &eled) ) {
//
// Recurse first
//
ACPIGpeBuildWakeMasks( childExtension );
//
// Is there a _PRW on this extension?
//
if (!(childExtension->Flags & DEV_CAP_WAKE) ) {
continue;
}
//
// Remember which register and mask are used by this
// gpe bit
//
gpeRegister = ACPIGpeIndexToGpeRegister(
childExtension->PowerInfo.WakeBit
);
gpeMask = 1 << ( (UCHAR) childExtension->PowerInfo.WakeBit % 8);
//
// Does this vector have a GPE?
//
if ( (GpeEnable[gpeRegister] & gpeMask) ) {
//
// If we got here, and we aren't marked as DEV_CAP_NO_DISABLE_WAKE,
// then we should turn off the GPE since this is a Wake event.
// The easiest way to do this is to make sure that GpeWakeHandler
// is masked with the appropriate bit
//
if (!(childExtension->Flags & DEV_CAP_NO_DISABLE_WAKE) ) {
//
// It has a GPE mask, so remember that there is a wake handler
// for it. This should prevent us from arming the GPE without
// a request for it.
//
if (!(GpeSpecialHandler[gpeRegister] & gpeMask) ) {
GpeWakeHandler[gpeRegister] |= gpeMask;
}
} else {
//
// If we got here, then we should remember that we can
// never consider this pin as *just* a wake handler
//
GpeSpecialHandler[gpeRegister] |= gpeMask;
//
// Make sure that the pin isn't set as a wake handler
//
GpeWakeHandler[gpeRegister] &= ~gpeMask;
}
}
} // for ( ... )
}
VOID
ACPIGpeClearEventMasks(
)
/*++
Routine Description:
This routine is called when the system wants to make sure that no
General Purpose Events are enabled.
This is typically done at:
-System Init Time
-Just before we load a namespace table
-Just before we unload a namespace table
Arguments:
None
Return Value:
None
--*/
{
KIRQL oldIrql;
//
// Need to hold the previous IRQL before we can touch these
// registers
//
KeAcquireSpinLock( &GpeTableLock, &oldIrql );
//
// Disable all of the events
//
ACPIGpeEnableDisableEvents( FALSE );
//
// Clear all the events
//
ACPIGpeClearRegisters();
//
// Zero out all of these fields, since we will recalc them later
//
RtlZeroMemory( GpeCurEnable, AcpiInformation->GpeSize );
RtlZeroMemory( GpeEnable, AcpiInformation->GpeSize );
RtlZeroMemory( GpeWakeEnable, AcpiInformation->GpeSize );
RtlZeroMemory( GpeWakeHandler, AcpiInformation->GpeSize );
RtlZeroMemory( GpeSpecialHandler, AcpiInformation->GpeSize );
RtlZeroMemory( GpeRunMethod, AcpiInformation->GpeSize );
RtlZeroMemory( GpePending, AcpiInformation->GpeSize );
RtlZeroMemory( GpeComplete, AcpiInformation->GpeSize );
RtlZeroMemory( GpeIsLevel, AcpiInformation->GpeSize );
RtlZeroMemory( GpeHandlerType, AcpiInformation->GpeSize );
//
// Done with the spinlock
//
KeReleaseSpinLock( &GpeTableLock, oldIrql );
}
VOID
ACPIGpeClearRegisters(
VOID
)
/*++
Routine Description:
Reset the contents of the GP Registers
Arguments:
None
Return Value:
None
--*/
{
UCHAR scratch;
ULONG i;
//
// Clear all GPE status registers
//
for (i = 0; i < AcpiInformation->GpeSize; i++) {
//
// Read the register and mask off uninteresting GPE levels
//
scratch = ACPIReadGpeStatusRegister (i);
scratch &= GpeEnable[i] | GpeWakeEnable[i];
//
// Write back out to clear the status bits
//
ACPIWriteGpeStatusRegister (i, scratch);
}
}
VOID
ACPIGpeEnableDisableEvents (
BOOLEAN Enable
)
/*++
Routine Description:
Not Exported
Enable or disables GP events
Arguments:
Enable - TRUE if we want to enable GP events
Return Value
None
--*/
{
UCHAR Mask;
ULONG i;
//
// Transfer the current enable masks to their corresponding GPE registers
//
Mask = Enable ? (UCHAR) -1 : 0;
for (i = 0; i < AcpiInformation->GpeSize; i++) {
ACPIWriteGpeEnableRegister( i, (UCHAR) (GpeCurEnable[i] & Mask) );
}
}
VOID
ACPIGpeHalEnableDisableEvents(
BOOLEAN Enable
)
/*++
Routine Description:
Called from the HAL only.
Enables or disables GP events
Will snapshot the appropriate registers
Arguments:
Enable - TRUE if we want to enable GP events
Return Value:
None
--*/
{
ULONG i;
if (Enable) {
//
// We have presumably woken up, so remember the PM1 Status register
// and the GPE Status Register
//
for (i = 0; i < AcpiInformation->GpeSize; i++) {
GpeSavedWakeStatus[i] = ACPIReadGpeStatusRegister(i);
}
AcpiInformation->pm1_wake_status = READ_PM1_STATUS();
} else {
//
// We are going to standby without enabling any events. Make
// sure to clear all the masks
//
AcpiInformation->pm1_wake_mask = 0;
RtlZeroMemory( GpeSavedWakeMask, AcpiInformation->GpeSize );
}
//
// Make sure to still enable/disable the registers
//
ACPIGpeEnableDisableEvents( Enable );
}
VOID
ACPIGpeEnableWakeEvents(
VOID
)
/*++
Routine Description:
This routine is called with interrupts disabled for the purpose of enabling
those vectors that are required for wake support just before putting the
system to sleep
N.B. interrutps are disabled
Arguments:
None
Return Value:
None
--*/
{
ULONG i;
for (i = 0; i < AcpiInformation->GpeSize; i++) {
ACPIWriteGpeEnableRegister (i, GpeWakeEnable[i]);
GpeSavedWakeMask[i] = GpeWakeEnable[i];
}
AcpiInformation->pm1_wake_mask = READ_PM1_ENABLE();
}
ULONG
ACPIGpeIndexToByteIndex (
ULONG Index
)
/*++
Routine Description:
Translate a GpeIndex (event number) to a logical byte index (0 to GPE1 end, no hole).
Handles the case where the GPE1 block event numbers are not immediately after the
GPE0 event numbers (as specified by the GP1_Base_Index).
Arguments:
Index - The Gpe index to be translated (0-255);
Return Value:
The logical byte index.
--*/
{
if (Index < AcpiInformation->GP1_Base_Index) {
//
// GP0 case is very simple
//
return (Index);
} else {
//
// GP1 case must take into account:
// 1) The base index of the GPE1 block
// 2) The number of (logical) GPE0 registers preceeding the GPE1 registers
//
return ((Index - AcpiInformation->GP1_Base_Index) +
AcpiInformation->Gpe0Size);
}
}
ULONG
ACPIGpeIndexToGpeRegister (
ULONG Index
)
/*++
Routine Description:
Translate a GpeIndex (event number) to the logical Gpe register which contains it.
Handles the case where the GPE1 block event numbers are not immediately after the
GPE0 event numbers (as specified by the GP1_Base_Index).
Arguments:
Index - The Gpe index to be translated (0-255);
Return Value:
The logical Gpe register which contains the index.
--*/
{
if (Index < AcpiInformation->GP1_Base_Index) {
//
// GP0 case is very simple
//
return (Index / 8);
} else {
//
// GP1 case must take into account:
// 1) The base index of the GPE1 block
// 2) The number of (logical) GPE0 registers preceeding the GPE1 registers
//
return (((Index - AcpiInformation->GP1_Base_Index) / 8) +
AcpiInformation->Gpe0Size);
}
}
BOOLEAN
ACPIGpeInstallRemoveIndex (
ULONG GpeIndex,
ULONG Action, // Edge = 0, Level = 1, Remove = 2
ULONG Type,
PBOOLEAN HasControlMethod
)
/*++
Routine Description:
Installs or removes GPEs from the global tables.
NOTE: Should be called with the global GpeVectorTable locked, and GPEs disabled
Arguments:
GPEIndex - The GPE number to install or remove
Action - Action to be performed:
0 - Install this GPE as an edge-sensitive interrupt
1 - Install this GPE as a level-sensitive interrupt
2 - Remove this GPE
Type - Type of handler for this GPE:
0 - OS handler
1 - Control Method
Return Value:
None
--*/
{
ULONG bitOffset;
ULONG i;
ULONG bit;
//
// Validate the GPE index (GPE number)
//
if (AcpiInformation->GP0_LEN == 0) {
PACPI_GPE_ERROR_CONTEXT errContext;
errContext = ExAllocatePoolWithTag(
NonPagedPool,
sizeof(ACPI_GPE_ERROR_CONTEXT),
ACPI_MISC_POOLTAG
);
if (errContext) {
errContext->GpeIndex = GpeIndex;
ExInitializeWorkItem(
&(errContext->Item),
ACPIGpeInstallRemoveIndexErrorWorker,
(PVOID) errContext
);
ExQueueWorkItem( &(errContext->Item), DelayedWorkQueue );
}
return FALSE;
}
if (!(ACPIGpeValidIndex (GpeIndex))) {
return FALSE;
}
bitOffset = GpeIndex % 8;
bit = (1 << bitOffset);
i = ACPIGpeIndexToGpeRegister (GpeIndex);
ASSERT( (i < (ULONG) AcpiInformation->GpeSize) );
if (i >= (ULONG) AcpiInformation->GpeSize) {
return FALSE;
}
//
// Handler removal
//
if (Action == ACPI_GPE_REMOVE) {
//
// Fall back to using control method if there is one.
// Otherwise, disable the event.
//
if (*HasControlMethod) {
GpeEnable [i] |= bit;
GpeCurEnable [i] |= bit;
GpeHandlerType [i] |= bit;
} else {
GpeEnable [i] &= ~bit;
GpeCurEnable [i] &= ~bit;
GpeHandlerType [i] &= ~bit;
ASSERT (!(GpeWakeEnable[i] & bit));
}
ACPIPrint ( (
ACPI_PRINT_DPC,
"ACPIGpeInstallRemoveIndex: Removing GPE #%d: Byte 0x%x bit %u\n",
GpeIndex, i, bitOffset
) );
return TRUE;
}
//
// Handler installation
//
if ( (GpeEnable [i] & bit) ) {
if ( !(GpeHandlerType[i] & bit) ) {
//
// a handler is already installed
//
return FALSE;
}
//
// there is a control method (to be restored if handler removed)
//
*HasControlMethod = TRUE;
} else {
*HasControlMethod = FALSE;
}
//
// Install this event
//
GpeEnable[i] |= bit;
GpeCurEnable[i] |= bit;
if (Action == ACPI_GPE_LEVEL_INSTALL) {
//
// Level event
//
GpeIsLevel[i] |= bit;
} else {
//
// Edge event
//
GpeIsLevel[i] &= ~bit;
}
if (Type == ACPI_GPE_CONTROL_METHOD) {
GpeHandlerType [i] |= bit;
} else {
GpeHandlerType [i] &= ~bit;
}
ACPIPrint ( (
ACPI_PRINT_DPC,
"ACPIGpeInstallRemoveIndex: Setting GPE #%d: Byte 0x%x bit %u\n",
GpeIndex, i, bitOffset
) );
return TRUE;
}
VOID
ACPIGpeInstallRemoveIndexErrorWorker(
IN PVOID Context
)
{
PACPI_GPE_ERROR_CONTEXT errContext = (PACPI_GPE_ERROR_CONTEXT) Context;
PWCHAR prtEntry[1];
UNICODE_STRING indexName;
WCHAR index[20];
RtlInitUnicodeString(&indexName, index);
if (NT_SUCCESS(RtlIntegerToUnicodeString( errContext->GpeIndex,0,&indexName))) {
prtEntry[0] = index;
ACPIWriteEventLogEntry(
ACPI_ERR_NO_GPE_BLOCK,
&prtEntry,
1,
NULL,
0
);
}
ExFreePool( errContext );
}
BOOLEAN
ACPIGpeIsEvent(
VOID
)
/*++
Routine Description:
Not Exported
Detects where or not the a GP event caused an interrupt. This routine is
called at DIRQL or ISR time
Arguments:
None
Return Value:
TRUE - Yes, it was our interrupt
FALSE - No, it was not
--*/
{
UCHAR sts;
ULONG i;
//
// Check all GPE registers to see if any of the status bits are set.
//
for (i = 0; i < AcpiInformation->GpeSize; i++) {
sts = ACPIReadGpeStatusRegister (i);
if (sts & GpeCurEnable[i]) {
return TRUE;
}
}
//
// No GPE bits were set
//
return (FALSE);
}
ULONG
ACPIGpeRegisterToGpeIndex(
ULONG Register,
ULONG BitPosition
)
/*++
Routine Description:
Translate a logical Gpe register and bit position into the associated Gpe index (event
number). Handles the case where the GPE1 block event numbers are not immediately after the
GPE0 event numbers (as specified by the GP1_Base_Index).
Arguments:
Register - The logical Gpe register
BitPosition - Position of the index within the register
Return Value:
The Gpe index associated with the register/bit-position.
--*/
{
if (Register < AcpiInformation->Gpe0Size) {
//
// GP0 case is simple
//
return (Register * 8) +
BitPosition;
} else {
//
// GP1 case must adjust for:
// 1) The number of (logical) GPE0 registers preceeding the GPE1 registers
// 2) The base index of the GPE1 block.
//
return ((Register - AcpiInformation->Gpe0Size) * 8) +
AcpiInformation->GP1_Base_Index +
BitPosition;
}
}
VOID
ACPIGpeUpdateCurrentEnable(
IN ULONG GpeRegister,
IN UCHAR Completed
)
/*++
Routine Description:
This routine is called to re-arm the GpeCurEnable data structure
based on the contents of the GPE's that we have just processed
Arguments:
GpeRegister - Which index into the register we handled
Completed - Bitmask of the handled GPEs
Return Value:
None
--*/
{
//
// This vector is no longer pending
//
GpePending[GpeRegister] &= ~Completed;
//
// First, remove any events that aren't in the current list of
// enables, either wake or run-time
//
Completed &= (GpeEnable[GpeRegister] | GpeWakeEnable[GpeRegister]);
//
// Next, remove any events for which there is a wake handler,
// but is not in the list of wake enables
//
Completed &= ~(GpeWakeHandler[GpeRegister] & ~GpeWakeEnable[GpeRegister]);
//
// Okay, now the cmp value should be exactly the list of GPEs to
// re-enable
//
GpeCurEnable[GpeRegister] |= Completed;
}
BOOLEAN
ACPIGpeValidIndex (
ULONG Index
)
/*++
Routine Description:
Verifies that a GPE index is valid on this machine.
Note: There can be a hole (in the GPE index values) between the GPE0 and the GPE1 blocks.
This hole is defined by the size of the GPE0 block (which always starts at zero), and
GP1_Base_Index (whose value is obtained from the FACP table).
Arguments:
Index - The Gpe index to be verified (0-255);
Return Value:
TRUE if a valid index, FALSE otherwise.
--*/
{
if (Index < AcpiInformation->GP1_Base_Index) {
//
// GP0 case: Gpe index must fall within the range 0 to the end of GPE0
//
if (Index < (ULONG) (AcpiInformation->Gpe0Size * 8)) {
return TRUE;
} else {
return FALSE;
}
} else {
//
// GP1 case: Gpe index must fall within the range GP1_Base_Index to the end of GPE1
//
if (Index < (ULONG) (AcpiInformation->GP1_Base_Index + (AcpiInformation->Gpe1Size * 8))) {
return TRUE;
} else {
return FALSE;
}
}
}