/*++ 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; }