1032 lines
22 KiB
C
1032 lines
22 KiB
C
|
/*++
|
|||
|
|
|||
|
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;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|