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

5241 lines
141 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
irqarb.c
Abstract:
This module implements an arbiter for IRQs.
In a traditional machine, the BIOS sets up the
mapping of PCI interrupt sources (i.e. Bus 0, slot 4,
funtion 1, INT B maps to IRQ 10.) This mapping is
then forever fixed. On the other hand, an ACPI
machine can possibly change these mappings by
manipulating the "link nodes" in the AML namespace.
Since the ACPI driver is the agent of change, it is the
place to implement an arbiter.
Author:
Jake Oshins (jakeo) 6-2-97
Environment:
NT Kernel Model Driver only
Revision History:
--*/
#include "pch.h"
#include "string.h"
#include "stdlib.h"
#include "stdio.h"
#if DBG
extern LONG ArbDebugLevel;
#define DEBUG_PRINT(Level, Message) \
if (ArbDebugLevel >= Level) DbgPrint Message
#else
#define DEBUG_PRINT(Level, Message)
#endif
#define PciBridgeSwizzle(device, pin) \
((((pin - 1) + (device % 4)) % 4) + 1)
NTSTATUS
AcpiArbUnpackRequirement(
IN PIO_RESOURCE_DESCRIPTOR Descriptor,
OUT PULONGLONG Minimum,
OUT PULONGLONG Maximum,
OUT PULONG Length,
OUT PULONG Alignment
);
NTSTATUS
AcpiArbPackResource(
IN PIO_RESOURCE_DESCRIPTOR Requirement,
IN ULONGLONG Start,
OUT PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor
);
LONG
AcpiArbScoreRequirement(
IN PIO_RESOURCE_DESCRIPTOR Descriptor
);
NTSTATUS
AcpiArbUnpackResource(
IN PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor,
OUT PULONGLONG Start,
OUT PULONG Length
);
NTSTATUS
AcpiArbTestAllocation(
IN PARBITER_INSTANCE Arbiter,
IN OUT PLIST_ENTRY ArbitrationList
);
NTSTATUS
AcpiArbBootAllocation(
IN PARBITER_INSTANCE Arbiter,
IN OUT PLIST_ENTRY ArbitrationList
);
NTSTATUS
AcpiArbRetestAllocation(
IN PARBITER_INSTANCE Arbiter,
IN OUT PLIST_ENTRY ArbitrationList
);
NTSTATUS
AcpiArbRollbackAllocation(
PARBITER_INSTANCE Arbiter
);
NTSTATUS
AcpiArbCommitAllocation(
PARBITER_INSTANCE Arbiter
);
BOOLEAN
AcpiArbGetNextAllocationRange(
IN PARBITER_INSTANCE Arbiter,
IN OUT PARBITER_ALLOCATION_STATE State
);
NTSTATUS
AcpiArbCrackPRT(
IN PDEVICE_OBJECT Pdo,
IN OUT PNSOBJ *LinkNode,
IN OUT ULONG *Vector
);
PDEVICE_OBJECT
AcpiGetFilter(
IN PDEVICE_OBJECT Root,
IN PDEVICE_OBJECT Pdo
);
BOOLEAN
ArbFindSuitableRange(
PARBITER_INSTANCE Arbiter,
PARBITER_ALLOCATION_STATE State
);
BOOLEAN
AcpiArbFindSuitableRange(
PARBITER_INSTANCE Arbiter,
PARBITER_ALLOCATION_STATE State
);
VOID
AcpiArbAddAllocation(
IN PARBITER_INSTANCE Arbiter,
IN PARBITER_ALLOCATION_STATE State
);
VOID
AcpiArbBacktrackAllocation(
IN PARBITER_INSTANCE Arbiter,
IN PARBITER_ALLOCATION_STATE State
);
BOOLEAN
AcpiArbOverrideConflict(
IN PARBITER_INSTANCE Arbiter,
IN PARBITER_ALLOCATION_STATE State
);
BOOLEAN
LinkNodeInUse(
IN PARBITER_INSTANCE Arbiter,
IN PNSOBJ LinkNode,
IN OUT ULONG *Irq, OPTIONAL
IN OUT UCHAR *Flags OPTIONAL
);
NTSTATUS
AcpiArbReferenceLinkNode(
IN PARBITER_INSTANCE Arbiter,
IN PNSOBJ LinkNode,
IN ULONG Irq
);
NTSTATUS
AcpiArbDereferenceLinkNode(
IN PARBITER_INSTANCE Arbiter,
IN PNSOBJ LinkNode
);
NTSTATUS
AcpiArbSetLinkNodeIrq(
IN PNSOBJ LinkNode,
IN PCM_PARTIAL_RESOURCE_DESCRIPTOR LinkNodeIrq
);
NTSTATUS
EXPORT
AcpiArbSetLinkNodeIrqWorker(
IN PNSOBJ AcpiObject,
IN NTSTATUS Status,
IN POBJDATA Result,
IN PVOID Context
);
NTSTATUS
AcpiArbSetLinkNodeIrqAsync(
IN PNSOBJ LinkNode,
IN PCM_PARTIAL_RESOURCE_DESCRIPTOR LinkNodeIrq,
IN PFNACB CompletionHandler,
IN PVOID CompletionContext
);
NTSTATUS
AcpiArbPreprocessEntry(
IN PARBITER_INSTANCE Arbiter,
IN PARBITER_ALLOCATION_STATE State
);
NTSTATUS
AcpiArbQueryConflict(
IN PARBITER_INSTANCE Arbiter,
IN PDEVICE_OBJECT PhysicalDeviceObject,
IN PIO_RESOURCE_DESCRIPTOR ConflictingResource,
OUT PULONG ConflictCount,
OUT PARBITER_CONFLICT_INFO *Conflicts
);
NTSTATUS
EXPORT
IrqArbRestoreIrqRoutingWorker(
IN PNSOBJ AcpiObject,
IN NTSTATUS Status,
IN POBJDATA Result,
IN PVOID Context
);
NTSTATUS
DisableLinkNodesAsync(
IN PNSOBJ Root,
IN PFNACB CompletionHandler,
IN PVOID CompletionContext
);
NTSTATUS
EXPORT
DisableLinkNodesAsyncWorker(
IN PNSOBJ AcpiObject,
IN NTSTATUS Status,
IN POBJDATA Result,
IN PVOID Context
);
NTSTATUS
UnreferenceArbitrationList(
IN PARBITER_INSTANCE Arbiter,
IN OUT PLIST_ENTRY ArbitrationList
);
NTSTATUS
ClearTempLinkNodeCounts(
IN PARBITER_INSTANCE Arbiter
);
NTSTATUS
MakeTempLinkNodeCountsPermanent(
IN PARBITER_INSTANCE Arbiter
);
PVECTOR_BLOCK
HashVector(
IN ULONG Vector
);
NTSTATUS
AddVectorToTable(
IN ULONG Vector,
IN UCHAR ReferenceCount,
IN UCHAR TempRefCount,
IN UCHAR Flags
);
VOID
ClearTempVectorCounts(
VOID
);
VOID
MakeTempVectorCountsPermanent(
VOID
);
VOID
DumpVectorTable(
VOID
);
VOID
DereferenceVector(
IN ULONG Vector
);
VOID
ReferenceVector(
IN ULONG Vector,
IN UCHAR Flags
);
NTSTATUS
LookupIsaVectorOverride(
IN ULONG IsaVector,
IN OUT ULONG *RedirectionVector OPTIONAL,
IN OUT UCHAR *Flags OPTIONAL
);
NTSTATUS
GetLinkNodeFlags(
IN PARBITER_INSTANCE Arbiter,
IN PNSOBJ LinkNode,
IN OUT UCHAR *Flags
);
NTSTATUS
GetIsaVectorFlags(
IN ULONG Vector,
IN OUT UCHAR *Flags
);
VOID
TrackDevicesConnectedToLinkNode(
IN PNSOBJ LinkNode,
IN PDEVICE_OBJECT Pdo
);
NTSTATUS
FindVectorInAlternatives(
IN PARBITER_INSTANCE Arbiter,
IN PARBITER_ALLOCATION_STATE State,
IN ULONGLONG Vector,
OUT ULONG *Alternative
);
NTSTATUS
FindBootConfig(
IN PARBITER_INSTANCE Arbiter,
IN PARBITER_ALLOCATION_STATE State,
IN ULONGLONG *Vector
);
//
// The following is a hash table. It is VECTOR_HASH_TABLE_LENGTH entries
// long and VECTOR_HASH_TABLE_WIDTH entries wide. We hash on the numerical
// value of the IRQ modulo the length of the table. We look across the
// table until we find the entry that matches the vector. If we get to
// the end of the row and find an entry marked with TOKEN_VALUE, we follow
// the pointer to an extension of this row in the table.
//
// ---------------------------------------------------------------------
//| (IRQ number, ref counts, flags) | (IRQ number, ref counts, flags)
//| (IRQ number, ref counts, flags) | (IRQ number, ref counts, flags)
//| (IRQ number, ref counts, flags) | (TOKEN_VALUE, pointer to new table row)
//| (IRQ number, ref counts, flags) | (unused entry (0))
//| (IRQ number, ref counts, flags) | (IRQ number, ref counts, flags)
//----------------------------------------------------------------------
//
// New table row, pointed to by pointer following TOKEN_VALUE:
//
//----------------------------------------------------------
//| (IRQ number, ref counts, flags) | (unused entry (0))
//----------------------------------------------------------
//
#define HASH_ENTRY(x, y) \
(IrqHashTable + (x * VECTOR_HASH_TABLE_WIDTH) + y)
PVECTOR_BLOCK IrqHashTable;
ULONG InterruptModel = 0;
ULONG AcpiSciVector;
UCHAR AcpiIrqDefaultBootConfig = 0;
UCHAR AcpiArbPciAlternativeRotation = 0;
BOOLEAN AcpiArbCardbusPresent = FALSE;
enum {
AcpiIrqDistributionDispositionDontCare = 0,
AcpiIrqDistributionDispositionSpreadOut,
AcpiIrqDistributionDispositionStackUp
} AcpiIrqDistributionDisposition = 0;
typedef enum {
AcpiIrqNextRangeMinState = 0xfff,
AcpiIrqNextRangeInit,
AcpiIrqNextRangeInitPolicyNeutral,
AcpiIrqNextRangeInitPic,
AcpiIrqNextRangeInitLegacy,
AcpiIrqNextRangeBootRegAlternative,
AcpiIrqNextRangeSciAlternative,
AcpiIrqNextRangeUseBootConfig,
AcpiIrqNextRangeAlternativeZero,
AcpiIrqNextRangeAlternativeN,
AcpiIrqNextRangeMaxState
} NEXT_RANGE_STATE, *PNEXT_RANGE_STATE;
#define ARBITER_INTERRUPT_LEVEL_SENSATIVE 0x10
#define ARBITER_INTERRUPT_LATCHED 0x20
#define ARBITER_INTERRUPT_BITS (ARBITER_INTERRUPT_LATCHED | ARBITER_INTERRUPT_LEVEL_SENSATIVE)
#define ISA_PIC_VECTORS 16
#define ALTERNATIVE_SHUFFLE_SIZE 0x10
extern BOOLEAN AcpiInterruptRoutingFailed;
extern PACPIInformation AcpiInformation;
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, AcpiInitIrqArbiter)
#pragma alloc_text(PAGE, AcpiArbInitializePciRouting)
#pragma alloc_text(PAGE, AcpiArbUnpackRequirement)
#pragma alloc_text(PAGE, AcpiArbPackResource)
#pragma alloc_text(PAGE, AcpiArbScoreRequirement)
#pragma alloc_text(PAGE, AcpiArbUnpackResource)
#pragma alloc_text(PAGE, AcpiArbFindSuitableRange)
#pragma alloc_text(PAGE, AcpiArbAddAllocation)
#pragma alloc_text(PAGE, AcpiArbBacktrackAllocation)
#pragma alloc_text(PAGE, AcpiArbGetLinkNodeOptions)
#pragma alloc_text(PAGE, AcpiArbTestAllocation)
#pragma alloc_text(PAGE, AcpiArbBootAllocation)
#pragma alloc_text(PAGE, AcpiArbRetestAllocation)
#pragma alloc_text(PAGE, AcpiArbRollbackAllocation)
#pragma alloc_text(PAGE, AcpiArbCommitAllocation)
#pragma alloc_text(PAGE, AcpiArbReferenceLinkNode)
#pragma alloc_text(PAGE, AcpiArbDereferenceLinkNode)
#pragma alloc_text(PAGE, AcpiArbSetLinkNodeIrq)
#pragma alloc_text(PAGE, AcpiArbPreprocessEntry)
#pragma alloc_text(PAGE, AcpiArbOverrideConflict)
#pragma alloc_text(PAGE, AcpiArbQueryConflict)
#pragma alloc_text(PAGE, AcpiArbGetNextAllocationRange)
#pragma alloc_text(PAGE, LinkNodeInUse)
#pragma alloc_text(PAGE, GetLinkNodeFlags)
#pragma alloc_text(PAGE, UnreferenceArbitrationList)
#pragma alloc_text(PAGE, ClearTempLinkNodeCounts)
#pragma alloc_text(PAGE, MakeTempLinkNodeCountsPermanent)
#pragma alloc_text(PAGE, HashVector)
#pragma alloc_text(PAGE, GetVectorProperties)
#pragma alloc_text(PAGE, AddVectorToTable)
#pragma alloc_text(PAGE, ReferenceVector)
#pragma alloc_text(PAGE, DereferenceVector)
#pragma alloc_text(PAGE, ClearTempVectorCounts)
#pragma alloc_text(PAGE, MakeTempVectorCountsPermanent)
#pragma alloc_text(PAGE, TrackDevicesConnectedToLinkNode)
#pragma alloc_text(PAGE, LookupIsaVectorOverride)
#pragma alloc_text(PAGE, GetIsaVectorFlags)
#pragma alloc_text(PAGE, FindVectorInAlternatives)
#pragma alloc_text(PAGE, FindBootConfig)
#endif
NTSTATUS
AcpiInitIrqArbiter(
PDEVICE_OBJECT RootFdo
)
{
AMLISUPP_CONTEXT_PASSIVE context;
PARBITER_EXTENSION arbExt;
NTSTATUS status;
ULONG rawVector, adjVector, level;
UCHAR flags;
UCHAR buffer[PCI_COMMON_HDR_LENGTH];
PPCI_COMMON_CONFIG pciData;
BOOLEAN foundBootConfig, noBootConfigAgreement;
ULONG deviceNum, funcNum;
UCHAR lastBus, currentBus;
PCI_SLOT_NUMBER pciSlot;
UNICODE_STRING driverKey;
HANDLE driverKeyHandle = NULL;
PKEY_VALUE_PARTIAL_INFORMATION_ALIGN64 regValue=NULL;
PAGED_CODE();
//
// Set up arbiter.
//
arbExt = ExAllocatePoolWithTag(NonPagedPool, sizeof(ARBITER_EXTENSION), ACPI_ARBITER_POOLTAG);
if (!arbExt) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(arbExt, sizeof(ARBITER_EXTENSION));
InitializeListHead(&(arbExt->LinkNodeHead));
AcpiArbiter.ArbiterState.Extension = arbExt;
AcpiArbiter.DeviceObject = RootFdo;
AcpiArbiter.ArbiterState.UnpackRequirement = AcpiArbUnpackRequirement;
AcpiArbiter.ArbiterState.PackResource = AcpiArbPackResource;
AcpiArbiter.ArbiterState.UnpackResource = AcpiArbUnpackResource;
AcpiArbiter.ArbiterState.ScoreRequirement = AcpiArbScoreRequirement;
AcpiArbiter.ArbiterState.FindSuitableRange = AcpiArbFindSuitableRange;
AcpiArbiter.ArbiterState.TestAllocation = AcpiArbTestAllocation;
AcpiArbiter.ArbiterState.BootAllocation = AcpiArbBootAllocation;
AcpiArbiter.ArbiterState.RetestAllocation = AcpiArbRetestAllocation;
AcpiArbiter.ArbiterState.RollbackAllocation = AcpiArbRollbackAllocation;
AcpiArbiter.ArbiterState.CommitAllocation = AcpiArbCommitAllocation;
AcpiArbiter.ArbiterState.AddAllocation = AcpiArbAddAllocation;
AcpiArbiter.ArbiterState.BacktrackAllocation = AcpiArbBacktrackAllocation;
AcpiArbiter.ArbiterState.PreprocessEntry = AcpiArbPreprocessEntry;
AcpiArbiter.ArbiterState.OverrideConflict = AcpiArbOverrideConflict;
AcpiArbiter.ArbiterState.QueryConflict = AcpiArbQueryConflict;
AcpiArbiter.ArbiterState.GetNextAllocationRange = AcpiArbGetNextAllocationRange;
IrqHashTable = ExAllocatePoolWithTag(PagedPool,
VECTOR_HASH_TABLE_SIZE,
ACPI_ARBITER_POOLTAG
);
if (!IrqHashTable) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto AcpiInitIrqArbiterError;
}
RtlFillMemory(IrqHashTable,
VECTOR_HASH_TABLE_SIZE,
(UCHAR)(EMPTY_BLOCK_VALUE & 0xff));
//
// Do the generic part of initialization.
//
status = ArbInitializeArbiterInstance(&AcpiArbiter.ArbiterState,
RootFdo,
CmResourceTypeInterrupt,
L"ACPI_IRQ",
L"Root",
NULL
);
if (!NT_SUCCESS(status)) {
status = STATUS_UNSUCCESSFUL;
goto AcpiInitIrqArbiterError;
}
//
// Now claim the IRQ that ACPI itself is using.
//
rawVector = AcpiInformation->FixedACPIDescTable->sci_int_vector;
//
// Assume that the ACPI vector is active low,
// level triggered. (This may be changed
// by the MAPIC table.)
//
flags = VECTOR_LEVEL | VECTOR_ACTIVE_LOW;
adjVector = rawVector;
LookupIsaVectorOverride(adjVector,
&adjVector,
&flags);
RtlAddRange(AcpiArbiter.ArbiterState.Allocation,
(ULONGLONG)adjVector,
(ULONGLONG)adjVector,
0,
RTL_RANGE_LIST_ADD_SHARED,
NULL,
((PDEVICE_EXTENSION)RootFdo->DeviceExtension)->PhysicalDeviceObject
);
//
// Record the status for this vector
//
ReferenceVector(adjVector,
flags);
AcpiSciVector = adjVector;
MakeTempVectorCountsPermanent();
//
// Disable all the link nodes in the namespace so that we
// have a fresh slate to work with.
//
KeInitializeEvent(&context.Event, SynchronizationEvent, FALSE);
context.Status = STATUS_UNSUCCESSFUL;
status = DisableLinkNodesAsync(((PDEVICE_EXTENSION)RootFdo->DeviceExtension)->AcpiObject,
AmlisuppCompletePassive,
(PVOID)&context);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&context.Event,
Executive,
KernelMode,
FALSE,
NULL);
status = context.Status;
}
//
// Scan the machine looking at its initial configuration. If
// it a) has a cardbus controller and b) all the boot configs
// for PCI devices are the same, then record that boot config
// vector for use in AcpiArbGetNextAllocationRange.
//
// Note: This algorithm only scans the first PCI root and
// its children. The assumption is that multiple root machines
// will be running in APIC mode or have many different boot
// configs.
//
pciData = (PPCI_COMMON_CONFIG)buffer;
lastBus = 0;
currentBus = 0;
foundBootConfig = FALSE;
noBootConfigAgreement = FALSE;
while (TRUE) {
pciSlot.u.AsULONG = 0;
for (deviceNum = 0; deviceNum < PCI_MAX_DEVICES; deviceNum++) {
for (funcNum = 0; funcNum < PCI_MAX_FUNCTION; funcNum++) {
pciSlot.u.bits.DeviceNumber = deviceNum;
pciSlot.u.bits.FunctionNumber = funcNum;
HalPciInterfaceReadConfig(NULL,
currentBus,
pciSlot.u.AsULONG,
pciData,
0,
PCI_COMMON_HDR_LENGTH);
if (pciData->VendorID != PCI_INVALID_VENDORID) {
if (PCI_CONFIGURATION_TYPE(pciData) == PCI_DEVICE_TYPE) {
if (pciData->u.type0.InterruptPin) {
//
// This device generates an interrupt.
//
if ((pciData->u.type0.InterruptLine > 0) &&
(pciData->u.type0.InterruptLine < 0xff)) {
//
// And it has a boot config.
//
if (foundBootConfig) {
if (pciData->u.type0.InterruptLine != AcpiIrqDefaultBootConfig) {
noBootConfigAgreement = TRUE;
break;
}
} else {
//
// Record this boot config
//
AcpiIrqDefaultBootConfig = pciData->u.type0.InterruptLine;
foundBootConfig = TRUE;
}
}
}
} else {
//
// This is a bridge. Update lastBus with the Subordinate
// bus if it is higher.
//
lastBus = lastBus > pciData->u.type1.SubordinateBus ?
lastBus : pciData->u.type1.SubordinateBus;
if (PCI_CONFIGURATION_TYPE(pciData) == PCI_CARDBUS_BRIDGE_TYPE) {
AcpiArbCardbusPresent = TRUE;
}
}
if (!PCI_MULTIFUNCTION_DEVICE(pciData) &&
(funcNum == 0)) {
break;
}
} else {
break;
}
}
}
if (lastBus == currentBus++) {
break;
}
}
if (!foundBootConfig ||
noBootConfigAgreement ||
!AcpiArbCardbusPresent) {
//
// There is no single default boot config.
//
AcpiIrqDefaultBootConfig = 0;
}
//
// Now look in the registry for configuration flags.
//
RtlInitUnicodeString( &driverKey,
L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\ACPI\\Parameters");
status = OSOpenUnicodeHandle(
&driverKey,
NULL,
&driverKeyHandle);
if (NT_SUCCESS(status)) {
status = OSGetRegistryValue(
driverKeyHandle,
L"IRQDistribution",
&regValue);
if (NT_SUCCESS(status)) {
if ((regValue->DataLength != 0) &&
(regValue->Type == REG_DWORD)) {
//
// We have successfully found the key for
// IRQ Distribution Disposition.
//
AcpiIrqDistributionDisposition =
*((ULONG*)( ((PUCHAR)regValue->Data)));
}
ExFreePool(regValue);
}
status = OSGetRegistryValue(
driverKeyHandle,
L"ForcePCIBootConfig",
&regValue);
if (NT_SUCCESS(status)) {
if ((regValue->DataLength != 0) &&
(regValue->Type == REG_DWORD)) {
//
// We have successfully found the key for
// PCI Boot Configs.
//
AcpiIrqDefaultBootConfig =
*(PUCHAR)regValue->Data;
}
ExFreePool(regValue);
}
OSCloseHandle(driverKeyHandle);
}
return STATUS_SUCCESS;
AcpiInitIrqArbiterError:
if (arbExt) ExFreePool(arbExt);
if (IrqHashTable) ExFreePool(IrqHashTable);
if (driverKeyHandle) OSCloseHandle(driverKeyHandle);
if (regValue) ExFreePool(regValue);
return status;
}
NTSTATUS
AcpiArbInitializePciRouting(
PDEVICE_OBJECT PciPdo
)
{
PINT_ROUTE_INTERFACE_STANDARD interface;
NTSTATUS status;
IO_STACK_LOCATION irpSp;
PWSTR buffer;
PDEVICE_OBJECT topDeviceInStack;
PAGED_CODE();
//
// Send an IRP to the PCI driver to get the Interrupt Routing Interface.
//
RtlZeroMemory( &irpSp, sizeof(IO_STACK_LOCATION) );
interface = ExAllocatePoolWithTag(NonPagedPool, sizeof(INT_ROUTE_INTERFACE_STANDARD), ACPI_ARBITER_POOLTAG);
if (!interface) {
return STATUS_INSUFFICIENT_RESOURCES;
}
topDeviceInStack = IoGetAttachedDeviceReference(PciPdo);
//
// Set the function codes and parameters.
//
irpSp.MajorFunction = IRP_MJ_PNP;
irpSp.MinorFunction = IRP_MN_QUERY_INTERFACE;
irpSp.Parameters.QueryInterface.InterfaceType = (LPGUID) &GUID_INT_ROUTE_INTERFACE_STANDARD;
irpSp.Parameters.QueryInterface.Version = PCI_INT_ROUTE_INTRF_STANDARD_VER;
irpSp.Parameters.QueryInterface.Size = sizeof (INT_ROUTE_INTERFACE_STANDARD);
irpSp.Parameters.QueryInterface.Interface = (PINTERFACE) interface;
irpSp.Parameters.QueryInterface.InterfaceSpecificData = NULL;
//
// Call the PCI driver (indirectly.)
//
status = ACPIInternalSendSynchronousIrp(topDeviceInStack,
&irpSp,
&buffer);
if (NT_SUCCESS(status)) {
//
// Attach this interface to the Arbiter Extension.
//
((PARBITER_EXTENSION)AcpiArbiter.ArbiterState.Extension)->InterruptRouting = interface;
//
// Reference it.
//
interface->InterfaceReference(interface->Context);
PciInterfacesInstantiated = TRUE;
} else {
ExFreePool(interface);
}
ObDereferenceObject(topDeviceInStack);
return status;
}
//
// Arbiter callbacks
//
NTSTATUS
AcpiArbUnpackRequirement(
IN PIO_RESOURCE_DESCRIPTOR Descriptor,
OUT PULONGLONG Minimum,
OUT PULONGLONG Maximum,
OUT PULONG Length,
OUT PULONG Alignment
)
{
PAGED_CODE();
ASSERT(Descriptor);
ASSERT(Descriptor->Type == CmResourceTypeInterrupt);
*Minimum = (ULONGLONG) Descriptor->u.Interrupt.MinimumVector;
*Maximum = (ULONGLONG) Descriptor->u.Interrupt.MaximumVector;
*Length = 1;
*Alignment = 1;
return STATUS_SUCCESS;
}
LONG
AcpiArbScoreRequirement(
IN PIO_RESOURCE_DESCRIPTOR Descriptor
)
{
LONG score;
ASSERT(Descriptor);
ASSERT(Descriptor->Type == CmResourceTypeInterrupt);
//
// TEMPTEMP HACKHACK
// (Possibly) temporary hack that allows the PnP
// manager to include invalid resources in the
// arbitration list.
//
if (Descriptor->u.Interrupt.MinimumVector >
Descriptor->u.Interrupt.MaximumVector) {
return 0;
}
ASSERT(Descriptor->u.Interrupt.MinimumVector <=
Descriptor->u.Interrupt.MaximumVector);
score = Descriptor->u.Interrupt.MaximumVector -
Descriptor->u.Interrupt.MinimumVector + 1;
//
// Give a little boost to any request above the
// traditional ISA range.
// N.B. This will probably never matter, as
// most machines will present all the choices
// either inside or outside of the ISA range.
//
if (Descriptor->u.Interrupt.MaximumVector >= 16) {
score += 5;
}
return score;
}
NTSTATUS
AcpiArbPackResource(
IN PIO_RESOURCE_DESCRIPTOR Requirement,
IN ULONGLONG Start,
OUT PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor
)
{
PAGED_CODE();
ASSERT(Descriptor);
ASSERT(Start < ((ULONG)-1));
ASSERT(Requirement);
ASSERT(Requirement->Type == CmResourceTypeInterrupt);
Descriptor->Type = CmResourceTypeInterrupt;
Descriptor->Flags = Requirement->Flags;
Descriptor->ShareDisposition = Requirement->ShareDisposition;
Descriptor->u.Interrupt.Vector = (ULONG) Start;
Descriptor->u.Interrupt.Level = (ULONG) Start;
Descriptor->u.Interrupt.Affinity = 0xFFFFFFFF;
return STATUS_SUCCESS;
}
NTSTATUS
AcpiArbUnpackResource(
IN PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor,
OUT PULONGLONG Start,
OUT PULONG Length
)
{
ASSERT(Descriptor);
ASSERT(Descriptor->Type == CmResourceTypeInterrupt);
*Start = Descriptor->u.Interrupt.Vector;
*Length = 1;
return STATUS_SUCCESS;
}
BOOLEAN
AcpiArbOverrideConflict(
IN PARBITER_INSTANCE Arbiter,
IN PARBITER_ALLOCATION_STATE State
)
{
//
// Self-conflicts are not allowable with this arbiter.
//
PAGED_CODE();
return FALSE;
}
NTSTATUS
AcpiArbPreprocessEntry(
IN PARBITER_INSTANCE Arbiter,
IN PARBITER_ALLOCATION_STATE State
)
/*++
Routine Description:
This routine is called from AllocateEntry to allow preprocessing of
entries
Arguments:
Arbiter - The instance data of the arbiter who was called.
State - The state of the current arbitration.
Return Value:
None.
--*/
{
#define CM_RESOURE_INTERRUPT_LEVEL_LATCHED_BITS 0x0001
PARBITER_ALTERNATIVE current;
USHORT flags;
PAGED_CODE();
//
// Check if this is a level (PCI) or latched (ISA) interrupt and set
// RangeAttributes accordingly so we set the appropriate flag when we add the
// range
//
if ((State->Alternatives[0].Descriptor->Flags
& CM_RESOURE_INTERRUPT_LEVEL_LATCHED_BITS)
== CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE) {
State->RangeAttributes &= ~ARBITER_INTERRUPT_BITS;
State->RangeAttributes |= ARBITER_INTERRUPT_LEVEL_SENSATIVE;
flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
} else {
ASSERT(State->Alternatives[0].Descriptor->Flags
& CM_RESOURCE_INTERRUPT_LATCHED);
State->RangeAttributes &= ~ARBITER_INTERRUPT_BITS;
State->RangeAttributes |= ARBITER_INTERRUPT_LATCHED;
flags = CM_RESOURCE_INTERRUPT_LATCHED;
}
#if 0
//
// Make sure that all the alternatives are of the same type
//
FOR_ALL_IN_ARRAY(State->Alternatives,
State->Entry->AlternativeCount,
current) {
ASSERT((current->Descriptor->Flags
& CM_RESOURE_INTERRUPT_LEVEL_LATCHED_BITS) == flags);
}
#endif
return STATUS_SUCCESS;
}
BOOLEAN
AcpiArbFindSuitableRange(
PARBITER_INSTANCE Arbiter,
PARBITER_ALLOCATION_STATE State
)
/*++
Routine Description:
This routine finds an IRQ for a device object. For
non-PCI devices, this is as simple as returning the
result from PnpFindSuitableRange. For PCI devices,
this is done by examining the state of the "link
nodes" described in the ACPI namespace.
Arguments:
Arbiter - the ACPI IRQ arbiter
State - the current allocation under consideration
Return Value:
TRUE if a suitable vector has been found,
FALSE otherwise.
Notes:
Statement of algorithm for PCI devices:
1) Find the entry in the _PRT that corresponds
to the device for which we are arbitrating
resources.
2) Determine, from the _PRT information, whether
this device is connected to a "link node."
(A PCI device will typically be connected to
a link node while in PIC mode but not in
APIC mode.)
3) If it is not, use the static mapping from the
_PRT.
4) If it is connected to a "link node," check
to see if the "link node" is in use.
5) If the link node is in use, then this
device must use the same IRQ that the link node is currently
using. This implies that there is already
some other device in the system connected to
this interrupt and now the two (or more) will
be sharing. This is acceptable and inevitable.
Two devices that have their interrupt lines
wire-or'd should be sharing. Two devices that
don't have their interrupt lines wire-or'd
should be represented by separate link nodes
in the namespace.
6) If the link node is not in use, pick an IRQ
from the list that the link node can support
and grant it to the device. There is some
attempt to pick an IRQ that is not currently
in use.
--*/
{
PCM_PARTIAL_RESOURCE_DESCRIPTOR potentialIrq;
PIO_RESOURCE_DESCRIPTOR alternative;
PARBITER_EXTENSION arbExtension;
PCM_RESOURCE_LIST linkNodeResList = NULL;
NTSTATUS status;
BOOLEAN possibleAllocation;
PNSOBJ linkNode = NULL;
ULONG deviceIrq = 0;
ULONG linkNodeIrqCount, i;
UCHAR vectorFlags, deviceFlags;
PAGED_CODE();
arbExtension = (PARBITER_EXTENSION)Arbiter->Extension;
ASSERT(arbExtension);
//
// First, see if this resource could even be made to work at all.
// (I.e. Has something already claimed this as non-sharable?)
//
possibleAllocation = ArbFindSuitableRange(Arbiter, State);
if (!possibleAllocation) {
return FALSE;
}
//
// Is this Device connected to a link node?
//
status = AcpiArbCrackPRT(State->Entry->PhysicalDeviceObject,
&linkNode,
&deviceIrq);
//
// If this PDO is connected to a link node, we want to clip
// the list of possible IRQ settings down.
//
switch (status) {
case STATUS_SUCCESS:
//
// AcpiArbCrackPRT fills in either linkNode or deviceIrq.
// If linkNode is filled in, then we need to look at it.
// If deviceIrq is filled in, then we only need to clip
// the list to that single IRQ.
//
if (linkNode) {
//
// If the link node is currently in use, then we can
// just connect this device to the IRQ that the link
// node is currently using.
//
if (LinkNodeInUse(Arbiter, linkNode, &deviceIrq, NULL)) {
if ((State->CurrentMinimum <= deviceIrq) &&
(State->CurrentMaximum >= deviceIrq)) {
State->Start = deviceIrq;
State->End = deviceIrq;
State->CurrentAlternative->Length = 1;
DEBUG_PRINT(1, ("FindSuitableRange found %x from a link node that is in use.\n",
(ULONG)(State->Start & 0xffffffff)));
ASSERT(HalIsVectorValid(deviceIrq));
return TRUE;
} else {
DEBUG_PRINT(1, ("FindSuitableRange found %x from a link node that is in use.\n",
deviceIrq));
DEBUG_PRINT(1, (" This was, however, not within the range of possibilites (%x-%x).\n",
(ULONG)(State->Start & 0xffffffff),
(ULONG)(State->End & 0xffffffff)));
return FALSE;
}
} else {
//
// Get the set of IRQs that this link node can
// connect to.
//
status = AcpiArbGetLinkNodeOptions(linkNode,
&linkNodeResList,
&deviceFlags);
DEBUG_PRINT(1, ("Link node contained CM(%p)\n", linkNodeResList));
if (NT_SUCCESS(status)) {
ASSERT(linkNodeResList->Count == 1);
linkNodeIrqCount =
linkNodeResList->List[0].PartialResourceList.Count;
for (i = 0; i < linkNodeIrqCount; i++) {
potentialIrq =
&(linkNodeResList->List[0].PartialResourceList.PartialDescriptors[(i + AcpiArbPciAlternativeRotation) % linkNodeIrqCount]);
ASSERT(potentialIrq->Type == CmResourceTypeInterrupt);
//
// Check for a conflict in mode.
//
status = GetVectorProperties(potentialIrq->u.Interrupt.Vector,
&vectorFlags);
if (NT_SUCCESS(status)) {
//
// Success here means that this vector is currently allocated
// to somebody. Check to see whether the link node being
// considered has the same mode and polarity as the other
// thing(s) assigned to this vector.
//
if (deviceFlags != vectorFlags) {
//
// The flags don't match. So skip this possibility.
//
continue;
}
}
if ((potentialIrq->u.Interrupt.Vector >= State->CurrentMinimum) &&
(potentialIrq->u.Interrupt.Vector <= State->CurrentMaximum)) {
if (!HalIsVectorValid(potentialIrq->u.Interrupt.Vector)) {
deviceIrq = potentialIrq->u.Interrupt.Vector;
ExFreePool(linkNodeResList);
goto FindSuitableRangeError;
}
State->Start = potentialIrq->u.Interrupt.Vector;
State->End = potentialIrq->u.Interrupt.Vector;
State->CurrentAlternative->Length = 1;
DEBUG_PRINT(1, ("FindSuitableRange found %x from an unused link node.\n",
(ULONG)(State->Start & 0xffffffff)));
ExFreePool(linkNodeResList);
//
// Record the link node that we got this from.
//
arbExtension->CurrentLinkNode = linkNode;
//
// Record this as the last PCI IRQ that we are handing out.
//
arbExtension->LastPciIrq[arbExtension->LastPciIrqIndex] =
State->Start;
arbExtension->LastPciIrqIndex =
(arbExtension->LastPciIrqIndex + 1) % LAST_PCI_IRQ_BUFFER_SIZE;
return TRUE;
}
}
ExFreePool(linkNodeResList);
}
DEBUG_PRINT(1, ("FindSuitableRange: AcpiArbGetLinkNodeOptions returned %x.\n\tlinkNodeResList: %p\n",
status, linkNodeResList));
// We didn't find a match.
return FALSE;
}
} else {
//
// This is the case where the _PRT contains a static mapping. Static
// Mappings imply active-low, level-triggered interrupts.
//
status = GetVectorProperties(deviceIrq,
&vectorFlags);
if (NT_SUCCESS(status)) {
//
// The vector is in use.
//
if (((vectorFlags & VECTOR_MODE) != VECTOR_LEVEL) ||
((vectorFlags & VECTOR_POLARITY) != VECTOR_ACTIVE_LOW)) {
//
// And it's flags don't match.
//
return FALSE;
}
}
// Valid static vector
if ((State->CurrentMinimum <= deviceIrq) &&
(State->CurrentMaximum >= deviceIrq)) {
DEBUG_PRINT(1, ("FindSuitableRange found %x from a static mapping.\n",
(ULONG)(State->Start & 0xffffffff)));
if (!HalIsVectorValid(deviceIrq)) {
goto FindSuitableRangeError;
}
State->Start = deviceIrq;
State->End = deviceIrq;
State->CurrentAlternative->Length = 1;
return TRUE;
} else {
return FALSE;
}
}
break;
case STATUS_UNSUCCESSFUL:
return FALSE;
break;
case STATUS_RESOURCE_REQUIREMENTS_CHANGED:
//
// Fall through to default.
//
default:
//
// Not PCI.
//
for (deviceIrq = (ULONG)(State->Start & 0xffffffff);
deviceIrq <= (ULONG)(State->End & 0xffffffff); deviceIrq++) {
status = GetIsaVectorFlags((ULONG)deviceIrq,
&deviceFlags);
if (!NT_SUCCESS(status)) {
//
// Not overridden. Assume that the device flags conform to bus.
//
deviceFlags = (State->CurrentAlternative->Descriptor->Flags
== CM_RESOURCE_INTERRUPT_LATCHED) ?
VECTOR_EDGE | VECTOR_ACTIVE_HIGH :
VECTOR_LEVEL | VECTOR_ACTIVE_LOW;
}
status = GetVectorProperties((ULONG)deviceIrq,
&vectorFlags);
if (NT_SUCCESS(status)) {
//
// This vector is currently in use. So if this is to be a suitable
// range, then the flags must match.
//
if (deviceFlags != vectorFlags) {
continue;
}
}
if (!HalIsVectorValid(deviceIrq)) {
goto FindSuitableRangeError;
}
State->Start = deviceIrq;
State->End = deviceIrq;
State->CurrentAlternative->Length = 1;
return TRUE;
}
return FALSE;
}
return FALSE;
FindSuitableRangeError:
{
UNICODE_STRING vectorName;
PWCHAR prtEntry[2];
WCHAR IRQARBname[20];
WCHAR vectorBuff[10];
//
// Make an errorlog entry saying that the chosen IRQ doesn't
// exist.
//
swprintf( IRQARBname, L"IRQARB");
RtlInitUnicodeString(&vectorName, vectorBuff);
if (!NT_SUCCESS(RtlIntegerToUnicodeString(deviceIrq, 0, &vectorName))) {
return FALSE;
}
prtEntry[0] = IRQARBname;
prtEntry[1] = vectorBuff;
ACPIWriteEventLogEntry(ACPI_ERR_ILLEGAL_IRQ_NUMBER,
&prtEntry,
2,
NULL,
0);
}
return FALSE;
}
VOID
AcpiArbAddAllocation(
IN PARBITER_INSTANCE Arbiter,
IN PARBITER_ALLOCATION_STATE State
)
{
NTSTATUS status;
PNSOBJ linkNode;
ULONG sourceIndex;
ULONG rangeFlags = 0;
PVOID referencedNode = NULL;
UCHAR flags, previousFlags;
ROUTING_TOKEN token;
BOOLEAN inUse;
UCHAR attributes = 0;
PAGED_CODE();
ASSERT(State->CurrentAlternative->Descriptor->Type == CmResourceTypeInterrupt);
DEBUG_PRINT(1, ("Adding allocation for IRQ %x for device %p\n",
(ULONG)(State->Start & 0xffffffff),
State->Entry->PhysicalDeviceObject));
//
// Identify the potential link node.
//
status = AcpiArbCrackPRT(State->Entry->PhysicalDeviceObject,
&linkNode,
&sourceIndex);
if (NT_SUCCESS(status)) {
//
// PCI device. Default flags are standard for PCI.
//
flags = VECTOR_LEVEL | VECTOR_ACTIVE_LOW;
ASSERT(State->Start == State->End);
if (!(State->Flags & ARBITER_STATE_FLAG_BOOT)) {
//
// Only keep track of link nodes manipulation if this is not
// a boot config allocation.
//
//
// If this device is connected to a link node, reference it.
//
if (linkNode) {
AcpiArbReferenceLinkNode(Arbiter,
linkNode,
(ULONG)State->Start);
referencedNode = (PVOID)linkNode;
//
// Find out what the flags for this link node are.
// Note that this is only guaranteed to be valid
// after we have referenced the link node.
//
inUse = LinkNodeInUse(Arbiter,
linkNode,
NULL,
&flags);
ASSERT(inUse);
ASSERT((flags & ~(VECTOR_MODE | VECTOR_POLARITY | VECTOR_TYPE)) == 0);
ASSERT(State->CurrentAlternative->Descriptor->Flags
== CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE ?
(flags & VECTOR_MODE) == VECTOR_LEVEL :
(flags & VECTOR_MODE) == VECTOR_EDGE);
#if DBG
TrackDevicesConnectedToLinkNode(linkNode,
State->Entry->PhysicalDeviceObject);
status = GetVectorProperties((ULONG)State->Start,
&previousFlags);
//
// This next bit is a hack. We need to make sure that
// the boot config code doesn't try to allocate the same
// vector for two different devices that need conflicting
// modes. This should never happen, as translation
// should filter out the problemating ones before we
// get to arbitration.
//
if (NT_SUCCESS(status)) {
//
// This vector is already in use for something.
//
ASSERT((previousFlags & ~(VECTOR_MODE | VECTOR_POLARITY | VECTOR_TYPE)) == 0);
ASSERT(flags == previousFlags);
}
#endif
} else {
//
// This is a PCI device that is not connected to a
// link node.
//
ASSERT(sourceIndex == State->Start);
}
} else {
if (InterruptModel == 1) {
//
// We are running in APIC mode. And we know that
// the PCI driver builds boot configs based on
// the Interrupt Line register, which only relates
// to PIC mode. So just say no to boot configs.
//
DEBUG_PRINT(1, ("Skipping this allocation. It's for a PCI device in APIC mode\n"));
return;
}
}
} else {
//
// Not a PCI device.
//
status = GetIsaVectorFlags((ULONG)State->Start,
&flags);
if (!NT_SUCCESS(status)) {
//
// Not overridden. Assume that the device flags conform to bus.
//
flags = (State->CurrentAlternative->Descriptor->Flags
== CM_RESOURCE_INTERRUPT_LATCHED) ?
VECTOR_EDGE | VECTOR_ACTIVE_HIGH :
VECTOR_LEVEL | VECTOR_ACTIVE_LOW;
}
ASSERT((flags & ~(VECTOR_MODE | VECTOR_POLARITY | VECTOR_TYPE)) == 0);
}
//
// There is a possibility that this allocation is impossible.
// (We may be setting a boot-config for a device after we
// have already started using the vector elsewhere.) Just
// don't do it.
//
if (State->Flags & ARBITER_STATE_FLAG_BOOT) {
attributes |= ARBITER_RANGE_BOOT_ALLOCATED;
status = GetVectorProperties((ULONG)State->Start,
&previousFlags);
if ((NT_SUCCESS(status)) &&
((flags & ~VECTOR_TYPE) != (previousFlags & ~VECTOR_TYPE))) {
DEBUG_PRINT(1, ("Skipping this allocation. It's for a vector that's incompatible.\n"));
return;
}
}
ReferenceVector((ULONG)State->Start,
flags);
// Figure out what flags we need to add the range
if ((flags & VECTOR_TYPE) == VECTOR_SIGNAL) {
// Non-MSI vectors can sometimes be shared and thus can have range conflicts, etc.
rangeFlags = RTL_RANGE_LIST_ADD_IF_CONFLICT +
(State->CurrentAlternative->Flags & ARBITER_ALTERNATIVE_FLAG_SHARED
? RTL_RANGE_LIST_ADD_SHARED : 0);
}
//
// Now do what the default function would do, marking this
// allocation as new.
//
status = RtlAddRange(
Arbiter->PossibleAllocation,
State->Start,
State->End,
attributes,
rangeFlags,
referencedNode, // This line is different from the default function
State->Entry->PhysicalDeviceObject
);
ASSERT(NT_SUCCESS(status));
}
VOID
AcpiArbBacktrackAllocation(
IN PARBITER_INSTANCE Arbiter,
IN PARBITER_ALLOCATION_STATE State
)
{
RTL_RANGE_LIST_ITERATOR iterator;
PRTL_RANGE current;
PNSOBJ linkNode;
PAGED_CODE();
DEBUG_PRINT(1, ("Backtracking allocation for IRQ %x for device %p\n",
State->CurrentAlternative->Descriptor->u.Interrupt.MinimumVector,
State->Entry->PhysicalDeviceObject));
ASSERT(!(State->Flags & ARBITER_STATE_FLAG_BOOT));
//
// Backtrack this assignment in the Edge/Level table.
//
DereferenceVector((ULONG)State->Start);
//
// Look for the range that we are backing out.
//
FOR_ALL_RANGES(Arbiter->PossibleAllocation, &iterator, current) {
if ((State->Entry->PhysicalDeviceObject == current->Owner) &&
(State->End == current->End) &&
(State->Start == current->Start)) {
//
// We stash the link node that we refereneced
// into Workspace.
//
linkNode = (PNSOBJ)current->UserData;
if (linkNode) {
//
// Dereference the link node that we referenced in
// AcpiArbAddAllocation.
//
AcpiArbDereferenceLinkNode(Arbiter,
linkNode);
}
break;
}
}
//
// Now call the default function.
//
ArbBacktrackAllocation(Arbiter, State);
}
NTSTATUS
UnreferenceArbitrationList(
IN PARBITER_INSTANCE Arbiter,
IN OUT PLIST_ENTRY ArbitrationList
)
{
RTL_RANGE_LIST_ITERATOR iterator;
PARBITER_LIST_ENTRY currentListEntry;
PRTL_RANGE currentRange;
NTSTATUS status;
PNSOBJ linkNode;
ULONG vector;
UCHAR flags;
PAGED_CODE();
//
// In order to keep the reference counts in line,
// we need to remove the counts that were added
// by previous allocations for this device. I.e.
// if the device is being rebalanced, we need to
// start by getting rid of the reference to the old
// vector.
//
// This is also necessary for references that were
// added as part of boot configs. But boot configs
// are a special case. There are a (very) few devices
// that want more than one IRQ. And it is possible
// that the boot config only reserved a single IRQ.
// (There is a Lucent Winmodem that actually does
// this.) We need to make sure that we only
// dereference the vector as many times as it was
// previously referenced.
//
// Note that there are still a few cases that this
// function doesn't handle. If a device lowers
// the number of IRQs that it wants dynamically,
// separate from its boot config, we will get out
// of synch. If a vector has both a boot config
// and a device that is not boot config'd on it,
// then we may get out of synch, depending on
// what combinations the PnP manager throws at us.
// Fixing either of these would involve tagging all
// vectors with a list of the PDOs that are connected
// which would be a lot of work. So unless these
// situations actually exist in the future, I'm not
// bothering to code for them now. 11/14/2000
//
FOR_ALL_RANGES(Arbiter->Allocation, &iterator, currentRange) {
DEBUG_PRINT(4, ("Looking at range: %x-%x %p\n",
(ULONG)(currentRange->Start & 0xffffffff),
(ULONG)(currentRange->End & 0xffffffff),
currentRange->Owner));
FOR_ALL_IN_LIST(ARBITER_LIST_ENTRY, ArbitrationList, currentListEntry) {
DEBUG_PRINT(2, ("Unreferencing allocations for device %p\n",
currentListEntry->PhysicalDeviceObject));
if (currentRange->Owner == currentListEntry->PhysicalDeviceObject) {
//
// Dereference the vector until there are no more
// references.
//
for (vector = (ULONG)(currentRange->Start & 0xffffffff);
vector <= (ULONG)(currentRange->End & 0xffffffff);
vector++) {
status = GetVectorProperties(vector, &flags);
if (NT_SUCCESS(status)) {
DEBUG_PRINT(2, ("Dereferencing %x\n", vector));
DereferenceVector(vector);
}
}
if (!(currentRange->Attributes & ARBITER_RANGE_BOOT_ALLOCATED)) {
//
// Now find out if we have to dereference a link node too.
//
status = AcpiArbCrackPRT(currentListEntry->PhysicalDeviceObject,
&linkNode,
&vector);
if (NT_SUCCESS(status)) {
if (linkNode) {
//
// This device is connected to a link node. So temporarily
// dereference this node.
//
ASSERT(LinkNodeInUse(Arbiter,
linkNode,
NULL,
NULL));
AcpiArbDereferenceLinkNode(Arbiter,
linkNode);
}
}
}
}
}
}
return STATUS_SUCCESS;
}
NTSTATUS
AcpiArbBootAllocation(
IN PARBITER_INSTANCE Arbiter,
IN OUT PLIST_ENTRY ArbitrationList
)
{
NTSTATUS status;
PAGED_CODE();
//
// Start a new arbiter transaction and clear any
// temporary vector counts.
//
// N.B. This arbiter doesn't keep track of link
// node data for boot configs. This means that we don't have
// to worry about link node counts in this funtion.
//
ClearTempVectorCounts();
status = ArbBootAllocation(Arbiter, ArbitrationList);
MakeTempVectorCountsPermanent();
return status;
}
NTSTATUS
AcpiArbTestAllocation(
IN PARBITER_INSTANCE Arbiter,
IN OUT PLIST_ENTRY ArbitrationList
)
{
NTSTATUS status;
PAGED_CODE();
//
// ArbTestAllocation is the beginning of a
// new arbitration transaction. So clear
// out all temporary edge-level status and
// link node ref counts.
//
ClearTempVectorCounts();
status = ClearTempLinkNodeCounts(Arbiter);
ASSERT(NT_SUCCESS(status));
status = UnreferenceArbitrationList(Arbiter, ArbitrationList);
ASSERT(NT_SUCCESS(status));
return ArbTestAllocation(Arbiter, ArbitrationList);
}
NTSTATUS
AcpiArbRetestAllocation(
IN PARBITER_INSTANCE Arbiter,
IN OUT PLIST_ENTRY ArbitrationList
)
{
NTSTATUS status;
PAGED_CODE();
//
// ArbRetestAllocation (also) is the beginning
// of a new arbitration transaction. So clear
// out all temporary edge-level status and
// link node ref counts.
//
ClearTempVectorCounts();
status = ClearTempLinkNodeCounts(Arbiter);
ASSERT(NT_SUCCESS(status));
status = UnreferenceArbitrationList(Arbiter, ArbitrationList);
ASSERT(NT_SUCCESS(status));
return ArbRetestAllocation(Arbiter, ArbitrationList);
}
NTSTATUS
AcpiArbRollbackAllocation(
PARBITER_INSTANCE Arbiter
)
{
PAGED_CODE();
return ArbRollbackAllocation(Arbiter);
}
NTSTATUS
AcpiArbCommitAllocation(
PARBITER_INSTANCE Arbiter
)
/*++
Routine Description:
This provides the implementation of the CommitAllocation
action. It frees the old allocation and replaces it with
the new allocation. After that, it dereferences all the
link nodes in the old allocation and references the ones
in the new allocation. This potentially results in the
IRQ router being reprogrammed to match the new set of
allocations.
Parameters:
Arbiter - The arbiter instance data for the arbiter being called.
Return Value:
Status code that indicates whether or not the function was successful.
--*/
{
PINT_ROUTE_INTERFACE_STANDARD pciInterface = NULL;
RTL_RANGE_LIST_ITERATOR iterator;
PRTL_RANGE_LIST temp;
PRTL_RANGE current;
NTSTATUS status;
PNSOBJ linkNode;
ULONG sourceIndex;
ULONG pciBus;
PCI_SLOT_NUMBER pciSlot;
UCHAR interruptLine;
ULONG_PTR dummy;
ROUTING_TOKEN token;
PAGED_CODE();
if (PciInterfacesInstantiated) {
pciInterface = ((PARBITER_EXTENSION)AcpiArbiter.ArbiterState.Extension)->InterruptRouting;
ASSERT(pciInterface);
FOR_ALL_RANGES(Arbiter->PossibleAllocation, &iterator, current) {
if (current->Owner) {
//
// Make sure that the InterruptLine register
// matches the assignment. (This is so that
// broken drivers that rely on the contents
// of the InterruptLine register instead of
// the resources handed back in a StartDevice
// still work.
//
pciBus = (ULONG)-1;
pciSlot.u.AsULONG = (ULONG)-1;
status = pciInterface->GetInterruptRouting(current->Owner,
&pciBus,
&pciSlot.u.AsULONG,
&interruptLine,
(PUCHAR)&dummy,
(PUCHAR)&dummy,
(PUCHAR)&dummy,
(PDEVICE_OBJECT*)&dummy,
&token,
(PUCHAR)&dummy);
if (NT_SUCCESS(status)) {
if (interruptLine != (UCHAR)current->Start) {
//
// We need to update the hardware.
//
ASSERT(current->Start < MAXUCHAR);
pciInterface->UpdateInterruptLine(current->Owner,
(UCHAR)current->Start
);
}
}
}
}
}
//
// Free up the current allocation
//
RtlFreeRangeList(Arbiter->Allocation);
//
// Swap the allocated and duplicate lists
//
temp = Arbiter->Allocation;
Arbiter->Allocation = Arbiter->PossibleAllocation;
Arbiter->PossibleAllocation = temp;
//
// Since we have committed the new allocation, we
// need to make the edge-level state and the new
// link node counts permanent.
//
MakeTempVectorCountsPermanent();
status = MakeTempLinkNodeCountsPermanent(Arbiter);
return status;
}
NTSTATUS
AcpiArbQueryConflict(
IN PARBITER_INSTANCE Arbiter,
IN PDEVICE_OBJECT PhysicalDeviceObject,
IN PIO_RESOURCE_DESCRIPTOR ConflictingResource,
OUT PULONG ConflictCount,
OUT PARBITER_CONFLICT_INFO *Conflicts
)
{
PINT_ROUTE_INTERFACE_STANDARD pciInterface = NULL;
NTSTATUS status;
ROUTING_TOKEN routingToken;
ULONG_PTR dummy;
PAGED_CODE();
if (PciInterfacesInstantiated) {
pciInterface = ((PARBITER_EXTENSION)AcpiArbiter.ArbiterState.Extension)->InterruptRouting;
ASSERT(pciInterface);
status = pciInterface->GetInterruptRouting(PhysicalDeviceObject,
(PULONG)&dummy,
(PULONG)&dummy,
&(UCHAR)dummy,
&(UCHAR)dummy,
&(UCHAR)dummy,
&(UCHAR)dummy,
(PDEVICE_OBJECT*)&dummy,
&routingToken,
&(UCHAR)dummy);
if (NT_SUCCESS(status)) {
//
// This is a PCI device. It's interrupt should not ever
// show a conflict.
//
*ConflictCount = 0;
return STATUS_SUCCESS;
}
}
//
// This isn't a PCI device. Call the base arbiter code.
//
return ArbQueryConflict(Arbiter,
PhysicalDeviceObject,
ConflictingResource,
ConflictCount,
Conflicts);
}
NTSTATUS
FindVectorInAlternatives(
IN PARBITER_INSTANCE Arbiter,
IN PARBITER_ALLOCATION_STATE State,
IN ULONGLONG Vector,
OUT ULONG *Alternative
)
{
ULONG alt;
for (alt = 0; alt < State->AlternativeCount; alt++) {
if ((State->Alternatives[alt].Minimum <= Vector) &&
(State->Alternatives[alt].Maximum >= Vector)) {
*Alternative = alt;
return STATUS_SUCCESS;
}
}
return STATUS_NOT_FOUND;
}
NTSTATUS
FindBootConfig(
IN PARBITER_INSTANCE Arbiter,
IN PARBITER_ALLOCATION_STATE State,
IN ULONGLONG *Vector
)
{
RTL_RANGE_LIST_ITERATOR iterator;
PRTL_RANGE currentRange;
FOR_ALL_RANGES(Arbiter->Allocation, &iterator, currentRange) {
if (currentRange->Attributes & ARBITER_RANGE_BOOT_ALLOCATED) {
//
// We're only interested in boot configs.
//
if (State->Entry->PhysicalDeviceObject == currentRange->Owner) {
//
// This boot config is the one we are looking for.
//
ASSERT(currentRange->Start == currentRange->End);
*Vector = currentRange->Start;
return STATUS_SUCCESS;
}
}
}
return STATUS_NOT_FOUND;
}
BOOLEAN
AcpiArbGetNextAllocationRange(
IN PARBITER_INSTANCE Arbiter,
IN OUT PARBITER_ALLOCATION_STATE State
)
{
BOOLEAN nextRange = FALSE;
PINT_ROUTE_INTERFACE_STANDARD pciInterface;
NTSTATUS status;
ROUTING_TOKEN routingToken;
ULONG_PTR dummy;
BOOLEAN legacyFreeMachine;
ULONGLONG vector;
ULONG alternative;
PAGED_CODE();
if (State->Entry->PhysicalDeviceObject->DriverObject == AcpiDriverObject) {
//
// This is one of our PDOs.
//
ASSERT(((PDEVICE_EXTENSION)State->Entry->PhysicalDeviceObject->DeviceExtension)->Flags & DEV_TYPE_PDO);
ASSERT(((PDEVICE_EXTENSION)State->Entry->PhysicalDeviceObject->DeviceExtension)->Signature == ACPI_SIGNATURE);
if (((PDEVICE_EXTENSION)State->Entry->PhysicalDeviceObject->DeviceExtension)->Flags & DEV_CAP_PCI) {
//
// It's a PCI PDO, which means a root PCI bus,
// which means that we should just handle this
// as an ISA device.
//
return ArbGetNextAllocationRange(Arbiter, State);
}
}
status = STATUS_NOT_FOUND;
if (PciInterfacesInstantiated) {
pciInterface = ((PARBITER_EXTENSION)AcpiArbiter.ArbiterState.Extension)->InterruptRouting;
ASSERT(pciInterface);
status = pciInterface->GetInterruptRouting(State->Entry->PhysicalDeviceObject,
(PULONG)&dummy,
(PULONG)&dummy,
&(UCHAR)dummy,
&(UCHAR)dummy,
&(UCHAR)dummy,
&(UCHAR)dummy,
(PDEVICE_OBJECT*)&dummy,
&routingToken,
&(UCHAR)dummy);
}
if (status != STATUS_SUCCESS) {
//
// This is not a PCI device. Use the base function.
//
return ArbGetNextAllocationRange(Arbiter, State);
}
#if defined(_X86_)
legacyFreeMachine = (AcpiInformation->FixedACPIDescTable->Header.Revision > 1) &&
!(AcpiInformation->FixedACPIDescTable->boot_arch & LEGACY_DEVICES);
#else
legacyFreeMachine = TRUE;
#endif
//
// A PCI device.
//
if (!State->CurrentAlternative) {
//
// This is the first time we've called this function
// with this alternative list. Set up the state machine.
//
State->WorkSpace = AcpiIrqNextRangeInit;
}
while (TRUE) {
ASSERT((State->WorkSpace > AcpiIrqNextRangeMinState) &&
(State->WorkSpace < AcpiIrqNextRangeMaxState));
DEBUG_PRINT(4, ("GetNextRange, State: %x\n", State->WorkSpace));
switch (State->WorkSpace) {
case AcpiIrqNextRangeInit:
//
// Top of the state machine. See if the registry
// contained policy.
//
switch (AcpiIrqDistributionDisposition) {
case AcpiIrqDistributionDispositionSpreadOut:
State->WorkSpace = AcpiIrqNextRangeAlternativeZero;
break;
case AcpiIrqDistributionDispositionStackUp:
State->WorkSpace = AcpiIrqNextRangeInitLegacy;
break;
case AcpiIrqDistributionDispositionDontCare:
default:
State->WorkSpace = AcpiIrqNextRangeInitPolicyNeutral;
break;
}
break;
case AcpiIrqNextRangeInitPolicyNeutral:
//
// Look at the interrupt controller model.
//
if (InterruptModel == 0) {
State->WorkSpace = AcpiIrqNextRangeInitPic;
} else {
State->WorkSpace = AcpiIrqNextRangeUseBootConfig;
}
break;
case AcpiIrqNextRangeInitPic:
//
// There is a PIC interrupt controller. So we are somewhat
// IRQ constrained. If this is a legacy-free machine, or if there
// is no cardbus controller, we want to spread interrupts.
//
if (legacyFreeMachine || !AcpiArbCardbusPresent) {
State->WorkSpace = AcpiIrqNextRangeUseBootConfig;
} else {
State->WorkSpace = AcpiIrqNextRangeInitLegacy;
}
break;
case AcpiIrqNextRangeInitLegacy:
//
// See if all the devices were boot configged on the same
// vector, or if there was a registry override specifying
// the vector that we should favor.
//
if (AcpiIrqDefaultBootConfig) {
State->WorkSpace = AcpiIrqNextRangeBootRegAlternative;
} else {
State->WorkSpace = AcpiIrqNextRangeSciAlternative;
}
break;
case AcpiIrqNextRangeBootRegAlternative:
//
// If we re-enter this state machine after this state,
// then it means that this alternative wasn't available.
// So set the next state to AcpiIrqNextRangeAlternativeZero,
// assuming that we failed.
//
State->WorkSpace = AcpiIrqNextRangeAlternativeZero;
//
// See if the machine-wide boot config or the registry
// override is within the alternatives.
//
status = FindVectorInAlternatives(Arbiter,
State,
(ULONGLONG)AcpiIrqDefaultBootConfig,
&alternative);
if (NT_SUCCESS(status)) {
State->CurrentAlternative = &State->Alternatives[alternative];
State->CurrentMinimum = (ULONGLONG)AcpiIrqDefaultBootConfig;
State->CurrentMaximum = (ULONGLONG)AcpiIrqDefaultBootConfig;
goto GetNextAllocationSuccess;
}
break;
case AcpiIrqNextRangeSciAlternative:
//
// If we re-enter this state machine after this state,
// then it means that this alternative wasn't available.
// So set the next state to AcpiIrqNextRangeUseBootConfig,
// assuming that we failed.
//
State->WorkSpace = AcpiIrqNextRangeUseBootConfig;
//
// See if the SCI vector is within the alternatives.
//
status = FindVectorInAlternatives(Arbiter,
State,
(ULONGLONG)AcpiSciVector,
&alternative);
if (NT_SUCCESS(status)) {
State->CurrentAlternative = &State->Alternatives[alternative];
State->CurrentMinimum = (ULONGLONG)AcpiSciVector;
State->CurrentMaximum = (ULONGLONG)AcpiSciVector;
goto GetNextAllocationSuccess;
}
break;
case AcpiIrqNextRangeUseBootConfig:
//
// If we re-enter this state machine after this state,
// then it means that this alternative wasn't available.
// So set the next state to AcpiIrqNextRangeAlternativeZero,
// assuming that we failed.
//
State->WorkSpace = AcpiIrqNextRangeAlternativeZero;
//
// See if there is a boot config for this device
// within the alternatives.
//
status = FindBootConfig(Arbiter,
State,
&vector);
if (NT_SUCCESS(status)) {
status = FindVectorInAlternatives(Arbiter,
State,
vector,
&alternative);
if (NT_SUCCESS(status)) {
State->CurrentAlternative = &State->Alternatives[alternative];
State->CurrentMinimum = vector;
State->CurrentMaximum = vector;
goto GetNextAllocationSuccess;
}
}
break;
case AcpiIrqNextRangeAlternativeZero:
//
// If we re-enter this state machine after this state,
// then it means that this alternative wasn't available.
// So set the next state to AcpiIrqNextRangeAlternativeN,
// assuming that we failed.
//
State->WorkSpace = AcpiIrqNextRangeAlternativeN;
//
// Try alternative 0.
//
State->CurrentAlternative = &State->Alternatives[0];
State->CurrentMinimum = State->CurrentAlternative->Minimum;
State->CurrentMaximum = State->CurrentAlternative->Maximum;
goto GetNextAllocationSuccess;
break;
case AcpiIrqNextRangeAlternativeN:
if (++State->CurrentAlternative < &State->Alternatives[State->AlternativeCount]) {
//
// There are multiple ranges. Cycle through them.
//
DEBUG_PRINT(3, ("No next allocation range, exhausted all %08X alternatives", State->AlternativeCount));
State->CurrentMinimum = State->CurrentAlternative->Minimum;
State->CurrentMaximum = State->CurrentAlternative->Maximum;
goto GetNextAllocationSuccess;
} else {
//
// We're done. There is no solution among these alternatives.
//
return FALSE;
}
}
}
GetNextAllocationSuccess:
DEBUG_PRINT(3, ("Next allocation range 0x%I64x-0x%I64x\n", State->CurrentMinimum, State->CurrentMaximum));
AcpiArbPciAlternativeRotation++;
return TRUE;
}
VOID
ReferenceVector(
IN ULONG Vector,
IN UCHAR Flags
)
/*++
Routine Description:
This routine adds one to either the permanent or the
temporary reference count.
Parameters:
Vector - the IRQ
Flags - mode and polarity
Return Value:
none
--*/
{
PVECTOR_BLOCK block;
PAGED_CODE();
ASSERT((Flags & ~(VECTOR_MODE | VECTOR_POLARITY | VECTOR_TYPE)) == 0);
block = HashVector(Vector);
DEBUG_PRINT(5, ("Referencing vector %x : %d %d\n", Vector,
block ? block->Entry.Count : 0,
block ? block->Entry.TempCount : 0));
if (block == NULL) {
AddVectorToTable(Vector,
0,
1,
Flags);
return;
}
if ((block->Entry.TempCount + block->Entry.Count) == 0) {
//
// This vector has been temporarily set to an
// aggregate count of zero. This means that the arbiter
// is re-allocating it. Record the new flags.
//
block->Entry.TempFlags = Flags;
}
block->Entry.TempCount++;
ASSERT(Flags == block->Entry.TempFlags);
ASSERT(block->Entry.Count <= 255);
}
VOID
DereferenceVector(
IN ULONG Vector
)
{
PVECTOR_BLOCK block;
PAGED_CODE();
block = HashVector(Vector);
ASSERT(block);
DEBUG_PRINT(5, ("Dereferencing vector %x : %d %d\n", Vector,
block->Entry.Count,
block->Entry.TempCount));
block->Entry.TempCount--;
ASSERT((block->Entry.TempCount * -1) <= block->Entry.Count);
}
PVECTOR_BLOCK
HashVector(
IN ULONG Vector
)
/*++
Routine Description:
This function takes a "Global System Interrupt Vector"
and returns a pointer to its entry in the hash table.
Arguments:
Vector - an IRQ
Return Value:
pointer to the entry in the hash table, or NULL if not found
--*/
{
PVECTOR_BLOCK block;
ULONG row, column;
PAGED_CODE();
row = Vector % VECTOR_HASH_TABLE_LENGTH;
block = HASH_ENTRY(row, 0);
while (TRUE) {
//
// Search across the hash table looking for our Vector
//
for (column = 0; column < VECTOR_HASH_TABLE_WIDTH; column++) {
//
// Check to see if we should follow a chain
//
if (block->Chain.Token == TOKEN_VALUE) {
break;
}
if (block->Entry.Vector == Vector) {
return block;
}
if ((block->Entry.Vector == EMPTY_BLOCK_VALUE) ||
(column == VECTOR_HASH_TABLE_WIDTH - 1)) {
//
// Didn't find this vector in the table.
//
return NULL;
}
block += 1;
}
ASSERT(block->Chain.Token == TOKEN_VALUE);
block = block->Chain.Next;
}
return NULL;
}
NTSTATUS
GetVectorProperties(
IN ULONG Vector,
OUT UCHAR *Flags
)
/*++
Routine Description:
This function takes a "Global System Interrupt Vector"
and returns the associated flags.
N.B. This function returns flags based on the
*temporary* reference count. I.e. if the vector
has been temporarily dereferenced, then the
funtion will indicate that the vector is available
for allocation by returning STATUS_NOT_FOUND.
Arguments:
Vector - an IRQ
Flags - to be filled in with the flags
Return Value:
status
--*/
{
PVECTOR_BLOCK block;
PAGED_CODE();
block = HashVector(Vector);
if (!block) {
return STATUS_NOT_FOUND;
}
if (block->Entry.Vector == EMPTY_BLOCK_VALUE) {
return STATUS_NOT_FOUND;
}
ASSERT(block->Entry.Vector == Vector);
if (block->Entry.Count + block->Entry.TempCount == 0) {
//
// This vector has an aggregate reference count of
// zero. This means that it is effectively
// unallocated.
//
return STATUS_NOT_FOUND;
}
*Flags = block->Entry.TempFlags;
return STATUS_SUCCESS;
}
NTSTATUS
AddVectorToTable(
IN ULONG Vector,
IN UCHAR ReferenceCount,
IN UCHAR TempRefCount,
IN UCHAR Flags
)
{
PVECTOR_BLOCK block, newRow;
ULONG row, column;
PAGED_CODE();
ASSERT((Flags & ~(VECTOR_MODE | VECTOR_POLARITY | VECTOR_TYPE)) == 0);
row = Vector % VECTOR_HASH_TABLE_LENGTH;
block = HASH_ENTRY(row, 0);
while (TRUE) {
//
// Search across the hash table looking for our Vector
//
for (column = 0; column < VECTOR_HASH_TABLE_WIDTH; column++) {
//
// Check to see if we should follow a chain
//
if (block->Chain.Token == TOKEN_VALUE) {
break;
}
if (block->Entry.Vector == EMPTY_BLOCK_VALUE) {
block->Entry.Vector = Vector;
block->Entry.Count = ReferenceCount;
block->Entry.TempCount = TempRefCount;
block->Entry.Flags = Flags;
block->Entry.TempFlags = Flags;
return STATUS_SUCCESS;
}
if (column == VECTOR_HASH_TABLE_WIDTH - 1) {
//
// We have just looked at the last entry in
// the row and it wasn't empty. Create
// an extension to this row.
//
newRow = ExAllocatePoolWithTag(PagedPool,
sizeof(VECTOR_BLOCK)
* VECTOR_HASH_TABLE_WIDTH,
ACPI_ARBITER_POOLTAG
);
if (!newRow) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlFillMemory(newRow,
sizeof(VECTOR_BLOCK) * VECTOR_HASH_TABLE_WIDTH,
(UCHAR)(EMPTY_BLOCK_VALUE & 0xff));
//
// Move last entry into new row.
//
RtlMoveMemory(newRow, block, sizeof(VECTOR_BLOCK));
//
// Chain the old row to the new row.
//
block->Chain.Token = TOKEN_VALUE;
block->Chain.Next = newRow;
break;
}
block += 1;
}
block = block->Chain.Next;
}
return STATUS_INSUFFICIENT_RESOURCES;
}
VOID
ClearTempVectorCounts(
VOID
)
{
PVECTOR_BLOCK block;
ULONG row, column;
PAGED_CODE();
for (row = 0; row < VECTOR_HASH_TABLE_LENGTH; row++) {
block = HASH_ENTRY(row, 0);
//
// Search across the hash table looking for our Vector
//
ClearTempCountsStartRow:
for (column = 0; column < VECTOR_HASH_TABLE_WIDTH; column++) {
//
// Check to see if we should follow a chain
//
if (block->Chain.Token == TOKEN_VALUE) {
block = block->Chain.Next;
goto ClearTempCountsStartRow;
}
if (block->Entry.Vector == EMPTY_BLOCK_VALUE) {
break;
}
//
// This must be a valid entry.
//
block->Entry.TempCount = 0;
block->Entry.TempFlags = block->Entry.Flags;
block += 1;
}
}
}
VOID
MakeTempVectorCountsPermanent(
VOID
)
{
PVECTOR_BLOCK block;
ULONG row, column;
PAGED_CODE();
for (row = 0; row < VECTOR_HASH_TABLE_LENGTH; row++) {
block = HASH_ENTRY(row, 0);
//
// Search across the hash table looking for our Vector
//
MakeTempVectorCountsPermanentStartRow:
for (column = 0; column < VECTOR_HASH_TABLE_WIDTH; column++) {
//
// Check to see if we should follow a chain
//
if (block->Chain.Token == TOKEN_VALUE) {
block = block->Chain.Next;
goto MakeTempVectorCountsPermanentStartRow;
}
if (block->Entry.Vector == EMPTY_BLOCK_VALUE) {
break;
}
//
// This must be a valid entry.
//
if ((block->Entry.Count + block->Entry.TempCount != 0) &&
((block->Entry.Count == 0) ||
(block->Entry.TempFlags != block->Entry.Flags))) {
//
// This vector has just been allocated or it has
// been re-allocated. Tell the HAL which flags
// to use.
//
HalSetVectorState(block->Entry.Vector,
block->Entry.TempFlags);
}
//
// Record new flags and aggregate count.
//
block->Entry.Flags = block->Entry.TempFlags;
block->Entry.Count += block->Entry.TempCount;
block += 1;
}
}
}
#ifdef DBG
VOID
DumpVectorTable(
VOID
)
{
PVECTOR_BLOCK block;
ULONG row, column;
PAGED_CODE();
DEBUG_PRINT(1, ("\nIRQARB: Dumping vector table\n"));
for (row = 0; row < VECTOR_HASH_TABLE_LENGTH; row++) {
block = HASH_ENTRY(row, 0);
//
// Search across the hash table looking for our Vector
//
DumpVectorTableStartRow:
for (column = 0; column < VECTOR_HASH_TABLE_WIDTH; column++) {
//
// Check to see if we should follow a chain
//
if (block->Chain.Token == TOKEN_VALUE) {
block = block->Chain.Next;
goto DumpVectorTableStartRow;
}
if (block->Entry.Vector == EMPTY_BLOCK_VALUE) {
break;
}
DEBUG_PRINT(1, ("Vector: %x\tP: %d T: %d\t%s %s\n",
block->Entry.Vector,
block->Entry.Count,
(LONG)block->Entry.TempCount,
IS_LEVEL_TRIGGERED(block->Entry.Flags) ? "level" : "edge",
IS_ACTIVE_LOW(block->Entry.Flags) ? "low" : "high"));
block += 1;
}
}
}
#endif
//
// This section of the file contains functions used for
// reading and manipulating the AML code.
//
NTSTATUS
AcpiArbGetLinkNodeOptions(
IN PNSOBJ LinkNode,
IN OUT PCM_RESOURCE_LIST *LinkNodeIrqs,
IN OUT UCHAR *Flags
)
/*++
Routine Description:
This routine looks in the AML namespace for the named
link node and returns the range of IRQs that it can
trigger.
Arguments:
LinkNodeName - The name of the IRQ router (link node)
LInkNodeIrqs - The list of possible settings for the link node
Flags - flags associated with this link node
Return Value:
NTSTATUS
--*/
{
PIO_RESOURCE_REQUIREMENTS_LIST ioList = NULL;
PCM_RESOURCE_LIST cmList = NULL;
PUCHAR prsBuff = NULL;
NTSTATUS status;
PULONG polarity;
PAGED_CODE();
ASSERT(LinkNode);
//
// Read the _PRS
//
ACPIGetNSBufferSync(
LinkNode,
PACKED_PRS,
&prsBuff,
NULL);
if (!prsBuff) {
return STATUS_NOT_FOUND;
}
status = PnpBiosResourcesToNtResources(prsBuff, 0, &ioList);
ExFreePool(prsBuff);
if (!NT_SUCCESS(status)) {
return status;
}
//
// Huge HACK! Get the polarity for the Flags.
//
// An IO_RES_LIST has no real way of representing the polarity
// of an interrupt. So, in PnpiBiosExtendedIrqToIoDescriptor I
// stuck the information in the DWORD past 'MaximumVector.'
//
*Flags = 0;
ASSERT(ioList->AlternativeLists == 1);
polarity = (PULONG)(&ioList->List[0].Descriptors[0].u.Interrupt.MaximumVector) + 1;
*Flags |= (UCHAR)*polarity;
//
// Get the mode for the flags.
//
*Flags |= (ioList->List[0].Descriptors[0].Flags == CM_RESOURCE_INTERRUPT_LATCHED) ?
VECTOR_EDGE : VECTOR_LEVEL;
//
// Turn the list into a CM_RESOURCE_LIST
//
status = PnpIoResourceListToCmResourceList(
ioList,
&cmList
);
ExFreePool(ioList);
if (!NT_SUCCESS(status)) {
return status;
}
*LinkNodeIrqs = cmList;
return STATUS_SUCCESS;
}
typedef enum {
StateInitial,
StateGotPrs,
StateRanSrs
} SET_LINK_WORKER_STATE;
typedef struct {
PNSOBJ LinkNode;
PCM_PARTIAL_RESOURCE_DESCRIPTOR LinkNodeIrq;
PUCHAR PrsBuff;
PUCHAR SrsBuff;
SET_LINK_WORKER_STATE State;
LONG RunCompletionHandler;
OBJDATA ObjData;
PFNACB CompletionHandler;
PVOID CompletionContext;
} SET_LINK_NODE_STATE, *PSET_LINK_NODE_STATE;
NTSTATUS
AcpiArbSetLinkNodeIrq(
IN PNSOBJ LinkNode,
IN PCM_PARTIAL_RESOURCE_DESCRIPTOR LinkNodeIrq
)
/*++
Routine Description:
This routine sets the named link node to trigger
a particular IRQ.
N.B. This routine could simply build the right
buffer and call the _SRS method, but there
isn't enough information in a
CM_PARTIAL_RESOURCE_DESCRIPTOR to know whether
an interrupt will be delivered active-high
or active-low. So the algorithm here runs
the _PRS method and copies the buffer returned
by _PRS into the buffer sent to _SRS. This
way all the flags are preserved.
Arguments:
LinkNodeName - The name of the IRQ router (link node)
LinkNodeIrq - The IRQ that the link node will be programmed
to trigger. If it is NULL, then the link node
will be disabled.
Return Value:
NTSTATUS
--*/
{
AMLISUPP_CONTEXT_PASSIVE getDataContext;
NTSTATUS status;
PAGED_CODE();
KeInitializeEvent(&getDataContext.Event, SynchronizationEvent, FALSE);
getDataContext.Status = STATUS_NOT_FOUND;
status = AcpiArbSetLinkNodeIrqAsync(LinkNode,
LinkNodeIrq,
AmlisuppCompletePassive,
(PVOID)&getDataContext
);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&getDataContext.Event,
Executive,
KernelMode,
FALSE,
NULL);
status = getDataContext.Status;
}
return status;
}
NTSTATUS
AcpiArbSetLinkNodeIrqAsync(
IN PNSOBJ LinkNode,
IN PCM_PARTIAL_RESOURCE_DESCRIPTOR LinkNodeIrq,
IN PFNACB CompletionHandler,
IN PVOID CompletionContext
)
{
PSET_LINK_NODE_STATE state;
NTSTATUS status;
ASSERT(LinkNode);
state = ExAllocatePoolWithTag(NonPagedPool,
sizeof(SET_LINK_NODE_STATE),
ACPI_ARBITER_POOLTAG);
if (!state) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(state, sizeof(SET_LINK_NODE_STATE));
state->LinkNode = LinkNode;
state->LinkNodeIrq = LinkNodeIrq;
state->CompletionHandler = CompletionHandler;
state->CompletionContext = CompletionContext;
state->State = StateInitial;
state->RunCompletionHandler = INITIAL_RUN_COMPLETION;
return AcpiArbSetLinkNodeIrqWorker(LinkNode,
STATUS_SUCCESS,
NULL,
(PVOID)state
);
}
NTSTATUS
EXPORT
AcpiArbSetLinkNodeIrqWorker(
IN PNSOBJ AcpiObject,
IN NTSTATUS Status,
IN POBJDATA Result,
IN PVOID Context
)
{
BOOLEAN foundTag = FALSE;
BOOLEAN useEndChecksum = FALSE;
BOOLEAN useExtendedTag = FALSE;
NTSTATUS status;
PNSOBJ childobj;
PPNP_EXTENDED_IRQ_DESCRIPTOR largeIrq;
PPNP_IRQ_DESCRIPTOR smallIrq;
PSET_LINK_NODE_STATE state;
PUCHAR resource = NULL;
PUCHAR irqTag = NULL;
PUCHAR sumchar;
UCHAR sum = 0;
UCHAR tagName;
ULONG length = 0;
USHORT increment;
USHORT irqTagLength = 0;
state = (PSET_LINK_NODE_STATE)Context;
if (!NT_SUCCESS(Status)) {
status = Status;
goto AcpiArbSetLinkNodeIrqWorkerExit;
}
ASSERT(state->LinkNodeIrq->Type == CmResourceTypeInterrupt);
//
// Entering this function twice with the same state
// means that we need to run the completion routine.
//
InterlockedIncrement(&state->RunCompletionHandler);
switch (state->State) {
case StateInitial:
//
// Read the _PRS, so that we can choose the appropriate
// entry and write that back into the _SRS.
//
state->State = StateGotPrs;
status = ACPIGetNSBufferAsync(
state->LinkNode,
PACKED_PRS,
AcpiArbSetLinkNodeIrqWorker,
(PVOID)state,
&state->PrsBuff,
NULL
);
if (status == STATUS_PENDING) {
return status;
} else if (!NT_SUCCESS(status)) {
goto AcpiArbSetLinkNodeIrqWorkerExit;
}
//
// Fallthrough to next state
//
case StateGotPrs:
state->State = StateRanSrs;
if (!state->PrsBuff) {
status = STATUS_NOT_FOUND;
goto AcpiArbSetLinkNodeIrqWorkerExit;
}
DEBUG_PRINT(7, ("Read _PRS buffer %p\n", state->PrsBuff));
resource = state->PrsBuff;
while ( *resource ) {
tagName = *resource;
if ( !(tagName & LARGE_RESOURCE_TAG)) {
increment = (USHORT) (tagName & SMALL_TAG_SIZE_MASK) + 1;
tagName &= SMALL_TAG_MASK;
} else {
increment = ( *(USHORT UNALIGNED *)(resource + 1) ) + 3;
}
if (tagName == TAG_END) {
length += increment;
if (increment > 1) {
useEndChecksum = TRUE;
}
break;
}
//
// This is the check to see if find a resource that correctly
// matches the assignment
//
// This code is weak. It need to check to see
// if the flags and interrupt match the descriptor we just found.
// It is possible for a vendor to use overlapping descriptors that
// would describe different interrupt settings.
//
if (tagName == TAG_IRQ || tagName == TAG_EXTENDED_IRQ) {
irqTag = resource;
if (tagName == TAG_EXTENDED_IRQ) {
irqTagLength = sizeof(PNP_EXTENDED_IRQ_DESCRIPTOR);
useExtendedTag = TRUE;
} else {
irqTagLength = increment;
}
length += (ULONG) irqTagLength;
foundTag = TRUE;
}
resource += increment;
}
//
// Did we find the tag that we are looking for?
//
if (foundTag == FALSE) {
ExFreePool( state->PrsBuff );
status = STATUS_NOT_FOUND;
goto AcpiArbSetLinkNodeIrqWorkerExit;
}
//
// The next task is to fashion a buffer containing an ACPI-style
// resource descriptor with exactly one interrupt destination in
// it. We do this by allocating one
//
state->SrsBuff = ExAllocatePoolWithTag(
NonPagedPool,
length,
ACPI_ARBITER_POOLTAG
);
if (!state->SrsBuff) {
ExFreePool(state->PrsBuff);
status = STATUS_INSUFFICIENT_RESOURCES;
goto AcpiArbSetLinkNodeIrqWorkerExit;
}
ASSERT(irqTagLength <= length);
RtlCopyMemory(state->SrsBuff, irqTag, irqTagLength);
ExFreePool(state->PrsBuff);
//
// Change the buffer to reflect our choice of interrupts.
//
if (!useExtendedTag) {
// small IRQ
smallIrq = (PPNP_IRQ_DESCRIPTOR)state->SrsBuff;
smallIrq->IrqMask = (USHORT)(1 << state->LinkNodeIrq->u.Interrupt.Level);
} else {
DEBUG_PRINT(7, ("Found large IRQ descriptor\n"));
// large IRQ
largeIrq = (PPNP_EXTENDED_IRQ_DESCRIPTOR)state->SrsBuff;
largeIrq->Length = irqTagLength - 3;
largeIrq->TableSize = 1;
largeIrq->Table[0] = state->LinkNodeIrq->u.Interrupt.Level;
}
//
// Work on the END descriptor
//
resource = (state->SrsBuff + irqTagLength);
*resource = TAG_END;
if (useEndChecksum) {
*resource |= 1; // The one is to represent the checksum
//
// Calculate the Checksum
sumchar = state->SrsBuff;
while (*sumchar != *resource) {
sum = *sumchar++;
}
*(resource+1) = 256 - sum;
}
//
// Now run the _SRS method with this buffer
//
//
// Get the object that we are looking for
//
childobj = ACPIAmliGetNamedChild(
state->LinkNode,
PACKED_SRS
);
if (childobj == NULL) {
status = STATUS_OBJECT_NAME_NOT_FOUND;
ExFreePool( state->SrsBuff );
goto AcpiArbSetLinkNodeIrqWorkerExit;
}
state->ObjData.dwDataType = OBJTYPE_BUFFDATA;
state->ObjData.dwDataLen = length;
state->ObjData.pbDataBuff = state->SrsBuff;
DEBUG_PRINT(7, ("Running _SRS\n"));
status = AMLIAsyncEvalObject(
childobj,
NULL,
1,
&state->ObjData,
AcpiArbSetLinkNodeIrqWorker,
(PVOID)state
);
if (status == STATUS_PENDING) {
return status;
} else if (!NT_SUCCESS(status)) {
goto AcpiArbSetLinkNodeIrqWorkerExit;
}
case StateRanSrs:
//
// We are done.
//
ExFreePool(state->SrsBuff);
status = STATUS_SUCCESS;
break;
default:
ACPIInternalError( ACPI_IRQARB );
}
AcpiArbSetLinkNodeIrqWorkerExit:
if (state->RunCompletionHandler) {
state->CompletionHandler(
AcpiObject,
status,
NULL,
state->CompletionContext
);
}
ExFreePool(state);
return status;
}
NTSTATUS
AcpiArbCrackPRT(
IN PDEVICE_OBJECT Pdo,
IN OUT PNSOBJ *LinkNode,
IN OUT ULONG *Vector
)
/*++
Routine Description:
This routine takes a PDO for a device and returns the
associated link node, if any. The ACPI spec says that
a _PRT can optionally return a single interrupt vector
instead of a link node. If this is the case, this function
returns that vector.
Arguments:
Pdo - The PDO of the device that needs to be granted an
IRQ.
LinkNode - A pointer to the link node, or NULL if the device
isn't connected to a link node.
Vector - The global system interrupt vector that this PCI
device is connected to. This is meaningless if
LinkNode is not NULL.
Return Value:
If we find a link node or a vector for this device, STATUS_SUCCESS.
If this isn't a PCI device, STATUS_DEVICE_NOT_FOUND.
If this is an IDE device, then we have to treat it specially, so
we return STATUS_RESOURCE_REQUIREMENTS_CHANGED.
--*/
{
PINT_ROUTE_INTERFACE_STANDARD pciInterface;
NTSTATUS status;
PDEVICE_OBJECT filter;
PDEVICE_OBJECT parent;
PDEVICE_EXTENSION filterExtension;
OBJDATA adrData;
OBJDATA pinData;
OBJDATA prtData;
OBJDATA linkData;
OBJDATA indexData;
PNSOBJ pciBusObj;
PNSOBJ prtObj;
ULONG prtElement = 0;
BOOLEAN found = FALSE;
KIRQL oldIrql;
PCI_SLOT_NUMBER pciSlot;
PCI_SLOT_NUMBER parentSlot;
ULONG pciBus;
UCHAR interruptLine;
UCHAR interruptPin;
UCHAR parentPin;
UCHAR classCode;
UCHAR subClassCode;
UCHAR flags;
UCHAR interfaceByte;
ROUTING_TOKEN routingToken;
ULONG dummy;
ULONG bus;
if (Pdo->DriverObject == AcpiDriverObject) {
//
// This is one of our PDOs.
//
ASSERT(((PDEVICE_EXTENSION)Pdo->DeviceExtension)->Flags & DEV_TYPE_PDO);
ASSERT(((PDEVICE_EXTENSION)Pdo->DeviceExtension)->Signature == ACPI_SIGNATURE);
if (((PDEVICE_EXTENSION)Pdo->DeviceExtension)->Flags & DEV_CAP_PCI) {
//
// It's a PCI PDO, which means a root PCI bus,
// which means that we should just handle this
// as an ISA device.
//
return STATUS_NOT_FOUND;
}
}
ASSERT(PciInterfacesInstantiated);
*LinkNode = NULL;
pciInterface = ((PARBITER_EXTENSION)AcpiArbiter.ArbiterState.Extension)->InterruptRouting;
ASSERT(pciInterface);
//
// Call into the PCI driver to find out what we are dealing with.
//
pciBus = (ULONG)-1;
pciSlot.u.AsULONG = (ULONG)-1;
status = pciInterface->GetInterruptRouting(Pdo,
&pciBus,
&pciSlot.u.AsULONG,
&interruptLine,
&interruptPin,
&classCode,
&subClassCode,
&parent,
&routingToken,
&flags);
if (!NT_SUCCESS(status)) {
return STATUS_NOT_FOUND;
}
if ((classCode == PCI_CLASS_MASS_STORAGE_CTLR) &&
(subClassCode == PCI_SUBCLASS_MSC_IDE_CTLR)) {
HalPciInterfaceReadConfig(NULL,
(UCHAR)pciBus,
pciSlot.u.AsULONG,
&interfaceByte,
FIELD_OFFSET (PCI_COMMON_CONFIG,
ProgIf),
1);
if ((interfaceByte & 0x5) == 0) {
//
// PCI IDE devices in legacy mode don't use interrupts
// the PCI way. So bail if this is an IDE device without
// any native-mode bits set.
//
return STATUS_RESOURCE_REQUIREMENTS_CHANGED;
}
}
//
// See if we have cached this lookup.
//
if ((routingToken.LinkNode != 0) ||
(routingToken.Flags & PCI_STATIC_ROUTING)) {
if (routingToken.LinkNode) {
*LinkNode = routingToken.LinkNode;
} else {
*Vector = routingToken.StaticVector;
}
return STATUS_SUCCESS;
}
//
// Now look for a parent PCI bus that has a _PRT. We may have to
// look up the tree a bit.
//
while (TRUE) {
//
// Find the parent's filter
//
KeAcquireSpinLock( &AcpiDeviceTreeLock, &oldIrql );
filter = AcpiGetFilter(AcpiArbiter.DeviceObject, parent);
KeReleaseSpinLock( &AcpiDeviceTreeLock, oldIrql );
if (filter) {
//
// This is a PCI bus that we either enumerated or
// filtered.
//
ASSERT(IsPciBus(filter));
filterExtension = filter->DeviceExtension;
pciBusObj = filterExtension->AcpiObject;
//
// Look for a _PRT for this PCI bus.
//
prtObj = ACPIAmliGetNamedChild(pciBusObj, PACKED_PRT);
if (prtObj) {
//
// We found the _PRT we are looking for.
//
break;
}
}
//
// We didn't find a _PRT. So go up the PCI tree one
// and look again.
//
bus = (ULONG)-1;
parentSlot.u.AsULONG = (ULONG)-1;
status = pciInterface->GetInterruptRouting(parent,
&bus,
&parentSlot.u.AsULONG,
(PUCHAR)&dummy,
&parentPin,
&classCode,
&subClassCode,
&parent,
&routingToken,
(PUCHAR)&dummy);
if (!NT_SUCCESS(status) ||
classCode != PCI_CLASS_BRIDGE_DEV) {
//
// The parent was not also a PCI device. So
// this means that there is no _PRT related to
// this device. Just return the contents of
// the Interrupt Line register.
//
*Vector = interruptLine;
AcpiInterruptRoutingFailed = TRUE;
return STATUS_SUCCESS;
}
if (subClassCode == PCI_SUBCLASS_BR_PCI_TO_PCI) {
//
// Swizzle the interrupt pin according to
// the PCI-PCI bridge spec.
//
interruptPin = PciBridgeSwizzle((UCHAR)pciSlot.u.bits.DeviceNumber, interruptPin);
pciSlot.u.AsULONG = parentSlot.u.AsULONG;
} else if (subClassCode == PCI_SUBCLASS_BR_CARDBUS) {
//
// Swizzle the interrupt pin according to
// the Cardbus bridge spec.
//
interruptPin = parentPin;
pciSlot.u.AsULONG = parentSlot.u.AsULONG;
} else {
//
// Bail.
//
*Vector = interruptLine;
AcpiInterruptRoutingFailed = TRUE;
return STATUS_SUCCESS;
}
}
if (AcpiInterruptRoutingFailed == TRUE) {
//
// We succeeded in finding a _PRT to work with,
// but we have failed in the past. This situation
// is unrecoverable because we now have dependencies
// on IRQ routers that we might now accidentally
// change
//
KeBugCheckEx(ACPI_BIOS_ERROR,
ACPI_CANNOT_ROUTE_INTERRUPTS,
(ULONG_PTR) Pdo,
(ULONG_PTR)parent,
(ULONG_PTR)prtObj);
}
// convert interrupt pin from PCI units to ACPI units
interruptPin--;
DEBUG_PRINT(2, ("PCI Device %p had _ADR of %x\n", Pdo, pciSlot.u.AsULONG));
DEBUG_PRINT(2, ("This device connected to Pin %x\n", interruptPin));
DEBUG_PRINT(2, ("prtObj: %p\n", prtObj));
//
// Cycle through all the elements in the _PRT package
// (each one of which is also a package) looking for
// the one that describes the link node that we are
// looking for.
//
do {
status = AMLIEvalPackageElement(prtObj,
prtElement++,
&prtData);
if (!NT_SUCCESS(status)) break;
ASSERT(prtData.dwDataType == OBJTYPE_PKGDATA);
if (NT_SUCCESS(AMLIEvalPkgDataElement(&prtData,
0,
&adrData))) {
if (pciSlot.u.bits.DeviceNumber == (adrData.uipDataValue >> 16)) {
if ((adrData.uipDataValue & 0xffff) != 0xffff) {
////
//// An _ADR in a _PRT must be of the form xxxxFFFF,
//// which means that the PCI Device Number is specified,
//// but the Function Number isn't. If it isn't done this
//// way, then the machine vendor can introduce
//// dangerous ambiguities. (Beside that, Pierre makes
//// Memphis bugcheck if it sees this and I'm trying to
//// be consistent.) So bugcheck.
////
//KeBugCheckEx(ACPI_BIOS_ERROR,
// ACPI_PRT_HAS_INVALID_FUNCTION_NUMBERS,
// (ULONG_PTR)prtObj,
// prtElement,
// adrData.uipDataValue);
DEBUG_PRINT(0, ("PRT entry has ambiguous address %x\n", adrData.uipDataValue));
status = STATUS_INVALID_PARAMETER;
pciSlot.u.bits.DeviceNumber = (ULONG)(adrData.uipDataValue >> 16) & 0xffff;
pciSlot.u.bits.FunctionNumber = (ULONG)(adrData.uipDataValue & 0xffff);
AMLIFreeDataBuffs(&adrData, 1);
AMLIFreeDataBuffs(&prtData, 1);
goto AcpiArbCrackPRTError;
}
//
// This sub-package does refer to the PCI device
// that we are concerned with. Now look to see if
// we have found the link node that is connected
// to the PCI interrupt PIN that this device will trigger.
//
// N.B. We only have to compare the top 16 bits
// because the function number is irrelevent
// when considering interrupts. We get the
// pin from config space.
//
if (NT_SUCCESS(AMLIEvalPkgDataElement(&prtData,
1,
&pinData))) {
if (pinData.uipDataValue == interruptPin) {
//
// This is the package that describes the link node we
// are interested in. Get the name of the link node.
//
if (NT_SUCCESS(AMLIEvalPkgDataElement(&prtData,
2,
&linkData))) {
found = TRUE;
}
//
// Look at the Source Index, too.
//
if (NT_SUCCESS(AMLIEvalPkgDataElement(&prtData,
3,
&indexData))) {
found = TRUE;
}
}
AMLIFreeDataBuffs(&pinData, 1);
}
}
AMLIFreeDataBuffs(&adrData, 1);
}
AMLIFreeDataBuffs(&prtData, 1);
} while (found == FALSE);
status = STATUS_NOT_FOUND;
if (found) {
//
// First check to see if linkData is valid. If it is,
// then we use it.
//
if (linkData.dwDataType == OBJTYPE_STRDATA) {
if (linkData.pbDataBuff) {
status = AMLIGetNameSpaceObject(linkData.pbDataBuff,
prtObj,
LinkNode,
0);
if (NT_SUCCESS(status)) {
routingToken.LinkNode = *LinkNode;
routingToken.StaticVector = 0;
routingToken.Flags = 0;
pciInterface->SetInterruptRoutingToken(Pdo,
&routingToken);
goto AcpiArbCrackPRTExit;
}
status = STATUS_OBJECT_NAME_NOT_FOUND;
goto AcpiArbCrackPRTError;
}
}
//
// If linkData didn't pan out, then use indexData.
//
if (indexData.dwDataType == OBJTYPE_INTDATA) {
//
// We have an integer which describes the "Global System Interrupt Vector"
// that this PCI device will trigger.
//
*Vector = (ULONG)indexData.uipDataValue;
status = STATUS_SUCCESS;
routingToken.LinkNode = 0;
routingToken.StaticVector = *Vector;
routingToken.Flags = PCI_STATIC_ROUTING;
pciInterface->SetInterruptRoutingToken(Pdo,
&routingToken);
goto AcpiArbCrackPRTExit;
}
status = STATUS_INVALID_IMAGE_FORMAT;
AcpiArbCrackPRTExit:
AMLIFreeDataBuffs(&linkData, 1);
AMLIFreeDataBuffs(&indexData, 1);
}
else
AcpiArbCrackPRTError:
{
ANSI_STRING ansiString;
UNICODE_STRING unicodeString;
UNICODE_STRING slotName;
UNICODE_STRING funcName;
PWCHAR prtEntry[4];
WCHAR IRQARBname[20];
WCHAR slotBuff[10];
WCHAR funcBuff[10];
swprintf( IRQARBname, L"IRQARB");
RtlInitUnicodeString(&slotName, slotBuff);
RtlInitUnicodeString(&funcName, funcBuff);
if (!NT_SUCCESS(RtlIntegerToUnicodeString(pciSlot.u.bits.DeviceNumber, 0, &slotName))) {
return status;
}
if (!NT_SUCCESS(RtlIntegerToUnicodeString(pciSlot.u.bits.FunctionNumber, 0, &funcName))) {
return status;
}
prtEntry[0] = IRQARBname;
prtEntry[1] = slotBuff;
prtEntry[2] = funcBuff;
switch (status) {
case STATUS_OBJECT_NAME_NOT_FOUND:
RtlInitAnsiString(&ansiString,
linkData.pbDataBuff);
RtlAnsiStringToUnicodeString(&unicodeString,
&ansiString,
TRUE);
prtEntry[3] = unicodeString.Buffer;
ACPIWriteEventLogEntry(ACPI_ERR_MISSING_LINK_NODE,
&prtEntry,
4,
NULL,
0);
RtlFreeUnicodeString(&unicodeString);
DEBUG_PRINT(0, ("Couldn't find link node (%s)\n", linkData.pbDataBuff));
//KeBugCheckEx(ACPI_BIOS_ERROR,
// ACPI_PRT_CANNOT_FIND_LINK_NODE,
// (ULONG_PTR)Pdo,
// (ULONG_PTR)linkData.pbDataBuff,
// (ULONG_PTR)prtObj);
break;
case STATUS_NOT_FOUND:
ACPIWriteEventLogEntry(ACPI_ERR_MISSING_PRT_ENTRY,
&prtEntry,
3,
NULL,
0);
DEBUG_PRINT(0, ("The ACPI _PRT package didn't contain a mapping for the PCI\n"));
DEBUG_PRINT(0, ("device at _ADR %x\n", pciSlot.u.AsULONG));
//KeBugCheckEx(ACPI_BIOS_ERROR,
// ACPI_PRT_CANNOT_FIND_DEVICE_ENTRY,
// (ULONG_PTR)Pdo,
// pciSlot.u.AsULONG,
// (ULONG_PTR)prtObj);
break;
case STATUS_INVALID_PARAMETER:
ACPIWriteEventLogEntry(ACPI_ERR_AMBIGUOUS_DEVICE_ADDRESS,
&prtEntry,
3,
NULL,
0);
break;
}
status = STATUS_UNSUCCESSFUL;
}
return status;
}
PDEVICE_OBJECT
AcpiGetFilter(
IN PDEVICE_OBJECT Root,
IN PDEVICE_OBJECT Pdo
)
/*++
Routine Description:
This routine takes a PDO for a device and returns the
DO of the filter that ACPI has slapped onto it. In the
case that this PDO belongs to the ACPI driver, then
it is returned.
Arguments:
Root - The device object that we are using as the
root of the search.
Pdo - The PDO of the device who's filter we seek
Return Value:
a DEVICE_OBJECT, if ACPI is filtering this Pdo, NULL otherwise
--*/
{
PDEVICE_EXTENSION deviceExtension;
PDEVICE_EXTENSION childExtension;
PDEVICE_EXTENSION firstChild;
PDEVICE_OBJECT filter;
deviceExtension = Root->DeviceExtension;
//
// If Root is the filter, we are done.
//
if (((deviceExtension->Flags & DEV_TYPE_PDO) ||
(deviceExtension->Flags & DEV_TYPE_FILTER)) &&
(deviceExtension->PhysicalDeviceObject == Pdo)) {
ASSERT(Root->Type == IO_TYPE_DEVICE);
return Root;
}
//
// Return NULL if this device has no children,
// (which is signified by the ChildDeviceList pointer
// pointing to itself.
//
if (deviceExtension->ChildDeviceList.Flink ==
(PVOID)&(deviceExtension->ChildDeviceList.Flink)) {
return NULL;
}
firstChild = (PDEVICE_EXTENSION) CONTAINING_RECORD(
deviceExtension->ChildDeviceList.Flink,
DEVICE_EXTENSION,
SiblingDeviceList );
childExtension = firstChild;
do {
//
// Make sure the device extension is complete.
//
if (childExtension->DeviceObject) {
filter = AcpiGetFilter(childExtension->DeviceObject, Pdo);
if (filter) {
return filter;
}
}
childExtension = (PDEVICE_EXTENSION) CONTAINING_RECORD(
childExtension->SiblingDeviceList.Flink,
DEVICE_EXTENSION,
SiblingDeviceList );
} while (childExtension != firstChild);
//
// Must not be on this branch...
//
return NULL;
}
BOOLEAN
LinkNodeInUse(
IN PARBITER_INSTANCE Arbiter,
IN PNSOBJ LinkNode,
IN OUT ULONG *Irq, OPTIONAL
IN OUT UCHAR *Flags OPTIONAL
)
/*++
Routine Description:
This routine indicates whether a link node is current
in use and, if so, returns the IRQ that it is currently
connected to.
Arguments:
Arbiter - current arbiter state
LinkNode - link node in question
Irq - "Global System Interrupt Vector" that
the link node is currently using.
Flags - flags associated with the vector that
the link node is connected to.
Return Value:
TRUE if the link node is currently being used.
--*/
{
PLIST_ENTRY linkNodes;
PLINK_NODE linkNode;
NTSTATUS status;
PAGED_CODE();
ASSERT(LinkNode);
linkNodes = &((PARBITER_EXTENSION)(Arbiter->Extension))->LinkNodeHead;
if (IsListEmpty(linkNodes)) {
//
// There are no link nodes in use.
//
DEBUG_PRINT(3, ("LinkNode list empty\n"));
return FALSE;
}
linkNode = (PLINK_NODE)linkNodes->Flink;
while (linkNode != (PLINK_NODE)linkNodes) {
//
// Is this the node we were looking for?
//
if (linkNode->NameSpaceObject == LinkNode) {
if((LONG)(linkNode->ReferenceCount + linkNode->TempRefCount) > 0) {
//
// This link node is on the list and it is currently referenced.
//
if (Irq) *Irq = (ULONG)linkNode->TempIrq;
if (Flags) *Flags = linkNode->Flags;
DEBUG_PRINT(3, ("Link Node %p is in use\n", LinkNode));
return TRUE;
} else {
DEBUG_PRINT(3, ("Link Node %p is currently unreferenced\n", LinkNode));
return FALSE;
}
}
linkNode = (PLINK_NODE)linkNode->List.Flink;
}
DEBUG_PRINT(3, ("Didn't find our link node (%p) on the Link Node List\n", LinkNode));
//
// Didn't ever find the link node we were looking for.
//
return FALSE;
}
NTSTATUS
GetLinkNodeFlags(
IN PARBITER_INSTANCE Arbiter,
IN PNSOBJ LinkNode,
IN OUT UCHAR *Flags
)
{
NTSTATUS status;
BOOLEAN inUse;
PAGED_CODE();
//
// This guarantees that LinkNodeInUse will succeed
// and will contain the valid flags.
//
status = AcpiArbReferenceLinkNode(Arbiter,
LinkNode,
0);
if (!NT_SUCCESS(status)) {
return status;
}
inUse = LinkNodeInUse(Arbiter,
LinkNode,
NULL,
Flags);
ASSERT(inUse);
//
// Set the state back to the way we found it.
//
status = AcpiArbDereferenceLinkNode(Arbiter,
LinkNode);
ASSERT(NT_SUCCESS(status));
return STATUS_SUCCESS;
}
NTSTATUS
AcpiArbReferenceLinkNode(
IN PARBITER_INSTANCE Arbiter,
IN PNSOBJ LinkNode,
IN ULONG Irq
)
/*++
Routine Description:
This routine keeps two reference counts. The first
is a permanent count, representing hardware resources
that have been committed. The second is a delta
representing what is currently under consideration.
Arguments:
Arbiter - current arbiter state
LinkNode - link node in question
Irq - "Global System Interrupt Vector" that
the link node is connected to.
Permanently - indicates whether this reference is
for a committed allocation
Return Value:
status
--*/
{
PCM_RESOURCE_LIST resList = NULL;
PLIST_ENTRY linkNodes;
PLINK_NODE linkNode;
BOOLEAN found = FALSE;
NTSTATUS status;
UCHAR flags;
PAGED_CODE();
DEBUG_PRINT(3, ("Referencing link node %p, Irq: %x\n",
LinkNode,
Irq));
ASSERT(LinkNode);
linkNodes = &((PARBITER_EXTENSION)(Arbiter->Extension))->LinkNodeHead;
linkNode = (PLINK_NODE)linkNodes->Flink;
//
// Search to see if we are already know about this link node.
//
while (linkNode != (PLINK_NODE)linkNodes) {
if (linkNode->NameSpaceObject == LinkNode) {
found = TRUE;
break;
}
linkNode = (PLINK_NODE)linkNode->List.Flink;
}
//
// If not, then we need to keep track of it. And
// the hardware needs to be made to match it.
//
if (!found) {
//
// This is the first permanent reference. So
// program the link node hardware.
//
linkNode = ExAllocatePoolWithTag(NonPagedPool, sizeof(LINK_NODE), ACPI_ARBITER_POOLTAG);
if (!linkNode) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(linkNode, sizeof(LINK_NODE));
linkNode->NameSpaceObject = LinkNode;
linkNode->CurrentIrq = Irq;
linkNode->TempIrq = Irq;
linkNode->AttachedDevices.Next = (PSINGLE_LIST_ENTRY)&linkNode->AttachedDevices;
InsertTailList(linkNodes, ((PLIST_ENTRY)(linkNode)));
//
// Figure out what the flags ought to be.
//
status = AcpiArbGetLinkNodeOptions(LinkNode,
&resList,
&flags);
if (NT_SUCCESS(status)) {
ExFreePool(resList); // not actually needed here
//
// Record the flags associated with this link node.
//
linkNode->Flags = flags;
} else {
ASSERT(NT_SUCCESS(status));
//
// Something is wrong. Make up reasonable flags.
//
linkNode->Flags = VECTOR_LEVEL | VECTOR_ACTIVE_LOW;
}
DEBUG_PRINT(3, ("Link node object connected to vector %x\n", Irq));
}
#if DBG
else {
if (!((linkNode->ReferenceCount == 0) &&
(linkNode->TempRefCount == 0))) {
//
// Make sure that we maintain consistency
// with the flags.
//
//
// Check to see that the link node hasn't changed.
//
status = AcpiArbGetLinkNodeOptions(LinkNode,
&resList,
&flags);
if (resList) ExFreePool(resList); // not actually needed here
ASSERT(NT_SUCCESS(status));
ASSERT(flags == linkNode->Flags);
}
}
#endif
DEBUG_PRINT(3, (" %d:%d\n", linkNode->ReferenceCount, linkNode->TempRefCount));
//
// Increase its reference count.
//
linkNode->TempIrq = Irq;
linkNode->TempRefCount++;
return STATUS_SUCCESS;
}
NTSTATUS
AcpiArbDereferenceLinkNode(
IN PARBITER_INSTANCE Arbiter,
IN PNSOBJ LinkNode
)
/*++
Routine Description:
This routine is the converse of the one above.
Arguments:
Arbiter - current arbiter state
LinkNode - link node in question
Permanently - indicates whether this reference is
for a committed allocation
Return Value:
status
--*/
{
PSINGLE_LIST_ENTRY attachedDev;
PLIST_ENTRY linkNodes;
PLINK_NODE linkNode;
BOOLEAN found = FALSE;
PAGED_CODE();
ASSERT(LinkNode);
linkNodes = &((PARBITER_EXTENSION)(Arbiter->Extension))->LinkNodeHead;
linkNode = (PLINK_NODE)linkNodes->Flink;
//
// Search for this link node.
//
while (linkNode != (PLINK_NODE)linkNodes) {
if (linkNode->NameSpaceObject == LinkNode) {
found = TRUE;
break;
}
linkNode = (PLINK_NODE)linkNode->List.Flink;
}
ASSERT(found);
DEBUG_PRINT(3, ("Dereferencing link node %p %d:%d\n", LinkNode, linkNode->ReferenceCount, linkNode->TempRefCount));
linkNode->TempRefCount--;
return STATUS_SUCCESS;
}
NTSTATUS
ClearTempLinkNodeCounts(
IN PARBITER_INSTANCE Arbiter
)
/*++
Routine Description:
This routine resets all the temporary counts (deltas)
to zero because the allocations being considered are
being thrown away instead of being committed.
Arguments:
Arbiter - current arbiter state
Return Value:
status
--*/
{
PLIST_ENTRY linkNodes;
PLINK_NODE linkNode;
PAGED_CODE();
linkNodes = &((PARBITER_EXTENSION)(Arbiter->Extension))->LinkNodeHead;
linkNode = (PLINK_NODE)linkNodes->Flink;
//
// Run through the link nodes.
//
while (linkNode != (PLINK_NODE)linkNodes) {
linkNode->TempRefCount = 0;
linkNode->TempIrq = linkNode->CurrentIrq;
linkNode = (PLINK_NODE)linkNode->List.Flink;
}
return STATUS_SUCCESS;
}
NTSTATUS
MakeTempLinkNodeCountsPermanent(
IN PARBITER_INSTANCE Arbiter
)
/*++
Routine Description:
This routine reconciles the temporary and
permanent references because the resources being
considered are being committed.
Arguments:
Arbiter - current arbiter state
Return Value:
status
--*/
{
CM_PARTIAL_RESOURCE_DESCRIPTOR irqDesc;
PLIST_ENTRY linkNodes;
PLINK_NODE linkNode, nextNode;
UCHAR flags;
PNSOBJ dis;
PAGED_CODE();
DEBUG_PRINT(3, ("MakeTempLinkNodeCountsPermanent\n"));
//
// Run through the link nodes.
//
linkNodes = &((PARBITER_EXTENSION)(Arbiter->Extension))->LinkNodeHead;
linkNode = (PLINK_NODE)linkNodes->Flink;
while (linkNode != (PLINK_NODE)linkNodes) {
nextNode = (PLINK_NODE)linkNode->List.Flink;
DEBUG_PRINT(3, ("LinkNode: %p -- Perm: %d, Temp: %d\n",
linkNode,
linkNode->ReferenceCount,
linkNode->TempRefCount));
//
// Attempt to sanity check this link node.
//
ASSERT(linkNode);
ASSERT(linkNode->List.Flink);
ASSERT(linkNode->ReferenceCount <= 70);
ASSERT(linkNode->TempRefCount <= 70);
ASSERT(linkNode->TempRefCount >= -70);
ASSERT(linkNode->CurrentIrq < 0x80000000);
ASSERT((linkNode->Flags & ~(VECTOR_MODE | VECTOR_POLARITY)) == 0);
//
// Program the link node if either the previous reference count
// was 0 or if the previous IRQ was different. *And* the current
// reference count is non-zero.
//
if (((linkNode->ReferenceCount == 0) ||
(linkNode->CurrentIrq != linkNode->TempIrq)) &&
((linkNode->ReferenceCount + linkNode->TempRefCount) != 0)) {
irqDesc.Type = CmResourceTypeInterrupt;
irqDesc.ShareDisposition = CmResourceShareShared;
irqDesc.Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
irqDesc.u.Interrupt.Level = (ULONG)linkNode->TempIrq;
irqDesc.u.Interrupt.Vector = (ULONG)linkNode->TempIrq;
irqDesc.u.Interrupt.Affinity = 0xffffffff;
AcpiArbSetLinkNodeIrq(linkNode->NameSpaceObject,
&irqDesc);
}
if ((linkNode->ReferenceCount + linkNode->TempRefCount) == 0) {
//
// This link node has no more references. Disable it.
//
dis = ACPIAmliGetNamedChild(linkNode->NameSpaceObject, PACKED_DIS);
if (dis) {
AMLIEvalNameSpaceObject(dis, NULL, 0, NULL);
}
}
linkNode->ReferenceCount = linkNode->ReferenceCount +
linkNode->TempRefCount;
linkNode->TempRefCount = 0;
linkNode->CurrentIrq = linkNode->TempIrq;
linkNode = nextNode;
}
return STATUS_SUCCESS;
}
#ifdef DBG
VOID
TrackDevicesConnectedToLinkNode(
IN PNSOBJ LinkNode,
IN PDEVICE_OBJECT Pdo
)
{
PLINK_NODE_ATTACHED_DEVICES attachedDevs, newPdo;
PLIST_ENTRY linkNodes;
PLINK_NODE linkNode, nextNode;
BOOLEAN found = FALSE;
PAGED_CODE();
//
// Run through the link nodes.
//
linkNodes = &((PARBITER_EXTENSION)(AcpiArbiter.ArbiterState.Extension))->LinkNodeHead;
linkNode = (PLINK_NODE)linkNodes->Flink;
while (linkNode != (PLINK_NODE)linkNodes) {
if (linkNode->NameSpaceObject == LinkNode) {
found = TRUE;
break;
}
linkNode = (PLINK_NODE)linkNode->List.Flink;
}
if (found) {
attachedDevs = (PLINK_NODE_ATTACHED_DEVICES)linkNode->AttachedDevices.Next;
found = FALSE;
while (attachedDevs != (PLINK_NODE_ATTACHED_DEVICES)&linkNode->AttachedDevices.Next) {
if (attachedDevs->Pdo == Pdo) {
found = TRUE;
break;
}
attachedDevs = (PLINK_NODE_ATTACHED_DEVICES)attachedDevs->List.Next;
}
if (!found) {
newPdo = ExAllocatePoolWithTag(PagedPool,
sizeof(LINK_NODE_ATTACHED_DEVICES),
ACPI_ARBITER_POOLTAG);
if (!newPdo) {
return;
}
RtlZeroMemory(newPdo, sizeof(LINK_NODE_ATTACHED_DEVICES));
newPdo->Pdo = Pdo;
PushEntryList(&linkNode->AttachedDevices,
(PSINGLE_LIST_ENTRY)newPdo);
}
}
}
#endif
typedef enum {
RestoreStateInitial,
RestoreStateDisabled,
RestoreStateEnabled
} RESTORE_IRQ_STATE, *PRESTORE_IRQ_STATE;
typedef struct {
CM_PARTIAL_RESOURCE_DESCRIPTOR IrqDesc;
PLIST_ENTRY LinkNodes;
PLINK_NODE LinkNode;
RESTORE_IRQ_STATE State;
KSPIN_LOCK SpinLock;
KIRQL OldIrql;
BOOLEAN CompletingSetLink;
LONG RunCompletion;
PFNACB CompletionHandler;
PVOID CompletionContext;
} RESTORE_ROUTING_STATE, *PRESTORE_ROUTING_STATE;
NTSTATUS
IrqArbRestoreIrqRouting(
PFNACB CompletionHandler,
PVOID CompletionContext
)
/*++
Routine Description:
This routine will set all the IRQ router settings
to whatever is described in the Link Node list.
This is useful when the machine is coming out
of hibernation.
Arguments:
Return Value:
status
Notes:
This function is expected to run at DPC level
during machine wakeup. It is assumed that no
other part of the arbiter code will be running
at that time. Since we can't wait for the
arbiter lock at DPC level, we will have to
assume it is not taken.
--*/
{
PRESTORE_ROUTING_STATE state;
PARBITER_INSTANCE arbiter;
NTSTATUS status;
//
// First check to see if there is any work to do.
//
if (HalPicStateIntact()) {
return STATUS_SUCCESS;
}
state = ExAllocatePoolWithTag(NonPagedPool,
sizeof(RESTORE_ROUTING_STATE),
ACPI_ARBITER_POOLTAG);
if (!state) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(state, sizeof(RESTORE_ROUTING_STATE));
state->State = RestoreStateInitial;
state->RunCompletion = INITIAL_RUN_COMPLETION;
state->CompletionHandler = CompletionHandler;
state->CompletionContext = CompletionContext;
state->IrqDesc.Type = CmResourceTypeInterrupt;
state->IrqDesc.ShareDisposition = CmResourceShareShared;
state->IrqDesc.Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
state->IrqDesc.u.Interrupt.Affinity = 0xffffffff;
arbiter = &AcpiArbiter.ArbiterState;
state->LinkNodes = &((PARBITER_EXTENSION)(arbiter->Extension))->LinkNodeHead;
state->LinkNode = (PLINK_NODE)state->LinkNodes->Flink;
KeInitializeSpinLock(&state->SpinLock);
return IrqArbRestoreIrqRoutingWorker(state->LinkNode->NameSpaceObject,
STATUS_SUCCESS,
NULL,
(PVOID)state
);
}
NTSTATUS
EXPORT
IrqArbRestoreIrqRoutingWorker(
IN PNSOBJ AcpiObject,
IN NTSTATUS Status,
IN POBJDATA Result,
IN PVOID Context
)
{
NTSTATUS status = Status;
PDEVICE_EXTENSION deviceExtension;
PRESTORE_ROUTING_STATE state;
state = (PRESTORE_ROUTING_STATE)Context;
//
// Entering this function twice with the same state
// means that we need to run the completion routine.
//
InterlockedIncrement(&state->RunCompletion);
switch (state->State) {
case RestoreStateInitial:
state->State = RestoreStateDisabled;
deviceExtension = ACPIInternalGetDeviceExtension( AcpiArbiter.DeviceObject );
status = DisableLinkNodesAsync(
deviceExtension->AcpiObject,
IrqArbRestoreIrqRoutingWorker,
(PVOID)state);
if (status == STATUS_PENDING) {
return status;
}
//
// Fall through
//
case RestoreStateDisabled:
KeAcquireSpinLock(&state->SpinLock,
&state->OldIrql);
while (state->LinkNode != (PLINK_NODE)state->LinkNodes) {
if (state->LinkNode->ReferenceCount > 0) {
//
// Program the link node.
//
state->IrqDesc.u.Interrupt.Level = (ULONG)state->LinkNode->CurrentIrq;
state->IrqDesc.u.Interrupt.Vector = (ULONG)state->LinkNode->CurrentIrq;
if (!state->CompletingSetLink) {
status = AcpiArbSetLinkNodeIrqAsync(state->LinkNode->NameSpaceObject,
&state->IrqDesc,
IrqArbRestoreIrqRoutingWorker,
(PVOID)state
);
if (status == STATUS_PENDING) {
state->CompletingSetLink = TRUE;
KeReleaseSpinLock(&state->SpinLock,
state->OldIrql);
return status;
}
}
}
state->CompletingSetLink = FALSE;
state->LinkNode = (PLINK_NODE)state->LinkNode->List.Flink;
}
state->State = RestoreStateEnabled;
KeReleaseSpinLock(&state->SpinLock,
state->OldIrql);
case RestoreStateEnabled:
//
// Now that we are done programming all the link nodes,
// we need to restore the ELCR and unmask all the
// device interrupts.
//
HalRestorePicState();
if (state->RunCompletion) {
state->CompletionHandler(AcpiObject,
status,
NULL,
state->CompletionContext
);
}
}
ExFreePool(state);
return status;
}
typedef enum {
DisableStateInitial,
DisableStateGotHid,
DisableStateRanDis,
DisableStateGetChild,
DisableStateRecursing
} DISABLE_LINK_NODES_STATE;
typedef struct {
DISABLE_LINK_NODES_STATE State;
PNSOBJ RootDevice;
PUCHAR Hid;
PNSOBJ Dis;
PNSOBJ Sibling;
PNSOBJ NextSibling;
LONG RunCompletionHandler;
PFNACB CompletionHandler;
PVOID CompletionContext;
} DISABLE_LINK_NODES_CONTEXT, *PDISABLE_LINK_NODES_CONTEXT;
NTSTATUS
DisableLinkNodesAsync(
IN PNSOBJ Root,
IN PFNACB CompletionHandler,
IN PVOID CompletionContext
)
{
PDISABLE_LINK_NODES_CONTEXT context;
NTSTATUS status;
context = ExAllocatePoolWithTag(NonPagedPool,
sizeof(DISABLE_LINK_NODES_CONTEXT),
ACPI_ARBITER_POOLTAG);
if (!context) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(context, sizeof(DISABLE_LINK_NODES_CONTEXT));
context->State = DisableStateInitial;
context->RootDevice = Root;
context->CompletionHandler = CompletionHandler;
context->CompletionContext = CompletionContext;
context->RunCompletionHandler = INITIAL_RUN_COMPLETION;
return DisableLinkNodesAsyncWorker(Root,
STATUS_SUCCESS,
NULL,
(PVOID)context
);
}
NTSTATUS
EXPORT
DisableLinkNodesAsyncWorker(
IN PNSOBJ AcpiObject,
IN NTSTATUS Status,
IN POBJDATA Result,
IN PVOID Context
)
{
PDISABLE_LINK_NODES_CONTEXT context;
NTSTATUS status = STATUS_SUCCESS;
PNSOBJ sib;
PNSOBJ dis;
context = (PDISABLE_LINK_NODES_CONTEXT)Context;
ASSERT(context);
//
// Entering this function twice with the same state
// means that we need to run the completion routine.
//
InterlockedIncrement(&context->RunCompletionHandler);
DisableLinkNodeStartState:
switch (context->State) {
case DisableStateInitial:
//
// Get the _HID of this device to see if
// it is a link node.
//
context->State = DisableStateGotHid;
status = ACPIGetNSPnpIDAsync(
context->RootDevice,
DisableLinkNodesAsyncWorker,
context,
&context->Hid,
NULL);
if (status == STATUS_PENDING) {
return status;
} else if (!NT_SUCCESS(status)) {
context->State = DisableStateGetChild;
goto DisableLinkNodeStartState;
}
//
// Fall through to next state.
//
case DisableStateGotHid:
context->State = DisableStateGetChild;
if (context->Hid) {
if (strstr(context->Hid, LINK_NODE_PNP_ID)) {
//
// We found a _HID of PNP0C0F, which is a
// link node. So disable it.
//
dis = ACPIAmliGetNamedChild(context->RootDevice,
PACKED_DIS);
if (dis) {
context->State = DisableStateRanDis;
status = AMLIAsyncEvalObject(dis,
NULL,
0,
NULL,
DisableLinkNodesAsyncWorker,
(PVOID)context
);
if (status == STATUS_PENDING) {
return status;
} else if (NT_SUCCESS(status)) {
//
// We're done. Jump to the cleanup code.
//
break;
}
} else {
//
// Link nodes must be disablable.
//
KeBugCheckEx(ACPI_BIOS_ERROR,
ACPI_LINK_NODE_CANNOT_BE_DISABLED,
(ULONG_PTR)context->RootDevice,
0,
0);
}
}
}
case DisableStateGetChild:
//
// Recurse to all of the children. Propagate any errors,
// but don't stop for them.
//
context->Sibling = NSGETFIRSTCHILD(context->RootDevice);
if (!context->Sibling) {
status = STATUS_SUCCESS;
break;
}
context->State = DisableStateRecursing;
case DisableStateRecursing:
while (context->Sibling) {
//
// Cycle through all the children (child and its
// siblings)
//
sib = context->Sibling;
context->Sibling = NSGETNEXTSIBLING(context->Sibling);
switch (NSGETOBJTYPE(sib)) {
case OBJTYPE_DEVICE:
//
// This name child of Root is also a device.
// Recurse.
//
status = DisableLinkNodesAsync(sib,
DisableLinkNodesAsyncWorker,
(PVOID)context);
break;
default:
break;
}
if (status == STATUS_PENDING) {
return status;
}
}
case DisableStateRanDis:
break;
}
//
// Done. Clean up and return.
//
if (context->RunCompletionHandler) {
context->CompletionHandler(context->RootDevice,
status,
NULL,
context->CompletionContext
);
}
if (context->Hid) ExFreePool(context->Hid);
ExFreePool(context);
return status;
}
NTSTATUS
GetIsaVectorFlags(
IN ULONG Vector,
IN OUT UCHAR *Flags
)
{
ULONG i;
ULONG irq;
UCHAR flags;
NTSTATUS returnStatus = STATUS_NOT_FOUND;
NTSTATUS status;
PAGED_CODE();
for (i = 0; i < ISA_PIC_VECTORS; i++) {
status = LookupIsaVectorOverride(i,
&irq,
&flags);
if (NT_SUCCESS(status)) {
if (irq == Vector) {
//
// This vector's flags have been overriden.
//
*Flags = flags;
ASSERT((*Flags & ~(VECTOR_MODE | VECTOR_POLARITY | VECTOR_TYPE)) == 0);
returnStatus = STATUS_SUCCESS;
break;
}
}
}
return returnStatus;
}
NTSTATUS
LookupIsaVectorOverride(
IN ULONG IsaVector,
IN OUT ULONG *RedirectionVector OPTIONAL,
IN OUT UCHAR *Flags OPTIONAL
)
/*++
Routine Description:
This function looks to see if this vector has
been overridden in the MAPIC table.
Arguments:
IsaVector - ISA vector
RedirectionVector - vector that the ISA vector
will actually trigger
Flags - flags in the override table
Return Value:
STATUS_SUCCESS if the ISA vector exists in the
MAPIC table
STATUS_NOT_FOUND if it doesn't
--*/
{
PAPICTABLE ApicEntry;
PISA_VECTOR IsaEntry;
PUCHAR TraversePtr;
PMAPIC ApicTable;
USHORT entryFlags;
ULONG_PTR TableEnd;
PAGED_CODE();
if (InterruptModel == 0) {
//
// This machine is running in PIC mode, so
// we should ignore anything from an APIC table.
//
return STATUS_NOT_FOUND;
}
if (IsaVector >= ISA_PIC_VECTORS) {
//
// This vector was never an ISA vector.
//
return STATUS_NOT_FOUND;
}
//
// Walk the MAPIC table.
//
ApicTable = AcpiInformation->MultipleApicTable;
if (!ApicTable) {
//
// This machine didn't have an MAPIC table. So it
// must not be running in APIC mode. So there must
// not be any overrides.
//
return STATUS_NOT_FOUND;
}
TraversePtr = (PUCHAR)ApicTable->APICTables;
TableEnd = (ULONG_PTR)ApicTable +ApicTable->Header.Length;
while ((ULONG_PTR)TraversePtr < TableEnd) {
ApicEntry = (PAPICTABLE) TraversePtr;
if (ApicEntry->Type == ISA_VECTOR_OVERRIDE &&
ApicEntry->Length == ISA_VECTOR_OVERRIDE_LENGTH) {
//
// Found an ISA vector redirection entry.
//
IsaEntry = (PISA_VECTOR) TraversePtr;
if (IsaEntry->Source == IsaVector) {
if (RedirectionVector) {
*RedirectionVector = IsaEntry->GlobalSystemInterruptVector;
}
if (Flags) {
entryFlags = IsaEntry->Flags;
*Flags = 0;
if (((entryFlags & PO_BITS) == POLARITY_HIGH) ||
((entryFlags & PO_BITS) == POLARITY_CONFORMS_WITH_BUS)) {
*Flags |= VECTOR_ACTIVE_HIGH;
} else {
*Flags |= VECTOR_ACTIVE_LOW;
}
if (((entryFlags & EL_BITS) == EL_EDGE_TRIGGERED) ||
((entryFlags & EL_BITS) == EL_CONFORMS_WITH_BUS)) {
*Flags |= VECTOR_EDGE;
} else {
*Flags |= VECTOR_LEVEL;
}
}
return STATUS_SUCCESS;
}
}
//
// Sanity check to make sure that we abort tables with bogus length
// entries
//
if (ApicEntry->Length == 0) {
break;
}
TraversePtr += (ApicEntry->Length);
}
return STATUS_NOT_FOUND;
}