Sync with trunk r58740.
[reactos.git] / drivers / bus / pcix / 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(FALSE, "PCI Successfully did the impossible!");
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 (%d 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 /* Check for too many, or no, debug ports */
755 ASSERT(PciDebugPortsCount <= MAX_DEBUGGING_DEVICES_SUPPORTED);
756 if (!PciDebugPortsCount) return FALSE;
757
758 /* eVb has not been able to test such devices yet */
759 UNIMPLEMENTED_DBGBREAK();
760 return FALSE;
761 }
762
763 NTSTATUS
764 NTAPI
765 PciGetBiosConfig(IN PPCI_PDO_EXTENSION DeviceExtension,
766 OUT PPCI_COMMON_HEADER PciData)
767 {
768 HANDLE KeyHandle, SubKeyHandle;
769 OBJECT_ATTRIBUTES ObjectAttributes;
770 UNICODE_STRING KeyName, KeyValue;
771 WCHAR Buffer[32];
772 WCHAR DataBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + PCI_COMMON_HDR_LENGTH];
773 PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PVOID)DataBuffer;
774 NTSTATUS Status;
775 ULONG ResultLength;
776 PAGED_CODE();
777
778 /* Open the PCI key */
779 Status = IoOpenDeviceRegistryKey(DeviceExtension->ParentFdoExtension->
780 PhysicalDeviceObject,
781 TRUE,
782 KEY_ALL_ACCESS,
783 &KeyHandle);
784 if (!NT_SUCCESS(Status)) return Status;
785
786 /* Create a volatile BIOS configuration key */
787 RtlInitUnicodeString(&KeyName, L"BiosConfig");
788 InitializeObjectAttributes(&ObjectAttributes,
789 &KeyName,
790 OBJ_KERNEL_HANDLE,
791 KeyHandle,
792 NULL);
793 Status = ZwCreateKey(&SubKeyHandle,
794 KEY_READ,
795 &ObjectAttributes,
796 0,
797 NULL,
798 REG_OPTION_VOLATILE,
799 NULL);
800 ZwClose(KeyHandle);
801 if (!NT_SUCCESS(Status)) return Status;
802
803 /* Create the key value based on the device and function number */
804 swprintf(Buffer,
805 L"DEV_%02x&FUN_%02x",
806 DeviceExtension->Slot.u.bits.DeviceNumber,
807 DeviceExtension->Slot.u.bits.FunctionNumber);
808 RtlInitUnicodeString(&KeyValue, Buffer);
809
810 /* Query the value information (PCI BIOS configuration header) */
811 Status = ZwQueryValueKey(SubKeyHandle,
812 &KeyValue,
813 KeyValuePartialInformation,
814 PartialInfo,
815 sizeof(DataBuffer),
816 &ResultLength);
817 ZwClose(SubKeyHandle);
818 if (!NT_SUCCESS(Status)) return Status;
819
820 /* If any information was returned, go ahead and copy its data */
821 ASSERT(PartialInfo->DataLength == PCI_COMMON_HDR_LENGTH);
822 RtlCopyMemory(PciData, PartialInfo->Data, PCI_COMMON_HDR_LENGTH);
823 return Status;
824 }
825
826 NTSTATUS
827 NTAPI
828 PciSaveBiosConfig(IN PPCI_PDO_EXTENSION DeviceExtension,
829 IN PPCI_COMMON_HEADER PciData)
830 {
831 HANDLE KeyHandle, SubKeyHandle;
832 OBJECT_ATTRIBUTES ObjectAttributes;
833 UNICODE_STRING KeyName, KeyValue;
834 WCHAR Buffer[32];
835 NTSTATUS Status;
836 PAGED_CODE();
837
838 /* Open the PCI key */
839 Status = IoOpenDeviceRegistryKey(DeviceExtension->ParentFdoExtension->
840 PhysicalDeviceObject,
841 TRUE,
842 KEY_READ | KEY_WRITE,
843 &KeyHandle);
844 if (!NT_SUCCESS(Status)) return Status;
845
846 /* Create a volatile BIOS configuration key */
847 RtlInitUnicodeString(&KeyName, L"BiosConfig");
848 InitializeObjectAttributes(&ObjectAttributes,
849 &KeyName,
850 OBJ_KERNEL_HANDLE,
851 KeyHandle,
852 NULL);
853 Status = ZwCreateKey(&SubKeyHandle,
854 KEY_READ | KEY_WRITE,
855 &ObjectAttributes,
856 0,
857 NULL,
858 REG_OPTION_VOLATILE,
859 NULL);
860 ZwClose(KeyHandle);
861 if (!NT_SUCCESS(Status)) return Status;
862
863 /* Create the key value based on the device and function number */
864 swprintf(Buffer,
865 L"DEV_%02x&FUN_%02x",
866 DeviceExtension->Slot.u.bits.DeviceNumber,
867 DeviceExtension->Slot.u.bits.FunctionNumber);
868 RtlInitUnicodeString(&KeyValue, Buffer);
869
870 /* Set the value data (the PCI BIOS configuration header) */
871 Status = ZwSetValueKey(SubKeyHandle,
872 &KeyValue,
873 0,
874 REG_BINARY,
875 PciData,
876 PCI_COMMON_HDR_LENGTH);
877 ZwClose(SubKeyHandle);
878 return Status;
879 }
880
881 UCHAR
882 NTAPI
883 PciReadDeviceCapability(IN PPCI_PDO_EXTENSION DeviceExtension,
884 IN UCHAR Offset,
885 IN ULONG CapabilityId,
886 OUT PPCI_CAPABILITIES_HEADER Buffer,
887 IN ULONG Length)
888 {
889 ULONG CapabilityCount = 0;
890
891 /* If the device has no capabilility list, fail */
892 if (!Offset) return 0;
893
894 /* Validate a PDO with capabilities, a valid buffer, and a valid length */
895 ASSERT(DeviceExtension->ExtensionType == PciPdoExtensionType);
896 ASSERT(DeviceExtension->CapabilitiesPtr != 0);
897 ASSERT(Buffer);
898 ASSERT(Length >= sizeof(PCI_CAPABILITIES_HEADER));
899
900 /* Loop all capabilities */
901 while (Offset)
902 {
903 /* Make sure the pointer is spec-aligned and spec-sized */
904 ASSERT((Offset >= PCI_COMMON_HDR_LENGTH) && ((Offset & 0x3) == 0));
905
906 /* Read the capability header */
907 PciReadDeviceConfig(DeviceExtension,
908 Buffer,
909 Offset,
910 sizeof(PCI_CAPABILITIES_HEADER));
911
912 /* Check if this is the capability being looked up */
913 if ((Buffer->CapabilityID == CapabilityId) || !(CapabilityId))
914 {
915 /* Check if was at a valid offset and length */
916 if ((Offset) && (Length > sizeof(PCI_CAPABILITIES_HEADER)))
917 {
918 /* Sanity check */
919 ASSERT(Length <= (sizeof(PCI_COMMON_CONFIG) - Offset));
920
921 /* Now read the whole capability data into the buffer */
922 PciReadDeviceConfig(DeviceExtension,
923 (PVOID)((ULONG_PTR)Buffer +
924 sizeof(PCI_CAPABILITIES_HEADER)),
925 Offset + sizeof(PCI_CAPABILITIES_HEADER),
926 Length - sizeof(PCI_CAPABILITIES_HEADER));
927 }
928
929 /* Return the offset where the capability was found */
930 return Offset;
931 }
932
933 /* Try the next capability instead */
934 CapabilityCount++;
935 Offset = Buffer->Next;
936
937 /* There can't be more than 48 capabilities (256 bytes max) */
938 if (CapabilityCount > 48)
939 {
940 /* Fail, since this is basically a broken PCI device */
941 DPRINT1("PCI device %p capabilities list is broken.\n", DeviceExtension);
942 return 0;
943 }
944 }
945
946 /* Capability wasn't found, fail */
947 return 0;
948 }
949
950 BOOLEAN
951 NTAPI
952 PciCanDisableDecodes(IN PPCI_PDO_EXTENSION DeviceExtension,
953 IN PPCI_COMMON_HEADER Config,
954 IN ULONGLONG HackFlags,
955 IN BOOLEAN ForPowerDown)
956 {
957 UCHAR BaseClass, SubClass;
958 BOOLEAN IsVga;
959
960 /* Is there a device extension or should the PCI header be used? */
961 if (DeviceExtension)
962 {
963 /* Never disable decodes for a debug PCI Device */
964 if (DeviceExtension->OnDebugPath) return FALSE;
965
966 /* Hack flags will be obtained from the extension, not the caller */
967 ASSERT(HackFlags == 0);
968
969 /* Get hacks and classification from the device extension */
970 HackFlags = DeviceExtension->HackFlags;
971 SubClass = DeviceExtension->SubClass;
972 BaseClass = DeviceExtension->BaseClass;
973 }
974 else
975 {
976 /* There must be a PCI header, go read the classification information */
977 ASSERT(Config != NULL);
978 BaseClass = Config->BaseClass;
979 SubClass = Config->SubClass;
980 }
981
982 /* Check for hack flags that prevent disabling the decodes */
983 if (HackFlags & (PCI_HACK_PRESERVE_COMMAND |
984 PCI_HACK_CB_SHARE_CMD_BITS |
985 PCI_HACK_DONT_DISABLE_DECODES))
986 {
987 /* Don't do it */
988 return FALSE;
989 }
990
991 /* Is this a VGA adapter? */
992 if ((BaseClass == PCI_CLASS_DISPLAY_CTLR) &&
993 (SubClass == PCI_SUBCLASS_VID_VGA_CTLR))
994 {
995 /* Never disable decodes if this is for power down */
996 return ForPowerDown;
997 }
998
999 /* Check for legacy devices */
1000 if (BaseClass == PCI_CLASS_PRE_20)
1001 {
1002 /* Never disable video adapter cards if this is for power down */
1003 if (SubClass == PCI_SUBCLASS_PRE_20_VGA) return ForPowerDown;
1004 }
1005 else if (BaseClass == PCI_CLASS_DISPLAY_CTLR)
1006 {
1007 /* Never disable VGA adapters if this is for power down */
1008 if (SubClass == PCI_SUBCLASS_VID_VGA_CTLR) return ForPowerDown;
1009 }
1010 else if (BaseClass == PCI_CLASS_BRIDGE_DEV)
1011 {
1012 /* Check for legacy bridges */
1013 if ((SubClass == PCI_SUBCLASS_BR_ISA) ||
1014 (SubClass == PCI_SUBCLASS_BR_EISA) ||
1015 (SubClass == PCI_SUBCLASS_BR_MCA) ||
1016 (SubClass == PCI_SUBCLASS_BR_HOST) ||
1017 (SubClass == PCI_SUBCLASS_BR_OTHER))
1018 {
1019 /* Never disable these */
1020 return FALSE;
1021 }
1022 else if ((SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI) ||
1023 (SubClass == PCI_SUBCLASS_BR_CARDBUS))
1024 {
1025 /* This is a supported bridge, but does it have a VGA card? */
1026 if (!DeviceExtension)
1027 {
1028 /* Read the bridge control flag from the PCI header */
1029 IsVga = Config->u.type1.BridgeControl & PCI_ENABLE_BRIDGE_VGA;
1030 }
1031 else
1032 {
1033 /* Read the cached flag in the device extension */
1034 IsVga = DeviceExtension->Dependent.type1.VgaBitSet;
1035 }
1036
1037 /* Never disable VGA adapters if this is for power down */
1038 if (IsVga) return ForPowerDown;
1039 }
1040 }
1041
1042 /* Finally, never disable decodes if there's no power management */
1043 return !(HackFlags & PCI_HACK_NO_PM_CAPS);
1044 }
1045
1046 PCI_DEVICE_TYPES
1047 NTAPI
1048 PciClassifyDeviceType(IN PPCI_PDO_EXTENSION PdoExtension)
1049 {
1050 ASSERT(PdoExtension->ExtensionType == PciPdoExtensionType);
1051
1052 /* Differenriate between devices and bridges */
1053 if (PdoExtension->BaseClass != PCI_CLASS_BRIDGE_DEV) return PciTypeDevice;
1054
1055 /* The PCI Bus driver handles only CardBus and PCI bridges (plus host) */
1056 if (PdoExtension->SubClass == PCI_SUBCLASS_BR_HOST) return PciTypeHostBridge;
1057 if (PdoExtension->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI) return PciTypePciBridge;
1058 if (PdoExtension->SubClass == PCI_SUBCLASS_BR_CARDBUS) return PciTypeCardbusBridge;
1059
1060 /* Any other kind of bridge is treated like a device */
1061 return PciTypeDevice;
1062 }
1063
1064 ULONG_PTR
1065 NTAPI
1066 PciExecuteCriticalSystemRoutine(IN ULONG_PTR IpiContext)
1067 {
1068 PPCI_IPI_CONTEXT Context = (PPCI_IPI_CONTEXT)IpiContext;
1069
1070 /* Check if the IPI is already running */
1071 if (!InterlockedDecrement(&Context->RunCount))
1072 {
1073 /* Nope, this is the first instance, so execute the IPI function */
1074 Context->Function(Context->DeviceExtension, Context->Context);
1075
1076 /* Notify anyone that was spinning that they can stop now */
1077 Context->Barrier = 0;
1078 }
1079 else
1080 {
1081 /* Spin until it has finished running */
1082 while (Context->Barrier);
1083 }
1084
1085 /* Done */
1086 return 0;
1087 }
1088
1089 BOOLEAN
1090 NTAPI
1091 PciIsSlotPresentInParentMethod(IN PPCI_PDO_EXTENSION PdoExtension,
1092 IN ULONG Method)
1093 {
1094 BOOLEAN FoundSlot;
1095 PACPI_METHOD_ARGUMENT Argument;
1096 ACPI_EVAL_INPUT_BUFFER InputBuffer;
1097 PACPI_EVAL_OUTPUT_BUFFER OutputBuffer;
1098 ULONG i, Length;
1099 NTSTATUS Status;
1100 PAGED_CODE();
1101
1102 /* Assume slot is not part of the parent method */
1103 FoundSlot = FALSE;
1104
1105 /* Allocate a 2KB buffer for the method return parameters */
1106 Length = sizeof(ACPI_EVAL_OUTPUT_BUFFER) + 2048;
1107 OutputBuffer = ExAllocatePoolWithTag(PagedPool, Length, 'BicP');
1108 if (OutputBuffer)
1109 {
1110 /* Clear out the output buffer */
1111 RtlZeroMemory(OutputBuffer, Length);
1112
1113 /* Initialize the input buffer with the method requested */
1114 InputBuffer.Signature = 0;
1115 *(PULONG)InputBuffer.MethodName = Method;
1116 InputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE;
1117
1118 /* Send it to the ACPI driver */
1119 Status = PciSendIoctl(PdoExtension->ParentFdoExtension->PhysicalDeviceObject,
1120 IOCTL_ACPI_EVAL_METHOD,
1121 &InputBuffer,
1122 sizeof(ACPI_EVAL_INPUT_BUFFER),
1123 OutputBuffer,
1124 Length);
1125 if (NT_SUCCESS(Status))
1126 {
1127 /* Scan all output arguments */
1128 for (i = 0; i < OutputBuffer->Count; i++)
1129 {
1130 /* Make sure it's an integer */
1131 Argument = &OutputBuffer->Argument[i];
1132 if (Argument->Type != ACPI_METHOD_ARGUMENT_INTEGER) continue;
1133
1134 /* Check if the argument matches this PCI slot structure */
1135 if (Argument->Argument == ((PdoExtension->Slot.u.bits.DeviceNumber) |
1136 ((PdoExtension->Slot.u.bits.FunctionNumber) << 16)))
1137 {
1138 /* This slot has been found, return it */
1139 FoundSlot = TRUE;
1140 break;
1141 }
1142 }
1143 }
1144
1145 /* Finished with the buffer, free it */
1146 ExFreePoolWithTag(OutputBuffer, 0);
1147 }
1148
1149 /* Return if the slot was found */
1150 return FoundSlot;
1151 }
1152
1153 ULONG
1154 NTAPI
1155 PciGetLengthFromBar(IN ULONG Bar)
1156 {
1157 ULONG Length;
1158
1159 /* I/O addresses vs. memory addresses start differently due to alignment */
1160 Length = 1 << ((Bar & PCI_ADDRESS_IO_SPACE) ? 2 : 4);
1161
1162 /* Keep going until a set bit */
1163 while (!(Length & Bar) && (Length)) Length <<= 1;
1164
1165 /* Return the length (might be 0 on 64-bit because it's the low-word) */
1166 if ((Bar & PCI_ADDRESS_MEMORY_TYPE_MASK) != PCI_TYPE_64BIT) ASSERT(Length);
1167 return Length;
1168 }
1169
1170 BOOLEAN
1171 NTAPI
1172 PciCreateIoDescriptorFromBarLimit(PIO_RESOURCE_DESCRIPTOR ResourceDescriptor,
1173 IN PULONG BarArray,
1174 IN BOOLEAN Rom)
1175 {
1176 ULONG CurrentBar, BarLength, BarMask;
1177 BOOLEAN Is64BitBar = FALSE;
1178
1179 /* Check if the BAR is nor I/O nor memory */
1180 CurrentBar = BarArray[0];
1181 if (!(CurrentBar & ~PCI_ADDRESS_IO_SPACE))
1182 {
1183 /* Fail this descriptor */
1184 ResourceDescriptor->Type = CmResourceTypeNull;
1185 return FALSE;
1186 }
1187
1188 /* Set default flag and clear high words */
1189 ResourceDescriptor->Flags = 0;
1190 ResourceDescriptor->u.Generic.MaximumAddress.HighPart = 0;
1191 ResourceDescriptor->u.Generic.MinimumAddress.LowPart = 0;
1192 ResourceDescriptor->u.Generic.MinimumAddress.HighPart = 0;
1193
1194 /* Check for ROM Address */
1195 if (Rom)
1196 {
1197 /* Clean up the BAR to get just the address */
1198 CurrentBar &= PCI_ADDRESS_ROM_ADDRESS_MASK;
1199 if (!CurrentBar)
1200 {
1201 /* Invalid ar, fail this descriptor */
1202 ResourceDescriptor->Type = CmResourceTypeNull;
1203 return FALSE;
1204 }
1205
1206 /* ROM Addresses are always read only */
1207 ResourceDescriptor->Flags = CM_RESOURCE_MEMORY_READ_ONLY;
1208 }
1209
1210 /* Compute the length, assume it's the alignment for now */
1211 BarLength = PciGetLengthFromBar(CurrentBar);
1212 ResourceDescriptor->u.Generic.Length = BarLength;
1213 ResourceDescriptor->u.Generic.Alignment = BarLength;
1214
1215 /* Check what kind of BAR this is */
1216 if (CurrentBar & PCI_ADDRESS_IO_SPACE)
1217 {
1218 /* Use correct mask to decode the address */
1219 BarMask = PCI_ADDRESS_IO_ADDRESS_MASK;
1220
1221 /* Set this as an I/O Port descriptor */
1222 ResourceDescriptor->Type = CmResourceTypePort;
1223 ResourceDescriptor->Flags = CM_RESOURCE_PORT_IO;
1224 }
1225 else
1226 {
1227 /* Use correct mask to decode the address */
1228 BarMask = PCI_ADDRESS_MEMORY_ADDRESS_MASK;
1229
1230 /* Set this as a memory descriptor */
1231 ResourceDescriptor->Type = CmResourceTypeMemory;
1232
1233 /* Check if it's 64-bit or 20-bit decode */
1234 if ((CurrentBar & PCI_ADDRESS_MEMORY_TYPE_MASK) == PCI_TYPE_64BIT)
1235 {
1236 /* The next BAR has the high word, read it */
1237 ResourceDescriptor->u.Port.MaximumAddress.HighPart = BarArray[1];
1238 Is64BitBar = TRUE;
1239 }
1240 else if ((CurrentBar & PCI_ADDRESS_MEMORY_TYPE_MASK) == PCI_TYPE_20BIT)
1241 {
1242 /* Use the correct mask to decode the address */
1243 BarMask = ~0xFFF0000F;
1244 }
1245
1246 /* Check if the BAR is listed as prefetchable memory */
1247 if (CurrentBar & PCI_ADDRESS_MEMORY_PREFETCHABLE)
1248 {
1249 /* Mark the descriptor in the same way */
1250 ResourceDescriptor->Flags |= CM_RESOURCE_MEMORY_PREFETCHABLE;
1251 }
1252 }
1253
1254 /* Now write down the maximum address based on the base + length */
1255 ResourceDescriptor->u.Port.MaximumAddress.QuadPart = (CurrentBar & BarMask) +
1256 BarLength - 1;
1257
1258 /* Return if this is a 64-bit BAR, so the loop code knows to skip the next one */
1259 return Is64BitBar;
1260 }
1261
1262 VOID
1263 NTAPI
1264 PciDecodeEnable(IN PPCI_PDO_EXTENSION PdoExtension,
1265 IN BOOLEAN Enable,
1266 OUT PUSHORT Command)
1267 {
1268 USHORT CommandValue;
1269
1270 /*
1271 * If decodes are being disabled, make sure it's allowed, and in both cases,
1272 * make sure that a hackflag isn't preventing touching the decodes at all.
1273 */
1274 if (((Enable) || (PciCanDisableDecodes(PdoExtension, 0, 0, 0))) &&
1275 !(PdoExtension->HackFlags & PCI_HACK_PRESERVE_COMMAND))
1276 {
1277 /* Did the caller already have a command word? */
1278 if (Command)
1279 {
1280 /* Use the caller's */
1281 CommandValue = *Command;
1282 }
1283 else
1284 {
1285 /* Otherwise, read the current command */
1286 PciReadDeviceConfig(PdoExtension,
1287 &Command,
1288 FIELD_OFFSET(PCI_COMMON_HEADER, Command),
1289 sizeof(USHORT));
1290 }
1291
1292 /* Turn off decodes by default */
1293 CommandValue &= ~(PCI_ENABLE_IO_SPACE |
1294 PCI_ENABLE_MEMORY_SPACE |
1295 PCI_ENABLE_BUS_MASTER);
1296
1297 /* If requested, enable the decodes that were enabled at init time */
1298 if (Enable) CommandValue |= PdoExtension->CommandEnables &
1299 (PCI_ENABLE_IO_SPACE |
1300 PCI_ENABLE_MEMORY_SPACE |
1301 PCI_ENABLE_BUS_MASTER);
1302
1303 /* Update the command word */
1304 PciWriteDeviceConfig(PdoExtension,
1305 &CommandValue,
1306 FIELD_OFFSET(PCI_COMMON_HEADER, Command),
1307 sizeof(USHORT));
1308 }
1309 }
1310
1311 NTSTATUS
1312 NTAPI
1313 PciQueryBusInformation(IN PPCI_PDO_EXTENSION PdoExtension,
1314 IN PPNP_BUS_INFORMATION* Buffer)
1315 {
1316 PPNP_BUS_INFORMATION BusInfo;
1317
1318 /* Allocate a structure for the bus information */
1319 BusInfo = ExAllocatePoolWithTag(PagedPool,
1320 sizeof(PNP_BUS_INFORMATION),
1321 'BicP');
1322 if (!BusInfo) return STATUS_INSUFFICIENT_RESOURCES;
1323
1324 /* Write the correct GUID and bus type identifier, and fill the bus number */
1325 BusInfo->BusTypeGuid = GUID_BUS_TYPE_PCI;
1326 BusInfo->LegacyBusType = PCIBus;
1327 BusInfo->BusNumber = PdoExtension->ParentFdoExtension->BaseBus;
1328 return STATUS_SUCCESS;
1329 }
1330
1331 NTSTATUS
1332 NTAPI
1333 PciDetermineSlotNumber(IN PPCI_PDO_EXTENSION PdoExtension,
1334 OUT PULONG SlotNumber)
1335 {
1336 PPCI_FDO_EXTENSION ParentExtension;
1337 ULONG ResultLength;
1338 NTSTATUS Status;
1339 PSLOT_INFO SlotInfo;
1340
1341 /* Check if a $PIR from the BIOS is used (legacy IRQ routing) */
1342 ParentExtension = PdoExtension->ParentFdoExtension;
1343 DPRINT1("Slot lookup for %d.%d.%d\n",
1344 ParentExtension ? ParentExtension->BaseBus : -1,
1345 PdoExtension->Slot.u.bits.DeviceNumber,
1346 PdoExtension->Slot.u.bits.FunctionNumber);
1347 if ((PciIrqRoutingTable) && (ParentExtension))
1348 {
1349 /* Read every slot information entry */
1350 SlotInfo = &PciIrqRoutingTable->Slot[0];
1351 DPRINT1("PIR$ %p is %lx bytes, slot 0 is at: %lx\n",
1352 PciIrqRoutingTable, PciIrqRoutingTable->TableSize, SlotInfo);
1353 while (SlotInfo < (PSLOT_INFO)((ULONG_PTR)PciIrqRoutingTable +
1354 PciIrqRoutingTable->TableSize))
1355 {
1356 DPRINT1("Slot Info: %d.%d->#%d\n",
1357 SlotInfo->BusNumber,
1358 SlotInfo->DeviceNumber,
1359 SlotInfo->SlotNumber);
1360
1361 /* Check if this slot information matches the PDO being queried */
1362 if ((ParentExtension->BaseBus == SlotInfo->BusNumber) &&
1363 (PdoExtension->Slot.u.bits.DeviceNumber == SlotInfo->DeviceNumber >> 3) &&
1364 (SlotInfo->SlotNumber))
1365 {
1366 /* We found it, return it and return success */
1367 *SlotNumber = SlotInfo->SlotNumber;
1368 return STATUS_SUCCESS;
1369 }
1370
1371 /* Try the next slot */
1372 SlotInfo++;
1373 }
1374 }
1375
1376 /* Otherwise, grab the parent FDO and check if it's the root */
1377 if (PCI_IS_ROOT_FDO(ParentExtension))
1378 {
1379 /* The root FDO doesn't have a slot number */
1380 Status = STATUS_UNSUCCESSFUL;
1381 }
1382 else
1383 {
1384 /* Otherwise, query the slot/UI address/number as a device property */
1385 Status = IoGetDeviceProperty(ParentExtension->PhysicalDeviceObject,
1386 DevicePropertyUINumber,
1387 sizeof(ULONG),
1388 SlotNumber,
1389 &ResultLength);
1390 }
1391
1392 /* Return the status of this endeavour */
1393 return Status;
1394 }
1395
1396 NTSTATUS
1397 NTAPI
1398 PciGetDeviceCapabilities(IN PDEVICE_OBJECT DeviceObject,
1399 IN OUT PDEVICE_CAPABILITIES DeviceCapability)
1400 {
1401 PIRP Irp;
1402 NTSTATUS Status;
1403 KEVENT Event;
1404 PDEVICE_OBJECT AttachedDevice;
1405 PIO_STACK_LOCATION IoStackLocation;
1406 IO_STATUS_BLOCK IoStatusBlock;
1407 PAGED_CODE();
1408
1409 /* Zero out capabilities and set undefined values to start with */
1410 RtlZeroMemory(DeviceCapability, sizeof(DEVICE_CAPABILITIES));
1411 DeviceCapability->Size = sizeof(DEVICE_CAPABILITIES);
1412 DeviceCapability->Version = 1;
1413 DeviceCapability->Address = -1;
1414 DeviceCapability->UINumber = -1;
1415
1416 /* Build the wait event for the IOCTL */
1417 KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
1418
1419 /* Find the device the PDO is attached to */
1420 AttachedDevice = IoGetAttachedDeviceReference(DeviceObject);
1421
1422 /* And build an IRP for it */
1423 Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
1424 AttachedDevice,
1425 NULL,
1426 0,
1427 NULL,
1428 &Event,
1429 &IoStatusBlock);
1430 if (!Irp)
1431 {
1432 /* The IRP failed, fail the request as well */
1433 ObDereferenceObject(AttachedDevice);
1434 return STATUS_INSUFFICIENT_RESOURCES;
1435 }
1436
1437 /* Set default status */
1438 Irp->IoStatus.Information = 0;
1439 Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
1440
1441 /* Get a stack location in this IRP */
1442 IoStackLocation = IoGetNextIrpStackLocation(Irp);
1443 ASSERT(IoStackLocation);
1444
1445 /* Initialize it as a query capabilities IRP, with no completion routine */
1446 RtlZeroMemory(IoStackLocation, sizeof(IO_STACK_LOCATION));
1447 IoStackLocation->MajorFunction = IRP_MJ_PNP;
1448 IoStackLocation->MinorFunction = IRP_MN_QUERY_CAPABILITIES;
1449 IoStackLocation->Parameters.DeviceCapabilities.Capabilities = DeviceCapability;
1450 IoSetCompletionRoutine(Irp, NULL, NULL, FALSE, FALSE, FALSE);
1451
1452 /* Send the IOCTL to the driver */
1453 Status = IoCallDriver(AttachedDevice, Irp);
1454 if (Status == STATUS_PENDING)
1455 {
1456 /* Wait for a response and update the actual status */
1457 KeWaitForSingleObject(&Event,
1458 Executive,
1459 KernelMode,
1460 FALSE,
1461 NULL);
1462 Status = Irp->IoStatus.Status;
1463 }
1464
1465 /* Done, dereference the attached device and return the final result */
1466 ObDereferenceObject(AttachedDevice);
1467 return Status;
1468 }
1469
1470 NTSTATUS
1471 NTAPI
1472 PciQueryPowerCapabilities(IN PPCI_PDO_EXTENSION PdoExtension,
1473 IN PDEVICE_CAPABILITIES DeviceCapability)
1474 {
1475 PDEVICE_OBJECT DeviceObject;
1476 NTSTATUS Status;
1477 DEVICE_CAPABILITIES AttachedCaps;
1478 DEVICE_POWER_STATE NewPowerState, DevicePowerState, DeviceWakeLevel, DeviceWakeState;
1479 SYSTEM_POWER_STATE SystemWakeState, DeepestWakeState, CurrentState;
1480
1481 /* Nothing is known at first */
1482 DeviceWakeState = PowerDeviceUnspecified;
1483 SystemWakeState = DeepestWakeState = PowerSystemUnspecified;
1484
1485 /* Get the PCI capabilities for the parent PDO */
1486 DeviceObject = PdoExtension->ParentFdoExtension->PhysicalDeviceObject;
1487 Status = PciGetDeviceCapabilities(DeviceObject, &AttachedCaps);
1488 ASSERT(NT_SUCCESS(Status));
1489 if (!NT_SUCCESS(Status)) return Status;
1490
1491 /* Check if there's not an existing device state for S0 */
1492 if (!AttachedCaps.DeviceState[PowerSystemWorking])
1493 {
1494 /* Set D0<->S0 mapping */
1495 AttachedCaps.DeviceState[PowerSystemWorking] = PowerDeviceD0;
1496 }
1497
1498 /* Check if there's not an existing device state for S3 */
1499 if (!AttachedCaps.DeviceState[PowerSystemShutdown])
1500 {
1501 /* Set D3<->S3 mapping */
1502 AttachedCaps.DeviceState[PowerSystemShutdown] = PowerDeviceD3;
1503 }
1504
1505 /* Check for a PDO with broken, or no, power capabilities */
1506 if (PdoExtension->HackFlags & PCI_HACK_NO_PM_CAPS)
1507 {
1508 /* Unknown wake device states */
1509 DeviceCapability->DeviceWake = PowerDeviceUnspecified;
1510 DeviceCapability->SystemWake = PowerSystemUnspecified;
1511
1512 /* No device state support */
1513 DeviceCapability->DeviceD1 = FALSE;
1514 DeviceCapability->DeviceD2 = FALSE;
1515
1516 /* No waking from any low-power device state is supported */
1517 DeviceCapability->WakeFromD0 = FALSE;
1518 DeviceCapability->WakeFromD1 = FALSE;
1519 DeviceCapability->WakeFromD2 = FALSE;
1520 DeviceCapability->WakeFromD3 = FALSE;
1521
1522 /* For the rest, copy whatever the parent PDO had */
1523 RtlCopyMemory(DeviceCapability->DeviceState,
1524 AttachedCaps.DeviceState,
1525 sizeof(DeviceCapability->DeviceState));
1526 return STATUS_SUCCESS;
1527 }
1528
1529 /* The PCI Device has power capabilities, so read which ones are supported */
1530 DeviceCapability->DeviceD1 = PdoExtension->PowerCapabilities.Support.D1;
1531 DeviceCapability->DeviceD2 = PdoExtension->PowerCapabilities.Support.D2;
1532 DeviceCapability->WakeFromD0 = PdoExtension->PowerCapabilities.Support.PMED0;
1533 DeviceCapability->WakeFromD1 = PdoExtension->PowerCapabilities.Support.PMED1;
1534 DeviceCapability->WakeFromD2 = PdoExtension->PowerCapabilities.Support.PMED2;
1535
1536 /* Can the attached device wake from D3? */
1537 if (AttachedCaps.DeviceWake != PowerDeviceD3)
1538 {
1539 /* It can't, so check if this PDO supports hot D3 wake */
1540 DeviceCapability->WakeFromD3 = PdoExtension->PowerCapabilities.Support.PMED3Hot;
1541 }
1542 else
1543 {
1544 /* It can, is this the root bus? */
1545 if (PCI_IS_ROOT_FDO(PdoExtension->ParentFdoExtension))
1546 {
1547 /* This is the root bus, so just check if it supports hot D3 wake */
1548 DeviceCapability->WakeFromD3 = PdoExtension->PowerCapabilities.Support.PMED3Hot;
1549 }
1550 else
1551 {
1552 /* Take the minimums? -- need to check with briang at work */
1553 UNIMPLEMENTED;
1554 }
1555 }
1556
1557 /* Now loop each system power state to determine its device state mapping */
1558 for (CurrentState = PowerSystemWorking;
1559 CurrentState < PowerSystemMaximum;
1560 CurrentState++)
1561 {
1562 /* Read the current mapping from the attached device */
1563 DevicePowerState = AttachedCaps.DeviceState[CurrentState];
1564 NewPowerState = DevicePowerState;
1565
1566 /* The attachee suports D1, but this PDO does not */
1567 if ((NewPowerState == PowerDeviceD1) &&
1568 !(PdoExtension->PowerCapabilities.Support.D1))
1569 {
1570 /* Fall back to D2 */
1571 NewPowerState = PowerDeviceD2;
1572 }
1573
1574 /* The attachee supports D2, but this PDO does not */
1575 if ((NewPowerState == PowerDeviceD2) &&
1576 !(PdoExtension->PowerCapabilities.Support.D2))
1577 {
1578 /* Fall back to D3 */
1579 NewPowerState = PowerDeviceD3;
1580 }
1581
1582 /* Set the mapping based on the best state supported */
1583 DeviceCapability->DeviceState[CurrentState] = NewPowerState;
1584
1585 /* Check if sleep states are being processed, and a mapping was found */
1586 if ((CurrentState < PowerSystemHibernate) &&
1587 (NewPowerState != PowerDeviceUnspecified))
1588 {
1589 /* Save this state as being the deepest one found until now */
1590 DeepestWakeState = CurrentState;
1591 }
1592
1593 /*
1594 * Finally, check if the computed sleep state is within the states that
1595 * this device can wake the system from, and if it's higher or equal to
1596 * the sleep state mapping that came from the attachee, assuming that it
1597 * had a valid mapping to begin with.
1598 *
1599 * It this is the case, then make sure that the computed sleep state is
1600 * matched by the device's ability to actually wake from that state.
1601 *
1602 * For devices that support D3, the PCI device only needs Hot D3 as long
1603 * as the attachee's state is less than D3. Otherwise, if the attachee
1604 * might also be at D3, this would require a Cold D3 wake, so check that
1605 * the device actually support this.
1606 */
1607 if ((CurrentState < AttachedCaps.SystemWake) &&
1608 (NewPowerState >= DevicePowerState) &&
1609 (DevicePowerState != PowerDeviceUnspecified) &&
1610 (((NewPowerState == PowerDeviceD0) && (DeviceCapability->WakeFromD0)) ||
1611 ((NewPowerState == PowerDeviceD1) && (DeviceCapability->WakeFromD1)) ||
1612 ((NewPowerState == PowerDeviceD2) && (DeviceCapability->WakeFromD2)) ||
1613 ((NewPowerState == PowerDeviceD3) &&
1614 (PdoExtension->PowerCapabilities.Support.PMED3Hot) &&
1615 ((DevicePowerState < PowerDeviceD3) ||
1616 (PdoExtension->PowerCapabilities.Support.PMED3Cold)))))
1617 {
1618 /* The mapping is valid, so this will be the lowest wake state */
1619 SystemWakeState = CurrentState;
1620 DeviceWakeState = NewPowerState;
1621 }
1622 }
1623
1624 /* Read the current wake level */
1625 DeviceWakeLevel = PdoExtension->PowerState.DeviceWakeLevel;
1626
1627 /* Check if the attachee's wake levels are valid, and the PDO's is higher */
1628 if ((AttachedCaps.SystemWake != PowerSystemUnspecified) &&
1629 (AttachedCaps.DeviceWake != PowerDeviceUnspecified) &&
1630 (DeviceWakeLevel != PowerDeviceUnspecified) &&
1631 (DeviceWakeLevel >= AttachedCaps.DeviceWake))
1632 {
1633 /* Inherit the system wake from the attachee, and this PDO's wake level */
1634 DeviceCapability->SystemWake = AttachedCaps.SystemWake;
1635 DeviceCapability->DeviceWake = DeviceWakeLevel;
1636
1637 /* Now check if the wake level is D0, but the PDO doesn't support it */
1638 if ((DeviceCapability->DeviceWake == PowerDeviceD0) &&
1639 !(DeviceCapability->WakeFromD0))
1640 {
1641 /* Bump to D1 */
1642 DeviceCapability->DeviceWake = PowerDeviceD1;
1643 }
1644
1645 /* Now check if the wake level is D1, but the PDO doesn't support it */
1646 if ((DeviceCapability->DeviceWake == PowerDeviceD1) &&
1647 !(DeviceCapability->WakeFromD1))
1648 {
1649 /* Bump to D2 */
1650 DeviceCapability->DeviceWake = PowerDeviceD2;
1651 }
1652
1653 /* Now check if the wake level is D2, but the PDO doesn't support it */
1654 if ((DeviceCapability->DeviceWake == PowerDeviceD2) &&
1655 !(DeviceCapability->WakeFromD2))
1656 {
1657 /* Bump it to D3 */
1658 DeviceCapability->DeviceWake = PowerDeviceD3;
1659 }
1660
1661 /* Now check if the wake level is D3, but the PDO doesn't support it */
1662 if ((DeviceCapability->DeviceWake == PowerDeviceD3) &&
1663 !(DeviceCapability->WakeFromD3))
1664 {
1665 /* Then no valid wake state exists */
1666 DeviceCapability->DeviceWake = PowerDeviceUnspecified;
1667 DeviceCapability->SystemWake = PowerSystemUnspecified;
1668 }
1669
1670 /* Check if no valid wake state was found */
1671 if ((DeviceCapability->DeviceWake == PowerDeviceUnspecified) ||
1672 (DeviceCapability->SystemWake == PowerSystemUnspecified))
1673 {
1674 /* Check if one was computed earlier */
1675 if ((SystemWakeState != PowerSystemUnspecified) &&
1676 (DeviceWakeState != PowerDeviceUnspecified))
1677 {
1678 /* Use the wake state that had been computed earlier */
1679 DeviceCapability->DeviceWake = DeviceWakeState;
1680 DeviceCapability->SystemWake = SystemWakeState;
1681
1682 /* If that state was D3, then the device supports Hot/Cold D3 */
1683 if (DeviceWakeState == PowerDeviceD3) DeviceCapability->WakeFromD3 = TRUE;
1684 }
1685 }
1686
1687 /*
1688 * Finally, check for off states (lower than S3, such as hibernate) and
1689 * make sure that the device both supports waking from D3 as well as
1690 * supports a Cold wake
1691 */
1692 if ((DeviceCapability->SystemWake > PowerSystemSleeping3) &&
1693 ((DeviceCapability->DeviceWake != PowerDeviceD3) ||
1694 !(PdoExtension->PowerCapabilities.Support.PMED3Cold)))
1695 {
1696 /* It doesn't, so pick the computed lowest wake state from earlier */
1697 DeviceCapability->SystemWake = DeepestWakeState;
1698 }
1699
1700 /* Set the PCI Specification mandated maximum latencies for transitions */
1701 DeviceCapability->D1Latency = 0;
1702 DeviceCapability->D2Latency = 2;
1703 DeviceCapability->D3Latency = 100;
1704
1705 /* Sanity check */
1706 ASSERT(DeviceCapability->DeviceState[PowerSystemWorking] == PowerDeviceD0);
1707 }
1708 else
1709 {
1710 /* No valid sleep states, no latencies to worry about */
1711 DeviceCapability->D1Latency = 0;
1712 DeviceCapability->D2Latency = 0;
1713 DeviceCapability->D3Latency = 0;
1714 }
1715
1716 /* This function always succeeds, even without power management support */
1717 return STATUS_SUCCESS;
1718 }
1719
1720 NTSTATUS
1721 NTAPI
1722 PciQueryCapabilities(IN PPCI_PDO_EXTENSION PdoExtension,
1723 IN OUT PDEVICE_CAPABILITIES DeviceCapability)
1724 {
1725 NTSTATUS Status;
1726
1727 /* A PDO ID is never unique, and its address is its function and device */
1728 DeviceCapability->UniqueID = FALSE;
1729 DeviceCapability->Address = PdoExtension->Slot.u.bits.FunctionNumber |
1730 (PdoExtension->Slot.u.bits.DeviceNumber << 16);
1731
1732 /* Check for host bridges */
1733 if ((PdoExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
1734 (PdoExtension->SubClass == PCI_SUBCLASS_BR_HOST))
1735 {
1736 /* Raw device opens to a host bridge are acceptable */
1737 DeviceCapability->RawDeviceOK = TRUE;
1738 }
1739 else
1740 {
1741 /* Otherwise, other PDOs cannot be directly opened */
1742 DeviceCapability->RawDeviceOK = FALSE;
1743 }
1744
1745 /* PCI PDOs are pretty fixed things */
1746 DeviceCapability->LockSupported = FALSE;
1747 DeviceCapability->EjectSupported = FALSE;
1748 DeviceCapability->Removable = FALSE;
1749 DeviceCapability->DockDevice = FALSE;
1750
1751 /* The slot number is stored as a device property, go query it */
1752 PciDetermineSlotNumber(PdoExtension, &DeviceCapability->UINumber);
1753
1754 /* Finally, query and power capabilities and convert them for PnP usage */
1755 Status = PciQueryPowerCapabilities(PdoExtension, DeviceCapability);
1756
1757 /* Dump the capabilities if it all worked, and return the status */
1758 if (NT_SUCCESS(Status)) PciDebugDumpQueryCapabilities(DeviceCapability);
1759 return Status;
1760 }
1761
1762 PCM_PARTIAL_RESOURCE_DESCRIPTOR
1763 NTAPI
1764 PciNextPartialDescriptor(PCM_PARTIAL_RESOURCE_DESCRIPTOR CmDescriptor)
1765 {
1766 PCM_PARTIAL_RESOURCE_DESCRIPTOR NextDescriptor;
1767
1768 /* Assume the descriptors are the fixed size ones */
1769 NextDescriptor = CmDescriptor + 1;
1770
1771 /* But check if this is actually a variable-sized descriptor */
1772 if (CmDescriptor->Type == CmResourceTypeDeviceSpecific)
1773 {
1774 /* Add the size of the variable section as well */
1775 NextDescriptor = (PVOID)((ULONG_PTR)NextDescriptor +
1776 CmDescriptor->u.DeviceSpecificData.DataSize);
1777 }
1778
1779 /* Now the correct pointer has been computed, return it */
1780 return NextDescriptor;
1781 }
1782
1783 /* EOF */