09f586f6f8c446b2e36693c64a2440b804decb81
[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 ULONG i, j, k;
99 LONGLONG HackFlags;
100 UCHAR Buffer[PCI_COMMON_HDR_LENGTH];
101 PPCI_COMMON_HEADER PciData = (PVOID)Buffer;
102 PCI_SLOT_NUMBER PciSlot;
103 PWCHAR DescriptionText;
104 USHORT SubVendorId, SubSystemId;
105 PPCI_PDO_EXTENSION PdoExtension;
106 DPRINT1("PCI Scan Bus: FDO Extension @ 0x%x, Base Bus = 0x%x\n",
107 DeviceExtension, DeviceExtension->BaseBus);
108
109 /* Is this the root FDO? */
110 if (!PCI_IS_ROOT_FDO(DeviceExtension))
111 {
112 /* Other FDOs are not currently supported */
113 UNIMPLEMENTED;
114 while (TRUE);
115 }
116
117 /* Loop every device on the bus */
118 PciSlot.u.bits.Reserved = 0;
119 i = DeviceExtension->BaseBus;
120 for (j = 0; j < MaxDevice; j++)
121 {
122 /* Loop every function of each device */
123 PciSlot.u.bits.DeviceNumber = j;
124 for (k = 0; k < PCI_MAX_FUNCTION; k++)
125 {
126 /* Build the final slot structure */
127 PciSlot.u.bits.FunctionNumber = k;
128
129 /* Read the vendor for this slot */
130 PciReadSlotConfig(DeviceExtension,
131 PciSlot,
132 PciData,
133 0,
134 sizeof(USHORT));
135
136 /* Skip invalid device */
137 if (PciData->VendorID == PCI_INVALID_VENDORID) continue;
138
139 /* Now read the whole header */
140 PciReadSlotConfig(DeviceExtension,
141 PciSlot,
142 &PciData->DeviceID,
143 sizeof(USHORT),
144 PCI_COMMON_HDR_LENGTH - sizeof(USHORT));
145
146 /* Dump device that was found */
147 DPRINT1("Scan Found Device 0x%x (b=0x%x, d=0x%x, f=0x%x)\n",
148 PciSlot.u.AsULONG,
149 i,
150 j,
151 k);
152
153 /* Dump the device's header */
154 PciDebugDumpCommonConfig(PciData);
155
156 /* Find description for this device for the debugger's sake */
157 DescriptionText = PciGetDeviceDescriptionMessage(PciData->BaseClass,
158 PciData->SubClass);
159 DPRINT1("Device Description \"%S\".\n", DescriptionText ? DescriptionText : L"(NULL)");
160 if (DescriptionText) ExFreePoolWithTag(DescriptionText, 0);
161
162 /* Check if there is an ACPI Watchdog Table */
163 if (WdTable)
164 {
165 /* Check if this PCI device is the ACPI Watchdog Device... */
166 UNIMPLEMENTED;
167 while (TRUE);
168 }
169
170 /* Check for non-simple devices */
171 if ((PCI_MULTIFUNCTION_DEVICE(PciData)) ||
172 (PciData->BaseClass == PCI_CLASS_BRIDGE_DEV))
173 {
174 /* No subsystem data defined for these kinds of bridges */
175 SubVendorId = 0;
176 SubSystemId = 0;
177 }
178 else
179 {
180 /* Read the subsystem information from the PCI header */
181 SubVendorId = PciData->u.type0.SubVendorID;
182 SubSystemId = PciData->u.type0.SubSystemID;
183 }
184
185 /* Get any hack flags for this device */
186 HackFlags = PciGetHackFlags(PciData->VendorID,
187 PciData->DeviceID,
188 SubVendorId,
189 SubSystemId,
190 PciData->RevisionID);
191
192 /* Check if this device is considered critical by the OS */
193 if (PciIsCriticalDeviceClass(PciData->BaseClass, PciData->SubClass))
194 {
195 /* Check if normally the decodes would be disabled */
196 if (!(HackFlags & PCI_HACK_DONT_DISABLE_DECODES))
197 {
198 /* Because this device is critical, don't disable them */
199 DPRINT1("Not allowing PM Because device is critical\n");
200 HackFlags |= PCI_HACK_CRITICAL_DEVICE;
201 }
202 }
203
204 /* PCI bridges with a VGA card are also considered critical */
205 if ((PciData->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
206 (PciData->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI) &&
207 (PciData->u.type1.BridgeControl & PCI_ENABLE_BRIDGE_VGA) &&
208 !(HackFlags & PCI_HACK_DONT_DISABLE_DECODES))
209 {
210 /* Do not disable their decodes either */
211 DPRINT1("Not allowing PM because device is VGA\n");
212 HackFlags |= PCI_HACK_CRITICAL_DEVICE;
213 }
214
215 /* Also skip devices that should not be enumerated */
216 if (PciSkipThisFunction(PciData, PciSlot, 1, HackFlags)) continue;
217
218 /* Check if a PDO has already been created for this device */
219 PdoExtension = PciFindPdoByFunction(DeviceExtension,
220 PciSlot.u.bits.FunctionNumber,
221 PciData);
222 if (PdoExtension)
223 {
224 /* Rescan scenarios are not yet implemented */
225 UNIMPLEMENTED;
226 while (TRUE);
227 }
228 }
229 }
230
231 /* Enumeration is completed */
232 return STATUS_SUCCESS;
233 }
234
235 NTSTATUS
236 NTAPI
237 PciQueryDeviceRelations(IN PPCI_FDO_EXTENSION DeviceExtension,
238 IN OUT PDEVICE_RELATIONS *pDeviceRelations)
239 {
240 NTSTATUS Status;
241 PPCI_PDO_EXTENSION PdoExtension;
242 ULONG PdoCount = 0;
243 PDEVICE_RELATIONS DeviceRelations, NewRelations;
244 SIZE_T Size;
245 PDEVICE_OBJECT DeviceObject, *ObjectArray;
246 PAGED_CODE();
247
248 /* Make sure the FDO is started */
249 ASSERT(DeviceExtension->DeviceState == PciStarted);
250
251 /* Synchronize while we enumerate the bus */
252 Status = PciBeginStateTransition(DeviceExtension, PciSynchronizedOperation);
253 if (!NT_SUCCESS(Status)) return Status;
254
255 /* Scan all children PDO */
256 for (PdoExtension = DeviceExtension->ChildPdoList;
257 PdoExtension;
258 PdoExtension = PdoExtension->Next)
259 {
260 /* Invalidate them */
261 PdoExtension->NotPresent = TRUE;
262 }
263
264 /* Scan the PCI Bus */
265 Status = PciScanBus(DeviceExtension);
266 ASSERT(NT_SUCCESS(Status));
267
268 /* Enumerate all children PDO again */
269 for (PdoExtension = DeviceExtension->ChildPdoList;
270 PdoExtension;
271 PdoExtension = PdoExtension->Next)
272 {
273 /* Check for PDOs that are still invalidated */
274 if (PdoExtension->NotPresent)
275 {
276 /* This means this PDO existed before, but not anymore */
277 PdoExtension->ReportedMissing = TRUE;
278 DPRINT1("PCI - Old device (pdox) %08x not found on rescan.\n",
279 PdoExtension);
280 }
281 else
282 {
283 /* Increase count of detected PDOs */
284 PdoCount++;
285 }
286 }
287
288 /* Read the current relations and add the newly discovered relations */
289 DeviceRelations = *pDeviceRelations;
290 Size = FIELD_OFFSET(DEVICE_RELATIONS, Objects) +
291 PdoCount * sizeof(PDEVICE_OBJECT);
292 if (DeviceRelations) Size += sizeof(PDEVICE_OBJECT) * DeviceRelations->Count;
293
294 /* Allocate the device relations */
295 NewRelations = (PDEVICE_RELATIONS)ExAllocatePoolWithTag(0, Size, 'BicP');
296 if (!NewRelations)
297 {
298 /* Out of space, cancel the operation */
299 PciCancelStateTransition(DeviceExtension, PciSynchronizedOperation);
300 return STATUS_INSUFFICIENT_RESOURCES;
301 }
302
303 /* Check if there were any older relations */
304 NewRelations->Count = 0;
305 if (DeviceRelations)
306 {
307 /* Copy the old relations into the new buffer, then free the old one */
308 RtlCopyMemory(NewRelations,
309 DeviceRelations,
310 FIELD_OFFSET(DEVICE_RELATIONS, Objects) +
311 DeviceRelations->Count * sizeof(PDEVICE_OBJECT));
312 ExFreePoolWithTag(DeviceRelations, 0);
313 }
314
315 /* Print out that we're ready to dump relations */
316 DPRINT1("PCI QueryDeviceRelations/BusRelations FDOx %08x (bus 0x%02x)\n",
317 DeviceExtension,
318 DeviceExtension->BaseBus);
319
320 /* Loop the current PDO children and the device relation object array */
321 PdoExtension = DeviceExtension->ChildPdoList;
322 ObjectArray = &NewRelations->Objects[NewRelations->Count];
323 while (PdoExtension)
324 {
325 /* Dump this relation */
326 DPRINT1(" QDR PDO %08x (x %08x)%s\n",
327 PdoExtension->PhysicalDeviceObject,
328 PdoExtension,
329 PdoExtension->NotPresent ?
330 "<Omitted, device flaged not present>" : "");
331
332 /* Is this PDO present? */
333 if (!PdoExtension->NotPresent)
334 {
335 /* Reference it and add it to the array */
336 DeviceObject = PdoExtension->PhysicalDeviceObject;
337 ObfReferenceObject(DeviceObject);
338 *ObjectArray++ = DeviceObject;
339 }
340
341 /* Go to the next PDO */
342 PdoExtension = PdoExtension->Next;
343 }
344
345 /* Terminate dumping the relations */
346 DPRINT1(" QDR Total PDO count = %d (%d already in list)\n",
347 NewRelations->Count + PdoCount,
348 NewRelations->Count);
349
350 /* Return the final count and the new buffer */
351 NewRelations->Count += PdoCount;
352 *pDeviceRelations = NewRelations;
353 return STATUS_SUCCESS;
354 }
355
356 /* EOF */