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