db2313250f1ce1c60a723e990154c439320ed5a7
[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 PciSkipThisFunction(IN PPCI_COMMON_HEADER PciData,
22 IN PCI_SLOT_NUMBER Slot,
23 IN UCHAR OperationType,
24 IN ULONGLONG HackFlags)
25 {
26 do
27 {
28 /* Check if this is device enumeration */
29 if (OperationType == PCI_SKIP_DEVICE_ENUMERATION)
30 {
31 /* Check if there's a hackflag saying not to enumerate this device */
32 if (HackFlags & PCI_HACK_NO_ENUM_AT_ALL) break;
33
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))
37 {
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",
40 PciData->VendorID,
41 PciData->DeviceID,
42 Slot.u.bits.DeviceNumber,
43 Slot.u.bits.FunctionNumber);
44 break;
45 }
46 }
47 else if (OperationType == PCI_SKIP_RESOURCE_ENUMERATION)
48 {
49 /* Resource enumeration, check for a hackflag saying not to do it */
50 if (HackFlags & PCI_HACK_ENUM_NO_RESOURCE) break;
51 }
52 else
53 {
54 /* Logic error in the driver */
55 ASSERTMSG(FALSE, "PCI Skip Function - Operation type unknown.");
56 }
57
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))
62 {
63 /* Their resources are not enumerated, only PCI and Cardbus/PCMCIA */
64 break;
65 }
66 else if (PciData->BaseClass == PCI_CLASS_NOT_DEFINED)
67 {
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",
70 PciData->VendorID,
71 PciData->DeviceID);
72
73 /*
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.
80 */
81 if ((PciData->VendorID == 0x8086) && (PciData->DeviceID == 8)) break;
82 }
83
84 /* Other normal PCI cards and bridges are enumerated */
85 if (PCI_CONFIGURATION_TYPE(PciData) <= PCI_CARDBUS_BRIDGE_TYPE) return FALSE;
86 } while (FALSE);
87
88 /* Hit one of the known bugs/hackflags, or this is a new kind of PCI unit */
89 DPRINT1(" Device skipped (not enumerated).\n");
90 return TRUE;
91 }
92
93 NTSTATUS
94 NTAPI
95 PciScanBus(IN PPCI_FDO_EXTENSION DeviceExtension)
96 {
97 ULONG MaxDevice = PCI_MAX_DEVICES;
98 BOOLEAN ProcessFlag = FALSE;
99 ULONG i, j, k;
100 LONGLONG HackFlags;
101 PDEVICE_OBJECT DeviceObject;
102 UCHAR Buffer[PCI_COMMON_HDR_LENGTH];
103 PPCI_COMMON_HEADER PciData = (PVOID)Buffer;
104 PCI_SLOT_NUMBER PciSlot;
105 NTSTATUS Status;
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);
112
113 /* Is this the root FDO? */
114 if (!PCI_IS_ROOT_FDO(DeviceExtension))
115 {
116 /* Other FDOs are not currently supported */
117 UNIMPLEMENTED;
118 while (TRUE);
119 }
120
121 /* Loop every device on the bus */
122 PciSlot.u.bits.Reserved = 0;
123 i = DeviceExtension->BaseBus;
124 for (j = 0; j < MaxDevice; j++)
125 {
126 /* Loop every function of each device */
127 PciSlot.u.bits.DeviceNumber = j;
128 for (k = 0; k < PCI_MAX_FUNCTION; k++)
129 {
130 /* Build the final slot structure */
131 PciSlot.u.bits.FunctionNumber = k;
132
133 /* Read the vendor for this slot */
134 PciReadSlotConfig(DeviceExtension,
135 PciSlot,
136 PciData,
137 0,
138 sizeof(USHORT));
139
140 /* Skip invalid device */
141 if (PciData->VendorID == PCI_INVALID_VENDORID) continue;
142
143 /* Now read the whole header */
144 PciReadSlotConfig(DeviceExtension,
145 PciSlot,
146 &PciData->DeviceID,
147 sizeof(USHORT),
148 PCI_COMMON_HDR_LENGTH - sizeof(USHORT));
149
150 /* Dump device that was found */
151 DPRINT1("Scan Found Device 0x%x (b=0x%x, d=0x%x, f=0x%x)\n",
152 PciSlot.u.AsULONG,
153 i,
154 j,
155 k);
156
157 /* Dump the device's header */
158 PciDebugDumpCommonConfig(PciData);
159
160 /* Find description for this device for the debugger's sake */
161 DescriptionText = PciGetDeviceDescriptionMessage(PciData->BaseClass,
162 PciData->SubClass);
163 DPRINT1("Device Description \"%S\".\n", DescriptionText ? DescriptionText : L"(NULL)");
164 if (DescriptionText) ExFreePoolWithTag(DescriptionText, 0);
165
166 /* Check if there is an ACPI Watchdog Table */
167 if (WdTable)
168 {
169 /* Check if this PCI device is the ACPI Watchdog Device... */
170 UNIMPLEMENTED;
171 while (TRUE);
172 }
173
174 /* Check for non-simple devices */
175 if ((PCI_MULTIFUNCTION_DEVICE(PciData)) ||
176 (PciData->BaseClass == PCI_CLASS_BRIDGE_DEV))
177 {
178 /* No subsystem data defined for these kinds of bridges */
179 SubVendorId = 0;
180 SubSystemId = 0;
181 }
182 else
183 {
184 /* Read the subsystem information from the PCI header */
185 SubVendorId = PciData->u.type0.SubVendorID;
186 SubSystemId = PciData->u.type0.SubSystemID;
187 }
188
189 /* Get any hack flags for this device */
190 HackFlags = PciGetHackFlags(PciData->VendorID,
191 PciData->DeviceID,
192 SubVendorId,
193 SubSystemId,
194 PciData->RevisionID);
195
196 /* Check if this device is considered critical by the OS */
197 if (PciIsCriticalDeviceClass(PciData->BaseClass, PciData->SubClass))
198 {
199 /* Check if normally the decodes would be disabled */
200 if (!(HackFlags & PCI_HACK_DONT_DISABLE_DECODES))
201 {
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;
205 }
206 }
207
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))
213 {
214 /* Do not disable their decodes either */
215 DPRINT1("Not allowing PM because device is VGA\n");
216 HackFlags |= PCI_HACK_CRITICAL_DEVICE;
217 }
218
219 /* Also skip devices that should not be enumerated */
220 if (PciSkipThisFunction(PciData, PciSlot, 1, HackFlags)) continue;
221
222 /* Check if a PDO has already been created for this device */
223 PdoExtension = PciFindPdoByFunction(DeviceExtension,
224 PciSlot.u.AsULONG,
225 PciData);
226 if (PdoExtension)
227 {
228 /* Rescan scenarios are not yet implemented */
229 UNIMPLEMENTED;
230 while (TRUE);
231 }
232
233 /* Bus processing will need to happen */
234 ProcessFlag = TRUE;
235
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;
240
241 /* Check for broken devices with wrong/no class codes */
242 if (HackFlags & PCI_HACK_FAKE_CLASS_CODE)
243 {
244 /* Setup a default one */
245 PciData->BaseClass = PCI_CLASS_BASE_SYSTEM_DEV;
246 PciData->SubClass = PCI_SUBCLASS_SYS_OTHER;
247
248 /* Device will behave erratically when reading back data */
249 NewExtension->ExpectedWritebackFailure = TRUE;
250 }
251
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);
260
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)))
265 {
266 /* Acquire this device's lock */
267 KeEnterCriticalRegion();
268 KeWaitForSingleObject(&DeviceExtension->ChildListLock,
269 Executive,
270 KernelMode,
271 FALSE,
272 NULL);
273
274 /* Scan the bridge list until the first free entry */
275 for (BridgeExtension = &DeviceExtension->ChildBridgePdoList;
276 *BridgeExtension;
277 BridgeExtension = &(*BridgeExtension)->NextBridge);
278
279 /* Add this PDO as a bridge */
280 *BridgeExtension = NewExtension;
281 ASSERT(NewExtension->NextBridge == NULL);
282
283 /* Release this device's lock */
284 KeSetEvent(&DeviceExtension->ChildListLock, IO_NO_INCREMENT, FALSE);
285 KeLeaveCriticalRegion();
286 }
287
288 /* Check for IDE controllers */
289 if ((NewExtension->BaseClass == PCI_CLASS_MASS_STORAGE_CTLR) &&
290 (NewExtension->SubClass == PCI_SUBCLASS_MSC_IDE_CTLR))
291 {
292 /* Do not allow them to power down completely */
293 NewExtension->DisablePowerDown = TRUE;
294 }
295
296 /*
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.
300 */
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)))
306 {
307 /* Do not allow these legacy bridges to be powered down */
308 NewExtension->DisablePowerDown = TRUE;
309 }
310
311 /* Save latency and cache size information */
312 NewExtension->SavedLatencyTimer = PciData->LatencyTimer;
313 NewExtension->SavedCacheLineSize = PciData->CacheLineSize;
314
315 /* The PDO is now ready to go */
316 DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
317 }
318 }
319
320 /* Enumeration is completed */
321 return STATUS_SUCCESS;
322 }
323
324 NTSTATUS
325 NTAPI
326 PciQueryDeviceRelations(IN PPCI_FDO_EXTENSION DeviceExtension,
327 IN OUT PDEVICE_RELATIONS *pDeviceRelations)
328 {
329 NTSTATUS Status;
330 PPCI_PDO_EXTENSION PdoExtension;
331 ULONG PdoCount = 0;
332 PDEVICE_RELATIONS DeviceRelations, NewRelations;
333 SIZE_T Size;
334 PDEVICE_OBJECT DeviceObject, *ObjectArray;
335 PAGED_CODE();
336
337 /* Make sure the FDO is started */
338 ASSERT(DeviceExtension->DeviceState == PciStarted);
339
340 /* Synchronize while we enumerate the bus */
341 Status = PciBeginStateTransition(DeviceExtension, PciSynchronizedOperation);
342 if (!NT_SUCCESS(Status)) return Status;
343
344 /* Scan all children PDO */
345 for (PdoExtension = DeviceExtension->ChildPdoList;
346 PdoExtension;
347 PdoExtension = PdoExtension->Next)
348 {
349 /* Invalidate them */
350 PdoExtension->NotPresent = TRUE;
351 }
352
353 /* Scan the PCI Bus */
354 Status = PciScanBus(DeviceExtension);
355 ASSERT(NT_SUCCESS(Status));
356
357 /* Enumerate all children PDO again */
358 for (PdoExtension = DeviceExtension->ChildPdoList;
359 PdoExtension;
360 PdoExtension = PdoExtension->Next)
361 {
362 /* Check for PDOs that are still invalidated */
363 if (PdoExtension->NotPresent)
364 {
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",
368 PdoExtension);
369 }
370 else
371 {
372 /* Increase count of detected PDOs */
373 PdoCount++;
374 }
375 }
376
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;
382
383 /* Allocate the device relations */
384 NewRelations = (PDEVICE_RELATIONS)ExAllocatePoolWithTag(0, Size, 'BicP');
385 if (!NewRelations)
386 {
387 /* Out of space, cancel the operation */
388 PciCancelStateTransition(DeviceExtension, PciSynchronizedOperation);
389 return STATUS_INSUFFICIENT_RESOURCES;
390 }
391
392 /* Check if there were any older relations */
393 NewRelations->Count = 0;
394 if (DeviceRelations)
395 {
396 /* Copy the old relations into the new buffer, then free the old one */
397 RtlCopyMemory(NewRelations,
398 DeviceRelations,
399 FIELD_OFFSET(DEVICE_RELATIONS, Objects) +
400 DeviceRelations->Count * sizeof(PDEVICE_OBJECT));
401 ExFreePoolWithTag(DeviceRelations, 0);
402 }
403
404 /* Print out that we're ready to dump relations */
405 DPRINT1("PCI QueryDeviceRelations/BusRelations FDOx %08x (bus 0x%02x)\n",
406 DeviceExtension,
407 DeviceExtension->BaseBus);
408
409 /* Loop the current PDO children and the device relation object array */
410 PdoExtension = DeviceExtension->ChildPdoList;
411 ObjectArray = &NewRelations->Objects[NewRelations->Count];
412 while (PdoExtension)
413 {
414 /* Dump this relation */
415 DPRINT1(" QDR PDO %08x (x %08x)%s\n",
416 PdoExtension->PhysicalDeviceObject,
417 PdoExtension,
418 PdoExtension->NotPresent ?
419 "<Omitted, device flaged not present>" : "");
420
421 /* Is this PDO present? */
422 if (!PdoExtension->NotPresent)
423 {
424 /* Reference it and add it to the array */
425 DeviceObject = PdoExtension->PhysicalDeviceObject;
426 ObfReferenceObject(DeviceObject);
427 *ObjectArray++ = DeviceObject;
428 }
429
430 /* Go to the next PDO */
431 PdoExtension = PdoExtension->Next;
432 }
433
434 /* Terminate dumping the relations */
435 DPRINT1(" QDR Total PDO count = %d (%d already in list)\n",
436 NewRelations->Count + PdoCount,
437 NewRelations->Count);
438
439 /* Return the final count and the new buffer */
440 NewRelations->Count += PdoCount;
441 *pDeviceRelations = NewRelations;
442 return STATUS_SUCCESS;
443 }
444
445 /* EOF */