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