2010-04-01 19:07:40 +00:00
|
|
|
/*
|
|
|
|
* 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 <pci.h>
|
|
|
|
#define NDEBUG
|
|
|
|
#include <debug.h>
|
|
|
|
|
|
|
|
/* GLOBALS ********************************************************************/
|
|
|
|
|
|
|
|
/* FUNCTIONS ******************************************************************/
|
|
|
|
|
2010-07-17 16:53:18 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2010-07-17 15:09:19 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2010-07-18 18:58:33 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-17 01:31:26 +00:00
|
|
|
NTSTATUS
|
|
|
|
NTAPI
|
|
|
|
PciScanBus(IN PPCI_FDO_EXTENSION DeviceExtension)
|
|
|
|
{
|
|
|
|
ULONG MaxDevice = PCI_MAX_DEVICES;
|
2010-07-17 15:59:09 +00:00
|
|
|
BOOLEAN ProcessFlag = FALSE;
|
2010-07-17 01:31:26 +00:00
|
|
|
ULONG i, j, k;
|
2010-07-17 15:09:19 +00:00
|
|
|
LONGLONG HackFlags;
|
2010-07-17 15:59:09 +00:00
|
|
|
PDEVICE_OBJECT DeviceObject;
|
2010-07-17 01:31:26 +00:00
|
|
|
UCHAR Buffer[PCI_COMMON_HDR_LENGTH];
|
2010-07-17 16:53:18 +00:00
|
|
|
UCHAR BiosBuffer[PCI_COMMON_HDR_LENGTH];
|
2010-07-17 01:31:26 +00:00
|
|
|
PPCI_COMMON_HEADER PciData = (PVOID)Buffer;
|
2010-07-17 16:53:18 +00:00
|
|
|
PPCI_COMMON_HEADER BiosData = (PVOID)BiosBuffer;
|
2010-07-17 01:31:26 +00:00
|
|
|
PCI_SLOT_NUMBER PciSlot;
|
2010-07-17 15:59:09 +00:00
|
|
|
NTSTATUS Status;
|
|
|
|
PPCI_PDO_EXTENSION PdoExtension, NewExtension;
|
|
|
|
PPCI_PDO_EXTENSION* BridgeExtension;
|
2010-07-17 01:31:26 +00:00
|
|
|
PWCHAR DescriptionText;
|
2010-07-17 15:09:19 +00:00
|
|
|
USHORT SubVendorId, SubSystemId;
|
2010-07-17 01:31:26 +00:00
|
|
|
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));
|
|
|
|
|
|
|
|
/* 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);
|
2010-07-17 16:53:18 +00:00
|
|
|
DPRINT1("Device Description \"%S\".\n",
|
|
|
|
DescriptionText ? DescriptionText : L"(NULL)");
|
2010-07-17 01:31:26 +00:00
|
|
|
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);
|
|
|
|
}
|
2010-07-17 15:09:19 +00:00
|
|
|
|
|
|
|
/* 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,
|
2010-07-17 15:59:09 +00:00
|
|
|
PciSlot.u.AsULONG,
|
2010-07-17 15:09:19 +00:00
|
|
|
PciData);
|
|
|
|
if (PdoExtension)
|
|
|
|
{
|
|
|
|
/* Rescan scenarios are not yet implemented */
|
|
|
|
UNIMPLEMENTED;
|
|
|
|
while (TRUE);
|
|
|
|
}
|
2010-07-17 15:59:09 +00:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
2010-07-17 16:53:18 +00:00
|
|
|
/* Check for modern bridge types, which are managed by the driver */
|
2010-07-17 15:59:09 +00:00
|
|
|
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 */
|
2010-07-17 16:53:18 +00:00
|
|
|
KeSetEvent(&DeviceExtension->ChildListLock,
|
|
|
|
IO_NO_INCREMENT,
|
|
|
|
FALSE);
|
2010-07-17 15:59:09 +00:00
|
|
|
KeLeaveCriticalRegion();
|
|
|
|
}
|
2010-07-17 16:53:18 +00:00
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
2010-07-18 18:58:33 +00:00
|
|
|
/* Get power, AGP, and other capability data */
|
|
|
|
PciGetEnhancedCapabilities(NewExtension, PciData);
|
|
|
|
|
|
|
|
/* Power up the device */
|
|
|
|
PciSetPowerManagedDevicePowerState(NewExtension, PowerDeviceD0, FALSE);
|
|
|
|
|
2010-07-17 16:53:18 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2010-07-17 15:59:09 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
2010-07-17 16:53:18 +00:00
|
|
|
|
2010-07-17 15:59:09 +00:00
|
|
|
/*
|
|
|
|
* Check if this is a legacy bridge. Note that the i82375 PCI/EISA
|
2010-07-17 16:53:18 +00:00
|
|
|
* bridge that is present on certain NT Alpha machines appears as
|
2010-07-17 15:59:09 +00:00
|
|
|
* 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))) ||
|
2010-07-17 16:53:18 +00:00
|
|
|
((NewExtension->VendorId == 0x8086) &&
|
|
|
|
(NewExtension->DeviceId == 0x482)))
|
2010-07-17 15:59:09 +00:00
|
|
|
{
|
|
|
|
/* Do not allow these legacy bridges to be powered down */
|
|
|
|
NewExtension->DisablePowerDown = TRUE;
|
|
|
|
}
|
2010-07-17 16:53:18 +00:00
|
|
|
|
2010-07-17 15:59:09 +00:00
|
|
|
/* Save latency and cache size information */
|
|
|
|
NewExtension->SavedLatencyTimer = PciData->LatencyTimer;
|
|
|
|
NewExtension->SavedCacheLineSize = PciData->CacheLineSize;
|
2010-07-17 16:53:18 +00:00
|
|
|
|
2010-07-17 15:59:09 +00:00
|
|
|
/* The PDO is now ready to go */
|
|
|
|
DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
2010-07-17 01:31:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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 ?
|
|
|
|
"<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;
|
|
|
|
}
|
|
|
|
|
2010-04-01 19:07:40 +00:00
|
|
|
/* EOF */
|