/* * PROJECT: ReactOS PCI Bus Driver * LICENSE: BSD - See COPYING.ARM in the top level directory * FILE: drivers/bus/pci/enum.c * PURPOSE: PCI Bus/Device Enumeration * PROGRAMMERS: ReactOS Portable Systems Group */ /* INCLUDES *******************************************************************/ #include #define NDEBUG #include /* 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; } 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 is completed */ 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 ? "" : ""); /* 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 */