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