5241 lines
141 KiB
C
5241 lines
141 KiB
C
/*++
|
||
|
||
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",
|
||
®Value);
|
||
|
||
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",
|
||
®Value);
|
||
|
||
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;
|
||
}
|
||
|