You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
786 lines
16 KiB
786 lines
16 KiB
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
vector.c
|
|
|
|
Abstract:
|
|
|
|
This module is how external drivers add / remove hooks to deal with
|
|
ACPI Gpe Events
|
|
|
|
Author:
|
|
|
|
Stephane Plante
|
|
|
|
Environment:
|
|
|
|
NT Kernel Mode Driver Only
|
|
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
|
|
//
|
|
// Table for installed GPE handlers
|
|
//
|
|
PGPE_VECTOR_ENTRY GpeVectorTable = NULL;
|
|
UCHAR GpeVectorFree = 0;
|
|
ULONG GpeVectorTableSize = 0;
|
|
|
|
|
|
VOID
|
|
ACPIVectorBuildVectorMasks(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to walk the GPE Vector Table and properly
|
|
enable all the events that we think should be enabled.
|
|
|
|
This routine is typically called after we have loaded a new set
|
|
of tables or we have unloaded an existing set of tables.
|
|
|
|
We have to call this routine because at the start of the operation,
|
|
we clear out all the knowledge of these additional vectors.
|
|
|
|
This routine is called with GPEs disabled and the GPE Table locked
|
|
acquired.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN installed;
|
|
ULONG i;
|
|
ULONG mode;
|
|
|
|
//
|
|
// Walk all the elements in the table
|
|
//
|
|
for (i = 0; i < GpeVectorTableSize; i++) {
|
|
|
|
//
|
|
// Does this entry point to a vector object?
|
|
//
|
|
if (GpeVectorTable[i].GpeVectorObject == NULL) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (GpeVectorTable[i].GpeVectorObject->Mode == LevelSensitive) {
|
|
|
|
mode = ACPI_GPE_LEVEL_INSTALL;
|
|
|
|
} else {
|
|
|
|
mode = ACPI_GPE_EDGE_INSTALL;
|
|
|
|
}
|
|
|
|
//
|
|
// Install the GPE into bit-maps. This validates the GPE number.
|
|
//
|
|
installed = ACPIGpeInstallRemoveIndex(
|
|
GpeVectorTable[i].GpeVectorObject->Vector,
|
|
mode,
|
|
ACPI_GPE_HANDLER,
|
|
&(GpeVectorTable[i].GpeVectorObject->HasControlMethod)
|
|
);
|
|
if (!installed) {
|
|
|
|
ACPIPrint( (
|
|
ACPI_PRINT_CRITICAL,
|
|
"ACPIVectorBuildVectorMasks: Could not reenable Vector Object %d\n",
|
|
i
|
|
) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIVectorClear(
|
|
PDEVICE_OBJECT AcpiDeviceObject,
|
|
PVOID GpeVectorObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Clear the GPE_STS (status) bit associated with a vector object
|
|
|
|
Arguments:
|
|
|
|
AcpiDeviceObject - The ACPI device object
|
|
GpeVectorObject - Pointer to the vector object returned by
|
|
ACPIGpeConnectVector
|
|
|
|
Return Value
|
|
|
|
Returns status
|
|
|
|
--*/
|
|
{
|
|
PGPE_VECTOR_OBJECT localVectorObject = GpeVectorObject;
|
|
ULONG gpeIndex;
|
|
ULONG bitOffset;
|
|
ULONG i;
|
|
|
|
ASSERT( localVectorObject );
|
|
|
|
//
|
|
// What is the GPE index for this vector?
|
|
//
|
|
gpeIndex = localVectorObject->Vector;
|
|
|
|
//
|
|
// Calculate the proper mask to use
|
|
//
|
|
bitOffset = gpeIndex % 8;
|
|
|
|
//
|
|
// Calculate the offset for the register
|
|
//
|
|
i = ACPIGpeIndexToGpeRegister (gpeIndex);
|
|
|
|
//
|
|
// Clear the register
|
|
//
|
|
ACPIWriteGpeStatusRegister (i, (UCHAR) (1 << bitOffset));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIVectorConnect(
|
|
PDEVICE_OBJECT AcpiDeviceObject,
|
|
ULONG GpeVector,
|
|
KINTERRUPT_MODE GpeMode,
|
|
BOOLEAN Sharable,
|
|
PGPE_SERVICE_ROUTINE ServiceRoutine,
|
|
PVOID ServiceContext,
|
|
PVOID *GpeVectorObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Connects a handler to a general-purpose event.
|
|
|
|
Arguments:
|
|
|
|
AcpiDeviceObject - The ACPI object
|
|
GpeVector - The event number to connect to
|
|
GpeMode - Level or edge interrupt
|
|
Sharable - Can this level be shared?
|
|
ServiceRoutine - Address of the handler
|
|
ServiceContext - Context object to be passed to the handler
|
|
*GpeVectorObject - Pointer to where the vector object is returned
|
|
|
|
Return Value
|
|
|
|
Returns status
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN installed;
|
|
KIRQL oldIrql;
|
|
NTSTATUS status;
|
|
PGPE_VECTOR_OBJECT localVectorObject;
|
|
ULONG mode;
|
|
|
|
ASSERT( GpeVectorObject );
|
|
|
|
ACPIPrint( (
|
|
ACPI_PRINT_INFO,
|
|
"ACPIVectorConnect: Attach GPE handler\n"
|
|
) );
|
|
|
|
status = STATUS_SUCCESS;
|
|
*GpeVectorObject = NULL;
|
|
|
|
//
|
|
// Do GPEs exist on this machine?
|
|
//
|
|
if (AcpiInformation->GpeSize == 0) {
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
|
|
}
|
|
|
|
//
|
|
// Validate the vector number (GPE number)
|
|
//
|
|
if ( !ACPIGpeValidIndex(GpeVector) ) {
|
|
|
|
return STATUS_INVALID_PARAMETER_2;
|
|
|
|
}
|
|
|
|
//
|
|
// Create and initialize a vector object
|
|
//
|
|
localVectorObject = ExAllocatePoolWithTag (
|
|
NonPagedPool,
|
|
sizeof(GPE_VECTOR_OBJECT),
|
|
ACPI_SHARED_GPE_POOLTAG
|
|
);
|
|
if (localVectorObject == NULL) {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
RtlZeroMemory( localVectorObject, sizeof(GPE_VECTOR_OBJECT) );
|
|
localVectorObject->Vector = GpeVector;
|
|
localVectorObject->Handler = ServiceRoutine;
|
|
localVectorObject->Context = ServiceContext;
|
|
localVectorObject->Mode = GpeMode;
|
|
|
|
//
|
|
// We don't implement anything other than sharable...
|
|
//
|
|
localVectorObject->Sharable = Sharable;
|
|
|
|
//
|
|
// Level/Edge mode for ACPIGpeInstallRemoveIndex()
|
|
//
|
|
if (GpeMode == LevelSensitive) {
|
|
|
|
mode = ACPI_GPE_LEVEL_INSTALL;
|
|
|
|
} else {
|
|
|
|
mode = ACPI_GPE_EDGE_INSTALL;
|
|
|
|
}
|
|
|
|
//
|
|
// Lock the global tables
|
|
//
|
|
KeAcquireSpinLock (&GpeTableLock, &oldIrql);
|
|
|
|
//
|
|
// Disable GPEs while we are installing the handler
|
|
//
|
|
ACPIGpeEnableDisableEvents(FALSE);
|
|
|
|
//
|
|
// Install the GPE into bit-maps. This validates the GPE number.
|
|
//
|
|
installed = ACPIGpeInstallRemoveIndex(
|
|
GpeVector,
|
|
mode,
|
|
ACPI_GPE_HANDLER,
|
|
&(localVectorObject->HasControlMethod)
|
|
);
|
|
if (!installed) {
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Install GPE handler into vector table.
|
|
//
|
|
installed = ACPIVectorInstall(
|
|
GpeVector,
|
|
localVectorObject
|
|
);
|
|
if (!installed) {
|
|
|
|
ACPIGpeInstallRemoveIndex(
|
|
GpeVector,
|
|
ACPI_GPE_REMOVE,
|
|
0,
|
|
&localVectorObject->HasControlMethod
|
|
);
|
|
status = STATUS_UNSUCCESSFUL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
ExFreePool (localVectorObject);
|
|
|
|
} else {
|
|
|
|
*GpeVectorObject = localVectorObject;
|
|
|
|
}
|
|
|
|
//
|
|
// Update hardware to match us
|
|
//
|
|
ACPIGpeEnableDisableEvents (TRUE);
|
|
|
|
//
|
|
// Unlock tables and return status
|
|
//
|
|
KeReleaseSpinLock (&GpeTableLock, oldIrql);
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIVectorDisable(
|
|
PDEVICE_OBJECT AcpiDeviceObject,
|
|
PVOID GpeVectorObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Temporarily disable a GPE that is already attached to a handler.
|
|
|
|
Arguments:
|
|
|
|
AcpiDeviceObject - The ACPI device object
|
|
GpeVectorObject - Pointer to the vector object returned by ACPIGpeConnectVector
|
|
|
|
Return Value
|
|
|
|
Returns status
|
|
|
|
--*/
|
|
{
|
|
PGPE_VECTOR_OBJECT localVectorObject = GpeVectorObject;
|
|
KIRQL oldIrql;
|
|
ULONG gpeIndex;
|
|
ULONG bit;
|
|
ULONG i;
|
|
|
|
//
|
|
// The GPE index was validated when the handler was attached
|
|
//
|
|
gpeIndex = localVectorObject->Vector;
|
|
|
|
//
|
|
// Calculate the mask and index
|
|
//
|
|
bit = (1 << (gpeIndex % 8));
|
|
i = ACPIGpeIndexToGpeRegister (gpeIndex);
|
|
|
|
//
|
|
// Lock the global tables
|
|
//
|
|
KeAcquireSpinLock (&GpeTableLock, &oldIrql);
|
|
|
|
//
|
|
// Disable GPEs while we are fussing with the enable bits
|
|
//
|
|
ACPIGpeEnableDisableEvents(FALSE);
|
|
|
|
//
|
|
// Remove the GPE from the enable bit-maps. This event will be completely disabled,
|
|
// but the handler has not been removed.
|
|
//
|
|
GpeEnable [i] &= ~bit;
|
|
GpeCurEnable [i] &= ~bit;
|
|
ASSERT(!(GpeWakeEnable[i] & bit));
|
|
|
|
//
|
|
// Update hardware to match us
|
|
//
|
|
ACPIGpeEnableDisableEvents (TRUE);
|
|
|
|
//
|
|
// Unlock tables and return status
|
|
//
|
|
KeReleaseSpinLock (&GpeTableLock, oldIrql);
|
|
ACPIPrint( (
|
|
ACPI_PRINT_RESOURCES_2,
|
|
"ACPIVectorDisable: GPE %x disabled\n",
|
|
gpeIndex
|
|
) );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIVectorDisconnect(
|
|
PVOID GpeVectorObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Disconnects a handler from a general-purpose event.
|
|
|
|
Arguments:
|
|
|
|
GpeVectorObject - Pointer to the vector object returned by
|
|
ACPIGpeConnectVector
|
|
|
|
Return Value
|
|
|
|
Returns status
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN removed;
|
|
KIRQL oldIrql;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PGPE_VECTOR_OBJECT gpeVectorObj = GpeVectorObject;
|
|
|
|
ACPIPrint( (
|
|
ACPI_PRINT_INFO,
|
|
"ACPIVectorDisconnect: Detach GPE handler\n"
|
|
) );
|
|
|
|
//
|
|
// Lock the global tables
|
|
//
|
|
KeAcquireSpinLock (&GpeTableLock, &oldIrql);
|
|
|
|
//
|
|
// Disable GPEs while we are removing the handler
|
|
//
|
|
ACPIGpeEnableDisableEvents (FALSE);
|
|
|
|
//
|
|
// Remove GPE handler From vector table.
|
|
//
|
|
ACPIVectorRemove(gpeVectorObj->Vector);
|
|
|
|
//
|
|
// Remove the GPE from the bit-maps. Fall back to using control method
|
|
// if available.
|
|
//
|
|
removed = ACPIGpeInstallRemoveIndex(
|
|
gpeVectorObj->Vector,
|
|
ACPI_GPE_REMOVE,
|
|
0,
|
|
&(gpeVectorObj->HasControlMethod)
|
|
);
|
|
if (!removed) {
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
|
|
}
|
|
|
|
//
|
|
// Update hardware to match us
|
|
//
|
|
ACPIGpeEnableDisableEvents(TRUE);
|
|
|
|
//
|
|
// Unlock tables and return status
|
|
//
|
|
KeReleaseSpinLock (&GpeTableLock, oldIrql);
|
|
|
|
//
|
|
// Free the vector object, it's purpose is done.
|
|
//
|
|
if (status == STATUS_SUCCESS) {
|
|
|
|
ExFreePool (GpeVectorObject);
|
|
|
|
}
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIVectorEnable(
|
|
PDEVICE_OBJECT AcpiDeviceObject,
|
|
PVOID GpeVectorObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Enable (a previously disabled) GPE that is already attached to a handler.
|
|
|
|
Arguments:
|
|
|
|
AcpiDeviceObject - The ACPI device object
|
|
GpeVectorObject - Pointer to the vector object returned by ACPIGpeConnectVector
|
|
|
|
Return Value
|
|
|
|
Returns status
|
|
|
|
--*/
|
|
{
|
|
KIRQL oldIrql;
|
|
PGPE_VECTOR_OBJECT localVectorObject = GpeVectorObject;
|
|
ULONG bit;
|
|
ULONG gpeIndex;
|
|
ULONG gpeRegister;
|
|
|
|
//
|
|
// The GPE index was validated when the handler was attached
|
|
//
|
|
gpeIndex = localVectorObject->Vector;
|
|
bit = (1 << (gpeIndex % 8));
|
|
gpeRegister = ACPIGpeIndexToGpeRegister (gpeIndex);
|
|
|
|
//
|
|
// Lock the global tables
|
|
//
|
|
KeAcquireSpinLock (&GpeTableLock, &oldIrql);
|
|
|
|
//
|
|
// Disable GPEs while we are fussing with the enable bits
|
|
//
|
|
ACPIGpeEnableDisableEvents (FALSE);
|
|
|
|
//
|
|
// Enable the GPE in the bit maps.
|
|
//
|
|
GpeEnable [gpeRegister] |= bit;
|
|
GpeCurEnable [gpeRegister] |= bit;
|
|
|
|
//
|
|
// Update hardware to match us
|
|
//
|
|
ACPIGpeEnableDisableEvents (TRUE);
|
|
|
|
//
|
|
// Unlock tables and return status
|
|
//
|
|
KeReleaseSpinLock (&GpeTableLock, oldIrql);
|
|
ACPIPrint( (
|
|
ACPI_PRINT_RESOURCES_2,
|
|
"ACPIVectorEnable: GPE %x enabled\n",
|
|
gpeIndex
|
|
) );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
ACPIVectorFreeEntry (
|
|
ULONG TableIndex
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Free a GPE vector table entry.
|
|
NOTE: Should be called with the global GpeVectorTable locked.
|
|
|
|
Arguments:
|
|
|
|
TableIndex - Index into GPE vector table of entry to be freed
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Put onto free list
|
|
//
|
|
GpeVectorTable[TableIndex].Next = GpeVectorFree;
|
|
GpeVectorFree = (UCHAR) TableIndex;
|
|
}
|
|
|
|
BOOLEAN
|
|
ACPIVectorGetEntry (
|
|
PULONG TableIndex
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get a new vector entry from the GPE vector table.
|
|
NOTE: Should be called with the global GpeVectorTable locked.
|
|
|
|
Arguments:
|
|
|
|
TableIndex - Pointer to where the vector table index of the entry is returned
|
|
|
|
Return Value:
|
|
|
|
TRUE - Success
|
|
FALSE - Failure
|
|
|
|
--*/
|
|
{
|
|
PGPE_VECTOR_ENTRY Vector;
|
|
ULONG i, j;
|
|
|
|
#define NEW_TABLE_ENTRIES 4
|
|
|
|
if (!GpeVectorFree) {
|
|
|
|
//
|
|
// No free entries on vector table, make some
|
|
//
|
|
i = GpeVectorTableSize;
|
|
Vector = ExAllocatePoolWithTag (
|
|
NonPagedPool,
|
|
sizeof (GPE_VECTOR_ENTRY) * (i + NEW_TABLE_ENTRIES),
|
|
ACPI_SHARED_GPE_POOLTAG
|
|
);
|
|
if (Vector == NULL) {
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
//
|
|
// Make sure that its in a known state
|
|
//
|
|
RtlZeroMemory(
|
|
Vector,
|
|
(sizeof(GPE_VECTOR_ENTRY) * (i + NEW_TABLE_ENTRIES) )
|
|
);
|
|
|
|
//
|
|
// Copy old table to new
|
|
//
|
|
if (GpeVectorTable) {
|
|
|
|
RtlCopyMemory(
|
|
Vector,
|
|
GpeVectorTable,
|
|
sizeof (GPE_VECTOR_ENTRY) * i
|
|
);
|
|
ExFreePool (GpeVectorTable);
|
|
|
|
}
|
|
|
|
GpeVectorTableSize += NEW_TABLE_ENTRIES;
|
|
GpeVectorTable = Vector;
|
|
|
|
//
|
|
// Link new entries
|
|
//
|
|
for (j=0; j < NEW_TABLE_ENTRIES; j++) {
|
|
|
|
GpeVectorTable[i+j].Next = (UCHAR) (i+j+1);
|
|
|
|
}
|
|
|
|
//
|
|
// The last entry in the list gets pointed to 0, because we then
|
|
// want to grow this list again
|
|
//
|
|
GpeVectorTable[i+j-1].Next = 0;
|
|
|
|
//
|
|
// The next free vector the head of the list that we just allocated
|
|
//
|
|
GpeVectorFree = (UCHAR) i;
|
|
|
|
}
|
|
|
|
*TableIndex = GpeVectorFree;
|
|
Vector = &GpeVectorTable[GpeVectorFree];
|
|
GpeVectorFree = Vector->Next;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
ACPIVectorInstall(
|
|
ULONG GpeIndex,
|
|
PGPE_VECTOR_OBJECT GpeVectorObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Install a GPE handler into the Map and Vector tables
|
|
NOTE: Should be called with the global GpeVectorTable locked, and GPEs disabled
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE - Success
|
|
FALSE - Failure
|
|
|
|
--*/
|
|
{
|
|
ULONG byteIndex;
|
|
ULONG tableIndex;
|
|
|
|
//
|
|
// Get an entry in the global vector table
|
|
//
|
|
if (ACPIVectorGetEntry (&tableIndex)) {
|
|
|
|
//
|
|
// Install the entry into the map table
|
|
//
|
|
byteIndex = ACPIGpeIndexToByteIndex (GpeIndex);
|
|
GpeMap [byteIndex] = (UCHAR) tableIndex;
|
|
|
|
//
|
|
// Install the vector object in the vector table entry
|
|
//
|
|
GpeVectorTable [tableIndex].GpeVectorObject = GpeVectorObject;
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOLEAN
|
|
ACPIVectorRemove(
|
|
ULONG GpeIndex
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Remove a GPE handler from the Map and Vector tables
|
|
NOTE: Should be called with the global GpeVectorTable locked,
|
|
and GPEs disabled
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE - Success
|
|
FALSE - Failure
|
|
|
|
--*/
|
|
{
|
|
ULONG byteIndex;
|
|
ULONG tableIndex;
|
|
|
|
//
|
|
// Get the table index from the map table
|
|
//
|
|
byteIndex = ACPIGpeIndexToByteIndex (GpeIndex);
|
|
tableIndex = GpeMap [byteIndex];
|
|
|
|
//
|
|
// Bounds check
|
|
//
|
|
if (tableIndex >= GpeVectorTableSize) {
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
//
|
|
// Remember that we don't have this GpeVectorObject anymore
|
|
//
|
|
GpeVectorTable[tableIndex].GpeVectorObject = NULL;
|
|
|
|
//
|
|
// Free the slot in the master vector table
|
|
//
|
|
ACPIVectorFreeEntry (tableIndex);
|
|
return TRUE;
|
|
}
|
|
|