2 * PROJECT: ReactOS PCI Bus Driver
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: drivers/bus/pci/enum.c
5 * PURPOSE: PCI Bus/Device Enumeration
6 * PROGRAMMERS: ReactOS Portable Systems Group
9 /* INCLUDES *******************************************************************/
15 /* GLOBALS ********************************************************************/
17 /* FUNCTIONS ******************************************************************/
21 PciSkipThisFunction(IN PPCI_COMMON_HEADER PciData
,
22 IN PCI_SLOT_NUMBER Slot
,
23 IN UCHAR OperationType
,
24 IN ULONGLONG HackFlags
)
28 /* Check if this is device enumeration */
29 if (OperationType
== PCI_SKIP_DEVICE_ENUMERATION
)
31 /* Check if there's a hackflag saying not to enumerate this device */
32 if (HackFlags
& PCI_HACK_NO_ENUM_AT_ALL
) break;
34 /* Check if this is the high end of a double decker device */
35 if ((HackFlags
& PCI_HACK_DOUBLE_DECKER
) &&
36 (Slot
.u
.bits
.DeviceNumber
>= 16))
38 /* It belongs to the same device, so skip it */
39 DPRINT1(" Device (Ven %04x Dev %04x (d=0x%x, f=0x%x)) is a ghost.\n",
42 Slot
.u
.bits
.DeviceNumber
,
43 Slot
.u
.bits
.FunctionNumber
);
47 else if (OperationType
== PCI_SKIP_RESOURCE_ENUMERATION
)
49 /* Resource enumeration, check for a hackflag saying not to do it */
50 if (HackFlags
& PCI_HACK_ENUM_NO_RESOURCE
) break;
54 /* Logic error in the driver */
55 ASSERTMSG(FALSE
, "PCI Skip Function - Operation type unknown.");
58 /* Check for legacy bridges during resource enumeration */
59 if ((PciData
->BaseClass
== PCI_CLASS_BRIDGE_DEV
) &&
60 (PciData
->SubClass
<= PCI_SUBCLASS_BR_MCA
) &&
61 (OperationType
== PCI_SKIP_RESOURCE_ENUMERATION
))
63 /* Their resources are not enumerated, only PCI and Cardbus/PCMCIA */
66 else if (PciData
->BaseClass
== PCI_CLASS_NOT_DEFINED
)
68 /* Undefined base class (usually a PCI BIOS/ROM bug) */
69 DPRINT1(" Vendor %04x, Device %04x has class code of PCI_CLASS_NOT_DEFINED\n",
74 * The Alder has an Intel Extended Express System Support Controller
75 * which presents apparently spurious BARs. When the PCI resource
76 * code tries to reassign these BARs, the second IO-APIC gets
77 * disabled (with disastrous consequences). The first BAR is the
78 * actual IO-APIC, the remaining five bars seem to be spurious
79 * resources, so ignore this device completely.
81 if ((PciData
->VendorID
== 0x8086) && (PciData
->DeviceID
== 8)) break;
84 /* Other normal PCI cards and bridges are enumerated */
85 if (PCI_CONFIGURATION_TYPE(PciData
) <= PCI_CARDBUS_BRIDGE_TYPE
) return FALSE
;
88 /* Hit one of the known bugs/hackflags, or this is a new kind of PCI unit */
89 DPRINT1(" Device skipped (not enumerated).\n");
95 PciScanBus(IN PPCI_FDO_EXTENSION DeviceExtension
)
97 ULONG MaxDevice
= PCI_MAX_DEVICES
;
98 BOOLEAN ProcessFlag
= FALSE
;
101 PDEVICE_OBJECT DeviceObject
;
102 UCHAR Buffer
[PCI_COMMON_HDR_LENGTH
];
103 PPCI_COMMON_HEADER PciData
= (PVOID
)Buffer
;
104 PCI_SLOT_NUMBER PciSlot
;
106 PPCI_PDO_EXTENSION PdoExtension
, NewExtension
;
107 PPCI_PDO_EXTENSION
* BridgeExtension
;
108 PWCHAR DescriptionText
;
109 USHORT SubVendorId
, SubSystemId
;
110 DPRINT1("PCI Scan Bus: FDO Extension @ 0x%x, Base Bus = 0x%x\n",
111 DeviceExtension
, DeviceExtension
->BaseBus
);
113 /* Is this the root FDO? */
114 if (!PCI_IS_ROOT_FDO(DeviceExtension
))
116 /* Other FDOs are not currently supported */
121 /* Loop every device on the bus */
122 PciSlot
.u
.bits
.Reserved
= 0;
123 i
= DeviceExtension
->BaseBus
;
124 for (j
= 0; j
< MaxDevice
; j
++)
126 /* Loop every function of each device */
127 PciSlot
.u
.bits
.DeviceNumber
= j
;
128 for (k
= 0; k
< PCI_MAX_FUNCTION
; k
++)
130 /* Build the final slot structure */
131 PciSlot
.u
.bits
.FunctionNumber
= k
;
133 /* Read the vendor for this slot */
134 PciReadSlotConfig(DeviceExtension
,
140 /* Skip invalid device */
141 if (PciData
->VendorID
== PCI_INVALID_VENDORID
) continue;
143 /* Now read the whole header */
144 PciReadSlotConfig(DeviceExtension
,
148 PCI_COMMON_HDR_LENGTH
- sizeof(USHORT
));
150 /* Dump device that was found */
151 DPRINT1("Scan Found Device 0x%x (b=0x%x, d=0x%x, f=0x%x)\n",
157 /* Dump the device's header */
158 PciDebugDumpCommonConfig(PciData
);
160 /* Find description for this device for the debugger's sake */
161 DescriptionText
= PciGetDeviceDescriptionMessage(PciData
->BaseClass
,
163 DPRINT1("Device Description \"%S\".\n", DescriptionText
? DescriptionText
: L
"(NULL)");
164 if (DescriptionText
) ExFreePoolWithTag(DescriptionText
, 0);
166 /* Check if there is an ACPI Watchdog Table */
169 /* Check if this PCI device is the ACPI Watchdog Device... */
174 /* Check for non-simple devices */
175 if ((PCI_MULTIFUNCTION_DEVICE(PciData
)) ||
176 (PciData
->BaseClass
== PCI_CLASS_BRIDGE_DEV
))
178 /* No subsystem data defined for these kinds of bridges */
184 /* Read the subsystem information from the PCI header */
185 SubVendorId
= PciData
->u
.type0
.SubVendorID
;
186 SubSystemId
= PciData
->u
.type0
.SubSystemID
;
189 /* Get any hack flags for this device */
190 HackFlags
= PciGetHackFlags(PciData
->VendorID
,
194 PciData
->RevisionID
);
196 /* Check if this device is considered critical by the OS */
197 if (PciIsCriticalDeviceClass(PciData
->BaseClass
, PciData
->SubClass
))
199 /* Check if normally the decodes would be disabled */
200 if (!(HackFlags
& PCI_HACK_DONT_DISABLE_DECODES
))
202 /* Because this device is critical, don't disable them */
203 DPRINT1("Not allowing PM Because device is critical\n");
204 HackFlags
|= PCI_HACK_CRITICAL_DEVICE
;
208 /* PCI bridges with a VGA card are also considered critical */
209 if ((PciData
->BaseClass
== PCI_CLASS_BRIDGE_DEV
) &&
210 (PciData
->SubClass
== PCI_SUBCLASS_BR_PCI_TO_PCI
) &&
211 (PciData
->u
.type1
.BridgeControl
& PCI_ENABLE_BRIDGE_VGA
) &&
212 !(HackFlags
& PCI_HACK_DONT_DISABLE_DECODES
))
214 /* Do not disable their decodes either */
215 DPRINT1("Not allowing PM because device is VGA\n");
216 HackFlags
|= PCI_HACK_CRITICAL_DEVICE
;
219 /* Also skip devices that should not be enumerated */
220 if (PciSkipThisFunction(PciData
, PciSlot
, 1, HackFlags
)) continue;
222 /* Check if a PDO has already been created for this device */
223 PdoExtension
= PciFindPdoByFunction(DeviceExtension
,
228 /* Rescan scenarios are not yet implemented */
233 /* Bus processing will need to happen */
236 /* Create the PDO for this device */
237 Status
= PciPdoCreate(DeviceExtension
, PciSlot
, &DeviceObject
);
238 ASSERT(NT_SUCCESS(Status
));
239 NewExtension
= (PPCI_PDO_EXTENSION
)DeviceObject
->DeviceExtension
;
241 /* Check for broken devices with wrong/no class codes */
242 if (HackFlags
& PCI_HACK_FAKE_CLASS_CODE
)
244 /* Setup a default one */
245 PciData
->BaseClass
= PCI_CLASS_BASE_SYSTEM_DEV
;
246 PciData
->SubClass
= PCI_SUBCLASS_SYS_OTHER
;
248 /* Device will behave erratically when reading back data */
249 NewExtension
->ExpectedWritebackFailure
= TRUE
;
252 /* Clone all the information from the header */
253 NewExtension
->VendorId
= PciData
->VendorID
;
254 NewExtension
->DeviceId
= PciData
->DeviceID
;
255 NewExtension
->RevisionId
= PciData
->RevisionID
;
256 NewExtension
->ProgIf
= PciData
->ProgIf
;
257 NewExtension
->SubClass
= PciData
->SubClass
;
258 NewExtension
->BaseClass
= PciData
->BaseClass
;
259 NewExtension
->HeaderType
= PCI_CONFIGURATION_TYPE(PciData
);
261 /* Check for PCI or Cardbus bridges, which are supported by this driver */
262 if ((NewExtension
->BaseClass
== PCI_CLASS_BRIDGE_DEV
) &&
263 ((NewExtension
->SubClass
== PCI_SUBCLASS_BR_PCI_TO_PCI
) ||
264 (NewExtension
->SubClass
== PCI_SUBCLASS_BR_CARDBUS
)))
266 /* Acquire this device's lock */
267 KeEnterCriticalRegion();
268 KeWaitForSingleObject(&DeviceExtension
->ChildListLock
,
274 /* Scan the bridge list until the first free entry */
275 for (BridgeExtension
= &DeviceExtension
->ChildBridgePdoList
;
277 BridgeExtension
= &(*BridgeExtension
)->NextBridge
);
279 /* Add this PDO as a bridge */
280 *BridgeExtension
= NewExtension
;
281 ASSERT(NewExtension
->NextBridge
== NULL
);
283 /* Release this device's lock */
284 KeSetEvent(&DeviceExtension
->ChildListLock
, IO_NO_INCREMENT
, FALSE
);
285 KeLeaveCriticalRegion();
288 /* Check for IDE controllers */
289 if ((NewExtension
->BaseClass
== PCI_CLASS_MASS_STORAGE_CTLR
) &&
290 (NewExtension
->SubClass
== PCI_SUBCLASS_MSC_IDE_CTLR
))
292 /* Do not allow them to power down completely */
293 NewExtension
->DisablePowerDown
= TRUE
;
297 * Check if this is a legacy bridge. Note that the i82375 PCI/EISA
298 * bridge that is present on certain NT Alpha machines appears as
299 * non-classified so detect it manually by scanning for its VID/PID.
301 if (((NewExtension
->BaseClass
== PCI_CLASS_BRIDGE_DEV
) &&
302 ((NewExtension
->SubClass
== PCI_SUBCLASS_BR_ISA
) ||
303 (NewExtension
->SubClass
== PCI_SUBCLASS_BR_EISA
) ||
304 (NewExtension
->SubClass
== PCI_SUBCLASS_BR_MCA
))) ||
305 ((NewExtension
->VendorId
== 0x8086) && (NewExtension
->DeviceId
== 0x482)))
307 /* Do not allow these legacy bridges to be powered down */
308 NewExtension
->DisablePowerDown
= TRUE
;
311 /* Save latency and cache size information */
312 NewExtension
->SavedLatencyTimer
= PciData
->LatencyTimer
;
313 NewExtension
->SavedCacheLineSize
= PciData
->CacheLineSize
;
315 /* The PDO is now ready to go */
316 DeviceObject
->Flags
&= ~DO_DEVICE_INITIALIZING
;
320 /* Enumeration is completed */
321 return STATUS_SUCCESS
;
326 PciQueryDeviceRelations(IN PPCI_FDO_EXTENSION DeviceExtension
,
327 IN OUT PDEVICE_RELATIONS
*pDeviceRelations
)
330 PPCI_PDO_EXTENSION PdoExtension
;
332 PDEVICE_RELATIONS DeviceRelations
, NewRelations
;
334 PDEVICE_OBJECT DeviceObject
, *ObjectArray
;
337 /* Make sure the FDO is started */
338 ASSERT(DeviceExtension
->DeviceState
== PciStarted
);
340 /* Synchronize while we enumerate the bus */
341 Status
= PciBeginStateTransition(DeviceExtension
, PciSynchronizedOperation
);
342 if (!NT_SUCCESS(Status
)) return Status
;
344 /* Scan all children PDO */
345 for (PdoExtension
= DeviceExtension
->ChildPdoList
;
347 PdoExtension
= PdoExtension
->Next
)
349 /* Invalidate them */
350 PdoExtension
->NotPresent
= TRUE
;
353 /* Scan the PCI Bus */
354 Status
= PciScanBus(DeviceExtension
);
355 ASSERT(NT_SUCCESS(Status
));
357 /* Enumerate all children PDO again */
358 for (PdoExtension
= DeviceExtension
->ChildPdoList
;
360 PdoExtension
= PdoExtension
->Next
)
362 /* Check for PDOs that are still invalidated */
363 if (PdoExtension
->NotPresent
)
365 /* This means this PDO existed before, but not anymore */
366 PdoExtension
->ReportedMissing
= TRUE
;
367 DPRINT1("PCI - Old device (pdox) %08x not found on rescan.\n",
372 /* Increase count of detected PDOs */
377 /* Read the current relations and add the newly discovered relations */
378 DeviceRelations
= *pDeviceRelations
;
379 Size
= FIELD_OFFSET(DEVICE_RELATIONS
, Objects
) +
380 PdoCount
* sizeof(PDEVICE_OBJECT
);
381 if (DeviceRelations
) Size
+= sizeof(PDEVICE_OBJECT
) * DeviceRelations
->Count
;
383 /* Allocate the device relations */
384 NewRelations
= (PDEVICE_RELATIONS
)ExAllocatePoolWithTag(0, Size
, 'BicP');
387 /* Out of space, cancel the operation */
388 PciCancelStateTransition(DeviceExtension
, PciSynchronizedOperation
);
389 return STATUS_INSUFFICIENT_RESOURCES
;
392 /* Check if there were any older relations */
393 NewRelations
->Count
= 0;
396 /* Copy the old relations into the new buffer, then free the old one */
397 RtlCopyMemory(NewRelations
,
399 FIELD_OFFSET(DEVICE_RELATIONS
, Objects
) +
400 DeviceRelations
->Count
* sizeof(PDEVICE_OBJECT
));
401 ExFreePoolWithTag(DeviceRelations
, 0);
404 /* Print out that we're ready to dump relations */
405 DPRINT1("PCI QueryDeviceRelations/BusRelations FDOx %08x (bus 0x%02x)\n",
407 DeviceExtension
->BaseBus
);
409 /* Loop the current PDO children and the device relation object array */
410 PdoExtension
= DeviceExtension
->ChildPdoList
;
411 ObjectArray
= &NewRelations
->Objects
[NewRelations
->Count
];
414 /* Dump this relation */
415 DPRINT1(" QDR PDO %08x (x %08x)%s\n",
416 PdoExtension
->PhysicalDeviceObject
,
418 PdoExtension
->NotPresent
?
419 "<Omitted, device flaged not present>" : "");
421 /* Is this PDO present? */
422 if (!PdoExtension
->NotPresent
)
424 /* Reference it and add it to the array */
425 DeviceObject
= PdoExtension
->PhysicalDeviceObject
;
426 ObfReferenceObject(DeviceObject
);
427 *ObjectArray
++ = DeviceObject
;
430 /* Go to the next PDO */
431 PdoExtension
= PdoExtension
->Next
;
434 /* Terminate dumping the relations */
435 DPRINT1(" QDR Total PDO count = %d (%d already in list)\n",
436 NewRelations
->Count
+ PdoCount
,
437 NewRelations
->Count
);
439 /* Return the final count and the new buffer */
440 NewRelations
->Count
+= PdoCount
;
441 *pDeviceRelations
= NewRelations
;
442 return STATUS_SUCCESS
;