Sync with trunk (r48545)
[reactos.git] / drivers / bus / pcix / enum.c
index d67db28..a4d53dd 100644 (file)
 
 /* GLOBALS ********************************************************************/
 
+PCI_CONFIGURATOR PciConfigurators[] =
+{
+    {
+        Device_MassageHeaderForLimitsDetermination,
+        Device_RestoreCurrent,
+        Device_SaveLimits,
+        Device_SaveCurrentSettings,
+        Device_ChangeResourceSettings,
+        Device_GetAdditionalResourceDescriptors,
+        Device_ResetDevice
+    },
+    {
+        PPBridge_MassageHeaderForLimitsDetermination,
+        PPBridge_RestoreCurrent,
+        PPBridge_SaveLimits,
+        PPBridge_SaveCurrentSettings,
+        PPBridge_ChangeResourceSettings,
+        PPBridge_GetAdditionalResourceDescriptors,
+        PPBridge_ResetDevice
+    },
+    {
+        Cardbus_MassageHeaderForLimitsDetermination,
+        Cardbus_RestoreCurrent,
+        Cardbus_SaveLimits,
+        Cardbus_SaveCurrentSettings,
+        Cardbus_ChangeResourceSettings,
+        Cardbus_GetAdditionalResourceDescriptors,
+        Cardbus_ResetDevice
+    }
+};
+
 /* FUNCTIONS ******************************************************************/
 
+/*
+ * 7. The IO/MEM/Busmaster decodes are disabled for the device.
+ * 8. The PCI bus driver sets the operating mode bits of the Programming
+ *    Interface byte to switch the controller to native mode.
+ *
+ *    Important: When the controller is set to native mode, it must quiet itself
+ *    and must not decode I/O resources or generate interrupts until the operating
+ *    system has enabled the ports in the PCI configuration header.
+ *    The IO/MEM/BusMaster bits will be disabled before the mode change, but it
+ *    is not possible to disable interrupts on the device. The device must not
+ *    generate interrupts (either legacy or native mode) while the decodes are
+ *    disabled in the command register.
+ *
+ *    This operation is expected to be instantaneous and the operating system does
+ *    not stall afterward. It is also expected that the interrupt pin register in
+ *    the PCI Configuration space for this device is accurate. The operating system
+ *    re-reads this data after previously ignoring it.
+ */
+BOOLEAN
+NTAPI
+PciConfigureIdeController(IN PPCI_PDO_EXTENSION PdoExtension,
+                          IN PPCI_COMMON_HEADER PciData,
+                          IN BOOLEAN Initial)
+{
+    UCHAR MasterMode, SlaveMode, MasterFixed, SlaveFixed, ProgIf, NewProgIf;
+    BOOLEAN Switched;
+    USHORT Command;
+
+    /* Assume it won't work */
+    Switched = FALSE;
+
+    /* Get master and slave current settings, and programmability flag */
+    ProgIf = PciData->ProgIf;
+    MasterMode = (ProgIf & 1) == 1;
+    MasterFixed = (ProgIf & 2) == 0;
+    SlaveMode = (ProgIf & 4) == 4;
+    SlaveFixed = (ProgIf & 8) == 0;
+
+    /*
+     * [..] In order for Windows XP SP1 and Windows Server 2003 to switch an ATA
+     * ATA controller from compatible mode to native mode, the following must be
+     * true:
+     *
+     * - The controller must indicate in its programming interface that both channels
+     *   can be switched to native mode. Windows XP SP1 and Windows Server 2003 do
+     *   not support switching only one IDE channel to native mode. See the PCI IDE
+     *   Controller Specification Revision 1.0 for details.
+     */
+    if ((MasterMode != SlaveMode) || (MasterFixed != SlaveFixed))
+    {
+        /* Windows does not support this configuration, fail */
+        DPRINT1("PCI: Warning unsupported IDE controller configuration for VEN_%04x&DEV_%04x!",
+                PdoExtension->VendorId,
+                PdoExtension->DeviceId);
+        return Switched;
+    }
+
+    /* Check if the controller is already in native mode */
+    if ((MasterMode) && (SlaveMode))
+    {
+        /* Check if I/O decodes should be disabled */
+        if ((Initial) || (PdoExtension->IoSpaceUnderNativeIdeControl))
+        {
+            /* Read the current command */
+            PciReadDeviceConfig(PdoExtension,
+                                &Command,
+                                FIELD_OFFSET(PCI_COMMON_HEADER, Command),
+                                sizeof(USHORT));
+
+            /* Disable I/O space decode */
+            Command &= ~PCI_ENABLE_IO_SPACE;
+
+            /* Update new command in PCI IDE controller */
+            PciWriteDeviceConfig(PdoExtension,
+                                 &Command,
+                                 FIELD_OFFSET(PCI_COMMON_HEADER, Command),
+                                 sizeof(USHORT));
+
+            /* Save updated command value */
+            PciData->Command = Command;
+        }
+
+        /* The controller is now in native mode */
+        Switched = TRUE;
+    }
+    else if (!(MasterFixed) &&
+             !(SlaveFixed) &&
+             (PdoExtension->BIOSAllowsIDESwitchToNativeMode) &&
+             !(PdoExtension->HackFlags & PCI_HACK_DISABLE_IDE_NATIVE_MODE))
+    {
+        /* Turn off decodes */
+        PciDecodeEnable(PdoExtension, FALSE, NULL);
+
+        /* Update the current command */
+        PciReadDeviceConfig(PdoExtension,
+                            &PciData->Command,
+                            FIELD_OFFSET(PCI_COMMON_HEADER, Command),
+                            sizeof(USHORT));
+
+        /* Enable native mode */
+        ProgIf = PciData->ProgIf | 5;
+        PciWriteDeviceConfig(PdoExtension,
+                             &ProgIf,
+                             FIELD_OFFSET(PCI_COMMON_HEADER, ProgIf),
+                             sizeof(UCHAR));
+
+        /* Verify the setting "stuck" */
+        PciReadDeviceConfig(PdoExtension,
+                            &NewProgIf,
+                            FIELD_OFFSET(PCI_COMMON_HEADER, ProgIf),
+                            sizeof(UCHAR));
+        if (NewProgIf == ProgIf)
+        {
+            /* Update the header and PDO data with the new programming mode */
+            PciData->ProgIf = ProgIf;
+            PdoExtension->ProgIf = NewProgIf;
+
+            /* Clear the first four BARs to reset current BAR setttings */
+            PciData->u.type0.BaseAddresses[0] = 0;
+            PciData->u.type0.BaseAddresses[1] = 0;
+            PciData->u.type0.BaseAddresses[2] = 0;
+            PciData->u.type0.BaseAddresses[3] = 0;
+            PciWriteDeviceConfig(PdoExtension,
+                                 PciData->u.type0.BaseAddresses,
+                                 FIELD_OFFSET(PCI_COMMON_HEADER,
+                                              u.type0.BaseAddresses),
+                                 4 * sizeof(ULONG));
+
+            /* Re-read the BARs to have the latest data for native mode IDE */
+            PciReadDeviceConfig(PdoExtension,
+                                PciData->u.type0.BaseAddresses,
+                                FIELD_OFFSET(PCI_COMMON_HEADER,
+                                             u.type0.BaseAddresses),
+                                4 * sizeof(ULONG));
+
+            /* Re-read the interrupt pin used for native mode IDE */
+            PciReadDeviceConfig(PdoExtension,
+                                &PciData->u.type0.InterruptPin,
+                                FIELD_OFFSET(PCI_COMMON_HEADER,
+                                             u.type0.InterruptPin),
+                                sizeof(UCHAR));
+
+            /* The IDE Controller is now in native mode */
+            Switched = TRUE;
+        }
+        else
+        {
+            /* Settings did not work, fail */
+            DPRINT1("PCI: Warning failed switch to native mode for IDE controller VEN_%04x&DEV_%04x!",
+                    PciData->VendorID,
+                    PciData->DeviceID);
+        }
+   }
+
+   /* Return whether or not native mode was enabled on the IDE controller */
+   return Switched;
+}
+
+VOID
+NTAPI
+PciApplyHacks(IN PPCI_FDO_EXTENSION DeviceExtension,
+              IN PPCI_COMMON_HEADER PciData,
+              IN PCI_SLOT_NUMBER SlotNumber,
+              IN ULONG OperationType,
+              PPCI_PDO_EXTENSION PdoExtension)
+{
+    ULONG LegacyBaseAddress;
+    USHORT Command;
+    UCHAR RegValue;
+
+    /* Check what kind of hack operation this is */
+    switch (OperationType)
+    {
+        /*
+         * This is mostly concerned with fixing up incorrect class data that can
+         * exist on certain PCI hardware before the 2.0 spec was ratified.
+         */
+        case PCI_HACK_FIXUP_BEFORE_CONFIGURATION:
+
+            /* Note that the i82375 PCI/EISA and the i82378 PCI/ISA bridges that
+             * are present on certain DEC/NT Alpha machines are pre-PCI 2.0 devices
+             * and appear as non-classified, so their correct class/subclass data
+             * is written here instead.
+             */
+            if ((PciData->VendorID == 0x8086) &&
+                ((PciData->DeviceID == 0x482) || (PciData->DeviceID == 0x484)))
+            {
+                /* Note that 0x482 is the i82375 (EISA), 0x484 is the i82378 (ISA) */
+                PciData->SubClass = PciData->DeviceID == 0x482 ?
+                                    PCI_SUBCLASS_BR_EISA : PCI_SUBCLASS_BR_ISA;
+                PciData->BaseClass = PCI_CLASS_BRIDGE_DEV;
+
+                /*
+                 * Because the software is modifying the actual header data from
+                 * the BIOS, this flag tells the driver to ignore failures when
+                 * comparing the original BIOS data with the PCI data.
+                 */
+                if (PdoExtension) PdoExtension->ExpectedWritebackFailure = TRUE;
+            }
+
+            /* Note that in this case, an immediate return is issued */
+            return;
+
+        /*
+         * This is concerned with setting up interrupts correctly for native IDE
+         * mode, but will also handle broken VGA decoding on older bridges as
+         * well as a PAE-specific hack for certain Compaq Hot-Plug Controllers.
+         */
+        case PCI_HACK_FIXUP_AFTER_CONFIGURATION:
+
+            /* There should always be a PDO extension passed in */
+            ASSERT(PdoExtension);
+
+            /*
+             * On the OPTi Viper-M IDE controller, Linux doesn't support IDE-DMA
+             * and FreeBSD bug reports indicate that the system crashes when the
+             * feature is enabled (so it's disabled on that OS as well). In the
+             * NT PCI Bus Driver, it seems Microsoft too, completely disables
+             * Native IDE functionality on this controller, so it would seem OPTi
+             * simply frelled up this controller.
+             */
+            if ((PciData->VendorID == 0x1045) && (PciData->DeviceID != 0xC621))
+            {
+                /* Disable native mode */
+                PciData->ProgIf &= ~5;
+                PciData->u.type0.InterruptPin = 0;
+
+                /*
+                 * Because the software is modifying the actual header data from
+                 * the BIOS, this flag tells the driver to ignore failures when
+                 * comparing the original BIOS data with the PCI data.
+                 */
+                PdoExtension->ExpectedWritebackFailure = TRUE;
+            }
+            else if ((PciData->BaseClass == PCI_CLASS_MASS_STORAGE_CTLR) &&
+                    (PciData->SubClass == PCI_SUBCLASS_MSC_IDE_CTLR))
+            {
+                /* For other IDE controllers, start out in compatible mode */
+                PdoExtension->BIOSAllowsIDESwitchToNativeMode = FALSE;
+
+                /*
+                 * Registry must have enabled native mode (typically as a result
+                 * of an INF file directive part of the IDE controller's driver)
+                 * and the system must not be booted in Safe Mode. If that checks
+                 * out, then evaluate the ACPI NATA method to see if the platform
+                 * supports this. See the section "BIOS and Platform Prerequisites
+                 * for Switching a Native-Mode-Capable Controller" in the Storage
+                 * section of the Windows Driver Kit for more details:
+                 *
+                 * 5. For each ATA controller enumerated, the PCI bus driver checks
+                 *    the Programming Interface register of the IDE controller to
+                 *    see if it supports switching both channels to native mode.
+                 * 6. The PCI bus driver checks whether the BIOS/platform supports
+                 *    switching the controller by checking the NATA method described
+                 *    earlier in this article.
+                 *
+                 *    If an ATA controller does not indicate that it is native
+                 *    mode-capable, or if the BIOS NATA control method is missing
+                 *    or does not list that device, the PCI bus driver does not
+                 *    switch the controller and it is assigned legacy resources.
+                 *
+                 *  If both the controller and the BIOS indicate that the controller
+                 *  can be switched, the process of switching the controller begins
+                 *  with the next step.
+                 */
+                if ((PciEnableNativeModeATA) &&
+                    !(InitSafeBootMode) &&
+                    (PciIsSlotPresentInParentMethod(PdoExtension, 'ATAN')))
+                {
+                    /* The platform supports it, remember that */
+                    PdoExtension->BIOSAllowsIDESwitchToNativeMode = TRUE;
+
+                    /*
+                     * Now switch the controller into native mode if both channels
+                     * support native IDE mode. See "How Windows Switches an ATA
+                     * Controller to Native Mode" in the Storage section of the
+                     * Windows Driver Kit for more details.
+                     */
+                    PdoExtension->SwitchedIDEToNativeMode =
+                        PciConfigureIdeController(PdoExtension, PciData, 1);
+                }
+
+                /* Is native mode enabled after all? */
+                if ((PciData->ProgIf & 5) != 5)
+                {
+                    /* Compatible mode, so force ISA-style IRQ14 and IRQ 15 */
+                    PciData->u.type0.InterruptPin = 0;
+                }
+            }
+
+            /* Is this a PCI device with legacy VGA card decodes on the root bus? */
+            if ((PdoExtension->HackFlags & PCI_HACK_VIDEO_LEGACY_DECODE) &&
+                (PCI_IS_ROOT_FDO(DeviceExtension)) &&
+                !(DeviceExtension->BrokenVideoHackApplied))
+            {
+                /* Tell the arbiter to apply a hack for these older devices */
+                ario_ApplyBrokenVideoHack(DeviceExtension);
+            }
+
+            /* Is this a Compaq PCI Hotplug Controller (r17) on a PAE system ? */
+            if ((PciData->VendorID == 0xE11) &&
+                (PciData->DeviceID == 0xA0F7) &&
+                (PciData->RevisionID == 17) &&
+                (ExIsProcessorFeaturePresent(PF_PAE_ENABLED)))
+            {
+                /* Turn off the decodes immediately */
+                PciData->Command &= ~(PCI_ENABLE_IO_SPACE |
+                                      PCI_ENABLE_MEMORY_SPACE |
+                                      PCI_ENABLE_BUS_MASTER);
+                PciWriteDeviceConfig(PdoExtension,
+                                     &PciData->Command,
+                                     FIELD_OFFSET(PCI_COMMON_HEADER, Command),
+                                     sizeof(USHORT));
+
+                /* Do not EVER turn them on again, this will blow up the system */
+                PdoExtension->CommandEnables &= ~(PCI_ENABLE_IO_SPACE |
+                                                  PCI_ENABLE_MEMORY_SPACE |
+                                                  PCI_ENABLE_BUS_MASTER);
+                PdoExtension->HackFlags |= PCI_HACK_PRESERVE_COMMAND;
+            }
+            break;
+
+        /*
+         * This is called whenever resources are changed and hardware needs to be
+         * updated. It is concerned with two highly specific erratas on an IBM
+         * hot-plug docking bridge used on the Thinkpad 600 Series and on Intel's
+         * ICH PCI Bridges.
+         */
+        case PCI_HACK_FIXUP_BEFORE_UPDATE:
+
+            /* There should always be a PDO extension passed in */
+            ASSERT(PdoExtension);
+
+            /* Is this an IBM 20H2999 PCI Docking Bridge, used on Thinkpads? */
+            if ((PdoExtension->VendorId == 0x1014) &&
+                (PdoExtension->DeviceId == 0x95))
+            {
+                /* Read the current command */
+                PciReadDeviceConfig(PdoExtension,
+                                    &Command,
+                                    FIELD_OFFSET(PCI_COMMON_HEADER, Command),
+                                    sizeof(USHORT));
+
+                /* Turn off the decodes */
+                PciDecodeEnable(PdoExtension, FALSE, &Command);
+
+                /* Apply the required IBM workaround */
+                PciReadDeviceConfig(PdoExtension, &RegValue, 0xE0, sizeof(UCHAR));
+                RegValue &= ~2;
+                RegValue |= 1;
+                PciWriteDeviceConfig(PdoExtension, &RegValue, 0xE0, sizeof(UCHAR));
+
+                /* Restore the command to its original value */
+                PciWriteDeviceConfig(PdoExtension,
+                                     &Command,
+                                     FIELD_OFFSET(PCI_COMMON_HEADER, Command),
+                                     sizeof(USHORT));
+
+            }
+
+            /*
+             * Check for Intel ICH PCI-to-PCI (i82801) bridges (used on the i810,
+             * i820, i840, i845 Chipsets) that have subtractive decode enabled,
+             * and whose hack flags do not specifiy that this support is broken.
+             */
+            if ((PdoExtension->HeaderType == PCI_BRIDGE_TYPE) &&
+                (PdoExtension->Dependent.type1.SubtractiveDecode) &&
+                ((PdoExtension->VendorId == 0x8086) &&
+                 ((PdoExtension->DeviceId == 0x2418) ||
+                  (PdoExtension->DeviceId == 0x2428) ||
+                  (PdoExtension->DeviceId == 0x244E) ||
+                  (PdoExtension->DeviceId == 0x2448))) &&
+               !(PdoExtension->HackFlags & PCI_HACK_BROKEN_SUBTRACTIVE_DECODE))
+            {
+                /*
+                 * The positive decode window shouldn't be used, these values are
+                 * normally all read-only or initialized to 0 by the BIOS, but
+                 * it appears Intel doesn't do this, so the PCI Bus Driver will
+                 * do it in software instead. Note that this is used to prevent
+                 * certain non-compliant PCI devices from breaking down due to the
+                 * fact that these ICH bridges have a known "quirk" (which Intel
+                 * documents as a known "erratum", although it's not not really
+                 * an ICH bug since the PCI specification does allow for it) in
+                 * that they will sometimes send non-zero addresses during special
+                 * cycles (ie: non-zero data during the address phase). These
+                 * broken PCI cards will mistakenly attempt to claim the special
+                 * cycle and corrupt their I/O and RAM ranges. Again, in Intel's
+                 * defense, the PCI specification only requires stable data, not
+                 * necessarily zero data, during the address phase.
+                 */
+                PciData->u.type1.MemoryBase = 0xFFFF;
+                PciData->u.type1.PrefetchBase = 0xFFFF;
+                PciData->u.type1.IOBase = 0xFF;
+                PciData->u.type1.IOLimit = 0;
+                PciData->u.type1.MemoryLimit = 0;
+                PciData->u.type1.PrefetchLimit = 0;
+                PciData->u.type1.PrefetchBaseUpper32 = 0;
+                PciData->u.type1.PrefetchLimitUpper32 = 0;
+                PciData->u.type1.IOBaseUpper16 = 0;
+                PciData->u.type1.IOLimitUpper16 = 0;
+            }
+            break;
+
+        default:
+            return;
+    }
+
+    /* Finally, also check if this is this a CardBUS device? */
+    if (PCI_CONFIGURATION_TYPE(PciData) == PCI_CARDBUS_BRIDGE_TYPE)
+    {
+        /*
+         * At offset 44h the LegacyBaseAddress is stored, which is cleared by
+         * ACPI-aware versions of Windows, to disable legacy-mode I/O access to
+         * CardBus controllers. For more information, see "Supporting CardBus
+         * Controllers under ACPI" in the "CardBus Controllers and Windows"
+         * Whitepaper on WHDC.
+         */
+        LegacyBaseAddress = 0;
+        PciWriteDeviceConfig(PdoExtension,
+                             &LegacyBaseAddress,
+                             sizeof(PCI_COMMON_HEADER) + sizeof(ULONG),
+                             sizeof(ULONG));
+    }
+}
+
+BOOLEAN
+NTAPI
+PcipIsSameDevice(IN PPCI_PDO_EXTENSION DeviceExtension,
+                 IN PPCI_COMMON_HEADER PciData)
+{
+    BOOLEAN IdMatch, RevMatch, SubsysMatch;
+    ULONGLONG HackFlags = DeviceExtension->HackFlags;
+
+    /* Check if the IDs match */
+    IdMatch = (PciData->VendorID == DeviceExtension->VendorId) &&
+              (PciData->DeviceID == DeviceExtension->DeviceId);
+    if (!IdMatch) return FALSE;
+
+    /* If the device has a valid revision, check if it matches */
+    RevMatch = (HackFlags & PCI_HACK_NO_REVISION_AFTER_D3) ||
+               (PciData->RevisionID == DeviceExtension->RevisionId);
+    if (!RevMatch) return FALSE;
+
+    /* For multifunction devices, this is enough to assume they're the same */
+    if (PCI_MULTIFUNCTION_DEVICE(PciData)) return TRUE;
+
+    /* For bridge devices, there's also nothing else that can be checked */
+    if (DeviceExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) return TRUE;
+
+    /* Devices, on the other hand, have subsystem data that can be compared */
+    SubsysMatch = (HackFlags & (PCI_HACK_NO_SUBSYSTEM |
+                                PCI_HACK_NO_SUBSYSTEM_AFTER_D3)) ||
+                  ((DeviceExtension->SubsystemVendorId ==
+                    PciData->u.type0.SubVendorID) &&
+                   (DeviceExtension->SubsystemId ==
+                    PciData->u.type0.SubSystemID));
+    return SubsysMatch;
+}
+
+BOOLEAN
+NTAPI
+PciSkipThisFunction(IN PPCI_COMMON_HEADER PciData,
+                    IN PCI_SLOT_NUMBER Slot,
+                    IN UCHAR OperationType,
+                    IN ULONGLONG HackFlags)
+{
+    do
+    {
+        /* Check if this is device enumeration */
+        if (OperationType == PCI_SKIP_DEVICE_ENUMERATION)
+        {
+            /* Check if there's a hackflag saying not to enumerate this device */
+            if (HackFlags & PCI_HACK_NO_ENUM_AT_ALL) break;
+
+            /* Check if this is the high end of a double decker device */
+            if ((HackFlags & PCI_HACK_DOUBLE_DECKER) &&
+                (Slot.u.bits.DeviceNumber >= 16))
+            {
+                /* It belongs to the same device, so skip it */
+                DPRINT1("    Device (Ven %04x Dev %04x (d=0x%x, f=0x%x)) is a ghost.\n",
+                        PciData->VendorID,
+                        PciData->DeviceID,
+                        Slot.u.bits.DeviceNumber,
+                        Slot.u.bits.FunctionNumber);
+                break;
+            }
+        }
+        else if (OperationType == PCI_SKIP_RESOURCE_ENUMERATION)
+        {
+            /* Resource enumeration, check for a hackflag saying not to do it */
+            if (HackFlags & PCI_HACK_ENUM_NO_RESOURCE) break;
+        }
+        else
+        {
+            /* Logic error in the driver */
+            ASSERTMSG(FALSE, "PCI Skip Function - Operation type unknown.");
+        }
+
+        /* Check for legacy bridges during resource enumeration */
+        if ((PciData->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
+            (PciData->SubClass <= PCI_SUBCLASS_BR_MCA) &&
+            (OperationType == PCI_SKIP_RESOURCE_ENUMERATION))
+        {
+            /* Their resources are not enumerated, only PCI and Cardbus/PCMCIA */
+            break;
+        }
+        else if (PciData->BaseClass == PCI_CLASS_NOT_DEFINED)
+        {
+            /* Undefined base class (usually a PCI BIOS/ROM bug) */
+            DPRINT1("    Vendor %04x, Device %04x has class code of PCI_CLASS_NOT_DEFINED\n",
+                    PciData->VendorID,
+                    PciData->DeviceID);
+
+            /*
+             * The Alder has an Intel Extended Express System Support Controller
+             * which presents apparently spurious BARs. When the PCI resource
+             * code tries to reassign these BARs, the second IO-APIC gets
+             * disabled (with disastrous consequences). The first BAR is the
+             * actual IO-APIC, the remaining five bars seem to be spurious
+             * resources, so ignore this device completely.
+             */
+            if ((PciData->VendorID == 0x8086) && (PciData->DeviceID == 8)) break;
+        }
+
+        /* Other normal PCI cards and bridges are enumerated */
+        if (PCI_CONFIGURATION_TYPE(PciData) <= PCI_CARDBUS_BRIDGE_TYPE) return FALSE;
+    } while (FALSE);
+
+    /* Hit one of the known bugs/hackflags, or this is a new kind of PCI unit */
+    DPRINT1("   Device skipped (not enumerated).\n");
+    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);
+    }
+}
+
+VOID
+NTAPI
+PciWriteLimitsAndRestoreCurrent(IN PVOID Reserved,
+                                IN PPCI_CONFIGURATOR_CONTEXT Context)
+{
+    PPCI_COMMON_HEADER PciData, Current;
+    PPCI_PDO_EXTENSION PdoExtension;
+
+    /* Grab all parameters from the context */
+    PdoExtension = Context->PdoExtension;
+    Current = Context->Current;
+    PciData = Context->PciData;
+
+    /* Write the limit discovery header */
+    PciWriteDeviceConfig(PdoExtension, PciData, 0, PCI_COMMON_HDR_LENGTH);
+
+    /* Now read what the device indicated the limits are */
+    PciReadDeviceConfig(PdoExtension, PciData, 0, PCI_COMMON_HDR_LENGTH);
+
+    /* Then write back the original configuration header */
+    PciWriteDeviceConfig(PdoExtension, Current, 0, PCI_COMMON_HDR_LENGTH);
+
+    /* Copy back the original command that was saved in the context */
+    Current->Command = Context->Command;
+    if (Context->Command)
+    {
+        /* Program it back into the device */
+        PciWriteDeviceConfig(PdoExtension,
+                             &Context->Command,
+                             FIELD_OFFSET(PCI_COMMON_HEADER, Command),
+                             sizeof(USHORT));
+    }
+
+    /* Copy back the original status that was saved as well */
+    Current->Status = Context->Status;
+
+    /* Call the configurator to restore any other data that might've changed */
+    Context->Configurator->RestoreCurrent(Context);
+}
+
+NTSTATUS
+NTAPI
+PcipGetFunctionLimits(IN PPCI_CONFIGURATOR_CONTEXT Context)
+{
+    PPCI_CONFIGURATOR Configurator;
+    PPCI_COMMON_HEADER PciData, Current;
+    PPCI_PDO_EXTENSION PdoExtension;
+    PCI_IPI_CONTEXT IpiContext;
+    PIO_RESOURCE_DESCRIPTOR IoDescriptor;
+    ULONG Offset;
+    PAGED_CODE();
+
+    /* Grab all parameters from the context */
+    PdoExtension = Context->PdoExtension;
+    Current = Context->Current;
+    PciData = Context->PciData;
+
+    /* Save the current PCI Command and Status word */
+    Context->Status = Current->Status;
+    Context->Command = Current->Command;
+
+    /* Now that they're saved, clear the status, and disable all decodes */
+    Current->Status = 0;
+    Current->Command &= ~(PCI_ENABLE_IO_SPACE |
+                          PCI_ENABLE_MEMORY_SPACE |
+                          PCI_ENABLE_BUS_MASTER);
+
+    /* Make a copy of the current PCI configuration header (with decodes off) */
+    RtlCopyMemory(PciData, Current, PCI_COMMON_HDR_LENGTH);
+
+    /* Locate the correct resource configurator for this type of device */
+    Configurator = &PciConfigurators[PdoExtension->HeaderType];
+    Context->Configurator = Configurator;
+
+    /* Initialize it, which will typically setup the BARs for limit discovery */
+    Configurator->Initialize(Context);
+
+    /* Check for critical devices and PCI Debugging devices */
+    if ((PdoExtension->HackFlags & PCI_HACK_CRITICAL_DEVICE) ||
+        (PdoExtension->OnDebugPath))
+    {
+        /* Specifically check for a PCI Debugging device */
+        if (PdoExtension->OnDebugPath)
+        {
+            /* Was it enabled for bus mastering? */
+            if (Context->Command & PCI_ENABLE_BUS_MASTER)
+            {
+                /* This decode needs to be re-enabled so debugging can work */
+                PciData->Command |= PCI_ENABLE_BUS_MASTER;
+                Current->Command |= PCI_ENABLE_BUS_MASTER;
+            }
+
+            /* Disable the debugger while the discovery is happening */
+            KdDisableDebugger();
+        }
+
+        /* For these devices, an IPI must be sent to force high-IRQL discovery */
+        IpiContext.Barrier = 1;
+        IpiContext.RunCount = 1;
+        IpiContext.PdoExtension = PdoExtension;
+        IpiContext.Function = PciWriteLimitsAndRestoreCurrent;
+        IpiContext.Context = Context;
+        KeIpiGenericCall(PciExecuteCriticalSystemRoutine, (ULONG_PTR)&IpiContext);
+
+        /* Re-enable the debugger if this was a PCI Debugging Device */
+        if (PdoExtension->OnDebugPath) KdEnableDebugger();
+    }
+    else
+    {
+        /* Otherwise, it's safe to do this in-line at low IRQL */
+        PciWriteLimitsAndRestoreCurrent(PdoExtension, Context);
+    }
+
+    /*
+     * Check if it's valid to compare the headers to see if limit discovery mode
+     * has properly exited (the expected case is that the PCI header would now
+     * be equal to what it was before). In some cases, it is known that this will
+     * fail, because during PciApplyHacks (among other places), software hacks
+     * had to be applied to the header, which the hardware-side will not see, and
+     * thus the headers would appear "different".
+     */
+    if (!PdoExtension->ExpectedWritebackFailure)
+    {
+        /* Read the current PCI header now, after discovery has completed */
+        PciReadDeviceConfig(PdoExtension, PciData + 1, 0, PCI_COMMON_HDR_LENGTH);
+
+        /* Check if the current header at entry, is equal to the header now */
+        Offset = RtlCompareMemory(PciData + 1, Current, PCI_COMMON_HDR_LENGTH);
+        if (Offset != PCI_COMMON_HDR_LENGTH)
+        {
+            /* It's not, which means configuration somehow changed, dump this */
+            DPRINT1("PCI - CFG space write verify failed at offset 0x%x\n", Offset);
+            PciDebugDumpCommonConfig(PciData + 1);
+            DPRINT1("----------\n");
+            PciDebugDumpCommonConfig(Current);
+        }
+    }
+
+    /* This PDO should not already have resources, since this is only done once */
+    ASSERT(PdoExtension->Resources == NULL);
+
+    /* Allocate the structure that will hold the discovered resources and limits */
+    PdoExtension->Resources = ExAllocatePoolWithTag(NonPagedPool,
+                                                    sizeof(PCI_FUNCTION_RESOURCES),
+                                                    'BicP');
+    if (!PdoExtension->Resources) return STATUS_INSUFFICIENT_RESOURCES;
+
+    /* Clear it out for now */
+    RtlZeroMemory(PdoExtension->Resources, sizeof(PCI_FUNCTION_RESOURCES));
+
+    /* Now call the configurator, which will first store the limits... */
+    Configurator->SaveLimits(Context);
+
+    /* ...and then store the current resources being used */
+    Configurator->SaveCurrentSettings(Context);
+
+    /* Loop all the limit descriptors backwards */
+    IoDescriptor = &PdoExtension->Resources->Limit[PCI_TYPE0_ADDRESSES + 1];
+    while (TRUE)
+    {
+        /* Keep going until a non-null descriptor is found */
+        IoDescriptor--;
+        if (IoDescriptor->Type != CmResourceTypeNull) break;
+
+        /* This is a null descriptor, is it the last one? */
+        if (IoDescriptor == &PdoExtension->Resources->Limit[PCI_TYPE0_ADDRESSES + 1])
+        {
+            /* This means the descriptor is NULL, which means discovery failed */
+            DPRINT1("PCI Resources fail!\n");
+
+            /* No resources will be assigned for the device */
+            ExFreePoolWithTag(PdoExtension->Resources, 0);
+            PdoExtension->Resources = NULL;
+            break;
+        }
+    }
+
+    /* Return success here, even if the device has no assigned resources */
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+PciGetFunctionLimits(IN PPCI_PDO_EXTENSION PdoExtension,
+                     IN PPCI_COMMON_HEADER Current,
+                     IN ULONGLONG HackFlags)
+{
+    NTSTATUS Status;
+    PPCI_COMMON_HEADER PciData;
+    PCI_CONFIGURATOR_CONTEXT Context;
+    PAGED_CODE();
+
+    /* Do the hackflags indicate this device should be skipped? */
+    if (PciSkipThisFunction(Current,
+                            PdoExtension->Slot,
+                            PCI_SKIP_RESOURCE_ENUMERATION,
+                            HackFlags))
+    {
+        /* Do not process its resources */
+        return STATUS_SUCCESS;
+    }
+
+    /* Allocate a buffer to hold two PCI configuration headers */
+    PciData = ExAllocatePoolWithTag(0, 2 * PCI_COMMON_HDR_LENGTH, 'BicP');
+    if (!PciData) return STATUS_INSUFFICIENT_RESOURCES;
+
+    /* Set up the context for the resource enumeration, and do it */
+    Context.Current = Current;
+    Context.PciData = PciData;
+    Context.PdoExtension = PdoExtension;
+    Status = PcipGetFunctionLimits(&Context);
+
+    /* Enumeration is completed, free the PCI headers and return the status */
+    ExFreePoolWithTag(PciData, 0);
+    return Status;
+}
+
+VOID
+NTAPI
+PciProcessBus(IN PPCI_FDO_EXTENSION DeviceExtension)
+{
+    PPCI_PDO_EXTENSION PdoExtension;
+    PDEVICE_OBJECT PhysicalDeviceObject;
+    PAGED_CODE();
+
+    /* Get the PDO Extension */
+    PhysicalDeviceObject = DeviceExtension->PhysicalDeviceObject;
+    PdoExtension = (PPCI_PDO_EXTENSION)PhysicalDeviceObject->DeviceExtension;
+
+    /* Cheeck if this is the root bus */
+    if (!PCI_IS_ROOT_FDO(DeviceExtension))
+    {
+        /* Not really handling this year */
+        UNIMPLEMENTED;
+        while (TRUE);
+
+        /* Check for PCI bridges with the ISA bit set, or required */
+        if ((PdoExtension) &&
+            (PciClassifyDeviceType(PdoExtension) == PciTypePciBridge) &&
+            ((PdoExtension->Dependent.type1.IsaBitRequired) ||
+             (PdoExtension->Dependent.type1.IsaBitSet)))
+        {
+            /* We'll need to do some legacy support */
+            UNIMPLEMENTED;
+            while (TRUE);
+        }
+    }
+    else
+    {
+        /* Scan all of the root bus' children bridges */
+        for (PdoExtension = DeviceExtension->ChildBridgePdoList;
+             PdoExtension;
+             PdoExtension = PdoExtension->NextBridge)
+        {
+            /* Find any that have the VGA decode bit on */
+            if (PdoExtension->Dependent.type1.VgaBitSet)
+            {
+                /* Again, some more legacy support we'll have to do */
+                UNIMPLEMENTED;
+                while (TRUE);
+            }
+        }
+    }
+
+    /* Check for ACPI systems where the OS assigns bus numbers */
+    if (PciAssignBusNumbers)
+    {
+        /* Not yet supported */
+        UNIMPLEMENTED;
+        while (TRUE);
+    }
+}
+
+NTSTATUS
+NTAPI
+PciScanBus(IN PPCI_FDO_EXTENSION DeviceExtension)
+{
+    ULONG MaxDevice = PCI_MAX_DEVICES;
+    BOOLEAN ProcessFlag = FALSE;
+    ULONG i, j, k, Size;
+    USHORT CapOffset, TempOffset;
+    LONGLONG HackFlags;
+    PDEVICE_OBJECT DeviceObject;
+    UCHAR Buffer[PCI_COMMON_HDR_LENGTH];
+    UCHAR BiosBuffer[PCI_COMMON_HDR_LENGTH];
+    PPCI_COMMON_HEADER PciData = (PVOID)Buffer;
+    PPCI_COMMON_HEADER BiosData = (PVOID)BiosBuffer;
+    PCI_SLOT_NUMBER PciSlot;
+    PCHAR Name;
+    NTSTATUS Status;
+    PPCI_PDO_EXTENSION PdoExtension, NewExtension;
+    PPCI_PDO_EXTENSION* BridgeExtension;
+    PWCHAR DescriptionText;
+    USHORT SubVendorId, SubSystemId;
+    PCI_CAPABILITIES_HEADER CapHeader, PcixCapHeader;
+    DPRINT1("PCI Scan Bus: FDO Extension @ 0x%x, Base Bus = 0x%x\n",
+            DeviceExtension, DeviceExtension->BaseBus);
+
+    /* Is this the root FDO? */
+    if (!PCI_IS_ROOT_FDO(DeviceExtension))
+    {
+        /* Other FDOs are not currently supported */
+        UNIMPLEMENTED;
+        while (TRUE);
+    }
+
+    /* Loop every device on the bus */
+    PciSlot.u.bits.Reserved = 0;
+    i = DeviceExtension->BaseBus;
+    for (j = 0; j < MaxDevice; j++)
+    {
+        /* Loop every function of each device */
+        PciSlot.u.bits.DeviceNumber = j;
+        for (k = 0; k < PCI_MAX_FUNCTION; k++)
+        {
+            /* Build the final slot structure */
+            PciSlot.u.bits.FunctionNumber = k;
+
+            /* Read the vendor for this slot */
+            PciReadSlotConfig(DeviceExtension,
+                              PciSlot,
+                              PciData,
+                              0,
+                              sizeof(USHORT));
+
+            /* Skip invalid device */
+            if (PciData->VendorID == PCI_INVALID_VENDORID) continue;
+
+            /* Now read the whole header */
+            PciReadSlotConfig(DeviceExtension,
+                              PciSlot,
+                              &PciData->DeviceID,
+                              sizeof(USHORT),
+                              PCI_COMMON_HDR_LENGTH - sizeof(USHORT));
+
+            /* Apply any hacks before even analyzing the configuration header */
+            PciApplyHacks(DeviceExtension,
+                          PciData,
+                          PciSlot,
+                          PCI_HACK_FIXUP_BEFORE_CONFIGURATION,
+                          NULL);
+
+            /* Dump device that was found */
+            DPRINT1("Scan Found Device 0x%x (b=0x%x, d=0x%x, f=0x%x)\n",
+                    PciSlot.u.AsULONG,
+                    i,
+                    j,
+                    k);
+
+            /* Dump the device's header */
+            PciDebugDumpCommonConfig(PciData);
+
+            /* Find description for this device for the debugger's sake */
+            DescriptionText = PciGetDeviceDescriptionMessage(PciData->BaseClass,
+                                                             PciData->SubClass);
+            DPRINT1("Device Description \"%S\".\n",
+                    DescriptionText ? DescriptionText : L"(NULL)");
+            if (DescriptionText) ExFreePoolWithTag(DescriptionText, 0);
+
+            /* Check if there is an ACPI Watchdog Table */
+            if (WdTable)
+            {
+                /* Check if this PCI device is the ACPI Watchdog Device... */
+                UNIMPLEMENTED;
+                while (TRUE);
+            }
+
+            /* Check for non-simple devices */
+            if ((PCI_MULTIFUNCTION_DEVICE(PciData)) ||
+                (PciData->BaseClass == PCI_CLASS_BRIDGE_DEV))
+            {
+                /* No subsystem data defined for these kinds of bridges */
+                SubVendorId = 0;
+                SubSystemId = 0;
+            }
+            else
+            {
+                /* Read the subsystem information from the PCI header */
+                SubVendorId = PciData->u.type0.SubVendorID;
+                SubSystemId = PciData->u.type0.SubSystemID;
+            }
+
+            /* Get any hack flags for this device */
+            HackFlags = PciGetHackFlags(PciData->VendorID,
+                                        PciData->DeviceID,
+                                        SubVendorId,
+                                        SubSystemId,
+                                        PciData->RevisionID);
+
+            /* Check if this device is considered critical by the OS */
+            if (PciIsCriticalDeviceClass(PciData->BaseClass, PciData->SubClass))
+            {
+                /* Check if normally the decodes would be disabled */
+                if (!(HackFlags & PCI_HACK_DONT_DISABLE_DECODES))
+                {
+                    /* Because this device is critical, don't disable them */
+                    DPRINT1("Not allowing PM Because device is critical\n");
+                    HackFlags |= PCI_HACK_CRITICAL_DEVICE;
+                }
+            }
+
+            /* PCI bridges with a VGA card are also considered critical */
+            if ((PciData->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
+                (PciData->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI) &&
+                (PciData->u.type1.BridgeControl & PCI_ENABLE_BRIDGE_VGA) &&
+               !(HackFlags & PCI_HACK_DONT_DISABLE_DECODES))
+            {
+                /* Do not disable their decodes either */
+                DPRINT1("Not allowing PM because device is VGA\n");
+                HackFlags |= PCI_HACK_CRITICAL_DEVICE;
+            }
+
+            /* Check if the device should be skipped for whatever reason */
+            if (PciSkipThisFunction(PciData,
+                                    PciSlot,
+                                    PCI_SKIP_DEVICE_ENUMERATION,
+                                    HackFlags))
+            {
+                /* Skip this device */
+                continue;
+            }
+
+            /* Check if a PDO has already been created for this device */
+            PdoExtension = PciFindPdoByFunction(DeviceExtension,
+                                                PciSlot.u.AsULONG,
+                                                PciData);
+            if (PdoExtension)
+            {
+                /* Rescan scenarios are not yet implemented */
+                UNIMPLEMENTED;
+                while (TRUE);
+            }
+
+            /* Bus processing will need to happen */
+            ProcessFlag = TRUE;
+
+            /* Create the PDO for this device */
+            Status = PciPdoCreate(DeviceExtension, PciSlot, &DeviceObject);
+            ASSERT(NT_SUCCESS(Status));
+            NewExtension = (PPCI_PDO_EXTENSION)DeviceObject->DeviceExtension;
+
+            /* Check for broken devices with wrong/no class codes */
+            if (HackFlags & PCI_HACK_FAKE_CLASS_CODE)
+            {
+                /* Setup a default one */
+                PciData->BaseClass = PCI_CLASS_BASE_SYSTEM_DEV;
+                PciData->SubClass = PCI_SUBCLASS_SYS_OTHER;
+
+                /* Device will behave erratically when reading back data */
+                NewExtension->ExpectedWritebackFailure = TRUE;
+            }
+
+            /* Clone all the information from the header */
+            NewExtension->VendorId = PciData->VendorID;
+            NewExtension->DeviceId = PciData->DeviceID;
+            NewExtension->RevisionId = PciData->RevisionID;
+            NewExtension->ProgIf = PciData->ProgIf;
+            NewExtension->SubClass = PciData->SubClass;
+            NewExtension->BaseClass = PciData->BaseClass;
+            NewExtension->HeaderType = PCI_CONFIGURATION_TYPE(PciData);
+
+            /* Check for modern bridge types, which are managed by the driver */
+            if ((NewExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
+                ((NewExtension->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI) ||
+                 (NewExtension->SubClass == PCI_SUBCLASS_BR_CARDBUS)))
+            {
+                /* Acquire this device's lock */
+                KeEnterCriticalRegion();
+                KeWaitForSingleObject(&DeviceExtension->ChildListLock,
+                                      Executive,
+                                      KernelMode,
+                                      FALSE,
+                                      NULL);
+
+                /* Scan the bridge list until the first free entry */
+                for (BridgeExtension = &DeviceExtension->ChildBridgePdoList;
+                     *BridgeExtension;
+                     BridgeExtension = &(*BridgeExtension)->NextBridge);
+
+                /* Add this PDO as a bridge */
+                *BridgeExtension = NewExtension;
+                ASSERT(NewExtension->NextBridge == NULL);
+
+                /* Release this device's lock */
+                KeSetEvent(&DeviceExtension->ChildListLock,
+                           IO_NO_INCREMENT,
+                           FALSE);
+                KeLeaveCriticalRegion();
+            }
+
+            /* Get the PCI BIOS configuration saved in the registry */
+            Status = PciGetBiosConfig(NewExtension, BiosData);
+            if (NT_SUCCESS(Status))
+            {
+                /* This path has not yet been fully tested by eVb */
+                DPRINT1("Have BIOS configuration!\n");
+                UNIMPLEMENTED;
+
+                /* Check if the PCI BIOS configuration has changed */
+                if (!PcipIsSameDevice(NewExtension, BiosData))
+                {
+                    /* This is considered failure, and new data will be saved */
+                    Status = STATUS_UNSUCCESSFUL;
+                }
+                else
+                {
+                    /* Data is still correct, check for interrupt line change */
+                    if (BiosData->u.type0.InterruptLine !=
+                        PciData->u.type0.InterruptLine)
+                    {
+                        /* Update the current BIOS with the saved interrupt line */
+                        PciWriteDeviceConfig(NewExtension,
+                                             &BiosData->u.type0.InterruptLine,
+                                             FIELD_OFFSET(PCI_COMMON_HEADER,
+                                                          u.type0.InterruptLine),
+                                             sizeof(UCHAR));
+                    }
+
+                    /* Save the BIOS interrupt line and the initial command */
+                    NewExtension->RawInterruptLine = BiosData->u.type0.InterruptLine;
+                    NewExtension->InitialCommand = BiosData->Command;
+                }
+            }
+
+            /* Check if no saved data was present or if it was a mismatch */
+            if (!NT_SUCCESS(Status))
+            {
+                /* Save the new data */
+                Status = PciSaveBiosConfig(NewExtension, PciData);
+                ASSERT(NT_SUCCESS(Status));
+
+                /* Save the interrupt line and command from the device */
+                NewExtension->RawInterruptLine = PciData->u.type0.InterruptLine;
+                NewExtension->InitialCommand = PciData->Command;
+            }
+
+            /* Save original command from the device and hack flags */
+            NewExtension->CommandEnables = PciData->Command;
+            NewExtension->HackFlags = HackFlags;
+
+            /* Get power, AGP, and other capability data */
+            PciGetEnhancedCapabilities(NewExtension, PciData);
+
+            /* Now configure the BARs */
+            Status = PciGetFunctionLimits(NewExtension, PciData, HackFlags);
+
+            /* Power up the device */
+            PciSetPowerManagedDevicePowerState(NewExtension, PowerDeviceD0, FALSE);
+
+            /* Apply any device hacks required for enumeration */
+            PciApplyHacks(DeviceExtension,
+                          PciData,
+                          PciSlot,
+                          PCI_HACK_FIXUP_AFTER_CONFIGURATION,
+                          NewExtension);
+
+            /* Save interrupt pin */
+            NewExtension->InterruptPin = PciData->u.type0.InterruptPin;
+
+            /*
+             * Use either this device's actual IRQ line or, if it's connected on
+             * a master bus whose IRQ line is actually connected to the host, use
+             * the HAL to query the bus' IRQ line and store that as the adjusted
+             * interrupt line instead
+             */
+            NewExtension->AdjustedInterruptLine = PciGetAdjustedInterruptLine(NewExtension);
+
+            /* Check if this device is used for PCI debugger cards */
+            NewExtension->OnDebugPath = PciIsDeviceOnDebugPath(NewExtension);
+
+            /* Check for devices with invalid/bogus subsystem data */
+            if (HackFlags & PCI_HACK_NO_SUBSYSTEM)
+            {
+                /* Set the subsystem information to zero instead */
+                NewExtension->SubsystemVendorId = 0;
+                NewExtension->SubsystemId = 0;
+            }
+
+            /* Scan all capabilities */
+            CapOffset = NewExtension->CapabilitiesPtr;
+            while (CapOffset)
+            {
+                /* Read this header */
+                TempOffset = PciReadDeviceCapability(NewExtension,
+                                                     CapOffset,
+                                                     0,
+                                                     &CapHeader,
+                                                     sizeof(PCI_CAPABILITIES_HEADER));
+                if (TempOffset != CapOffset)
+                {
+                    /* This is a strange issue that shouldn't happen normally */
+                    DPRINT1("PCI - Failed to read PCI capability at offset 0x%02x\n",
+                            CapOffset);
+                    ASSERT(TempOffset == CapOffset);
+                }
+
+                /* Check for capabilities that this driver cares about */
+                switch (CapHeader.CapabilityID)
+                {
+                    /* Power management capability is heavily used by the bus */
+                    case PCI_CAPABILITY_ID_POWER_MANAGEMENT:
+
+                        /* Dump the capability */
+                        Name = "POWER";
+                        Size = sizeof(PCI_PM_CAPABILITY);
+                        break;
+
+                    /* AGP capability is required for AGP bus functionality */
+                    case PCI_CAPABILITY_ID_AGP:
+
+                        /* Dump the capability */
+                        Name = "AGP";
+                        Size = sizeof(PCI_AGP_CAPABILITY);
+                        break;
+
+                    /* This driver doesn't really use anything other than that */
+                    default:
+
+                        /* Windows prints this, we could do a translation later */
+                        Name = "UNKNOWN CAPABILITY";
+                        Size = 0;
+                        break;
+                }
+
+                /* Check if this is a capability that should be dumped */
+                if (Size)
+                {
+                    /* Read the whole capability data */
+                    TempOffset = PciReadDeviceCapability(NewExtension,
+                                                         CapOffset,
+                                                         CapHeader.CapabilityID,
+                                                         &CapHeader,
+                                                         Size);
+
+                    if (TempOffset != CapOffset)
+                    {
+                        /* Again, a strange issue that shouldn't be seen */
+                        DPRINT1("- Failed to read capability data. ***\n");
+                        ASSERT(TempOffset == CapOffset);
+                    }
+                }
+
+                /* Dump this capability */
+                DPRINT1("CAP @%02x ID %02x (%s)\n",
+                        CapOffset, CapHeader.CapabilityID, Name);
+                for (i = 0; i < Size; i += 2)
+                    DPRINT1("  %04x\n", *(PUSHORT)((ULONG_PTR)&CapHeader + i));
+                DPRINT1("\n");
+
+                /* Check the next capability */
+                CapOffset = CapHeader.Next;
+            }
+
+            /* Check for IDE controllers */
+            if ((NewExtension->BaseClass == PCI_CLASS_MASS_STORAGE_CTLR) &&
+                (NewExtension->SubClass == PCI_SUBCLASS_MSC_IDE_CTLR))
+            {
+                /* Do not allow them to power down completely */
+                NewExtension->DisablePowerDown = TRUE;
+            }
+
+            /*
+             * Check if this is a legacy bridge. Note that the i82375 PCI/EISA
+             * bridge that is present on certain NT Alpha machines appears as
+             * non-classified so detect it manually by scanning for its VID/PID.
+             */
+            if (((NewExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
+                ((NewExtension->SubClass == PCI_SUBCLASS_BR_ISA) ||
+                 (NewExtension->SubClass == PCI_SUBCLASS_BR_EISA) ||
+                 (NewExtension->SubClass == PCI_SUBCLASS_BR_MCA))) ||
+                ((NewExtension->VendorId == 0x8086) &&
+                 (NewExtension->DeviceId == 0x482)))
+            {
+                /* Do not allow these legacy bridges to be powered down */
+                NewExtension->DisablePowerDown = TRUE;
+            }
+
+            /* Check if the BIOS did not configure a cache line size */
+            if (!PciData->CacheLineSize)
+            {
+                /* Check if the device is disabled */
+                if (!(NewExtension->CommandEnables & (PCI_ENABLE_IO_SPACE |
+                                                      PCI_ENABLE_MEMORY_SPACE |
+                                                      PCI_ENABLE_BUS_MASTER)))
+                {
+                    /* Check if this is a PCI-X device*/
+                    TempOffset = PciReadDeviceCapability(NewExtension,
+                                                         NewExtension->CapabilitiesPtr,
+                                                         PCI_CAPABILITY_ID_PCIX,
+                                                         &PcixCapHeader,
+                                                         sizeof(PCI_CAPABILITIES_HEADER));
+
+                    /*
+                     * A device with default cache line size and latency timer
+                     * settings is considered to be unconfigured. Note that on
+                     * PCI-X, the reset value of the latency timer field in the
+                     * header is 64, not 0, hence why the check for PCI-X caps
+                     * was required, and the value used here below.
+                     */
+                    if (!(PciData->LatencyTimer) ||
+                        ((TempOffset) && (PciData->LatencyTimer == 64)))
+                    {
+                        /* Keep track of the fact that it needs configuration */
+                        DPRINT1("PCI - ScanBus, PDOx %x found unconfigured\n",
+                                NewExtension);
+                        NewExtension->NeedsHotPlugConfiguration = TRUE;
+                    }
+                }
+            }
+
+            /* Save latency and cache size information */
+            NewExtension->SavedLatencyTimer = PciData->LatencyTimer;
+            NewExtension->SavedCacheLineSize = PciData->CacheLineSize;
+
+            /* The PDO is now ready to go */
+            DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
+        }
+    }
+
+    /* Enumeration completed, do a final pass now that all devices are found */
+    if (ProcessFlag) PciProcessBus(DeviceExtension);
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+PciQueryDeviceRelations(IN PPCI_FDO_EXTENSION DeviceExtension,
+                        IN OUT PDEVICE_RELATIONS *pDeviceRelations)
+{
+    NTSTATUS Status;
+    PPCI_PDO_EXTENSION PdoExtension;
+    ULONG PdoCount = 0;
+    PDEVICE_RELATIONS DeviceRelations, NewRelations;
+    SIZE_T Size;
+    PDEVICE_OBJECT DeviceObject, *ObjectArray;
+    PAGED_CODE();
+
+    /* Make sure the FDO is started */
+    ASSERT(DeviceExtension->DeviceState == PciStarted);
+
+    /* Synchronize while we enumerate the bus */
+    Status = PciBeginStateTransition(DeviceExtension, PciSynchronizedOperation);
+    if (!NT_SUCCESS(Status)) return Status;
+
+    /* Scan all children PDO */
+    for (PdoExtension = DeviceExtension->ChildPdoList;
+         PdoExtension;
+         PdoExtension = PdoExtension->Next)
+    {
+        /* Invalidate them */
+        PdoExtension->NotPresent = TRUE;
+    }
+
+    /* Scan the PCI Bus */
+    Status = PciScanBus(DeviceExtension);
+    ASSERT(NT_SUCCESS(Status));
+
+    /* Enumerate all children PDO again */
+    for (PdoExtension = DeviceExtension->ChildPdoList;
+         PdoExtension;
+         PdoExtension = PdoExtension->Next)
+    {
+        /* Check for PDOs that are still invalidated */
+        if (PdoExtension->NotPresent)
+        {
+            /* This means this PDO existed before, but not anymore */
+            PdoExtension->ReportedMissing = TRUE;
+            DPRINT1("PCI - Old device (pdox) %08x not found on rescan.\n",
+                    PdoExtension);
+        }
+        else
+        {
+            /* Increase count of detected PDOs */
+            PdoCount++;
+        }
+    }
+
+    /* Read the current relations and add the newly discovered relations */
+    DeviceRelations = *pDeviceRelations;
+    Size = FIELD_OFFSET(DEVICE_RELATIONS, Objects) +
+           PdoCount * sizeof(PDEVICE_OBJECT);
+    if (DeviceRelations) Size += sizeof(PDEVICE_OBJECT) * DeviceRelations->Count;
+
+    /* Allocate the device relations */
+    NewRelations = (PDEVICE_RELATIONS)ExAllocatePoolWithTag(0, Size, 'BicP');
+    if (!NewRelations)
+    {
+        /* Out of space, cancel the operation */
+        PciCancelStateTransition(DeviceExtension, PciSynchronizedOperation);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    /* Check if there were any older relations */
+    NewRelations->Count = 0;
+    if (DeviceRelations)
+    {
+        /* Copy the old relations into the new buffer, then free the old one */
+        RtlCopyMemory(NewRelations,
+                      DeviceRelations,
+                      FIELD_OFFSET(DEVICE_RELATIONS, Objects) +
+                      DeviceRelations->Count * sizeof(PDEVICE_OBJECT));
+        ExFreePoolWithTag(DeviceRelations, 0);
+    }
+
+    /* Print out that we're ready to dump relations */
+    DPRINT1("PCI QueryDeviceRelations/BusRelations FDOx %08x (bus 0x%02x)\n",
+            DeviceExtension,
+            DeviceExtension->BaseBus);
+
+    /* Loop the current PDO children and the device relation object array */
+    PdoExtension = DeviceExtension->ChildPdoList;
+    ObjectArray = &NewRelations->Objects[NewRelations->Count];
+    while (PdoExtension)
+    {
+        /* Dump this relation */
+        DPRINT1("  QDR PDO %08x (x %08x)%s\n",
+                PdoExtension->PhysicalDeviceObject,
+                PdoExtension,
+                PdoExtension->NotPresent ?
+                "<Omitted, device flaged not present>" : "");
+
+        /* Is this PDO present? */
+        if (!PdoExtension->NotPresent)
+        {
+            /* Reference it and add it to the array */
+            DeviceObject = PdoExtension->PhysicalDeviceObject;
+            ObfReferenceObject(DeviceObject);
+            *ObjectArray++ = DeviceObject;
+        }
+
+        /* Go to the next PDO */
+        PdoExtension = PdoExtension->Next;
+    }
+
+    /* Terminate dumping the relations */
+    DPRINT1("  QDR Total PDO count = %d (%d already in list)\n",
+            NewRelations->Count + PdoCount,
+            NewRelations->Count);
+
+    /* Return the final count and the new buffer */
+    NewRelations->Count += PdoCount;
+    *pDeviceRelations = NewRelations;
+    return STATUS_SUCCESS;
+}
+
 /* EOF */