From 8091e98e96e3be0cf7cf545e9e49f62ce90571f3 Mon Sep 17 00:00:00 2001 From: evb Date: Sun, 18 Jul 2010 18:58:33 +0000 Subject: [PATCH] Can't sleep so write more source codes! add scan bus functions to get power caps (PciGetEnhancedCapabilities) and AGP caps, use PciReadDeviceCapability util function, now can get wake levels, PCI power state plus target AGP ID. Now can find PCI Device that can't sleep neither! Ha-ha! Support PCI_HACK_NO_PM_CAPS, PCI_HACK_PRESERVE_COMMAND, PCI_HACK_DONT_DISABLE_DECOES Add scan bus function to set power for PCI, for now to power up (PciSetPowerManagedDevicePowerState), with support for device that is critical/broken (PciCanDisableDecodes) Check spec-correct with PciStallForPowerChange after define PciPowerDelayTable for D0<->D3 crossmatrix spec timings (add PciReadDeviceConfig for support) If bad spec timing use PCI verifier support (PciVerifierRetrieveFailureData) + STATUS_DEVICE_PROTOCOL_ERROR Add PciVerifierFailureTable with all failure type Almost the time for resource discovery of BARs! svn path=/trunk/; revision=48107 --- reactos/drivers/bus/pcix/enum.c | 126 ++++++++++++++++++ reactos/drivers/bus/pcix/pci.h | 58 ++++++++ reactos/drivers/bus/pcix/pci/config.c | 18 ++- reactos/drivers/bus/pcix/pcivrify.c | 54 ++++++++ reactos/drivers/bus/pcix/power.c | 184 ++++++++++++++++++++++++++ reactos/drivers/bus/pcix/utils.c | 167 ++++++++++++++++++++++- 6 files changed, 605 insertions(+), 2 deletions(-) diff --git a/reactos/drivers/bus/pcix/enum.c b/reactos/drivers/bus/pcix/enum.c index 3551a3b3506..4af77b33227 100644 --- a/reactos/drivers/bus/pcix/enum.c +++ b/reactos/drivers/bus/pcix/enum.c @@ -124,6 +124,126 @@ PciSkipThisFunction(IN PPCI_COMMON_HEADER PciData, return TRUE; } +VOID +NTAPI +PciGetEnhancedCapabilities(IN PPCI_PDO_EXTENSION PdoExtension, + IN PPCI_COMMON_HEADER PciData) +{ + ULONG HeaderType, CapPtr, TargetAgpCapabilityId; + DEVICE_POWER_STATE WakeLevel; + PCI_CAPABILITIES_HEADER AgpCapability; + PCI_PM_CAPABILITY PowerCapabilities; + PAGED_CODE(); + + /* Assume no known wake level */ + PdoExtension->PowerState.DeviceWakeLevel = PowerDeviceUnspecified; + + /* Make sure the device has capabilities */ + if (!(PciData->Status & PCI_STATUS_CAPABILITIES_LIST)) + { + /* If it doesn't, there will be no power management */ + PdoExtension->CapabilitiesPtr = 0; + PdoExtension->HackFlags |= PCI_HACK_NO_PM_CAPS; + } + else + { + /* There's capabilities, need to figure out where to get the offset */ + HeaderType = PCI_CONFIGURATION_TYPE(PciData); + if (HeaderType == PCI_CARDBUS_BRIDGE_TYPE) + { + /* Use the bridge's header */ + CapPtr = PciData->u.type2.CapabilitiesPtr; + } + else + { + /* Use the device header */ + ASSERT(HeaderType <= PCI_CARDBUS_BRIDGE_TYPE); + CapPtr = PciData->u.type0.CapabilitiesPtr; + } + + /* Make sure the pointer is spec-aligned and located, and save it */ + DPRINT1("Device has capabilities at: %lx\n", CapPtr); + ASSERT(((CapPtr & 0x3) == 0) && (CapPtr >= PCI_COMMON_HDR_LENGTH)); + PdoExtension->CapabilitiesPtr = CapPtr; + + /* Check for PCI-to-PCI Bridges and AGP bridges */ + if ((PdoExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) && + ((PdoExtension->SubClass == PCI_SUBCLASS_BR_HOST) || + (PdoExtension->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI))) + { + /* Query either the raw AGP capabilitity, or the Target AGP one */ + TargetAgpCapabilityId = (PdoExtension->SubClass == + PCI_SUBCLASS_BR_PCI_TO_PCI) ? + PCI_CAPABILITY_ID_AGP_TARGET : + PCI_CAPABILITY_ID_AGP; + if (PciReadDeviceCapability(PdoExtension, + PdoExtension->CapabilitiesPtr, + TargetAgpCapabilityId, + &AgpCapability, + sizeof(PCI_CAPABILITIES_HEADER))) + { + /* AGP target ID was found, store it */ + DPRINT1("AGP ID: %lx\n", TargetAgpCapabilityId); + PdoExtension->TargetAgpCapabilityId = TargetAgpCapabilityId; + } + } + + /* Check for devices that are known not to have proper power management */ + if (!(PdoExtension->HackFlags & PCI_HACK_NO_PM_CAPS)) + { + /* Query if this device supports power management */ + if (!PciReadDeviceCapability(PdoExtension, + PdoExtension->CapabilitiesPtr, + PCI_CAPABILITY_ID_POWER_MANAGEMENT, + &PowerCapabilities.Header, + sizeof(PCI_PM_CAPABILITY))) + { + /* No power management, so act as if it had the hackflag set */ + DPRINT1("No PM caps, disabling PM\n"); + PdoExtension->HackFlags |= PCI_HACK_NO_PM_CAPS; + } + else + { + /* Otherwise, pick the highest wake level that is supported */ + WakeLevel = PowerDeviceUnspecified; + if (PowerCapabilities.PMC.Capabilities.Support.PMED0) + WakeLevel = PowerDeviceD0; + if (PowerCapabilities.PMC.Capabilities.Support.PMED1) + WakeLevel = PowerDeviceD1; + if (PowerCapabilities.PMC.Capabilities.Support.PMED2) + WakeLevel = PowerDeviceD2; + if (PowerCapabilities.PMC.Capabilities.Support.PMED3Hot) + WakeLevel = PowerDeviceD3; + if (PowerCapabilities.PMC.Capabilities.Support.PMED3Cold) + WakeLevel = PowerDeviceD3; + PdoExtension->PowerState.DeviceWakeLevel = WakeLevel; + + /* Convert the PCI power state to the NT power state */ + PdoExtension->PowerState.CurrentDeviceState = + PowerCapabilities.PMCSR.ControlStatus.PowerState + 1; + + /* Save all the power capabilities */ + PdoExtension->PowerCapabilities = PowerCapabilities.PMC.Capabilities; + DPRINT1("PM Caps Found! Wake Level: %d Power State: %d\n", + WakeLevel, PdoExtension->PowerState.CurrentDeviceState); + } + } + } + + /* At the very end of all this, does this device not have power management? */ + if (PdoExtension->HackFlags & PCI_HACK_NO_PM_CAPS) + { + /* Then guess the current state based on whether the decodes are on */ + PdoExtension->PowerState.CurrentDeviceState = + PciData->Command & (PCI_ENABLE_IO_SPACE | + PCI_ENABLE_MEMORY_SPACE | + PCI_ENABLE_BUS_MASTER) ? + PowerDeviceD0: PowerDeviceD3; + DPRINT1("PM is off, so assumed device is: %d based on enables\n", + PdoExtension->PowerState.CurrentDeviceState); + } +} + NTSTATUS NTAPI PciScanBus(IN PPCI_FDO_EXTENSION DeviceExtension) @@ -374,6 +494,12 @@ PciScanBus(IN PPCI_FDO_EXTENSION DeviceExtension) NewExtension->CommandEnables = PciData->Command; NewExtension->HackFlags = HackFlags; + /* Get power, AGP, and other capability data */ + PciGetEnhancedCapabilities(NewExtension, PciData); + + /* Power up the device */ + PciSetPowerManagedDevicePowerState(NewExtension, PowerDeviceD0, FALSE); + /* Save interrupt pin */ NewExtension->InterruptPin = PciData->u.type0.InterruptPin; diff --git a/reactos/drivers/bus/pcix/pci.h b/reactos/drivers/bus/pcix/pci.h index 82eed3275b3..327cce1de5e 100644 --- a/reactos/drivers/bus/pcix/pci.h +++ b/reactos/drivers/bus/pcix/pci.h @@ -67,6 +67,11 @@ // #define MAX_DEBUGGING_DEVICES_SUPPORTED 0x04 +// +// PCI Driver Verifier Failures +// +#define PCI_VERIFIER_CODES 0x04 + // // Device Extension, Interface, Translator and Arbiter Signatures // @@ -375,6 +380,17 @@ typedef struct PCI_ARBITER_INSTANCE //ARBITER_INSTANCE CommonInstance; FIXME: Need Arbiter Headers } PCI_ARBITER_INSTANCE, *PPCI_ARBITER_INSTANCE; +// +// PCI Verifier Data +// +typedef struct _PCI_VERIFIER_DATA +{ + ULONG FailureCode; + VF_FAILURE_CLASS FailureClass; + ULONG AssertionControl; + PCHAR DebuggerMessageText; +} PCI_VERIFIER_DATA, *PPCI_VERIFIER_DATA; + // // IRP Dispatch Routines // @@ -442,6 +458,14 @@ PciFdoIrpQueryPower( IN PPCI_FDO_EXTENSION DeviceExtension ); +NTSTATUS +NTAPI +PciSetPowerManagedDevicePowerState( + IN PPCI_PDO_EXTENSION DeviceExtension, + IN DEVICE_POWER_STATE DeviceState, + IN BOOLEAN IrpSet +); + // // Bus FDO Routines // @@ -778,6 +802,12 @@ PciVerifierInit( IN PDRIVER_OBJECT DriverObject ); +PPCI_VERIFIER_DATA +NTAPI +PciVerifierRetrieveFailureData( + IN ULONG FailureCode +); + // // Utility Routines // @@ -925,6 +955,25 @@ PciSaveBiosConfig( OUT PPCI_COMMON_HEADER PciData ); +UCHAR +NTAPI +PciReadDeviceCapability( + IN PPCI_PDO_EXTENSION DeviceExtension, + IN UCHAR Offset, + IN ULONG CapabilityId, + OUT PPCI_CAPABILITIES_HEADER Buffer, + IN ULONG Length +); + +BOOLEAN +NTAPI +PciCanDisableDecodes( + IN PPCI_PDO_EXTENSION DeviceExtension, + IN PPCI_COMMON_HEADER Config, + IN ULONGLONG HackFlags, + IN BOOLEAN ForPowerDown +); + // // Configuration Routines // @@ -953,6 +1002,15 @@ PciWriteDeviceConfig( IN ULONG Length ); +VOID +NTAPI +PciReadDeviceConfig( + IN PPCI_PDO_EXTENSION DeviceExtension, + IN PVOID Buffer, + IN ULONG Offset, + IN ULONG Length +); + UCHAR NTAPI PciGetAdjustedInterruptLine( diff --git a/reactos/drivers/bus/pcix/pci/config.c b/reactos/drivers/bus/pcix/pci/config.c index e5382bf949f..51b9998e9e4 100644 --- a/reactos/drivers/bus/pcix/pci/config.c +++ b/reactos/drivers/bus/pcix/pci/config.c @@ -24,7 +24,7 @@ PciGetAdjustedInterruptLine(IN PPCI_PDO_EXTENSION PdoExtension) { UCHAR InterruptLine = 0, PciInterruptLine; ULONG Length; - + /* Does the device have an interrupt pin? */ if (PdoExtension->InterruptPin) { @@ -102,6 +102,22 @@ PciWriteDeviceConfig(IN PPCI_PDO_EXTENSION DeviceExtension, FALSE); } +VOID +NTAPI +PciReadDeviceConfig(IN PPCI_PDO_EXTENSION DeviceExtension, + IN PVOID Buffer, + IN ULONG Offset, + IN ULONG Length) +{ + /* Call the generic worker function */ + PciReadWriteConfigSpace(DeviceExtension->ParentFdoExtension, + DeviceExtension->Slot, + Buffer, + Offset, + Length, + TRUE); +} + VOID NTAPI PciReadSlotConfig(IN PPCI_FDO_EXTENSION DeviceExtension, diff --git a/reactos/drivers/bus/pcix/pcivrify.c b/reactos/drivers/bus/pcix/pcivrify.c index ed54474adc8..f51f02dba89 100644 --- a/reactos/drivers/bus/pcix/pcivrify.c +++ b/reactos/drivers/bus/pcix/pcivrify.c @@ -17,8 +17,62 @@ BOOLEAN PciVerifierRegistered; PVOID PciVerifierNotificationHandle; +PCI_VERIFIER_DATA PciVerifierFailureTable[PCI_VERIFIER_CODES] = +{ + { + 1, + VFFAILURE_FAIL_LOGO, + 0, + "The BIOS has reprogrammed the bus numbers of an active PCI device " + "(!devstack %DevObj) during a dock or undock!" + }, + { + 2, + VFFAILURE_FAIL_LOGO, + 0, + "A device in the system did not update it's PMCSR register in the spec " + "mandated time (!devstack %DevObj, Power state D%Ulong)" + }, + { + 3, + VFFAILURE_FAIL_LOGO, + 0, + "A driver controlling a PCI device has tried to access OS controlled " + "configuration space registers (!devstack %DevObj, Offset 0x%Ulong1, " + "Length 0x%Ulong2)" + }, + { + 4, + VFFAILURE_FAIL_UNDER_DEBUGGER, + 0, + "A driver controlling a PCI device has tried to read or write from an " + "invalid space using IRP_MN_READ/WRITE_CONFIG or via BUS_INTERFACE_STANDARD." + " NB: These functions take WhichSpace parameters of the form PCI_WHICHSPACE_*" + " and not a BUS_DATA_TYPE (!devstack %DevObj, WhichSpace 0x%Ulong1)" + }, +}; + /* FUNCTIONS ******************************************************************/ +PPCI_VERIFIER_DATA +NTAPI +PciVerifierRetrieveFailureData(IN ULONG FailureCode) +{ + PPCI_VERIFIER_DATA VerifierData; + + /* Scan the verifier failure table for this code */ + VerifierData = PciVerifierFailureTable; + while (VerifierData->FailureCode != FailureCode) + { + /* Keep searching */ + ++VerifierData; + ASSERT(VerifierData < &PciVerifierFailureTable[PCI_VERIFIER_CODES]); + } + + /* Return the entry for this code */ + return VerifierData; +} + NTSTATUS NTAPI PciVerifierProfileChangeCallback(IN PVOID NotificationStructure, diff --git a/reactos/drivers/bus/pcix/power.c b/reactos/drivers/bus/pcix/power.c index 426d39d6a3d..d57c1b78398 100644 --- a/reactos/drivers/bus/pcix/power.c +++ b/reactos/drivers/bus/pcix/power.c @@ -14,8 +14,192 @@ /* GLOBALS ********************************************************************/ +ULONG PciPowerDelayTable[PowerDeviceD3 * PowerDeviceD3] = +{ + 0, // D0 -> D0 + 0, // D1 -> D0 + 200, // D2 -> D0 + 10000, // D3 -> D0 + + 0, // D0 -> D1 + 0, // D1 -> D1 + 200, // D2 -> D1 + 10000, // D3 -> D1 + + 200, // D0 -> D2 + 200, // D1 -> D2 + 0, // D2 -> D2 + 10000, // D3 -> D2 + + 10000, // D0 -> D3 + 10000, // D1 -> D3 + 10000, // D2 -> D3 + 0 // D3 -> D3 +}; + /* FUNCTIONS ******************************************************************/ +NTSTATUS +NTAPI +PciStallForPowerChange(IN PPCI_PDO_EXTENSION PdoExtension, + IN DEVICE_POWER_STATE PowerState, + IN ULONG_PTR CapOffset) +{ + ULONG PciState, TimeoutEntry, PmcsrOffset, TryCount; + PPCI_VERIFIER_DATA VerifierData; + LARGE_INTEGER Interval; + PCI_PMCSR Pmcsr; + KIRQL Irql; + + /* Make sure the power state is valid, and the device can support it */ + ASSERT((PdoExtension->PowerState.CurrentDeviceState >= PowerDeviceD0) && + (PdoExtension->PowerState.CurrentDeviceState <= PowerDeviceD3)); + ASSERT((PowerState >= PowerDeviceD0) && (PowerState <= PowerDeviceD3)); + ASSERT(!(PdoExtension->HackFlags & PCI_HACK_NO_PM_CAPS)); + + /* Save the current IRQL */ + Irql = KeGetCurrentIrql(); + + /* Pick the expected timeout for this transition */ + TimeoutEntry = PciPowerDelayTable[PowerState * PdoExtension->PowerState.CurrentDeviceState]; + + /* PCI power states are one less than NT power states */ + PciState = PowerState - 1; + + /* The state status is stored in the PMCSR offset */ + PmcsrOffset = CapOffset + FIELD_OFFSET(PCI_PM_CAPABILITY, PMCSR); + + /* Try changing the power state up to 100 times */ + TryCount = 100; + while (--TryCount) + { + /* Check if this state transition will take time */ + if (TimeoutEntry > 0) + { + /* Check if this is happening at high IRQL */ + if (Irql >= DISPATCH_LEVEL) + { + /* Can't wait at high IRQL, stall the processor */ + KeStallExecutionProcessor(TimeoutEntry); + } + else + { + /* Do a wait for the timeout specified instead */ + Interval.QuadPart = -10 * TimeoutEntry; + Interval.QuadPart -= KeQueryTimeIncrement() - 1; + KeDelayExecutionThread(0, 0, &Interval); + } + } + + /* Read the PMCSR and see if the state has changed */ + PciReadDeviceConfig(PdoExtension, &Pmcsr, PmcsrOffset, sizeof(PCI_PMCSR)); + if (Pmcsr.PowerState == PciState) return STATUS_SUCCESS; + + /* Try again, forcing a timeout of 1ms */ + TimeoutEntry = 1000; + } + + /* Call verifier with this error */ + VerifierData = PciVerifierRetrieveFailureData(2); + ASSERT(VerifierData); + VfFailDeviceNode(PdoExtension->PhysicalDeviceObject, + PCI_VERIFIER_DETECTED_VIOLATION, + 2, // The PMCSR register was not updated within the spec-mandated time. + VerifierData->FailureClass, + &VerifierData->AssertionControl, + VerifierData->DebuggerMessageText, + "%DevObj%Ulong", + PdoExtension->PhysicalDeviceObject, + PciState); + + return STATUS_DEVICE_PROTOCOL_ERROR; +} + +NTSTATUS +NTAPI +PciSetPowerManagedDevicePowerState(IN PPCI_PDO_EXTENSION DeviceExtension, + IN DEVICE_POWER_STATE DeviceState, + IN BOOLEAN IrpSet) +{ + NTSTATUS Status; + PCI_PM_CAPABILITY PmCaps; + ULONG CapsOffset; + + /* Assume success */ + Status = STATUS_SUCCESS; + + /* Check if this device can support low power states */ + if (!(PciCanDisableDecodes(DeviceExtension, NULL, 0, TRUE)) && + (DeviceState != PowerDeviceD0)) + { + /* Simply return success, ignoring this request */ + DPRINT1("Cannot disable decodes on this device, ignoring PM request...\n"); + return Status; + } + + /* Does the device support power management at all? */ + if (!(DeviceExtension->HackFlags & PCI_HACK_NO_PM_CAPS)) + { + /* Get the PM capabailities register */ + CapsOffset = PciReadDeviceCapability(DeviceExtension, + DeviceExtension->CapabilitiesPtr, + PCI_CAPABILITY_ID_POWER_MANAGEMENT, + &PmCaps.Header, + sizeof(PCI_PM_CAPABILITY)); + ASSERT(CapsOffset); + ASSERT(DeviceState != PowerDeviceUnspecified); + + /* Check if the device is being powered up */ + if (DeviceState == PowerDeviceD0) + { + /* Set full power state */ + PmCaps.PMCSR.ControlStatus.PowerState = 0; + + /* Check if the device supports Cold-D3 poweroff */ + if (PmCaps.PMC.Capabilities.Support.PMED3Cold) + { + /* If there was a pending PME, clear it */ + PmCaps.PMCSR.ControlStatus.PMEStatus = 1; + } + } + else + { + /* Otherwise, just set the new power state, converting from NT */ + PmCaps.PMCSR.ControlStatus.PowerState = DeviceState - 1; + } + + /* Write the new power state in the PMCSR */ + PciWriteDeviceConfig(DeviceExtension, + &PmCaps.PMCSR, + CapsOffset + FIELD_OFFSET(PCI_PM_CAPABILITY, PMCSR), + sizeof(PCI_PMCSR)); + + /* Now wait for the change to "stick" based on the spec-mandated time */ + Status = PciStallForPowerChange(DeviceExtension, DeviceState, CapsOffset); + if (!NT_SUCCESS(Status)) return Status; + } + else + { + /* Nothing to do! */ + DPRINT1("No PM on this device, ignoring request\n"); + } + + /* Check if new resources have to be assigned */ + if (IrpSet) + { + /* Check if the new device state is lower (higher power) than now */ + if (DeviceState < DeviceExtension->PowerState.CurrentDeviceState) + { + /* We would normally re-assign resources after powerup */ + UNIMPLEMENTED; + while (TRUE); + } + } + + /* Return the power state change status */ + return Status; +} + NTSTATUS NTAPI PciFdoWaitWake(IN PIRP Irp, diff --git a/reactos/drivers/bus/pcix/utils.c b/reactos/drivers/bus/pcix/utils.c index 43ce2a884de..f2dac0c4e98 100644 --- a/reactos/drivers/bus/pcix/utils.c +++ b/reactos/drivers/bus/pcix/utils.c @@ -864,7 +864,7 @@ PciSaveBiosConfig(IN PPCI_PDO_EXTENSION DeviceExtension, DeviceExtension->Slot.u.bits.DeviceNumber, DeviceExtension->Slot.u.bits.FunctionNumber); RtlInitUnicodeString(&KeyValue, Buffer); - + /* Set the value data (the PCI BIOS configuration header) */ Status = ZwSetValueKey(SubKeyHandle, &KeyValue, @@ -876,4 +876,169 @@ PciSaveBiosConfig(IN PPCI_PDO_EXTENSION DeviceExtension, return Status; } +UCHAR +NTAPI +PciReadDeviceCapability(IN PPCI_PDO_EXTENSION DeviceExtension, + IN UCHAR Offset, + IN ULONG CapabilityId, + OUT PPCI_CAPABILITIES_HEADER Buffer, + IN ULONG Length) +{ + ULONG CapabilityCount = 0; + + /* If the device has no capabilility list, fail */ + if (!Offset) return 0; + + /* Validate a PDO with capabilities, a valid buffer, and a valid length */ + ASSERT(DeviceExtension->ExtensionType == PciPdoExtensionType); + ASSERT(DeviceExtension->CapabilitiesPtr != 0); + ASSERT(Buffer); + ASSERT(Length >= sizeof(PCI_CAPABILITIES_HEADER)); + + /* Loop all capabilities */ + while (Offset) + { + /* Make sure the pointer is spec-aligned and spec-sized */ + ASSERT((Offset >= PCI_COMMON_HDR_LENGTH) && ((Offset & 0x3) == 0)); + + /* Read the capability header */ + PciReadDeviceConfig(DeviceExtension, + Buffer, + Offset, + sizeof(PCI_CAPABILITIES_HEADER)); + + /* Check if this is the capability being looked up */ + if ((Buffer->CapabilityID == CapabilityId) || !(CapabilityId)) + { + /* Check if was at a valid offset and length */ + if ((Offset) && (Length > sizeof(PCI_CAPABILITIES_HEADER))) + { + /* Sanity check */ + ASSERT(Length <= (sizeof(PCI_COMMON_CONFIG) - Offset)); + + /* Now read the whole capability data into the buffer */ + PciReadDeviceConfig(DeviceExtension, + (PVOID)((ULONG_PTR)Buffer + + sizeof(PCI_CAPABILITIES_HEADER)), + Offset + sizeof(PCI_CAPABILITIES_HEADER), + Length - sizeof(PCI_CAPABILITIES_HEADER)); + } + + /* Return the offset where the capability was found */ + return Offset; + } + + /* Try the next capability instead */ + CapabilityCount++; + Offset = Buffer->Next; + + /* There can't be more than 48 capabilities (256 bytes max) */ + if (CapabilityCount > 48) + { + /* Fail, since this is basically a broken PCI device */ + DPRINT1("PCI device %p capabilities list is broken.\n", DeviceExtension); + return 0; + } + } + + /* Capability wasn't found, fail */ + return 0; +} + +BOOLEAN +NTAPI +PciCanDisableDecodes(IN PPCI_PDO_EXTENSION DeviceExtension, + IN PPCI_COMMON_HEADER Config, + IN ULONGLONG HackFlags, + IN BOOLEAN ForPowerDown) +{ + UCHAR BaseClass, SubClass; + BOOLEAN IsVga; + + /* Is there a device extension or should the PCI header be used? */ + if (DeviceExtension) + { + /* Never disable decodes for a debug PCI Device */ + if (DeviceExtension->OnDebugPath) return FALSE; + + /* Hack flags will be obtained from the extension, not the caller */ + ASSERT(HackFlags == 0); + + /* Get hacks and classification from the device extension */ + HackFlags = DeviceExtension->HackFlags; + SubClass = DeviceExtension->SubClass; + BaseClass = DeviceExtension->BaseClass; + } + else + { + /* There must be a PCI header, go read the classification information */ + ASSERT(Config != NULL); + BaseClass = Config->BaseClass; + SubClass = Config->SubClass; + } + + /* Check for hack flags that prevent disabling the decodes */ + if (HackFlags & (PCI_HACK_PRESERVE_COMMAND | + PCI_HACK_CB_SHARE_CMD_BITS | + PCI_HACK_DONT_DISABLE_DECODES)) + { + /* Don't do it */ + return FALSE; + } + + /* Is this a VGA adapter? */ + if ((BaseClass == PCI_CLASS_DISPLAY_CTLR) && + (SubClass == PCI_SUBCLASS_VID_VGA_CTLR)) + { + /* Never disable decodes if this is for power down */ + return ForPowerDown; + } + + /* Check for legacy devices */ + if (BaseClass == PCI_CLASS_PRE_20) + { + /* Never disable video adapter cards if this is for power down */ + if (SubClass == PCI_SUBCLASS_PRE_20_VGA) return ForPowerDown; + } + else if (BaseClass == PCI_CLASS_DISPLAY_CTLR) + { + /* Never disable VGA adapters if this is for power down */ + if (SubClass == PCI_SUBCLASS_VID_VGA_CTLR) return ForPowerDown; + } + else if (BaseClass == PCI_CLASS_BRIDGE_DEV) + { + /* Check for legacy bridges */ + if ((SubClass == PCI_SUBCLASS_BR_ISA) || + (SubClass == PCI_SUBCLASS_BR_EISA) || + (SubClass == PCI_SUBCLASS_BR_MCA) || + (SubClass == PCI_SUBCLASS_BR_HOST) || + (SubClass == PCI_SUBCLASS_BR_OTHER)) + { + /* Never disable these */ + return FALSE; + } + else if ((SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI) || + (SubClass == PCI_SUBCLASS_BR_CARDBUS)) + { + /* This is a supported bridge, but does it have a VGA card? */ + if (!DeviceExtension) + { + /* Read the bridge control flag from the PCI header */ + IsVga = Config->u.type1.BridgeControl & PCI_ENABLE_BRIDGE_VGA; + } + else + { + /* Read the cached flag in the device extension */ + IsVga = DeviceExtension->Dependent.type1.VgaBitSet; + } + + /* Never disable VGA adapters if this is for power down */ + if (IsVga) return ForPowerDown; + } + } + + /* Finally, never disable decodes if there's no power management */ + return !(HackFlags & PCI_HACK_NO_PM_CAPS); +} + /* EOF */