Can't sleep so write more source codes! add scan bus functions to get power caps...
[reactos.git] / reactos / drivers / bus / pcix / enum.c
1 /*
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
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include <pci.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* GLOBALS ********************************************************************/
16
17 /* FUNCTIONS ******************************************************************/
18
19 BOOLEAN
20 NTAPI
21 PcipIsSameDevice(IN PPCI_PDO_EXTENSION DeviceExtension,
22 IN PPCI_COMMON_HEADER PciData)
23 {
24 BOOLEAN IdMatch, RevMatch, SubsysMatch;
25 ULONGLONG HackFlags = DeviceExtension->HackFlags;
26
27 /* Check if the IDs match */
28 IdMatch = (PciData->VendorID == DeviceExtension->VendorId) &&
29 (PciData->DeviceID == DeviceExtension->DeviceId);
30 if (!IdMatch) return FALSE;
31
32 /* If the device has a valid revision, check if it matches */
33 RevMatch = (HackFlags & PCI_HACK_NO_REVISION_AFTER_D3) ||
34 (PciData->RevisionID == DeviceExtension->RevisionId);
35 if (!RevMatch) return FALSE;
36
37 /* For multifunction devices, this is enough to assume they're the same */
38 if (PCI_MULTIFUNCTION_DEVICE(PciData)) return TRUE;
39
40 /* For bridge devices, there's also nothing else that can be checked */
41 if (DeviceExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) return TRUE;
42
43 /* Devices, on the other hand, have subsystem data that can be compared */
44 SubsysMatch = (HackFlags & (PCI_HACK_NO_SUBSYSTEM |
45 PCI_HACK_NO_SUBSYSTEM_AFTER_D3)) ||
46 ((DeviceExtension->SubsystemVendorId ==
47 PciData->u.type0.SubVendorID) &&
48 (DeviceExtension->SubsystemId ==
49 PciData->u.type0.SubSystemID));
50 return SubsysMatch;
51 }
52
53 BOOLEAN
54 NTAPI
55 PciSkipThisFunction(IN PPCI_COMMON_HEADER PciData,
56 IN PCI_SLOT_NUMBER Slot,
57 IN UCHAR OperationType,
58 IN ULONGLONG HackFlags)
59 {
60 do
61 {
62 /* Check if this is device enumeration */
63 if (OperationType == PCI_SKIP_DEVICE_ENUMERATION)
64 {
65 /* Check if there's a hackflag saying not to enumerate this device */
66 if (HackFlags & PCI_HACK_NO_ENUM_AT_ALL) break;
67
68 /* Check if this is the high end of a double decker device */
69 if ((HackFlags & PCI_HACK_DOUBLE_DECKER) &&
70 (Slot.u.bits.DeviceNumber >= 16))
71 {
72 /* It belongs to the same device, so skip it */
73 DPRINT1(" Device (Ven %04x Dev %04x (d=0x%x, f=0x%x)) is a ghost.\n",
74 PciData->VendorID,
75 PciData->DeviceID,
76 Slot.u.bits.DeviceNumber,
77 Slot.u.bits.FunctionNumber);
78 break;
79 }
80 }
81 else if (OperationType == PCI_SKIP_RESOURCE_ENUMERATION)
82 {
83 /* Resource enumeration, check for a hackflag saying not to do it */
84 if (HackFlags & PCI_HACK_ENUM_NO_RESOURCE) break;
85 }
86 else
87 {
88 /* Logic error in the driver */
89 ASSERTMSG(FALSE, "PCI Skip Function - Operation type unknown.");
90 }
91
92 /* Check for legacy bridges during resource enumeration */
93 if ((PciData->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
94 (PciData->SubClass <= PCI_SUBCLASS_BR_MCA) &&
95 (OperationType == PCI_SKIP_RESOURCE_ENUMERATION))
96 {
97 /* Their resources are not enumerated, only PCI and Cardbus/PCMCIA */
98 break;
99 }
100 else if (PciData->BaseClass == PCI_CLASS_NOT_DEFINED)
101 {
102 /* Undefined base class (usually a PCI BIOS/ROM bug) */
103 DPRINT1(" Vendor %04x, Device %04x has class code of PCI_CLASS_NOT_DEFINED\n",
104 PciData->VendorID,
105 PciData->DeviceID);
106
107 /*
108 * The Alder has an Intel Extended Express System Support Controller
109 * which presents apparently spurious BARs. When the PCI resource
110 * code tries to reassign these BARs, the second IO-APIC gets
111 * disabled (with disastrous consequences). The first BAR is the
112 * actual IO-APIC, the remaining five bars seem to be spurious
113 * resources, so ignore this device completely.
114 */
115 if ((PciData->VendorID == 0x8086) && (PciData->DeviceID == 8)) break;
116 }
117
118 /* Other normal PCI cards and bridges are enumerated */
119 if (PCI_CONFIGURATION_TYPE(PciData) <= PCI_CARDBUS_BRIDGE_TYPE) return FALSE;
120 } while (FALSE);
121
122 /* Hit one of the known bugs/hackflags, or this is a new kind of PCI unit */
123 DPRINT1(" Device skipped (not enumerated).\n");
124 return TRUE;
125 }
126
127 VOID
128 NTAPI
129 PciGetEnhancedCapabilities(IN PPCI_PDO_EXTENSION PdoExtension,
130 IN PPCI_COMMON_HEADER PciData)
131 {
132 ULONG HeaderType, CapPtr, TargetAgpCapabilityId;
133 DEVICE_POWER_STATE WakeLevel;
134 PCI_CAPABILITIES_HEADER AgpCapability;
135 PCI_PM_CAPABILITY PowerCapabilities;
136 PAGED_CODE();
137
138 /* Assume no known wake level */
139 PdoExtension->PowerState.DeviceWakeLevel = PowerDeviceUnspecified;
140
141 /* Make sure the device has capabilities */
142 if (!(PciData->Status & PCI_STATUS_CAPABILITIES_LIST))
143 {
144 /* If it doesn't, there will be no power management */
145 PdoExtension->CapabilitiesPtr = 0;
146 PdoExtension->HackFlags |= PCI_HACK_NO_PM_CAPS;
147 }
148 else
149 {
150 /* There's capabilities, need to figure out where to get the offset */
151 HeaderType = PCI_CONFIGURATION_TYPE(PciData);
152 if (HeaderType == PCI_CARDBUS_BRIDGE_TYPE)
153 {
154 /* Use the bridge's header */
155 CapPtr = PciData->u.type2.CapabilitiesPtr;
156 }
157 else
158 {
159 /* Use the device header */
160 ASSERT(HeaderType <= PCI_CARDBUS_BRIDGE_TYPE);
161 CapPtr = PciData->u.type0.CapabilitiesPtr;
162 }
163
164 /* Make sure the pointer is spec-aligned and located, and save it */
165 DPRINT1("Device has capabilities at: %lx\n", CapPtr);
166 ASSERT(((CapPtr & 0x3) == 0) && (CapPtr >= PCI_COMMON_HDR_LENGTH));
167 PdoExtension->CapabilitiesPtr = CapPtr;
168
169 /* Check for PCI-to-PCI Bridges and AGP bridges */
170 if ((PdoExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
171 ((PdoExtension->SubClass == PCI_SUBCLASS_BR_HOST) ||
172 (PdoExtension->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI)))
173 {
174 /* Query either the raw AGP capabilitity, or the Target AGP one */
175 TargetAgpCapabilityId = (PdoExtension->SubClass ==
176 PCI_SUBCLASS_BR_PCI_TO_PCI) ?
177 PCI_CAPABILITY_ID_AGP_TARGET :
178 PCI_CAPABILITY_ID_AGP;
179 if (PciReadDeviceCapability(PdoExtension,
180 PdoExtension->CapabilitiesPtr,
181 TargetAgpCapabilityId,
182 &AgpCapability,
183 sizeof(PCI_CAPABILITIES_HEADER)))
184 {
185 /* AGP target ID was found, store it */
186 DPRINT1("AGP ID: %lx\n", TargetAgpCapabilityId);
187 PdoExtension->TargetAgpCapabilityId = TargetAgpCapabilityId;
188 }
189 }
190
191 /* Check for devices that are known not to have proper power management */
192 if (!(PdoExtension->HackFlags & PCI_HACK_NO_PM_CAPS))
193 {
194 /* Query if this device supports power management */
195 if (!PciReadDeviceCapability(PdoExtension,
196 PdoExtension->CapabilitiesPtr,
197 PCI_CAPABILITY_ID_POWER_MANAGEMENT,
198 &PowerCapabilities.Header,
199 sizeof(PCI_PM_CAPABILITY)))
200 {
201 /* No power management, so act as if it had the hackflag set */
202 DPRINT1("No PM caps, disabling PM\n");
203 PdoExtension->HackFlags |= PCI_HACK_NO_PM_CAPS;
204 }
205 else
206 {
207 /* Otherwise, pick the highest wake level that is supported */
208 WakeLevel = PowerDeviceUnspecified;
209 if (PowerCapabilities.PMC.Capabilities.Support.PMED0)
210 WakeLevel = PowerDeviceD0;
211 if (PowerCapabilities.PMC.Capabilities.Support.PMED1)
212 WakeLevel = PowerDeviceD1;
213 if (PowerCapabilities.PMC.Capabilities.Support.PMED2)
214 WakeLevel = PowerDeviceD2;
215 if (PowerCapabilities.PMC.Capabilities.Support.PMED3Hot)
216 WakeLevel = PowerDeviceD3;
217 if (PowerCapabilities.PMC.Capabilities.Support.PMED3Cold)
218 WakeLevel = PowerDeviceD3;
219 PdoExtension->PowerState.DeviceWakeLevel = WakeLevel;
220
221 /* Convert the PCI power state to the NT power state */
222 PdoExtension->PowerState.CurrentDeviceState =
223 PowerCapabilities.PMCSR.ControlStatus.PowerState + 1;
224
225 /* Save all the power capabilities */
226 PdoExtension->PowerCapabilities = PowerCapabilities.PMC.Capabilities;
227 DPRINT1("PM Caps Found! Wake Level: %d Power State: %d\n",
228 WakeLevel, PdoExtension->PowerState.CurrentDeviceState);
229 }
230 }
231 }
232
233 /* At the very end of all this, does this device not have power management? */
234 if (PdoExtension->HackFlags & PCI_HACK_NO_PM_CAPS)
235 {
236 /* Then guess the current state based on whether the decodes are on */
237 PdoExtension->PowerState.CurrentDeviceState =
238 PciData->Command & (PCI_ENABLE_IO_SPACE |
239 PCI_ENABLE_MEMORY_SPACE |
240 PCI_ENABLE_BUS_MASTER) ?
241 PowerDeviceD0: PowerDeviceD3;
242 DPRINT1("PM is off, so assumed device is: %d based on enables\n",
243 PdoExtension->PowerState.CurrentDeviceState);
244 }
245 }
246
247 NTSTATUS
248 NTAPI
249 PciScanBus(IN PPCI_FDO_EXTENSION DeviceExtension)
250 {
251 ULONG MaxDevice = PCI_MAX_DEVICES;
252 BOOLEAN ProcessFlag = FALSE;
253 ULONG i, j, k;
254 LONGLONG HackFlags;
255 PDEVICE_OBJECT DeviceObject;
256 UCHAR Buffer[PCI_COMMON_HDR_LENGTH];
257 UCHAR BiosBuffer[PCI_COMMON_HDR_LENGTH];
258 PPCI_COMMON_HEADER PciData = (PVOID)Buffer;
259 PPCI_COMMON_HEADER BiosData = (PVOID)BiosBuffer;
260 PCI_SLOT_NUMBER PciSlot;
261 NTSTATUS Status;
262 PPCI_PDO_EXTENSION PdoExtension, NewExtension;
263 PPCI_PDO_EXTENSION* BridgeExtension;
264 PWCHAR DescriptionText;
265 USHORT SubVendorId, SubSystemId;
266 DPRINT1("PCI Scan Bus: FDO Extension @ 0x%x, Base Bus = 0x%x\n",
267 DeviceExtension, DeviceExtension->BaseBus);
268
269 /* Is this the root FDO? */
270 if (!PCI_IS_ROOT_FDO(DeviceExtension))
271 {
272 /* Other FDOs are not currently supported */
273 UNIMPLEMENTED;
274 while (TRUE);
275 }
276
277 /* Loop every device on the bus */
278 PciSlot.u.bits.Reserved = 0;
279 i = DeviceExtension->BaseBus;
280 for (j = 0; j < MaxDevice; j++)
281 {
282 /* Loop every function of each device */
283 PciSlot.u.bits.DeviceNumber = j;
284 for (k = 0; k < PCI_MAX_FUNCTION; k++)
285 {
286 /* Build the final slot structure */
287 PciSlot.u.bits.FunctionNumber = k;
288
289 /* Read the vendor for this slot */
290 PciReadSlotConfig(DeviceExtension,
291 PciSlot,
292 PciData,
293 0,
294 sizeof(USHORT));
295
296 /* Skip invalid device */
297 if (PciData->VendorID == PCI_INVALID_VENDORID) continue;
298
299 /* Now read the whole header */
300 PciReadSlotConfig(DeviceExtension,
301 PciSlot,
302 &PciData->DeviceID,
303 sizeof(USHORT),
304 PCI_COMMON_HDR_LENGTH - sizeof(USHORT));
305
306 /* Dump device that was found */
307 DPRINT1("Scan Found Device 0x%x (b=0x%x, d=0x%x, f=0x%x)\n",
308 PciSlot.u.AsULONG,
309 i,
310 j,
311 k);
312
313 /* Dump the device's header */
314 PciDebugDumpCommonConfig(PciData);
315
316 /* Find description for this device for the debugger's sake */
317 DescriptionText = PciGetDeviceDescriptionMessage(PciData->BaseClass,
318 PciData->SubClass);
319 DPRINT1("Device Description \"%S\".\n",
320 DescriptionText ? DescriptionText : L"(NULL)");
321 if (DescriptionText) ExFreePoolWithTag(DescriptionText, 0);
322
323 /* Check if there is an ACPI Watchdog Table */
324 if (WdTable)
325 {
326 /* Check if this PCI device is the ACPI Watchdog Device... */
327 UNIMPLEMENTED;
328 while (TRUE);
329 }
330
331 /* Check for non-simple devices */
332 if ((PCI_MULTIFUNCTION_DEVICE(PciData)) ||
333 (PciData->BaseClass == PCI_CLASS_BRIDGE_DEV))
334 {
335 /* No subsystem data defined for these kinds of bridges */
336 SubVendorId = 0;
337 SubSystemId = 0;
338 }
339 else
340 {
341 /* Read the subsystem information from the PCI header */
342 SubVendorId = PciData->u.type0.SubVendorID;
343 SubSystemId = PciData->u.type0.SubSystemID;
344 }
345
346 /* Get any hack flags for this device */
347 HackFlags = PciGetHackFlags(PciData->VendorID,
348 PciData->DeviceID,
349 SubVendorId,
350 SubSystemId,
351 PciData->RevisionID);
352
353 /* Check if this device is considered critical by the OS */
354 if (PciIsCriticalDeviceClass(PciData->BaseClass, PciData->SubClass))
355 {
356 /* Check if normally the decodes would be disabled */
357 if (!(HackFlags & PCI_HACK_DONT_DISABLE_DECODES))
358 {
359 /* Because this device is critical, don't disable them */
360 DPRINT1("Not allowing PM Because device is critical\n");
361 HackFlags |= PCI_HACK_CRITICAL_DEVICE;
362 }
363 }
364
365 /* PCI bridges with a VGA card are also considered critical */
366 if ((PciData->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
367 (PciData->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI) &&
368 (PciData->u.type1.BridgeControl & PCI_ENABLE_BRIDGE_VGA) &&
369 !(HackFlags & PCI_HACK_DONT_DISABLE_DECODES))
370 {
371 /* Do not disable their decodes either */
372 DPRINT1("Not allowing PM because device is VGA\n");
373 HackFlags |= PCI_HACK_CRITICAL_DEVICE;
374 }
375
376 /* Also skip devices that should not be enumerated */
377 if (PciSkipThisFunction(PciData, PciSlot, 1, HackFlags)) continue;
378
379 /* Check if a PDO has already been created for this device */
380 PdoExtension = PciFindPdoByFunction(DeviceExtension,
381 PciSlot.u.AsULONG,
382 PciData);
383 if (PdoExtension)
384 {
385 /* Rescan scenarios are not yet implemented */
386 UNIMPLEMENTED;
387 while (TRUE);
388 }
389
390 /* Bus processing will need to happen */
391 ProcessFlag = TRUE;
392
393 /* Create the PDO for this device */
394 Status = PciPdoCreate(DeviceExtension, PciSlot, &DeviceObject);
395 ASSERT(NT_SUCCESS(Status));
396 NewExtension = (PPCI_PDO_EXTENSION)DeviceObject->DeviceExtension;
397
398 /* Check for broken devices with wrong/no class codes */
399 if (HackFlags & PCI_HACK_FAKE_CLASS_CODE)
400 {
401 /* Setup a default one */
402 PciData->BaseClass = PCI_CLASS_BASE_SYSTEM_DEV;
403 PciData->SubClass = PCI_SUBCLASS_SYS_OTHER;
404
405 /* Device will behave erratically when reading back data */
406 NewExtension->ExpectedWritebackFailure = TRUE;
407 }
408
409 /* Clone all the information from the header */
410 NewExtension->VendorId = PciData->VendorID;
411 NewExtension->DeviceId = PciData->DeviceID;
412 NewExtension->RevisionId = PciData->RevisionID;
413 NewExtension->ProgIf = PciData->ProgIf;
414 NewExtension->SubClass = PciData->SubClass;
415 NewExtension->BaseClass = PciData->BaseClass;
416 NewExtension->HeaderType = PCI_CONFIGURATION_TYPE(PciData);
417
418 /* Check for modern bridge types, which are managed by the driver */
419 if ((NewExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
420 ((NewExtension->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI) ||
421 (NewExtension->SubClass == PCI_SUBCLASS_BR_CARDBUS)))
422 {
423 /* Acquire this device's lock */
424 KeEnterCriticalRegion();
425 KeWaitForSingleObject(&DeviceExtension->ChildListLock,
426 Executive,
427 KernelMode,
428 FALSE,
429 NULL);
430
431 /* Scan the bridge list until the first free entry */
432 for (BridgeExtension = &DeviceExtension->ChildBridgePdoList;
433 *BridgeExtension;
434 BridgeExtension = &(*BridgeExtension)->NextBridge);
435
436 /* Add this PDO as a bridge */
437 *BridgeExtension = NewExtension;
438 ASSERT(NewExtension->NextBridge == NULL);
439
440 /* Release this device's lock */
441 KeSetEvent(&DeviceExtension->ChildListLock,
442 IO_NO_INCREMENT,
443 FALSE);
444 KeLeaveCriticalRegion();
445 }
446
447 /* Get the PCI BIOS configuration saved in the registry */
448 Status = PciGetBiosConfig(NewExtension, BiosData);
449 if (NT_SUCCESS(Status))
450 {
451 /* This path has not yet been fully tested by eVb */
452 DPRINT1("Have BIOS configuration!\n");
453 UNIMPLEMENTED;
454
455 /* Check if the PCI BIOS configuration has changed */
456 if (!PcipIsSameDevice(NewExtension, BiosData))
457 {
458 /* This is considered failure, and new data will be saved */
459 Status = STATUS_UNSUCCESSFUL;
460 }
461 else
462 {
463 /* Data is still correct, check for interrupt line change */
464 if (BiosData->u.type0.InterruptLine !=
465 PciData->u.type0.InterruptLine)
466 {
467 /* Update the current BIOS with the saved interrupt line */
468 PciWriteDeviceConfig(NewExtension,
469 &BiosData->u.type0.InterruptLine,
470 FIELD_OFFSET(PCI_COMMON_HEADER,
471 u.type0.InterruptLine),
472 sizeof(UCHAR));
473 }
474
475 /* Save the BIOS interrupt line and the initial command */
476 NewExtension->RawInterruptLine = BiosData->u.type0.InterruptLine;
477 NewExtension->InitialCommand = BiosData->Command;
478 }
479 }
480
481 /* Check if no saved data was present or if it was a mismatch */
482 if (!NT_SUCCESS(Status))
483 {
484 /* Save the new data */
485 Status = PciSaveBiosConfig(NewExtension, PciData);
486 ASSERT(NT_SUCCESS(Status));
487
488 /* Save the interrupt line and command from the device */
489 NewExtension->RawInterruptLine = PciData->u.type0.InterruptLine;
490 NewExtension->InitialCommand = PciData->Command;
491 }
492
493 /* Save original command from the device and hack flags */
494 NewExtension->CommandEnables = PciData->Command;
495 NewExtension->HackFlags = HackFlags;
496
497 /* Get power, AGP, and other capability data */
498 PciGetEnhancedCapabilities(NewExtension, PciData);
499
500 /* Power up the device */
501 PciSetPowerManagedDevicePowerState(NewExtension, PowerDeviceD0, FALSE);
502
503 /* Save interrupt pin */
504 NewExtension->InterruptPin = PciData->u.type0.InterruptPin;
505
506 /*
507 * Use either this device's actual IRQ line or, if it's connected on
508 * a master bus whose IRQ line is actually connected to the host, use
509 * the HAL to query the bus' IRQ line and store that as the adjusted
510 * interrupt line instead
511 */
512 NewExtension->AdjustedInterruptLine = PciGetAdjustedInterruptLine(NewExtension);
513
514 /* Check if this device is used for PCI debugger cards */
515 NewExtension->OnDebugPath = PciIsDeviceOnDebugPath(NewExtension);
516
517 /* Check for devices with invalid/bogus subsystem data */
518 if (HackFlags & PCI_HACK_NO_SUBSYSTEM)
519 {
520 /* Set the subsystem information to zero instead */
521 NewExtension->SubsystemVendorId = 0;
522 NewExtension->SubsystemId = 0;
523 }
524
525 /* Check for IDE controllers */
526 if ((NewExtension->BaseClass == PCI_CLASS_MASS_STORAGE_CTLR) &&
527 (NewExtension->SubClass == PCI_SUBCLASS_MSC_IDE_CTLR))
528 {
529 /* Do not allow them to power down completely */
530 NewExtension->DisablePowerDown = TRUE;
531 }
532
533 /*
534 * Check if this is a legacy bridge. Note that the i82375 PCI/EISA
535 * bridge that is present on certain NT Alpha machines appears as
536 * non-classified so detect it manually by scanning for its VID/PID.
537 */
538 if (((NewExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
539 ((NewExtension->SubClass == PCI_SUBCLASS_BR_ISA) ||
540 (NewExtension->SubClass == PCI_SUBCLASS_BR_EISA) ||
541 (NewExtension->SubClass == PCI_SUBCLASS_BR_MCA))) ||
542 ((NewExtension->VendorId == 0x8086) &&
543 (NewExtension->DeviceId == 0x482)))
544 {
545 /* Do not allow these legacy bridges to be powered down */
546 NewExtension->DisablePowerDown = TRUE;
547 }
548
549 /* Save latency and cache size information */
550 NewExtension->SavedLatencyTimer = PciData->LatencyTimer;
551 NewExtension->SavedCacheLineSize = PciData->CacheLineSize;
552
553 /* The PDO is now ready to go */
554 DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
555 }
556 }
557
558 /* Enumeration is completed */
559 return STATUS_SUCCESS;
560 }
561
562 NTSTATUS
563 NTAPI
564 PciQueryDeviceRelations(IN PPCI_FDO_EXTENSION DeviceExtension,
565 IN OUT PDEVICE_RELATIONS *pDeviceRelations)
566 {
567 NTSTATUS Status;
568 PPCI_PDO_EXTENSION PdoExtension;
569 ULONG PdoCount = 0;
570 PDEVICE_RELATIONS DeviceRelations, NewRelations;
571 SIZE_T Size;
572 PDEVICE_OBJECT DeviceObject, *ObjectArray;
573 PAGED_CODE();
574
575 /* Make sure the FDO is started */
576 ASSERT(DeviceExtension->DeviceState == PciStarted);
577
578 /* Synchronize while we enumerate the bus */
579 Status = PciBeginStateTransition(DeviceExtension, PciSynchronizedOperation);
580 if (!NT_SUCCESS(Status)) return Status;
581
582 /* Scan all children PDO */
583 for (PdoExtension = DeviceExtension->ChildPdoList;
584 PdoExtension;
585 PdoExtension = PdoExtension->Next)
586 {
587 /* Invalidate them */
588 PdoExtension->NotPresent = TRUE;
589 }
590
591 /* Scan the PCI Bus */
592 Status = PciScanBus(DeviceExtension);
593 ASSERT(NT_SUCCESS(Status));
594
595 /* Enumerate all children PDO again */
596 for (PdoExtension = DeviceExtension->ChildPdoList;
597 PdoExtension;
598 PdoExtension = PdoExtension->Next)
599 {
600 /* Check for PDOs that are still invalidated */
601 if (PdoExtension->NotPresent)
602 {
603 /* This means this PDO existed before, but not anymore */
604 PdoExtension->ReportedMissing = TRUE;
605 DPRINT1("PCI - Old device (pdox) %08x not found on rescan.\n",
606 PdoExtension);
607 }
608 else
609 {
610 /* Increase count of detected PDOs */
611 PdoCount++;
612 }
613 }
614
615 /* Read the current relations and add the newly discovered relations */
616 DeviceRelations = *pDeviceRelations;
617 Size = FIELD_OFFSET(DEVICE_RELATIONS, Objects) +
618 PdoCount * sizeof(PDEVICE_OBJECT);
619 if (DeviceRelations) Size += sizeof(PDEVICE_OBJECT) * DeviceRelations->Count;
620
621 /* Allocate the device relations */
622 NewRelations = (PDEVICE_RELATIONS)ExAllocatePoolWithTag(0, Size, 'BicP');
623 if (!NewRelations)
624 {
625 /* Out of space, cancel the operation */
626 PciCancelStateTransition(DeviceExtension, PciSynchronizedOperation);
627 return STATUS_INSUFFICIENT_RESOURCES;
628 }
629
630 /* Check if there were any older relations */
631 NewRelations->Count = 0;
632 if (DeviceRelations)
633 {
634 /* Copy the old relations into the new buffer, then free the old one */
635 RtlCopyMemory(NewRelations,
636 DeviceRelations,
637 FIELD_OFFSET(DEVICE_RELATIONS, Objects) +
638 DeviceRelations->Count * sizeof(PDEVICE_OBJECT));
639 ExFreePoolWithTag(DeviceRelations, 0);
640 }
641
642 /* Print out that we're ready to dump relations */
643 DPRINT1("PCI QueryDeviceRelations/BusRelations FDOx %08x (bus 0x%02x)\n",
644 DeviceExtension,
645 DeviceExtension->BaseBus);
646
647 /* Loop the current PDO children and the device relation object array */
648 PdoExtension = DeviceExtension->ChildPdoList;
649 ObjectArray = &NewRelations->Objects[NewRelations->Count];
650 while (PdoExtension)
651 {
652 /* Dump this relation */
653 DPRINT1(" QDR PDO %08x (x %08x)%s\n",
654 PdoExtension->PhysicalDeviceObject,
655 PdoExtension,
656 PdoExtension->NotPresent ?
657 "<Omitted, device flaged not present>" : "");
658
659 /* Is this PDO present? */
660 if (!PdoExtension->NotPresent)
661 {
662 /* Reference it and add it to the array */
663 DeviceObject = PdoExtension->PhysicalDeviceObject;
664 ObfReferenceObject(DeviceObject);
665 *ObjectArray++ = DeviceObject;
666 }
667
668 /* Go to the next PDO */
669 PdoExtension = PdoExtension->Next;
670 }
671
672 /* Terminate dumping the relations */
673 DPRINT1(" QDR Total PDO count = %d (%d already in list)\n",
674 NewRelations->Count + PdoCount,
675 NewRelations->Count);
676
677 /* Return the final count and the new buffer */
678 NewRelations->Count += PdoCount;
679 *pDeviceRelations = NewRelations;
680 return STATUS_SUCCESS;
681 }
682
683 /* EOF */