Can't sleep so write more source codes! add scan bus functions to get power caps...
authorevb <evb@svn.reactos.org>
Sun, 18 Jul 2010 18:58:33 +0000 (18:58 +0000)
committerevb <evb@svn.reactos.org>
Sun, 18 Jul 2010 18:58:33 +0000 (18:58 +0000)
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
reactos/drivers/bus/pcix/pci.h
reactos/drivers/bus/pcix/pci/config.c
reactos/drivers/bus/pcix/pcivrify.c
reactos/drivers/bus/pcix/power.c
reactos/drivers/bus/pcix/utils.c

index 3551a3b..4af77b3 100644 (file)
@@ -124,6 +124,126 @@ PciSkipThisFunction(IN PPCI_COMMON_HEADER PciData,
     return TRUE;
 }
 
     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)
 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;
 
             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;
 
             /* Save interrupt pin */
             NewExtension->InterruptPin = PciData->u.type0.InterruptPin;
 
index 82eed32..327cce1 100644 (file)
 //
 #define MAX_DEBUGGING_DEVICES_SUPPORTED     0x04
 
 //
 #define MAX_DEBUGGING_DEVICES_SUPPORTED     0x04
 
+//
+// PCI Driver Verifier Failures
+//
+#define PCI_VERIFIER_CODES                  0x04
 //
 // Device Extension, Interface, Translator and Arbiter Signatures
 //
 //
 // 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;
 
     //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
 //
 //
 // IRP Dispatch Routines
 //
@@ -442,6 +458,14 @@ PciFdoIrpQueryPower(
     IN PPCI_FDO_EXTENSION DeviceExtension
 );
 
     IN PPCI_FDO_EXTENSION DeviceExtension
 );
 
+NTSTATUS
+NTAPI
+PciSetPowerManagedDevicePowerState(
+    IN PPCI_PDO_EXTENSION DeviceExtension,
+    IN DEVICE_POWER_STATE DeviceState,
+    IN BOOLEAN IrpSet
+);
+
 //
 // Bus FDO Routines
 //
 //
 // Bus FDO Routines
 //
@@ -778,6 +802,12 @@ PciVerifierInit(
     IN PDRIVER_OBJECT DriverObject
 );
 
     IN PDRIVER_OBJECT DriverObject
 );
 
+PPCI_VERIFIER_DATA
+NTAPI
+PciVerifierRetrieveFailureData(
+    IN ULONG FailureCode
+);
+
 //
 // Utility Routines
 //
 //
 // Utility Routines
 //
@@ -925,6 +955,25 @@ PciSaveBiosConfig(
     OUT PPCI_COMMON_HEADER PciData
 );
 
     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
 //
 //
 // Configuration Routines
 //
@@ -953,6 +1002,15 @@ PciWriteDeviceConfig(
     IN ULONG Length
 );
 
     IN ULONG Length
 );
 
+VOID
+NTAPI
+PciReadDeviceConfig(
+    IN PPCI_PDO_EXTENSION DeviceExtension,
+    IN PVOID Buffer,
+    IN ULONG Offset,
+    IN ULONG Length
+);
+
 UCHAR
 NTAPI
 PciGetAdjustedInterruptLine(
 UCHAR
 NTAPI
 PciGetAdjustedInterruptLine(
index e5382bf..51b9998 100644 (file)
@@ -24,7 +24,7 @@ PciGetAdjustedInterruptLine(IN PPCI_PDO_EXTENSION PdoExtension)
 {
     UCHAR InterruptLine = 0, PciInterruptLine;
     ULONG Length;
 {
     UCHAR InterruptLine = 0, PciInterruptLine;
     ULONG Length;
-    
+
     /* Does the device have an interrupt pin? */
     if (PdoExtension->InterruptPin)
     {
     /* Does the device have an interrupt pin? */
     if (PdoExtension->InterruptPin)
     {
@@ -102,6 +102,22 @@ PciWriteDeviceConfig(IN PPCI_PDO_EXTENSION DeviceExtension,
                             FALSE);
 }
 
                             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,
 VOID
 NTAPI
 PciReadSlotConfig(IN PPCI_FDO_EXTENSION DeviceExtension,
index ed54474..f51f02d 100644 (file)
 BOOLEAN PciVerifierRegistered;
 PVOID PciVerifierNotificationHandle;
 
 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 ******************************************************************/
 
 /* 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,
 NTSTATUS
 NTAPI
 PciVerifierProfileChangeCallback(IN PVOID NotificationStructure,
index 426d39d..d57c1b7 100644 (file)
 
 /* GLOBALS ********************************************************************/
 
 
 /* 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 ******************************************************************/
 
 /* 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,
 NTSTATUS
 NTAPI
 PciFdoWaitWake(IN PIRP Irp,
index 43ce2a8..f2dac0c 100644 (file)
@@ -864,7 +864,7 @@ PciSaveBiosConfig(IN PPCI_PDO_EXTENSION DeviceExtension,
              DeviceExtension->Slot.u.bits.DeviceNumber,
              DeviceExtension->Slot.u.bits.FunctionNumber);
     RtlInitUnicodeString(&KeyValue, Buffer);
              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,
     /* 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;
 }
 
     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 */
 /* EOF */