xref: /reactos/drivers/bus/pcix/utils.c (revision abe8f0ab)
1 /*
2  * PROJECT:         ReactOS PCI Bus Driver
3  * LICENSE:         BSD - See COPYING.ARM in the top level directory
4  * FILE:            drivers/bus/pci/utils.c
5  * PURPOSE:         Utility/Helper Support Code
6  * PROGRAMMERS:     ReactOS Portable Systems Group
7  */
8 
9 /* INCLUDES *******************************************************************/
10 
11 #include <pci.h>
12 
13 #define NDEBUG
14 #include <debug.h>
15 
16 /* GLOBALS ********************************************************************/
17 
18 ULONG PciDebugPortsCount;
19 
20 RTL_RANGE_LIST PciIsaBitExclusionList;
21 RTL_RANGE_LIST PciVgaAndIsaBitExclusionList;
22 
23 /* FUNCTIONS ******************************************************************/
24 
25 BOOLEAN
26 NTAPI
PciUnicodeStringStrStr(IN PUNICODE_STRING InputString,IN PCUNICODE_STRING EqualString,IN BOOLEAN CaseInSensitive)27 PciUnicodeStringStrStr(IN PUNICODE_STRING InputString,
28                        IN PCUNICODE_STRING EqualString,
29                        IN BOOLEAN CaseInSensitive)
30 {
31     UNICODE_STRING PartialString;
32     LONG EqualChars, TotalChars;
33 
34     /* Build a partial string with the smaller substring */
35     PartialString.Length = EqualString->Length;
36     PartialString.MaximumLength = InputString->MaximumLength;
37     PartialString.Buffer = InputString->Buffer;
38 
39     /* Check how many characters that need comparing */
40     EqualChars = 0;
41     TotalChars = (InputString->Length - EqualString->Length) / sizeof(WCHAR);
42 
43     /* If the substring is bigger, just fail immediately */
44     if (TotalChars < 0) return FALSE;
45 
46     /* Keep checking each character */
47     while (!RtlEqualUnicodeString(EqualString, &PartialString, CaseInSensitive))
48     {
49         /* Continue checking until all the required characters are equal */
50         PartialString.Buffer++;
51         PartialString.MaximumLength -= sizeof(WCHAR);
52         if (++EqualChars > TotalChars) return FALSE;
53     }
54 
55     /* The string is equal */
56     return TRUE;
57 }
58 
59 BOOLEAN
60 NTAPI
PciStringToUSHORT(IN PWCHAR String,OUT PUSHORT Value)61 PciStringToUSHORT(IN PWCHAR String,
62                   OUT PUSHORT Value)
63 {
64     USHORT Short;
65     ULONG Low, High, Length;
66     WCHAR Char;
67 
68     /* Initialize everything to zero */
69     Short = 0;
70     Length = 0;
71     while (TRUE)
72     {
73         /* Get the character and set the high byte based on the previous one */
74         Char = *String++;
75         High = 16 * Short;
76 
77         /* Check for numbers */
78         if ( Char >= '0' && Char <= '9' )
79         {
80             /* Convert them to a byte */
81             Low = Char - '0';
82         }
83         else if ( Char >= 'A' && Char <= 'F' )
84         {
85             /* Convert upper-case hex letters into a byte */
86             Low = Char - '7';
87         }
88         else if ( Char >= 'a' && Char <= 'f' )
89         {
90             /* Convert lower-case hex letters into a byte */
91             Low = Char - 'W';
92         }
93         else
94         {
95             /* Invalid string, fail the conversion */
96             return FALSE;
97         }
98 
99         /* Combine the high and low byte */
100         Short = High | Low;
101 
102         /* If 4 letters have been reached, the 16-bit integer should exist */
103         if (++Length >= 4)
104         {
105             /* Return it to the caller */
106             *Value = Short;
107             return TRUE;
108         }
109     }
110 }
111 
112 BOOLEAN
113 NTAPI
PciIsSuiteVersion(IN USHORT SuiteMask)114 PciIsSuiteVersion(IN USHORT SuiteMask)
115 {
116     ULONGLONG Mask = 0;
117     RTL_OSVERSIONINFOEXW VersionInfo;
118 
119     /* Initialize the version information */
120     RtlZeroMemory(&VersionInfo, sizeof(RTL_OSVERSIONINFOEXW));
121     VersionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
122     VersionInfo.wSuiteMask = SuiteMask;
123 
124     /* Set the comparison mask and return if the passed suite mask matches */
125     VER_SET_CONDITION(Mask, VER_SUITENAME, VER_AND);
126     return NT_SUCCESS(RtlVerifyVersionInfo(&VersionInfo, VER_SUITENAME, Mask));
127 }
128 
129 BOOLEAN
130 NTAPI
PciIsDatacenter(VOID)131 PciIsDatacenter(VOID)
132 {
133     BOOLEAN Result;
134     PVOID Value;
135     ULONG ResultLength;
136     NTSTATUS Status;
137 
138     /* Assume this isn't Datacenter */
139     Result = FALSE;
140 
141     /* First, try opening the setup key */
142     Status = PciGetRegistryValue(L"",
143                                  L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\setupdd",
144                                  0,
145                                  REG_BINARY,
146                                  &Value,
147                                  &ResultLength);
148     if (!NT_SUCCESS(Status))
149     {
150         /* This is not an in-progress Setup boot, so query the suite version */
151         Result = PciIsSuiteVersion(VER_SUITE_DATACENTER);
152     }
153     else
154     {
155         /* This scenario shouldn't happen yet, since SetupDD isn't used */
156         UNIMPLEMENTED_FATAL("ReactOS doesn't use SetupDD for its installation program. Therefore this scenario must not happen!\n");
157     }
158 
159     /* Return if this is Datacenter or not */
160     return Result;
161 }
162 
163 BOOLEAN
164 NTAPI
PciOpenKey(IN PWCHAR KeyName,IN HANDLE RootKey,IN ACCESS_MASK DesiredAccess,OUT PHANDLE KeyHandle,OUT PNTSTATUS KeyStatus)165 PciOpenKey(IN PWCHAR KeyName,
166            IN HANDLE RootKey,
167            IN ACCESS_MASK DesiredAccess,
168            OUT PHANDLE KeyHandle,
169            OUT PNTSTATUS KeyStatus)
170 {
171     NTSTATUS Status;
172     OBJECT_ATTRIBUTES ObjectAttributes;
173     UNICODE_STRING KeyString;
174     PAGED_CODE();
175 
176     /* Initialize the object attributes */
177     RtlInitUnicodeString(&KeyString, KeyName);
178     InitializeObjectAttributes(&ObjectAttributes,
179                                &KeyString,
180                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
181                                RootKey,
182                                NULL);
183 
184     /* Open the key, returning a boolean, and the status, if requested */
185     Status = ZwOpenKey(KeyHandle, DesiredAccess, &ObjectAttributes);
186     if (KeyStatus) *KeyStatus = Status;
187     return NT_SUCCESS(Status);
188 }
189 
190 NTSTATUS
191 NTAPI
PciGetRegistryValue(IN PWCHAR ValueName,IN PWCHAR KeyName,IN HANDLE RootHandle,IN ULONG Type,OUT PVOID * OutputBuffer,OUT PULONG OutputLength)192 PciGetRegistryValue(IN PWCHAR ValueName,
193                     IN PWCHAR KeyName,
194                     IN HANDLE RootHandle,
195                     IN ULONG Type,
196                     OUT PVOID *OutputBuffer,
197                     OUT PULONG OutputLength)
198 {
199     NTSTATUS Status;
200     PKEY_VALUE_PARTIAL_INFORMATION PartialInfo;
201     ULONG NeededLength, ActualLength;
202     UNICODE_STRING ValueString;
203     HANDLE KeyHandle;
204     BOOLEAN Result;
205 
206     /* So we know what to free at the end of the body */
207     PartialInfo = NULL;
208     KeyHandle = NULL;
209     do
210     {
211         /* Open the key by name, rooted off the handle passed */
212         Result = PciOpenKey(KeyName,
213                             RootHandle,
214                             KEY_QUERY_VALUE,
215                             &KeyHandle,
216                             &Status);
217         if (!Result) break;
218 
219         /* Query for the size that's needed for the value that was passed in */
220         RtlInitUnicodeString(&ValueString, ValueName);
221         Status = ZwQueryValueKey(KeyHandle,
222                                  &ValueString,
223                                  KeyValuePartialInformation,
224                                  NULL,
225                                  0,
226                                  &NeededLength);
227         ASSERT(!NT_SUCCESS(Status));
228         if (Status != STATUS_BUFFER_TOO_SMALL) break;
229 
230         /* Allocate an appropriate buffer for the size that was returned */
231         ASSERT(NeededLength != 0);
232         Status = STATUS_INSUFFICIENT_RESOURCES;
233         PartialInfo = ExAllocatePoolWithTag(PagedPool,
234                                             NeededLength,
235                                             PCI_POOL_TAG);
236         if (!PartialInfo) break;
237 
238         /* Query the actual value information now that the size is known */
239         Status = ZwQueryValueKey(KeyHandle,
240                                  &ValueString,
241                                  KeyValuePartialInformation,
242                                  PartialInfo,
243                                  NeededLength,
244                                  &ActualLength);
245         if (!NT_SUCCESS(Status)) break;
246 
247         /* Make sure it's of the type that the caller expects */
248         Status = STATUS_INVALID_PARAMETER;
249         if (PartialInfo->Type != Type) break;
250 
251         /* Subtract the registry-specific header, to get the data size */
252         ASSERT(NeededLength == ActualLength);
253         NeededLength -= sizeof(KEY_VALUE_PARTIAL_INFORMATION);
254 
255         /* Allocate a buffer to hold the data and return it to the caller */
256         Status = STATUS_INSUFFICIENT_RESOURCES;
257         *OutputBuffer = ExAllocatePoolWithTag(PagedPool,
258                                               NeededLength,
259                                               PCI_POOL_TAG);
260         if (!*OutputBuffer) break;
261 
262         /* Copy the data into the buffer and return its length to the caller */
263         RtlCopyMemory(*OutputBuffer, PartialInfo->Data, NeededLength);
264         if (OutputLength) *OutputLength = NeededLength;
265         Status = STATUS_SUCCESS;
266     } while (0);
267 
268     /* Close any opened keys and free temporary allocations */
269     if (KeyHandle) ZwClose(KeyHandle);
270     if (PartialInfo) ExFreePoolWithTag(PartialInfo, 0);
271     return Status;
272 }
273 
274 NTSTATUS
275 NTAPI
PciBuildDefaultExclusionLists(VOID)276 PciBuildDefaultExclusionLists(VOID)
277 {
278     ULONG Start;
279     NTSTATUS Status;
280     ASSERT(PciIsaBitExclusionList.Count == 0);
281     ASSERT(PciVgaAndIsaBitExclusionList.Count == 0);
282 
283     /* Initialize the range lists */
284     RtlInitializeRangeList(&PciIsaBitExclusionList);
285     RtlInitializeRangeList(&PciVgaAndIsaBitExclusionList);
286 
287     /* Loop x86 I/O ranges */
288     for (Start = 0x100; Start <= 0xFEFF; Start += 0x400)
289     {
290         /* Add the ISA I/O ranges */
291         Status = RtlAddRange(&PciIsaBitExclusionList,
292                              Start,
293                              Start + 0x2FF,
294                              0,
295                              RTL_RANGE_LIST_ADD_IF_CONFLICT,
296                              NULL,
297                              NULL);
298         if (!NT_SUCCESS(Status)) break;
299 
300         /* Add the ISA I/O ranges */
301         Status = RtlAddRange(&PciVgaAndIsaBitExclusionList,
302                              Start,
303                              Start + 0x2AF,
304                              0,
305                              RTL_RANGE_LIST_ADD_IF_CONFLICT,
306                              NULL,
307                              NULL);
308         if (!NT_SUCCESS(Status)) break;
309 
310         /* Add the VGA I/O range for Monochrome Video */
311         Status = RtlAddRange(&PciVgaAndIsaBitExclusionList,
312                              Start + 0x2BC,
313                              Start + 0x2BF,
314                              0,
315                              RTL_RANGE_LIST_ADD_IF_CONFLICT,
316                              NULL,
317                              NULL);
318         if (!NT_SUCCESS(Status)) break;
319 
320         /* Add the VGA I/O range for certain CGA adapters */
321         Status = RtlAddRange(&PciVgaAndIsaBitExclusionList,
322                              Start + 0x2E0,
323                              Start + 0x2FF,
324                              0,
325                              RTL_RANGE_LIST_ADD_IF_CONFLICT,
326                              NULL,
327                              NULL);
328         if (!NT_SUCCESS(Status)) break;
329 
330         /* Success, ranges added done */
331     };
332 
333     RtlFreeRangeList(&PciIsaBitExclusionList);
334     RtlFreeRangeList(&PciVgaAndIsaBitExclusionList);
335     return Status;
336 }
337 
338 PPCI_FDO_EXTENSION
339 NTAPI
PciFindParentPciFdoExtension(IN PDEVICE_OBJECT DeviceObject,IN PKEVENT Lock)340 PciFindParentPciFdoExtension(IN PDEVICE_OBJECT DeviceObject,
341                              IN PKEVENT Lock)
342 {
343     PPCI_FDO_EXTENSION DeviceExtension;
344     PPCI_PDO_EXTENSION SearchExtension, FoundExtension;
345 
346     /* Assume we'll find nothing */
347     SearchExtension = DeviceObject->DeviceExtension;
348     FoundExtension = NULL;
349 
350     /* Check if a lock was specified */
351     if (Lock)
352     {
353         /* Wait for the lock to be released */
354         KeEnterCriticalRegion();
355         KeWaitForSingleObject(Lock, Executive, KernelMode, FALSE, NULL);
356     }
357 
358     /* Now search for the extension */
359     DeviceExtension = (PPCI_FDO_EXTENSION)PciFdoExtensionListHead.Next;
360     while (DeviceExtension)
361     {
362         /* Acquire this device's lock */
363         KeEnterCriticalRegion();
364         KeWaitForSingleObject(&DeviceExtension->ChildListLock,
365                               Executive,
366                               KernelMode,
367                               FALSE,
368                               NULL);
369 
370         /* Scan all children PDO, stop when no more PDOs, or found it */
371         for (FoundExtension = DeviceExtension->ChildPdoList;
372              ((FoundExtension) && (FoundExtension != SearchExtension));
373              FoundExtension = FoundExtension->Next);
374 
375         /* Release this device's lock */
376         KeSetEvent(&DeviceExtension->ChildListLock, IO_NO_INCREMENT, FALSE);
377         KeLeaveCriticalRegion();
378 
379         /* If we found it, break out */
380         if (FoundExtension) break;
381 
382         /* Move to the next device */
383         DeviceExtension = (PPCI_FDO_EXTENSION)DeviceExtension->List.Next;
384     }
385 
386     /* Check if we had acquired a lock previously */
387     if (Lock)
388     {
389         /* Release it */
390         KeSetEvent(Lock, IO_NO_INCREMENT, FALSE);
391         KeLeaveCriticalRegion();
392     }
393 
394     /* Return which extension was found, if any */
395     return DeviceExtension;
396 }
397 
398 VOID
399 NTAPI
PciInsertEntryAtTail(IN PSINGLE_LIST_ENTRY ListHead,IN PPCI_FDO_EXTENSION DeviceExtension,IN PKEVENT Lock)400 PciInsertEntryAtTail(IN PSINGLE_LIST_ENTRY ListHead,
401                      IN PPCI_FDO_EXTENSION DeviceExtension,
402                      IN PKEVENT Lock)
403 {
404     PSINGLE_LIST_ENTRY NextEntry;
405     PAGED_CODE();
406 
407     /* Check if a lock was specified */
408     if (Lock)
409     {
410         /* Wait for the lock to be released */
411         KeEnterCriticalRegion();
412         KeWaitForSingleObject(Lock, Executive, KernelMode, FALSE, NULL);
413     }
414 
415     /* Loop the list until we get to the end, then insert this entry there */
416     for (NextEntry = ListHead; NextEntry->Next; NextEntry = NextEntry->Next);
417     NextEntry->Next = &DeviceExtension->List;
418 
419     /* Check if we had acquired a lock previously */
420     if (Lock)
421     {
422         /* Release it */
423         KeSetEvent(Lock, IO_NO_INCREMENT, FALSE);
424         KeLeaveCriticalRegion();
425     }
426 }
427 
428 VOID
429 NTAPI
PciInsertEntryAtHead(IN PSINGLE_LIST_ENTRY ListHead,IN PSINGLE_LIST_ENTRY Entry,IN PKEVENT Lock)430 PciInsertEntryAtHead(IN PSINGLE_LIST_ENTRY ListHead,
431                      IN PSINGLE_LIST_ENTRY Entry,
432                      IN PKEVENT Lock)
433 {
434     PAGED_CODE();
435 
436     /* Check if a lock was specified */
437     if (Lock)
438     {
439         /* Wait for the lock to be released */
440         KeEnterCriticalRegion();
441         KeWaitForSingleObject(Lock, Executive, KernelMode, FALSE, NULL);
442     }
443 
444     /* Make the entry point to the current head and make the head point to it */
445     Entry->Next = ListHead->Next;
446     ListHead->Next = Entry;
447 
448     /* Check if we had acquired a lock previously */
449     if (Lock)
450     {
451         /* Release it */
452         KeSetEvent(Lock, IO_NO_INCREMENT, FALSE);
453         KeLeaveCriticalRegion();
454     }
455 }
456 
457 VOID
458 NTAPI
PcipLinkSecondaryExtension(IN PSINGLE_LIST_ENTRY List,IN PVOID Lock,IN PPCI_SECONDARY_EXTENSION SecondaryExtension,IN PCI_SIGNATURE ExtensionType,IN PVOID Destructor)459 PcipLinkSecondaryExtension(IN PSINGLE_LIST_ENTRY List,
460                            IN PVOID Lock,
461                            IN PPCI_SECONDARY_EXTENSION SecondaryExtension,
462                            IN PCI_SIGNATURE ExtensionType,
463                            IN PVOID Destructor)
464 {
465     PAGED_CODE();
466 
467     /* Setup the extension data, and insert it into the primary's list */
468     SecondaryExtension->ExtensionType = ExtensionType;
469     SecondaryExtension->Destructor = Destructor;
470     PciInsertEntryAtHead(List, &SecondaryExtension->List, Lock);
471 }
472 
473 NTSTATUS
474 NTAPI
PciGetDeviceProperty(IN PDEVICE_OBJECT DeviceObject,IN DEVICE_REGISTRY_PROPERTY DeviceProperty,OUT PVOID * OutputBuffer)475 PciGetDeviceProperty(IN PDEVICE_OBJECT DeviceObject,
476                      IN DEVICE_REGISTRY_PROPERTY DeviceProperty,
477                      OUT PVOID *OutputBuffer)
478 {
479     NTSTATUS Status;
480     ULONG BufferLength, ResultLength;
481     PVOID Buffer;
482     do
483     {
484         /* Query the requested property size */
485         Status = IoGetDeviceProperty(DeviceObject,
486                                      DeviceProperty,
487                                      0,
488                                      NULL,
489                                      &BufferLength);
490         if (Status != STATUS_BUFFER_TOO_SMALL)
491         {
492             /* Call should've failed with buffer too small! */
493             DPRINT1("PCI - Unexpected status from GetDeviceProperty, saw %08X, expected %08X.\n",
494                     Status,
495                     STATUS_BUFFER_TOO_SMALL);
496             *OutputBuffer = NULL;
497             ASSERTMSG("PCI Successfully did the impossible!\n", FALSE);
498             break;
499         }
500 
501         /* Allocate the required buffer */
502         Buffer = ExAllocatePoolWithTag(PagedPool, BufferLength, 'BicP');
503         if (!Buffer)
504         {
505             /* No memory, fail the request */
506             DPRINT1("PCI - Failed to allocate DeviceProperty buffer (%u bytes).\n", BufferLength);
507             Status = STATUS_INSUFFICIENT_RESOURCES;
508             break;
509         }
510 
511         /* Do the actual property query call */
512         Status = IoGetDeviceProperty(DeviceObject,
513                                      DeviceProperty,
514                                      BufferLength,
515                                      Buffer,
516                                      &ResultLength);
517         if (!NT_SUCCESS(Status)) break;
518 
519         /* Return the buffer to the caller */
520         ASSERT(BufferLength == ResultLength);
521         *OutputBuffer = Buffer;
522         return STATUS_SUCCESS;
523     } while (FALSE);
524 
525     /* Failure path */
526     return STATUS_UNSUCCESSFUL;
527 }
528 
529 NTSTATUS
530 NTAPI
PciSendIoctl(IN PDEVICE_OBJECT DeviceObject,IN ULONG IoControlCode,IN PVOID InputBuffer,IN ULONG InputBufferLength,IN PVOID OutputBuffer,IN ULONG OutputBufferLength)531 PciSendIoctl(IN PDEVICE_OBJECT DeviceObject,
532              IN ULONG IoControlCode,
533              IN PVOID InputBuffer,
534              IN ULONG InputBufferLength,
535              IN PVOID OutputBuffer,
536              IN ULONG OutputBufferLength)
537 {
538     PIRP Irp;
539     NTSTATUS Status;
540     KEVENT Event;
541     IO_STATUS_BLOCK IoStatusBlock;
542     PDEVICE_OBJECT AttachedDevice;
543     PAGED_CODE();
544 
545     /* Initialize the pending IRP event */
546     KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
547 
548     /* Get a reference to the root PDO (ACPI) */
549     AttachedDevice = IoGetAttachedDeviceReference(DeviceObject);
550     if (!AttachedDevice) return STATUS_INVALID_PARAMETER;
551 
552     /* Build the requested IOCTL IRP */
553     Irp = IoBuildDeviceIoControlRequest(IoControlCode,
554                                         AttachedDevice,
555                                         InputBuffer,
556                                         InputBufferLength,
557                                         OutputBuffer,
558                                         OutputBufferLength,
559                                         0,
560                                         &Event,
561                                         &IoStatusBlock);
562     if (!Irp) return STATUS_INSUFFICIENT_RESOURCES;
563 
564     /* Send the IOCTL to the driver */
565     Status = IoCallDriver(AttachedDevice, Irp);
566     if (Status == STATUS_PENDING)
567     {
568         /* Wait for a response */
569         KeWaitForSingleObject(&Event,
570                               Executive,
571                               KernelMode,
572                               FALSE,
573                               NULL);
574         Status = Irp->IoStatus.Status;
575     }
576 
577     /* Take away the reference we took and return the result to the caller */
578     ObDereferenceObject(AttachedDevice);
579     return Status;
580 }
581 
582 PPCI_SECONDARY_EXTENSION
583 NTAPI
PciFindNextSecondaryExtension(IN PSINGLE_LIST_ENTRY ListHead,IN PCI_SIGNATURE ExtensionType)584 PciFindNextSecondaryExtension(IN PSINGLE_LIST_ENTRY ListHead,
585                               IN PCI_SIGNATURE ExtensionType)
586 {
587     PSINGLE_LIST_ENTRY NextEntry;
588     PPCI_SECONDARY_EXTENSION Extension;
589 
590     /* Scan the list */
591     for (NextEntry = ListHead; NextEntry; NextEntry = NextEntry->Next)
592     {
593         /* Grab each extension and check if it's the one requested */
594         Extension = CONTAINING_RECORD(NextEntry, PCI_SECONDARY_EXTENSION, List);
595         if (Extension->ExtensionType == ExtensionType) return Extension;
596     }
597 
598     /* Nothing was found */
599     return NULL;
600 }
601 
602 ULONGLONG
603 NTAPI
PciGetHackFlags(IN USHORT VendorId,IN USHORT DeviceId,IN USHORT SubVendorId,IN USHORT SubSystemId,IN UCHAR RevisionId)604 PciGetHackFlags(IN USHORT VendorId,
605                 IN USHORT DeviceId,
606                 IN USHORT SubVendorId,
607                 IN USHORT SubSystemId,
608                 IN UCHAR RevisionId)
609 {
610     PPCI_HACK_ENTRY HackEntry;
611     ULONGLONG HackFlags;
612     ULONG LastWeight, MatchWeight;
613     ULONG EntryFlags;
614 
615     /* ReactOS SetupLDR Hack */
616     if (!PciHackTable) return 0;
617 
618     /* Initialize the variables before looping */
619     LastWeight = 0;
620     HackFlags = 0;
621     ASSERT(PciHackTable);
622 
623     /* Scan the hack table */
624     for (HackEntry = PciHackTable;
625          HackEntry->VendorID != PCI_INVALID_VENDORID;
626          ++HackEntry)
627     {
628         /* Check if there's an entry for this device */
629         if ((HackEntry->DeviceID == DeviceId) &&
630             (HackEntry->VendorID == VendorId))
631         {
632             /* This is a basic match */
633             EntryFlags = HackEntry->Flags;
634             MatchWeight = 1;
635 
636             /* Does the entry have revision information? */
637             if (EntryFlags & PCI_HACK_HAS_REVISION_INFO)
638             {
639                 /* Check if the revision matches, if so, this is a better match */
640                 if (HackEntry->RevisionID != RevisionId) continue;
641                 MatchWeight = 3;
642             }
643 
644             /* Does the netry have subsystem information? */
645             if (EntryFlags & PCI_HACK_HAS_SUBSYSTEM_INFO)
646             {
647                 /* Check if it matches, if so, this is the best possible match */
648                 if ((HackEntry->SubVendorID != SubVendorId) ||
649                     (HackEntry->SubSystemID != SubSystemId))
650                 {
651                     continue;
652                 }
653                 MatchWeight += 4;
654             }
655 
656             /* Is this the best match yet? */
657             if (MatchWeight > LastWeight)
658             {
659                 /* This is the best match for now, use this as the hack flags */
660                 HackFlags = HackEntry->HackFlags;
661                 LastWeight = MatchWeight;
662             }
663         }
664     }
665 
666     /* Return the best match */
667     return HackFlags;
668 }
669 
670 BOOLEAN
671 NTAPI
PciIsCriticalDeviceClass(IN UCHAR BaseClass,IN UCHAR SubClass)672 PciIsCriticalDeviceClass(IN UCHAR BaseClass,
673                          IN UCHAR SubClass)
674 {
675     /* Check for system or bridge devices */
676     if (BaseClass == PCI_CLASS_BASE_SYSTEM_DEV)
677     {
678         /* Interrupt controllers are critical */
679         return SubClass == PCI_SUBCLASS_SYS_INTERRUPT_CTLR;
680     }
681     else if (BaseClass == PCI_CLASS_BRIDGE_DEV)
682     {
683         /* ISA Bridges are critical */
684         return SubClass == PCI_SUBCLASS_BR_ISA;
685     }
686     else
687     {
688         /* All display controllers are critical */
689         return BaseClass == PCI_CLASS_DISPLAY_CTLR;
690     }
691 }
692 
693 PPCI_PDO_EXTENSION
694 NTAPI
PciFindPdoByFunction(IN PPCI_FDO_EXTENSION DeviceExtension,IN ULONG FunctionNumber,IN PPCI_COMMON_HEADER PciData)695 PciFindPdoByFunction(IN PPCI_FDO_EXTENSION DeviceExtension,
696                      IN ULONG FunctionNumber,
697                      IN PPCI_COMMON_HEADER PciData)
698 {
699     KIRQL Irql;
700     PPCI_PDO_EXTENSION PdoExtension;
701 
702     /* Get the current IRQL when this call was made */
703     Irql = KeGetCurrentIrql();
704 
705     /* Is this a low-IRQL call? */
706     if (Irql < DISPATCH_LEVEL)
707     {
708         /* Acquire this device's lock */
709         KeEnterCriticalRegion();
710         KeWaitForSingleObject(&DeviceExtension->ChildListLock,
711                               Executive,
712                               KernelMode,
713                               FALSE,
714                               NULL);
715     }
716 
717     /* Loop every child PDO */
718     for (PdoExtension = DeviceExtension->ChildPdoList;
719          PdoExtension;
720          PdoExtension = PdoExtension->Next)
721     {
722         /* Find only enumerated PDOs */
723         if (!PdoExtension->ReportedMissing)
724         {
725             /* Check if the function number and header data matches */
726             if ((FunctionNumber == PdoExtension->Slot.u.AsULONG) &&
727                 (PdoExtension->VendorId == PciData->VendorID) &&
728                 (PdoExtension->DeviceId == PciData->DeviceID) &&
729                 (PdoExtension->RevisionId == PciData->RevisionID))
730             {
731                 /* This is considered to be the same PDO */
732                 break;
733             }
734         }
735     }
736 
737     /* Was this a low-IRQL call? */
738     if (Irql < DISPATCH_LEVEL)
739     {
740         /* Release this device's lock */
741         KeSetEvent(&DeviceExtension->ChildListLock, IO_NO_INCREMENT, FALSE);
742         KeLeaveCriticalRegion();
743     }
744 
745     /* If the search found something, this is non-NULL, otherwise it's NULL */
746     return PdoExtension;
747 }
748 
749 BOOLEAN
750 NTAPI
PciIsDeviceOnDebugPath(IN PPCI_PDO_EXTENSION DeviceExtension)751 PciIsDeviceOnDebugPath(IN PPCI_PDO_EXTENSION DeviceExtension)
752 {
753     PAGED_CODE();
754 
755     UNREFERENCED_PARAMETER(DeviceExtension);
756 
757     /* Check for too many, or no, debug ports */
758     ASSERT(PciDebugPortsCount <= MAX_DEBUGGING_DEVICES_SUPPORTED);
759     if (!PciDebugPortsCount) return FALSE;
760 
761     /* eVb has not been able to test such devices yet */
762     UNIMPLEMENTED_DBGBREAK();
763     return FALSE;
764 }
765 
766 NTSTATUS
767 NTAPI
PciGetBiosConfig(IN PPCI_PDO_EXTENSION DeviceExtension,OUT PPCI_COMMON_HEADER PciData)768 PciGetBiosConfig(IN PPCI_PDO_EXTENSION DeviceExtension,
769                  OUT PPCI_COMMON_HEADER PciData)
770 {
771     HANDLE KeyHandle, SubKeyHandle;
772     OBJECT_ATTRIBUTES ObjectAttributes;
773     UNICODE_STRING KeyName, KeyValue;
774     WCHAR Buffer[32];
775     WCHAR DataBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + PCI_COMMON_HDR_LENGTH];
776     PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PVOID)DataBuffer;
777     NTSTATUS Status;
778     ULONG ResultLength;
779     PAGED_CODE();
780 
781     /* Open the PCI key */
782     Status = IoOpenDeviceRegistryKey(DeviceExtension->ParentFdoExtension->
783                                      PhysicalDeviceObject,
784                                      TRUE,
785                                      KEY_ALL_ACCESS,
786                                      &KeyHandle);
787     if (!NT_SUCCESS(Status)) return Status;
788 
789     /* Create a volatile BIOS configuration key */
790     RtlInitUnicodeString(&KeyName, L"BiosConfig");
791     InitializeObjectAttributes(&ObjectAttributes,
792                                &KeyName,
793                                OBJ_KERNEL_HANDLE,
794                                KeyHandle,
795                                NULL);
796     Status = ZwCreateKey(&SubKeyHandle,
797                          KEY_READ,
798                          &ObjectAttributes,
799                          0,
800                          NULL,
801                          REG_OPTION_VOLATILE,
802                          NULL);
803     ZwClose(KeyHandle);
804     if (!NT_SUCCESS(Status)) return Status;
805 
806     /* Create the key value based on the device and function number */
807     swprintf(Buffer,
808              L"DEV_%02x&FUN_%02x",
809              DeviceExtension->Slot.u.bits.DeviceNumber,
810              DeviceExtension->Slot.u.bits.FunctionNumber);
811     RtlInitUnicodeString(&KeyValue, Buffer);
812 
813     /* Query the value information (PCI BIOS configuration header) */
814     Status = ZwQueryValueKey(SubKeyHandle,
815                              &KeyValue,
816                              KeyValuePartialInformation,
817                              PartialInfo,
818                              sizeof(DataBuffer),
819                              &ResultLength);
820     ZwClose(SubKeyHandle);
821     if (!NT_SUCCESS(Status)) return Status;
822 
823     /* If any information was returned, go ahead and copy its data */
824     ASSERT(PartialInfo->DataLength == PCI_COMMON_HDR_LENGTH);
825     RtlCopyMemory(PciData, PartialInfo->Data, PCI_COMMON_HDR_LENGTH);
826     return Status;
827 }
828 
829 NTSTATUS
830 NTAPI
PciSaveBiosConfig(IN PPCI_PDO_EXTENSION DeviceExtension,IN PPCI_COMMON_HEADER PciData)831 PciSaveBiosConfig(IN PPCI_PDO_EXTENSION DeviceExtension,
832                   IN PPCI_COMMON_HEADER PciData)
833 {
834     HANDLE KeyHandle, SubKeyHandle;
835     OBJECT_ATTRIBUTES ObjectAttributes;
836     UNICODE_STRING KeyName, KeyValue;
837     WCHAR Buffer[32];
838     NTSTATUS Status;
839     PAGED_CODE();
840 
841     /* Open the PCI key */
842     Status = IoOpenDeviceRegistryKey(DeviceExtension->ParentFdoExtension->
843                                      PhysicalDeviceObject,
844                                      TRUE,
845                                      KEY_READ | KEY_WRITE,
846                                      &KeyHandle);
847     if (!NT_SUCCESS(Status)) return Status;
848 
849     /* Create a volatile BIOS configuration key */
850     RtlInitUnicodeString(&KeyName, L"BiosConfig");
851     InitializeObjectAttributes(&ObjectAttributes,
852                                &KeyName,
853                                OBJ_KERNEL_HANDLE,
854                                KeyHandle,
855                                NULL);
856     Status = ZwCreateKey(&SubKeyHandle,
857                          KEY_READ | KEY_WRITE,
858                          &ObjectAttributes,
859                          0,
860                          NULL,
861                          REG_OPTION_VOLATILE,
862                          NULL);
863     ZwClose(KeyHandle);
864     if (!NT_SUCCESS(Status)) return Status;
865 
866     /* Create the key value based on the device and function number */
867     swprintf(Buffer,
868              L"DEV_%02x&FUN_%02x",
869              DeviceExtension->Slot.u.bits.DeviceNumber,
870              DeviceExtension->Slot.u.bits.FunctionNumber);
871     RtlInitUnicodeString(&KeyValue, Buffer);
872 
873     /* Set the value data (the PCI BIOS configuration header) */
874     Status = ZwSetValueKey(SubKeyHandle,
875                            &KeyValue,
876                            0,
877                            REG_BINARY,
878                            PciData,
879                            PCI_COMMON_HDR_LENGTH);
880     ZwClose(SubKeyHandle);
881     return Status;
882 }
883 
884 UCHAR
885 NTAPI
PciReadDeviceCapability(IN PPCI_PDO_EXTENSION DeviceExtension,IN UCHAR Offset,IN ULONG CapabilityId,OUT PPCI_CAPABILITIES_HEADER Buffer,IN ULONG Length)886 PciReadDeviceCapability(IN PPCI_PDO_EXTENSION DeviceExtension,
887                         IN UCHAR Offset,
888                         IN ULONG CapabilityId,
889                         OUT PPCI_CAPABILITIES_HEADER Buffer,
890                         IN ULONG Length)
891 {
892     ULONG CapabilityCount = 0;
893 
894     /* If the device has no capabilility list, fail */
895     if (!Offset) return 0;
896 
897     /* Validate a PDO with capabilities, a valid buffer, and a valid length */
898     ASSERT(DeviceExtension->ExtensionType == PciPdoExtensionType);
899     ASSERT(DeviceExtension->CapabilitiesPtr != 0);
900     ASSERT(Buffer);
901     ASSERT(Length >= sizeof(PCI_CAPABILITIES_HEADER));
902 
903     /* Loop all capabilities */
904     while (Offset)
905     {
906         /* Make sure the pointer is spec-aligned and spec-sized */
907         ASSERT((Offset >= PCI_COMMON_HDR_LENGTH) && ((Offset & 0x3) == 0));
908 
909         /* Read the capability header */
910         PciReadDeviceConfig(DeviceExtension,
911                             Buffer,
912                             Offset,
913                             sizeof(PCI_CAPABILITIES_HEADER));
914 
915         /* Check if this is the capability being looked up */
916         if ((Buffer->CapabilityID == CapabilityId) || !(CapabilityId))
917         {
918             /* Check if was at a valid offset and length */
919             if ((Offset) && (Length > sizeof(PCI_CAPABILITIES_HEADER)))
920             {
921                 /* Sanity check */
922                 ASSERT(Length <= (sizeof(PCI_COMMON_CONFIG) - Offset));
923 
924                 /* Now read the whole capability data into the buffer */
925                 PciReadDeviceConfig(DeviceExtension,
926                                     (PVOID)((ULONG_PTR)Buffer +
927                                     sizeof(PCI_CAPABILITIES_HEADER)),
928                                     Offset + sizeof(PCI_CAPABILITIES_HEADER),
929                                     Length - sizeof(PCI_CAPABILITIES_HEADER));
930             }
931 
932             /* Return the offset where the capability was found */
933             return Offset;
934         }
935 
936         /* Try the next capability instead */
937         CapabilityCount++;
938         Offset = Buffer->Next;
939 
940         /* There can't be more than 48 capabilities (256 bytes max) */
941         if (CapabilityCount > 48)
942         {
943             /* Fail, since this is basically a broken PCI device */
944             DPRINT1("PCI device %p capabilities list is broken.\n", DeviceExtension);
945             return 0;
946         }
947     }
948 
949     /* Capability wasn't found, fail */
950     return 0;
951 }
952 
953 BOOLEAN
954 NTAPI
PciCanDisableDecodes(IN PPCI_PDO_EXTENSION DeviceExtension,IN PPCI_COMMON_HEADER Config,IN ULONGLONG HackFlags,IN BOOLEAN ForPowerDown)955 PciCanDisableDecodes(IN PPCI_PDO_EXTENSION DeviceExtension,
956                      IN PPCI_COMMON_HEADER Config,
957                      IN ULONGLONG HackFlags,
958                      IN BOOLEAN ForPowerDown)
959 {
960     UCHAR BaseClass, SubClass;
961     BOOLEAN IsVga;
962 
963     /* Is there a device extension or should the PCI header be used? */
964     if (DeviceExtension)
965     {
966         /* Never disable decodes for a debug PCI Device */
967         if (DeviceExtension->OnDebugPath) return FALSE;
968 
969         /* Hack flags will be obtained from the extension, not the caller */
970         ASSERT(HackFlags == 0);
971 
972         /* Get hacks and classification from the device extension */
973         HackFlags = DeviceExtension->HackFlags;
974         SubClass = DeviceExtension->SubClass;
975         BaseClass = DeviceExtension->BaseClass;
976     }
977     else
978     {
979         /* There must be a PCI header, go read the classification information */
980         ASSERT(Config != NULL);
981         BaseClass = Config->BaseClass;
982         SubClass = Config->SubClass;
983     }
984 
985     /* Check for hack flags that prevent disabling the decodes */
986     if (HackFlags & (PCI_HACK_PRESERVE_COMMAND |
987                      PCI_HACK_CB_SHARE_CMD_BITS |
988                      PCI_HACK_DONT_DISABLE_DECODES))
989     {
990         /* Don't do it */
991         return FALSE;
992     }
993 
994     /* Is this a VGA adapter? */
995     if ((BaseClass == PCI_CLASS_DISPLAY_CTLR) &&
996         (SubClass == PCI_SUBCLASS_VID_VGA_CTLR))
997     {
998         /* Never disable decodes if this is for power down */
999         return ForPowerDown;
1000     }
1001 
1002     /* Check for legacy devices */
1003     if (BaseClass == PCI_CLASS_PRE_20)
1004     {
1005         /* Never disable video adapter cards if this is for power down */
1006         if (SubClass == PCI_SUBCLASS_PRE_20_VGA) return ForPowerDown;
1007     }
1008     else if (BaseClass == PCI_CLASS_DISPLAY_CTLR)
1009     {
1010         /* Never disable VGA adapters if this is for power down */
1011         if (SubClass == PCI_SUBCLASS_VID_VGA_CTLR) return ForPowerDown;
1012     }
1013     else if (BaseClass == PCI_CLASS_BRIDGE_DEV)
1014     {
1015         /* Check for legacy bridges */
1016         if ((SubClass == PCI_SUBCLASS_BR_ISA) ||
1017             (SubClass == PCI_SUBCLASS_BR_EISA) ||
1018             (SubClass == PCI_SUBCLASS_BR_MCA) ||
1019             (SubClass == PCI_SUBCLASS_BR_HOST) ||
1020             (SubClass == PCI_SUBCLASS_BR_OTHER))
1021         {
1022             /* Never disable these */
1023             return FALSE;
1024         }
1025         else if ((SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI) ||
1026                  (SubClass == PCI_SUBCLASS_BR_CARDBUS))
1027         {
1028             /* This is a supported bridge, but does it have a VGA card? */
1029             if (!DeviceExtension)
1030             {
1031                 /* Read the bridge control flag from the PCI header */
1032                 IsVga = Config->u.type1.BridgeControl & PCI_ENABLE_BRIDGE_VGA;
1033             }
1034             else
1035             {
1036                 /* Read the cached flag in the device extension */
1037                 IsVga = DeviceExtension->Dependent.type1.VgaBitSet;
1038             }
1039 
1040             /* Never disable VGA adapters if this is for power down */
1041             if (IsVga) return ForPowerDown;
1042         }
1043     }
1044 
1045     /* Finally, never disable decodes if there's no power management */
1046     return !(HackFlags & PCI_HACK_NO_PM_CAPS);
1047 }
1048 
1049 PCI_DEVICE_TYPES
1050 NTAPI
PciClassifyDeviceType(IN PPCI_PDO_EXTENSION PdoExtension)1051 PciClassifyDeviceType(IN PPCI_PDO_EXTENSION PdoExtension)
1052 {
1053     ASSERT(PdoExtension->ExtensionType == PciPdoExtensionType);
1054 
1055     /* Differentiate between devices and bridges */
1056     if (PdoExtension->BaseClass != PCI_CLASS_BRIDGE_DEV) return PciTypeDevice;
1057 
1058     /* The PCI Bus driver handles only CardBus and PCI bridges (plus host) */
1059     if (PdoExtension->SubClass == PCI_SUBCLASS_BR_HOST) return PciTypeHostBridge;
1060     if (PdoExtension->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI) return PciTypePciBridge;
1061     if (PdoExtension->SubClass == PCI_SUBCLASS_BR_CARDBUS) return PciTypeCardbusBridge;
1062 
1063     /* Any other kind of bridge is treated like a device */
1064     return PciTypeDevice;
1065 }
1066 
1067 ULONG_PTR
1068 NTAPI
PciExecuteCriticalSystemRoutine(IN ULONG_PTR IpiContext)1069 PciExecuteCriticalSystemRoutine(IN ULONG_PTR IpiContext)
1070 {
1071     PPCI_IPI_CONTEXT Context = (PPCI_IPI_CONTEXT)IpiContext;
1072 
1073     /* Check if the IPI is already running */
1074     if (!InterlockedDecrement(&Context->RunCount))
1075     {
1076         /* Nope, this is the first instance, so execute the IPI function */
1077         Context->Function(Context->DeviceExtension, Context->Context);
1078 
1079         /* Notify anyone that was spinning that they can stop now */
1080         Context->Barrier = 0;
1081     }
1082     else
1083     {
1084         /* Spin until it has finished running */
1085         while (Context->Barrier);
1086     }
1087 
1088     /* Done */
1089     return 0;
1090 }
1091 
1092 BOOLEAN
1093 NTAPI
PciIsSlotPresentInParentMethod(IN PPCI_PDO_EXTENSION PdoExtension,IN ULONG Method)1094 PciIsSlotPresentInParentMethod(IN PPCI_PDO_EXTENSION PdoExtension,
1095                                IN ULONG Method)
1096 {
1097     BOOLEAN FoundSlot;
1098     PACPI_METHOD_ARGUMENT Argument;
1099     ACPI_EVAL_INPUT_BUFFER InputBuffer;
1100     PACPI_EVAL_OUTPUT_BUFFER OutputBuffer;
1101     ULONG i, Length;
1102     NTSTATUS Status;
1103     PAGED_CODE();
1104 
1105     /* Assume slot is not part of the parent method */
1106     FoundSlot = FALSE;
1107 
1108     /* Allocate a 2KB buffer for the method return parameters */
1109     Length = sizeof(ACPI_EVAL_OUTPUT_BUFFER) + 2048;
1110     OutputBuffer = ExAllocatePoolWithTag(PagedPool, Length, 'BicP');
1111     if (OutputBuffer)
1112     {
1113         /* Clear out the output buffer */
1114         RtlZeroMemory(OutputBuffer, Length);
1115 
1116         /* Initialize the input buffer with the method requested */
1117         InputBuffer.Signature = 0;
1118         *(PULONG)InputBuffer.MethodName = Method;
1119         InputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE;
1120 
1121         /* Send it to the ACPI driver */
1122         Status = PciSendIoctl(PdoExtension->ParentFdoExtension->PhysicalDeviceObject,
1123                               IOCTL_ACPI_EVAL_METHOD,
1124                               &InputBuffer,
1125                               sizeof(ACPI_EVAL_INPUT_BUFFER),
1126                               OutputBuffer,
1127                               Length);
1128         if (NT_SUCCESS(Status))
1129         {
1130             /* Scan all output arguments */
1131             for (i = 0; i < OutputBuffer->Count; i++)
1132             {
1133                 /* Make sure it's an integer */
1134                 Argument = &OutputBuffer->Argument[i];
1135                 if (Argument->Type != ACPI_METHOD_ARGUMENT_INTEGER) continue;
1136 
1137                 /* Check if the argument matches this PCI slot structure */
1138                 if (Argument->Argument == ((PdoExtension->Slot.u.bits.DeviceNumber) |
1139                                            ((PdoExtension->Slot.u.bits.FunctionNumber) << 16)))
1140                 {
1141                     /* This slot has been found, return it */
1142                     FoundSlot = TRUE;
1143                     break;
1144                 }
1145             }
1146         }
1147 
1148         /* Finished with the buffer, free it */
1149         ExFreePoolWithTag(OutputBuffer, 0);
1150     }
1151 
1152     /* Return if the slot was found */
1153     return FoundSlot;
1154 }
1155 
1156 ULONG
1157 NTAPI
PciGetLengthFromBar(IN ULONG Bar)1158 PciGetLengthFromBar(IN ULONG Bar)
1159 {
1160     ULONG Length;
1161 
1162     /* I/O addresses vs. memory addresses start differently due to alignment */
1163     Length = 1 << ((Bar & PCI_ADDRESS_IO_SPACE) ? 2 : 4);
1164 
1165     /* Keep going until a set bit */
1166     while (!(Length & Bar) && (Length)) Length <<= 1;
1167 
1168     /* Return the length (might be 0 on 64-bit because it's the low-word) */
1169     if ((Bar & PCI_ADDRESS_MEMORY_TYPE_MASK) != PCI_TYPE_64BIT) ASSERT(Length);
1170     return Length;
1171 }
1172 
1173 BOOLEAN
1174 NTAPI
PciCreateIoDescriptorFromBarLimit(PIO_RESOURCE_DESCRIPTOR ResourceDescriptor,IN PULONG BarArray,IN BOOLEAN Rom)1175 PciCreateIoDescriptorFromBarLimit(PIO_RESOURCE_DESCRIPTOR ResourceDescriptor,
1176                                   IN PULONG BarArray,
1177                                   IN BOOLEAN Rom)
1178 {
1179     ULONG CurrentBar, BarLength, BarMask;
1180     BOOLEAN Is64BitBar = FALSE;
1181 
1182     /* Check if the BAR is nor I/O nor memory */
1183     CurrentBar = BarArray[0];
1184     if (!(CurrentBar & ~PCI_ADDRESS_IO_SPACE))
1185     {
1186         /* Fail this descriptor */
1187         ResourceDescriptor->Type = CmResourceTypeNull;
1188         return FALSE;
1189     }
1190 
1191     /* Set default flag and clear high words */
1192     ResourceDescriptor->Flags = 0;
1193     ResourceDescriptor->u.Generic.MaximumAddress.HighPart = 0;
1194     ResourceDescriptor->u.Generic.MinimumAddress.LowPart = 0;
1195     ResourceDescriptor->u.Generic.MinimumAddress.HighPart = 0;
1196 
1197     /* Check for ROM Address */
1198     if (Rom)
1199     {
1200         /* Clean up the BAR to get just the address */
1201         CurrentBar &= PCI_ADDRESS_ROM_ADDRESS_MASK;
1202         if (!CurrentBar)
1203         {
1204             /* Invalid ar, fail this descriptor */
1205             ResourceDescriptor->Type = CmResourceTypeNull;
1206             return FALSE;
1207         }
1208 
1209         /* ROM Addresses are always read only */
1210         ResourceDescriptor->Flags = CM_RESOURCE_MEMORY_READ_ONLY;
1211     }
1212 
1213     /* Compute the length, assume it's the alignment for now */
1214     BarLength = PciGetLengthFromBar(CurrentBar);
1215     ResourceDescriptor->u.Generic.Length = BarLength;
1216     ResourceDescriptor->u.Generic.Alignment = BarLength;
1217 
1218     /* Check what kind of BAR this is */
1219     if (CurrentBar & PCI_ADDRESS_IO_SPACE)
1220     {
1221         /* Use correct mask to decode the address */
1222         BarMask = PCI_ADDRESS_IO_ADDRESS_MASK;
1223 
1224         /* Set this as an I/O Port descriptor */
1225         ResourceDescriptor->Type = CmResourceTypePort;
1226         ResourceDescriptor->Flags = CM_RESOURCE_PORT_IO;
1227     }
1228     else
1229     {
1230         /* Use correct mask to decode the address */
1231         BarMask = PCI_ADDRESS_MEMORY_ADDRESS_MASK;
1232 
1233         /* Set this as a memory descriptor */
1234         ResourceDescriptor->Type = CmResourceTypeMemory;
1235 
1236         /* Check if it's 64-bit or 20-bit decode */
1237         if ((CurrentBar & PCI_ADDRESS_MEMORY_TYPE_MASK) == PCI_TYPE_64BIT)
1238         {
1239             /* The next BAR has the high word, read it */
1240             ResourceDescriptor->u.Port.MaximumAddress.HighPart = BarArray[1];
1241             Is64BitBar = TRUE;
1242         }
1243         else if ((CurrentBar & PCI_ADDRESS_MEMORY_TYPE_MASK) == PCI_TYPE_20BIT)
1244         {
1245             /* Use the correct mask to decode the address */
1246             BarMask = ~0xFFF0000F;
1247         }
1248 
1249         /* Check if the BAR is listed as prefetchable memory */
1250         if (CurrentBar & PCI_ADDRESS_MEMORY_PREFETCHABLE)
1251         {
1252             /* Mark the descriptor in the same way */
1253             ResourceDescriptor->Flags |= CM_RESOURCE_MEMORY_PREFETCHABLE;
1254         }
1255     }
1256 
1257     /* Now write down the maximum address based on the base + length */
1258     ResourceDescriptor->u.Port.MaximumAddress.QuadPart = (CurrentBar & BarMask) +
1259                                                          BarLength - 1;
1260 
1261     /* Return if this is a 64-bit BAR, so the loop code knows to skip the next one */
1262     return Is64BitBar;
1263 }
1264 
1265 VOID
1266 NTAPI
PciDecodeEnable(IN PPCI_PDO_EXTENSION PdoExtension,IN BOOLEAN Enable,OUT PUSHORT Command)1267 PciDecodeEnable(IN PPCI_PDO_EXTENSION PdoExtension,
1268                 IN BOOLEAN Enable,
1269                 OUT PUSHORT Command)
1270 {
1271     USHORT CommandValue;
1272 
1273     /*
1274      * If decodes are being disabled, make sure it's allowed, and in both cases,
1275      * make sure that a hackflag isn't preventing touching the decodes at all.
1276      */
1277     if (((Enable) || (PciCanDisableDecodes(PdoExtension, 0, 0, 0))) &&
1278         !(PdoExtension->HackFlags & PCI_HACK_PRESERVE_COMMAND))
1279     {
1280         /* Did the caller already have a command word? */
1281         if (Command)
1282         {
1283             /* Use the caller's */
1284             CommandValue = *Command;
1285         }
1286         else
1287         {
1288             /* Otherwise, read the current command */
1289             PciReadDeviceConfig(PdoExtension,
1290                                 &Command,
1291                                 FIELD_OFFSET(PCI_COMMON_HEADER, Command),
1292                                 sizeof(USHORT));
1293         }
1294 
1295         /* Turn off decodes by default */
1296         CommandValue &= ~(PCI_ENABLE_IO_SPACE |
1297                           PCI_ENABLE_MEMORY_SPACE |
1298                           PCI_ENABLE_BUS_MASTER);
1299 
1300         /* If requested, enable the decodes that were enabled at init time */
1301         if (Enable) CommandValue |= PdoExtension->CommandEnables &
1302                                     (PCI_ENABLE_IO_SPACE |
1303                                      PCI_ENABLE_MEMORY_SPACE |
1304                                      PCI_ENABLE_BUS_MASTER);
1305 
1306         /* Update the command word */
1307         PciWriteDeviceConfig(PdoExtension,
1308                              &CommandValue,
1309                              FIELD_OFFSET(PCI_COMMON_HEADER, Command),
1310                              sizeof(USHORT));
1311     }
1312 }
1313 
1314 NTSTATUS
1315 NTAPI
PciQueryBusInformation(IN PPCI_PDO_EXTENSION PdoExtension,IN PPNP_BUS_INFORMATION * Buffer)1316 PciQueryBusInformation(IN PPCI_PDO_EXTENSION PdoExtension,
1317                        IN PPNP_BUS_INFORMATION* Buffer)
1318 {
1319     PPNP_BUS_INFORMATION BusInfo;
1320 
1321     UNREFERENCED_PARAMETER(Buffer);
1322 
1323     /* Allocate a structure for the bus information */
1324     BusInfo = ExAllocatePoolWithTag(PagedPool,
1325                                     sizeof(PNP_BUS_INFORMATION),
1326                                     'BicP');
1327     if (!BusInfo) return STATUS_INSUFFICIENT_RESOURCES;
1328 
1329     /* Write the correct GUID and bus type identifier, and fill the bus number */
1330     BusInfo->BusTypeGuid = GUID_BUS_TYPE_PCI;
1331     BusInfo->LegacyBusType = PCIBus;
1332     BusInfo->BusNumber = PdoExtension->ParentFdoExtension->BaseBus;
1333     return STATUS_SUCCESS;
1334 }
1335 
1336 NTSTATUS
1337 NTAPI
PciDetermineSlotNumber(IN PPCI_PDO_EXTENSION PdoExtension,OUT PULONG SlotNumber)1338 PciDetermineSlotNumber(IN PPCI_PDO_EXTENSION PdoExtension,
1339                        OUT PULONG SlotNumber)
1340 {
1341     PPCI_FDO_EXTENSION ParentExtension;
1342     ULONG ResultLength;
1343     NTSTATUS Status;
1344     PSLOT_INFO SlotInfo;
1345 
1346     /* Check if a $PIR from the BIOS is used (legacy IRQ routing) */
1347     ParentExtension = PdoExtension->ParentFdoExtension;
1348     DPRINT1("Slot lookup for %d.%u.%u\n",
1349             ParentExtension ? ParentExtension->BaseBus : -1,
1350             PdoExtension->Slot.u.bits.DeviceNumber,
1351             PdoExtension->Slot.u.bits.FunctionNumber);
1352     if ((PciIrqRoutingTable) && (ParentExtension))
1353     {
1354         /* Read every slot information entry */
1355         SlotInfo = &PciIrqRoutingTable->Slot[0];
1356         DPRINT1("PIR$ %p is %lx bytes, slot 0 is at: %p\n",
1357                 PciIrqRoutingTable, PciIrqRoutingTable->TableSize, SlotInfo);
1358         while (SlotInfo < (PSLOT_INFO)((ULONG_PTR)PciIrqRoutingTable +
1359                                        PciIrqRoutingTable->TableSize))
1360         {
1361             DPRINT1("Slot Info: %u.%u->#%u\n",
1362                     SlotInfo->BusNumber,
1363                     SlotInfo->DeviceNumber,
1364                     SlotInfo->SlotNumber);
1365 
1366             /* Check if this slot information matches the PDO being queried */
1367             if ((ParentExtension->BaseBus == SlotInfo->BusNumber) &&
1368                 (PdoExtension->Slot.u.bits.DeviceNumber == SlotInfo->DeviceNumber >> 3) &&
1369                 (SlotInfo->SlotNumber))
1370             {
1371                 /* We found it, return it and return success */
1372                 *SlotNumber = SlotInfo->SlotNumber;
1373                 return STATUS_SUCCESS;
1374             }
1375 
1376             /* Try the next slot */
1377             SlotInfo++;
1378         }
1379     }
1380 
1381     /* Otherwise, grab the parent FDO and check if it's the root */
1382     if (PCI_IS_ROOT_FDO(ParentExtension))
1383     {
1384         /* The root FDO doesn't have a slot number */
1385         Status = STATUS_UNSUCCESSFUL;
1386     }
1387     else
1388     {
1389         /* Otherwise, query the slot/UI address/number as a device property */
1390         Status = IoGetDeviceProperty(ParentExtension->PhysicalDeviceObject,
1391                                      DevicePropertyUINumber,
1392                                      sizeof(ULONG),
1393                                      SlotNumber,
1394                                      &ResultLength);
1395     }
1396 
1397     /* Return the status of this endeavour */
1398     return Status;
1399 }
1400 
1401 NTSTATUS
1402 NTAPI
PciGetDeviceCapabilities(IN PDEVICE_OBJECT DeviceObject,IN OUT PDEVICE_CAPABILITIES DeviceCapability)1403 PciGetDeviceCapabilities(IN PDEVICE_OBJECT DeviceObject,
1404                          IN OUT PDEVICE_CAPABILITIES DeviceCapability)
1405 {
1406     PIRP Irp;
1407     NTSTATUS Status;
1408     KEVENT Event;
1409     PDEVICE_OBJECT AttachedDevice;
1410     PIO_STACK_LOCATION IoStackLocation;
1411     IO_STATUS_BLOCK IoStatusBlock;
1412     PAGED_CODE();
1413 
1414     /* Zero out capabilities and set undefined values to start with */
1415     RtlZeroMemory(DeviceCapability, sizeof(DEVICE_CAPABILITIES));
1416     DeviceCapability->Size = sizeof(DEVICE_CAPABILITIES);
1417     DeviceCapability->Version = 1;
1418     DeviceCapability->Address = -1;
1419     DeviceCapability->UINumber = -1;
1420 
1421     /* Build the wait event for the IOCTL */
1422     KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
1423 
1424     /* Find the device the PDO is attached to */
1425     AttachedDevice = IoGetAttachedDeviceReference(DeviceObject);
1426 
1427     /* And build an IRP for it */
1428     Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
1429                                        AttachedDevice,
1430                                        NULL,
1431                                        0,
1432                                        NULL,
1433                                        &Event,
1434                                        &IoStatusBlock);
1435     if (!Irp)
1436     {
1437         /* The IRP failed, fail the request as well */
1438         ObDereferenceObject(AttachedDevice);
1439         return STATUS_INSUFFICIENT_RESOURCES;
1440     }
1441 
1442     /* Set default status */
1443     Irp->IoStatus.Information = 0;
1444     Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
1445 
1446     /* Get a stack location in this IRP */
1447     IoStackLocation = IoGetNextIrpStackLocation(Irp);
1448     ASSERT(IoStackLocation);
1449 
1450     /* Initialize it as a query capabilities IRP, with no completion routine */
1451     RtlZeroMemory(IoStackLocation, sizeof(IO_STACK_LOCATION));
1452     IoStackLocation->MajorFunction = IRP_MJ_PNP;
1453     IoStackLocation->MinorFunction = IRP_MN_QUERY_CAPABILITIES;
1454     IoStackLocation->Parameters.DeviceCapabilities.Capabilities = DeviceCapability;
1455     IoSetCompletionRoutine(Irp, NULL, NULL, FALSE, FALSE, FALSE);
1456 
1457     /* Send the IOCTL to the driver */
1458     Status = IoCallDriver(AttachedDevice, Irp);
1459     if (Status == STATUS_PENDING)
1460     {
1461         /* Wait for a response and update the actual status */
1462         KeWaitForSingleObject(&Event,
1463                               Executive,
1464                               KernelMode,
1465                               FALSE,
1466                               NULL);
1467         Status = Irp->IoStatus.Status;
1468     }
1469 
1470     /* Done, dereference the attached device and return the final result */
1471     ObDereferenceObject(AttachedDevice);
1472     return Status;
1473 }
1474 
1475 NTSTATUS
1476 NTAPI
PciQueryPowerCapabilities(IN PPCI_PDO_EXTENSION PdoExtension,IN PDEVICE_CAPABILITIES DeviceCapability)1477 PciQueryPowerCapabilities(IN PPCI_PDO_EXTENSION PdoExtension,
1478                           IN PDEVICE_CAPABILITIES DeviceCapability)
1479 {
1480     PDEVICE_OBJECT DeviceObject;
1481     NTSTATUS Status;
1482     DEVICE_CAPABILITIES AttachedCaps;
1483     DEVICE_POWER_STATE NewPowerState, DevicePowerState, DeviceWakeLevel, DeviceWakeState;
1484     SYSTEM_POWER_STATE SystemWakeState, DeepestWakeState, CurrentState;
1485 
1486     /* Nothing is known at first */
1487     DeviceWakeState = PowerDeviceUnspecified;
1488     SystemWakeState = DeepestWakeState = PowerSystemUnspecified;
1489 
1490     /* Get the PCI capabilities for the parent PDO */
1491     DeviceObject = PdoExtension->ParentFdoExtension->PhysicalDeviceObject;
1492     Status = PciGetDeviceCapabilities(DeviceObject, &AttachedCaps);
1493     ASSERT(NT_SUCCESS(Status));
1494     if (!NT_SUCCESS(Status)) return Status;
1495 
1496     /* Check if there's not an existing device state for S0 */
1497     if (!AttachedCaps.DeviceState[PowerSystemWorking])
1498     {
1499         /* Set D0<->S0 mapping */
1500         AttachedCaps.DeviceState[PowerSystemWorking] = PowerDeviceD0;
1501     }
1502 
1503     /* Check if there's not an existing device state for S3 */
1504     if (!AttachedCaps.DeviceState[PowerSystemShutdown])
1505     {
1506         /* Set D3<->S3 mapping */
1507         AttachedCaps.DeviceState[PowerSystemShutdown] = PowerDeviceD3;
1508     }
1509 
1510     /* Check for a PDO with broken, or no, power capabilities */
1511     if (PdoExtension->HackFlags & PCI_HACK_NO_PM_CAPS)
1512     {
1513         /* Unknown wake device states */
1514         DeviceCapability->DeviceWake = PowerDeviceUnspecified;
1515         DeviceCapability->SystemWake = PowerSystemUnspecified;
1516 
1517         /* No device state support */
1518         DeviceCapability->DeviceD1 = FALSE;
1519         DeviceCapability->DeviceD2 = FALSE;
1520 
1521         /* No waking from any low-power device state is supported */
1522         DeviceCapability->WakeFromD0 = FALSE;
1523         DeviceCapability->WakeFromD1 = FALSE;
1524         DeviceCapability->WakeFromD2 = FALSE;
1525         DeviceCapability->WakeFromD3 = FALSE;
1526 
1527         /* For the rest, copy whatever the parent PDO had */
1528         RtlCopyMemory(DeviceCapability->DeviceState,
1529                       AttachedCaps.DeviceState,
1530                       sizeof(DeviceCapability->DeviceState));
1531         return STATUS_SUCCESS;
1532     }
1533 
1534     /* The PCI Device has power capabilities, so read which ones are supported */
1535     DeviceCapability->DeviceD1 = PdoExtension->PowerCapabilities.Support.D1;
1536     DeviceCapability->DeviceD2 = PdoExtension->PowerCapabilities.Support.D2;
1537     DeviceCapability->WakeFromD0 = PdoExtension->PowerCapabilities.Support.PMED0;
1538     DeviceCapability->WakeFromD1 = PdoExtension->PowerCapabilities.Support.PMED1;
1539     DeviceCapability->WakeFromD2 = PdoExtension->PowerCapabilities.Support.PMED2;
1540 
1541     /* Can the attached device wake from D3? */
1542     if (AttachedCaps.DeviceWake != PowerDeviceD3)
1543     {
1544         /* It can't, so check if this PDO supports hot D3 wake */
1545         DeviceCapability->WakeFromD3 = PdoExtension->PowerCapabilities.Support.PMED3Hot;
1546     }
1547     else
1548     {
1549         /* It can, is this the root bus? */
1550         if (PCI_IS_ROOT_FDO(PdoExtension->ParentFdoExtension))
1551         {
1552             /* This is the root bus, so just check if it supports hot D3 wake */
1553             DeviceCapability->WakeFromD3 = PdoExtension->PowerCapabilities.Support.PMED3Hot;
1554         }
1555         else
1556         {
1557             /* Take the minimums? -- need to check with briang at work */
1558             UNIMPLEMENTED;
1559         }
1560     }
1561 
1562     /* Now loop each system power state to determine its device state mapping */
1563     for (CurrentState = PowerSystemWorking;
1564          CurrentState < PowerSystemMaximum;
1565          CurrentState++)
1566     {
1567         /* Read the current mapping from the attached device */
1568         DevicePowerState = AttachedCaps.DeviceState[CurrentState];
1569         NewPowerState = DevicePowerState;
1570 
1571         /* The attachee supports D1, but this PDO does not */
1572         if ((NewPowerState == PowerDeviceD1) &&
1573             !(PdoExtension->PowerCapabilities.Support.D1))
1574         {
1575             /* Fall back to D2 */
1576             NewPowerState = PowerDeviceD2;
1577         }
1578 
1579         /* The attachee supports D2, but this PDO does not */
1580         if ((NewPowerState == PowerDeviceD2) &&
1581             !(PdoExtension->PowerCapabilities.Support.D2))
1582         {
1583             /* Fall back to D3 */
1584             NewPowerState = PowerDeviceD3;
1585         }
1586 
1587         /* Set the mapping based on the best state supported */
1588         DeviceCapability->DeviceState[CurrentState] = NewPowerState;
1589 
1590         /* Check if sleep states are being processed, and a mapping was found */
1591         if ((CurrentState < PowerSystemHibernate) &&
1592             (NewPowerState != PowerDeviceUnspecified))
1593         {
1594             /* Save this state as being the deepest one found until now */
1595             DeepestWakeState = CurrentState;
1596         }
1597 
1598         /*
1599          * Finally, check if the computed sleep state is within the states that
1600          * this device can wake the system from, and if it's higher or equal to
1601          * the sleep state mapping that came from the attachee, assuming that it
1602          * had a valid mapping to begin with.
1603          *
1604          * It this is the case, then make sure that the computed sleep state is
1605          * matched by the device's ability to actually wake from that state.
1606          *
1607          * For devices that support D3, the PCI device only needs Hot D3 as long
1608          * as the attachee's state is less than D3. Otherwise, if the attachee
1609          * might also be at D3, this would require a Cold D3 wake, so check that
1610          * the device actually support this.
1611          */
1612         if ((CurrentState < AttachedCaps.SystemWake) &&
1613             (NewPowerState >= DevicePowerState) &&
1614             (DevicePowerState != PowerDeviceUnspecified) &&
1615             (((NewPowerState == PowerDeviceD0) && (DeviceCapability->WakeFromD0)) ||
1616              ((NewPowerState == PowerDeviceD1) && (DeviceCapability->WakeFromD1)) ||
1617              ((NewPowerState == PowerDeviceD2) && (DeviceCapability->WakeFromD2)) ||
1618              ((NewPowerState == PowerDeviceD3) &&
1619               (PdoExtension->PowerCapabilities.Support.PMED3Hot) &&
1620               ((DevicePowerState < PowerDeviceD3) ||
1621                (PdoExtension->PowerCapabilities.Support.PMED3Cold)))))
1622         {
1623             /* The mapping is valid, so this will be the lowest wake state */
1624             SystemWakeState = CurrentState;
1625             DeviceWakeState = NewPowerState;
1626         }
1627     }
1628 
1629     /* Read the current wake level */
1630     DeviceWakeLevel = PdoExtension->PowerState.DeviceWakeLevel;
1631 
1632     /* Check if the attachee's wake levels are valid, and the PDO's is higher */
1633     if ((AttachedCaps.SystemWake != PowerSystemUnspecified) &&
1634         (AttachedCaps.DeviceWake != PowerDeviceUnspecified) &&
1635         (DeviceWakeLevel != PowerDeviceUnspecified) &&
1636         (DeviceWakeLevel >= AttachedCaps.DeviceWake))
1637     {
1638         /* Inherit the system wake from the attachee, and this PDO's wake level */
1639         DeviceCapability->SystemWake = AttachedCaps.SystemWake;
1640         DeviceCapability->DeviceWake = DeviceWakeLevel;
1641 
1642         /* Now check if the wake level is D0, but the PDO doesn't support it */
1643         if ((DeviceCapability->DeviceWake == PowerDeviceD0) &&
1644             !(DeviceCapability->WakeFromD0))
1645         {
1646             /* Bump to D1 */
1647             DeviceCapability->DeviceWake = PowerDeviceD1;
1648         }
1649 
1650         /* Now check if the wake level is D1, but the PDO doesn't support it */
1651         if ((DeviceCapability->DeviceWake == PowerDeviceD1) &&
1652             !(DeviceCapability->WakeFromD1))
1653         {
1654             /* Bump to D2 */
1655             DeviceCapability->DeviceWake = PowerDeviceD2;
1656         }
1657 
1658         /* Now check if the wake level is D2, but the PDO doesn't support it */
1659         if ((DeviceCapability->DeviceWake == PowerDeviceD2) &&
1660             !(DeviceCapability->WakeFromD2))
1661         {
1662             /* Bump it to D3 */
1663             DeviceCapability->DeviceWake = PowerDeviceD3;
1664         }
1665 
1666         /* Now check if the wake level is D3, but the PDO doesn't support it */
1667         if ((DeviceCapability->DeviceWake == PowerDeviceD3) &&
1668             !(DeviceCapability->WakeFromD3))
1669         {
1670             /* Then no valid wake state exists */
1671             DeviceCapability->DeviceWake = PowerDeviceUnspecified;
1672             DeviceCapability->SystemWake = PowerSystemUnspecified;
1673         }
1674 
1675         /* Check if no valid wake state was found */
1676         if ((DeviceCapability->DeviceWake == PowerDeviceUnspecified) ||
1677             (DeviceCapability->SystemWake == PowerSystemUnspecified))
1678         {
1679             /* Check if one was computed earlier */
1680             if ((SystemWakeState != PowerSystemUnspecified) &&
1681                 (DeviceWakeState != PowerDeviceUnspecified))
1682             {
1683                 /* Use the wake state that had been computed earlier */
1684                 DeviceCapability->DeviceWake = DeviceWakeState;
1685                 DeviceCapability->SystemWake = SystemWakeState;
1686 
1687                 /* If that state was D3, then the device supports Hot/Cold D3 */
1688                 if (DeviceWakeState == PowerDeviceD3) DeviceCapability->WakeFromD3 = TRUE;
1689             }
1690         }
1691 
1692         /*
1693          * Finally, check for off states (lower than S3, such as hibernate) and
1694          * make sure that the device both supports waking from D3 as well as
1695          * supports a Cold wake
1696          */
1697         if ((DeviceCapability->SystemWake > PowerSystemSleeping3) &&
1698             ((DeviceCapability->DeviceWake != PowerDeviceD3) ||
1699              !(PdoExtension->PowerCapabilities.Support.PMED3Cold)))
1700         {
1701             /* It doesn't, so pick the computed lowest wake state from earlier */
1702             DeviceCapability->SystemWake = DeepestWakeState;
1703         }
1704 
1705         /* Set the PCI Specification mandated maximum latencies for transitions */
1706         DeviceCapability->D1Latency = 0;
1707         DeviceCapability->D2Latency = 2;
1708         DeviceCapability->D3Latency = 100;
1709 
1710         /* Sanity check */
1711         ASSERT(DeviceCapability->DeviceState[PowerSystemWorking] == PowerDeviceD0);
1712     }
1713     else
1714     {
1715         /* No valid sleep states, no latencies to worry about */
1716         DeviceCapability->D1Latency = 0;
1717         DeviceCapability->D2Latency = 0;
1718         DeviceCapability->D3Latency = 0;
1719     }
1720 
1721     /* This function always succeeds, even without power management support */
1722     return STATUS_SUCCESS;
1723 }
1724 
1725 NTSTATUS
1726 NTAPI
PciQueryCapabilities(IN PPCI_PDO_EXTENSION PdoExtension,IN OUT PDEVICE_CAPABILITIES DeviceCapability)1727 PciQueryCapabilities(IN PPCI_PDO_EXTENSION PdoExtension,
1728                      IN OUT PDEVICE_CAPABILITIES DeviceCapability)
1729 {
1730     NTSTATUS Status;
1731 
1732     /* A PDO ID is never unique, and its address is its function and device */
1733     DeviceCapability->UniqueID = FALSE;
1734     DeviceCapability->Address = PdoExtension->Slot.u.bits.FunctionNumber |
1735                                 (PdoExtension->Slot.u.bits.DeviceNumber << 16);
1736 
1737     /* Check for host bridges */
1738     if ((PdoExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
1739         (PdoExtension->SubClass == PCI_SUBCLASS_BR_HOST))
1740     {
1741         /* Raw device opens to a host bridge are acceptable */
1742         DeviceCapability->RawDeviceOK = TRUE;
1743     }
1744     else
1745     {
1746         /* Otherwise, other PDOs cannot be directly opened */
1747         DeviceCapability->RawDeviceOK = FALSE;
1748     }
1749 
1750     /* PCI PDOs are pretty fixed things */
1751     DeviceCapability->LockSupported = FALSE;
1752     DeviceCapability->EjectSupported = FALSE;
1753     DeviceCapability->Removable = FALSE;
1754     DeviceCapability->DockDevice = FALSE;
1755 
1756     /* The slot number is stored as a device property, go query it */
1757     PciDetermineSlotNumber(PdoExtension, &DeviceCapability->UINumber);
1758 
1759     /* Finally, query and power capabilities and convert them for PnP usage */
1760     Status = PciQueryPowerCapabilities(PdoExtension, DeviceCapability);
1761 
1762     /* Dump the capabilities if it all worked, and return the status */
1763     if (NT_SUCCESS(Status)) PciDebugDumpQueryCapabilities(DeviceCapability);
1764     return Status;
1765 }
1766 
1767 /* EOF */
1768