/* FUNCTIONS ******************************************************************/
+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);
+ }
+}
+
NTSTATUS
NTAPI
PciScanBus(IN PPCI_FDO_EXTENSION DeviceExtension)
{
ULONG MaxDevice = PCI_MAX_DEVICES;
+ BOOLEAN ProcessFlag = FALSE;
ULONG i, j, k;
+ 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;
+ NTSTATUS Status;
+ PPCI_PDO_EXTENSION PdoExtension, NewExtension;
+ PPCI_PDO_EXTENSION* BridgeExtension;
PWCHAR DescriptionText;
+ USHORT SubVendorId, SubSystemId;
DPRINT1("PCI Scan Bus: FDO Extension @ 0x%x, Base Bus = 0x%x\n",
DeviceExtension, DeviceExtension->BaseBus);
/* 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)");
+ DPRINT1("Device Description \"%S\".\n",
+ DescriptionText ? DescriptionText : L"(NULL)");
if (DescriptionText) ExFreePoolWithTag(DescriptionText, 0);
/* Check if there is an ACPI Watchdog Table */
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;
+ }
+
+ /* Also skip devices that should not be enumerated */
+ if (PciSkipThisFunction(PciData, PciSlot, 1, HackFlags)) 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);
+
+ /* Power up the device */
+ PciSetPowerManagedDevicePowerState(NewExtension, PowerDeviceD0, FALSE);
+
+ /* 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;
+ }
+
+ /* 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;
+ }
+
+ /* 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;
}
}