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