Sync with trunk r58740.
[reactos.git] / drivers / bus / pcix / utils.c
index a73b0de..bca16f2 100644 (file)
 
 /* GLOBALS ********************************************************************/
 
+ULONG PciDebugPortsCount;
+
+RTL_RANGE_LIST PciIsaBitExclusionList;
+RTL_RANGE_LIST PciVgaAndIsaBitExclusionList;
+
 /* FUNCTIONS ******************************************************************/
 
+BOOLEAN
+NTAPI
+PciUnicodeStringStrStr(IN PUNICODE_STRING InputString,
+                       IN PCUNICODE_STRING EqualString,
+                       IN BOOLEAN CaseInSensitive)
+{
+    UNICODE_STRING PartialString;
+    LONG EqualChars, TotalChars;
+
+    /* Build a partial string with the smaller substring */
+    PartialString.Length = EqualString->Length;
+    PartialString.MaximumLength = InputString->MaximumLength;;
+    PartialString.Buffer = InputString->Buffer;
+
+    /* Check how many characters that need comparing */
+    EqualChars = 0;
+    TotalChars = (InputString->Length - EqualString->Length) / sizeof(WCHAR);
+
+    /* If the substring is bigger, just fail immediately */
+    if (TotalChars < 0) return FALSE;
+
+    /* Keep checking each character */
+    while (!RtlEqualUnicodeString(EqualString, &PartialString, CaseInSensitive))
+    {
+        /* Continue checking until all the required characters are equal */
+        PartialString.Buffer++;
+        PartialString.MaximumLength -= sizeof(WCHAR);
+        if (++EqualChars > TotalChars) return FALSE;
+    }
+
+    /* The string is equal */
+    return TRUE;
+}
+
+BOOLEAN
+NTAPI
+PciStringToUSHORT(IN PWCHAR String,
+                  OUT PUSHORT Value)
+{
+    USHORT Short;
+    ULONG Low, High, Length;
+    WCHAR Char;
+
+    /* Initialize everything to zero */
+    Short = 0;
+    Length = 0;
+    while (TRUE)
+    {
+        /* Get the character and set the high byte based on the previous one */
+        Char = *String++;
+        High = 16 * Short;
+
+        /* Check for numbers */
+        if ( Char >= '0' && Char <= '9' )
+        {
+            /* Convert them to a byte */
+            Low = Char - '0';
+        }
+        else if ( Char >= 'A' && Char <= 'F' )
+        {
+            /* Convert upper-case hex letters into a byte */
+            Low = Char - '7';
+        }
+        else if ( Char >= 'a' && Char <= 'f' )
+        {
+            /* Convert lower-case hex letters into a byte */
+            Low = Char - 'W';
+        }
+        else
+        {
+            /* Invalid string, fail the conversion */
+            return FALSE;
+        }
+
+        /* Combine the high and low byte */
+        Short = High | Low;
+
+        /* If 4 letters have been reached, the 16-bit integer should exist */
+        if (++Length >= 4)
+        {
+            /* Return it to the caller */
+            *Value = Short;
+            return TRUE;
+        }
+    }
+}
+
+BOOLEAN
+NTAPI
+PciIsSuiteVersion(IN USHORT SuiteMask)
+{
+    ULONGLONG Mask = 0;
+    RTL_OSVERSIONINFOEXW VersionInfo;
+
+    /* Initialize the version information */
+    RtlZeroMemory(&VersionInfo, sizeof(RTL_OSVERSIONINFOEXW));
+    VersionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
+    VersionInfo.wSuiteMask = SuiteMask;
+
+    /* Set the comparison mask and return if the passed suite mask matches */
+    VER_SET_CONDITION(Mask, VER_SUITENAME, VER_AND);
+    return NT_SUCCESS(RtlVerifyVersionInfo(&VersionInfo, VER_SUITENAME, Mask));
+}
+
+BOOLEAN
+NTAPI
+PciIsDatacenter(VOID)
+{
+    BOOLEAN Result;
+    PVOID Value;
+    ULONG ResultLength;
+    NTSTATUS Status;
+
+    /* Assume this isn't Datacenter */
+    Result = FALSE;
+
+    /* First, try opening the setup key */
+    Status = PciGetRegistryValue(L"",
+                                 L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\setupdd",
+                                 0,
+                                 REG_BINARY,
+                                 &Value,
+                                 &ResultLength);
+    if (!NT_SUCCESS(Status))
+    {
+        /* This is not an in-progress Setup boot, so query the suite version */
+        Result = PciIsSuiteVersion(VER_SUITE_DATACENTER);
+    }
+    else
+    {
+        /* This scenario shouldn't happen yet, since SetupDD isn't used */
+        UNIMPLEMENTED_FATAL("ReactOS doesn't use SetupDD for its installation program. Therefore this scenario must not happen!\n");
+    }
+
+    /* Return if this is Datacenter or not */
+    return Result;
+}
+
+BOOLEAN
+NTAPI
+PciOpenKey(IN PWCHAR KeyName,
+           IN HANDLE RootKey,
+           IN ACCESS_MASK DesiredAccess,
+           OUT PHANDLE KeyHandle,
+           OUT PNTSTATUS KeyStatus)
+{
+    NTSTATUS Status;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    UNICODE_STRING KeyString;
+    PAGED_CODE();
+
+    /* Initialize the object attributes */
+    RtlInitUnicodeString(&KeyString, KeyName);
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &KeyString,
+                               OBJ_CASE_INSENSITIVE,
+                               RootKey,
+                               NULL);
+
+    /* Open the key, returning a boolean, and the status, if requested */
+    Status = ZwOpenKey(KeyHandle, DesiredAccess, &ObjectAttributes);
+    if (KeyStatus) *KeyStatus = Status;
+    return NT_SUCCESS(Status);
+}
+
+NTSTATUS
+NTAPI
+PciGetRegistryValue(IN PWCHAR ValueName,
+                    IN PWCHAR KeyName,
+                    IN HANDLE RootHandle,
+                    IN ULONG Type,
+                    OUT PVOID *OutputBuffer,
+                    OUT PULONG OutputLength)
+{
+    NTSTATUS Status;
+    PKEY_VALUE_PARTIAL_INFORMATION PartialInfo;
+    ULONG NeededLength, ActualLength;
+    UNICODE_STRING ValueString;
+    HANDLE KeyHandle;
+    BOOLEAN Result;
+
+    /* So we know what to free at the end of the body */
+    PartialInfo = NULL;
+    KeyHandle = NULL;
+    do
+    {
+        /* Open the key by name, rooted off the handle passed */
+        Result = PciOpenKey(KeyName,
+                            RootHandle,
+                            KEY_QUERY_VALUE,
+                            &KeyHandle,
+                            &Status);
+        if (!Result) break;
+
+        /* Query for the size that's needed for the value that was passed in */
+        RtlInitUnicodeString(&ValueString, ValueName);
+        Status = ZwQueryValueKey(KeyHandle,
+                                 &ValueString,
+                                 KeyValuePartialInformation,
+                                 NULL,
+                                 0,
+                                 &NeededLength);
+        ASSERT(!NT_SUCCESS(Status));
+        if (Status != STATUS_BUFFER_TOO_SMALL) break;
+
+        /* Allocate an appropriate buffer for the size that was returned */
+        ASSERT(NeededLength != 0);
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        PartialInfo = ExAllocatePoolWithTag(PagedPool,
+                                            NeededLength,
+                                            PCI_POOL_TAG);
+        if (!PartialInfo) break;
+
+        /* Query the actual value information now that the size is known */
+        Status = ZwQueryValueKey(KeyHandle,
+                                 &ValueString,
+                                 KeyValuePartialInformation,
+                                 PartialInfo,
+                                 NeededLength,
+                                 &ActualLength);
+        if (!NT_SUCCESS(Status)) break;
+
+        /* Make sure it's of the type that the caller expects */
+        Status = STATUS_INVALID_PARAMETER;
+        if (PartialInfo->Type != Type) break;
+
+        /* Subtract the registry-specific header, to get the data size */
+        ASSERT(NeededLength == ActualLength);
+        NeededLength -= sizeof(KEY_VALUE_PARTIAL_INFORMATION);
+
+        /* Allocate a buffer to hold the data and return it to the caller */
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        *OutputBuffer = ExAllocatePoolWithTag(PagedPool,
+                                              NeededLength,
+                                              PCI_POOL_TAG);
+        if (!*OutputBuffer) break;
+
+        /* Copy the data into the buffer and return its length to the caller */
+        RtlCopyMemory(*OutputBuffer, PartialInfo->Data, NeededLength);
+        if (OutputLength) *OutputLength = NeededLength;
+        Status = STATUS_SUCCESS;
+    } while (0);
+
+    /* Close any opened keys and free temporary allocations */
+    if (KeyHandle) ZwClose(KeyHandle);
+    if (PartialInfo) ExFreePoolWithTag(PartialInfo, 0);
+    return Status;
+}
+
+NTSTATUS
+NTAPI
+PciBuildDefaultExclusionLists(VOID)
+{
+    ULONG Start;
+    NTSTATUS Status;
+    ASSERT(PciIsaBitExclusionList.Count == 0);
+    ASSERT(PciVgaAndIsaBitExclusionList.Count == 0);
+
+    /* Initialize the range lists */
+    RtlInitializeRangeList(&PciIsaBitExclusionList);
+    RtlInitializeRangeList(&PciVgaAndIsaBitExclusionList);
+
+    /* Loop x86 I/O ranges */
+    for (Start = 0x100; Start <= 0xFEFF; Start += 0x400)
+    {
+        /* Add the ISA I/O ranges */
+        Status = RtlAddRange(&PciIsaBitExclusionList,
+                             Start,
+                             Start + 0x2FF,
+                             0,
+                             RTL_RANGE_LIST_ADD_IF_CONFLICT,
+                             NULL,
+                             NULL);
+        if (!NT_SUCCESS(Status)) break;
+
+        /* Add the ISA I/O ranges */
+        Status = RtlAddRange(&PciVgaAndIsaBitExclusionList,
+                             Start,
+                             Start + 0x2AF,
+                             0,
+                             RTL_RANGE_LIST_ADD_IF_CONFLICT,
+                             NULL,
+                             NULL);
+        if (!NT_SUCCESS(Status)) break;
+
+        /* Add the VGA I/O range for Monochrome Video */
+        Status = RtlAddRange(&PciVgaAndIsaBitExclusionList,
+                             Start + 0x2BC,
+                             Start + 0x2BF,
+                             0,
+                             RTL_RANGE_LIST_ADD_IF_CONFLICT,
+                             NULL,
+                             NULL);
+        if (!NT_SUCCESS(Status)) break;
+
+        /* Add the VGA I/O range for certain CGA adapters */
+        Status = RtlAddRange(&PciVgaAndIsaBitExclusionList,
+                             Start + 0x2E0,
+                             Start + 0x2FF,
+                             0,
+                             RTL_RANGE_LIST_ADD_IF_CONFLICT,
+                             NULL,
+                             NULL);
+        if (!NT_SUCCESS(Status)) break;
+
+        /* Success, ranges added done */
+    };
+
+    RtlFreeRangeList(&PciIsaBitExclusionList);
+    RtlFreeRangeList(&PciVgaAndIsaBitExclusionList);
+    return Status;
+}
+
+PPCI_FDO_EXTENSION
+NTAPI
+PciFindParentPciFdoExtension(IN PDEVICE_OBJECT DeviceObject,
+                             IN PKEVENT Lock)
+{
+    PPCI_FDO_EXTENSION DeviceExtension;
+    PPCI_PDO_EXTENSION SearchExtension, FoundExtension;
+
+    /* Assume we'll find nothing */
+    SearchExtension = DeviceObject->DeviceExtension;
+    FoundExtension = NULL;
+
+    /* Check if a lock was specified */
+    if (Lock)
+    {
+        /* Wait for the lock to be released */
+        KeEnterCriticalRegion();
+        KeWaitForSingleObject(Lock, Executive, KernelMode, FALSE, NULL);
+    }
+
+    /* Now search for the extension */
+    DeviceExtension = (PPCI_FDO_EXTENSION)PciFdoExtensionListHead.Next;
+    while (DeviceExtension)
+    {
+        /* Acquire this device's lock */
+        KeEnterCriticalRegion();
+        KeWaitForSingleObject(&DeviceExtension->ChildListLock,
+                              Executive,
+                              KernelMode,
+                              FALSE,
+                              NULL);
+
+        /* Scan all children PDO, stop when no more PDOs, or found it */
+        for (FoundExtension = DeviceExtension->ChildPdoList;
+             ((FoundExtension) && (FoundExtension != SearchExtension));
+             FoundExtension = FoundExtension->Next);
+
+        /* Release this device's lock */
+        KeSetEvent(&DeviceExtension->ChildListLock, IO_NO_INCREMENT, FALSE);
+        KeLeaveCriticalRegion();
+
+        /* If we found it, break out */
+        if (FoundExtension) break;
+
+        /* Move to the next device */
+        DeviceExtension = (PPCI_FDO_EXTENSION)DeviceExtension->List.Next;
+    }
+
+    /* Check if we had acquired a lock previously */
+    if (Lock)
+    {
+        /* Release it */
+        KeSetEvent(Lock, IO_NO_INCREMENT, FALSE);
+        KeLeaveCriticalRegion();
+    }
+
+    /* Return which extension was found, if any */
+    return DeviceExtension;
+}
+
+VOID
+NTAPI
+PciInsertEntryAtTail(IN PSINGLE_LIST_ENTRY ListHead,
+                     IN PPCI_FDO_EXTENSION DeviceExtension,
+                     IN PKEVENT Lock)
+{
+    PSINGLE_LIST_ENTRY NextEntry;
+    PAGED_CODE();
+
+    /* Check if a lock was specified */
+    if (Lock)
+    {
+        /* Wait for the lock to be released */
+        KeEnterCriticalRegion();
+        KeWaitForSingleObject(Lock, Executive, KernelMode, FALSE, NULL);
+    }
+
+    /* Loop the list until we get to the end, then insert this entry there */
+    for (NextEntry = ListHead; NextEntry->Next; NextEntry = NextEntry->Next);
+    NextEntry->Next = &DeviceExtension->List;
+
+    /* Check if we had acquired a lock previously */
+    if (Lock)
+    {
+        /* Release it */
+        KeSetEvent(Lock, IO_NO_INCREMENT, FALSE);
+        KeLeaveCriticalRegion();
+    }
+}
+
+VOID
+NTAPI
+PciInsertEntryAtHead(IN PSINGLE_LIST_ENTRY ListHead,
+                     IN PSINGLE_LIST_ENTRY Entry,
+                     IN PKEVENT Lock)
+{
+    PAGED_CODE();
+
+    /* Check if a lock was specified */
+    if (Lock)
+    {
+        /* Wait for the lock to be released */
+        KeEnterCriticalRegion();
+        KeWaitForSingleObject(Lock, Executive, KernelMode, FALSE, NULL);
+    }
+
+    /* Make the entry point to the current head and make the head point to it */
+    Entry->Next = ListHead->Next;
+    ListHead->Next = Entry;
+
+    /* Check if we had acquired a lock previously */
+    if (Lock)
+    {
+        /* Release it */
+        KeSetEvent(Lock, IO_NO_INCREMENT, FALSE);
+        KeLeaveCriticalRegion();
+    }
+}
+
+VOID
+NTAPI
+PcipLinkSecondaryExtension(IN PSINGLE_LIST_ENTRY List,
+                           IN PVOID Lock,
+                           IN PPCI_SECONDARY_EXTENSION SecondaryExtension,
+                           IN PCI_SIGNATURE ExtensionType,
+                           IN PVOID Destructor)
+{
+    PAGED_CODE();
+
+    /* Setup the extension data, and insert it into the primary's list */
+    SecondaryExtension->ExtensionType = ExtensionType;
+    SecondaryExtension->Destructor = Destructor;
+    PciInsertEntryAtHead(List, &SecondaryExtension->List, Lock);
+}
+
+NTSTATUS
+NTAPI
+PciGetDeviceProperty(IN PDEVICE_OBJECT DeviceObject,
+                     IN DEVICE_REGISTRY_PROPERTY DeviceProperty,
+                     OUT PVOID *OutputBuffer)
+{
+    NTSTATUS Status;
+    ULONG BufferLength, ResultLength;
+    PVOID Buffer;
+    do
+    {
+        /* Query the requested property size */
+        Status = IoGetDeviceProperty(DeviceObject,
+                                     DeviceProperty,
+                                     0,
+                                     NULL,
+                                     &BufferLength);
+        if (Status != STATUS_BUFFER_TOO_SMALL)
+        {
+            /* Call should've failed with buffer too small! */
+            DPRINT1("PCI - Unexpected status from GetDeviceProperty, saw %08X, expected %08X.\n",
+                    Status,
+                    STATUS_BUFFER_TOO_SMALL);
+            *OutputBuffer = NULL;
+            ASSERTMSG(FALSE, "PCI Successfully did the impossible!");
+            break;
+        }
+
+        /* Allocate the required buffer */
+        Buffer = ExAllocatePoolWithTag(PagedPool, BufferLength, 'BicP');
+        if (!Buffer)
+        {
+            /* No memory, fail the request */
+            DPRINT1("PCI - Failed to allocate DeviceProperty buffer (%d bytes).\n", BufferLength);
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            break;
+        }
+
+        /* Do the actual property query call */
+        Status = IoGetDeviceProperty(DeviceObject,
+                                     DeviceProperty,
+                                     BufferLength,
+                                     Buffer,
+                                     &ResultLength);
+        if (!NT_SUCCESS(Status)) break;
+
+        /* Return the buffer to the caller */
+        ASSERT(BufferLength == ResultLength);
+        *OutputBuffer = Buffer;
+        return STATUS_SUCCESS;
+    } while (FALSE);
+
+    /* Failure path */
+    return STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS
+NTAPI
+PciSendIoctl(IN PDEVICE_OBJECT DeviceObject,
+             IN ULONG IoControlCode,
+             IN PVOID InputBuffer,
+             IN ULONG InputBufferLength,
+             IN PVOID OutputBuffer,
+             IN ULONG OutputBufferLength)
+{
+    PIRP Irp;
+    NTSTATUS Status;
+    KEVENT Event;
+    IO_STATUS_BLOCK IoStatusBlock;
+    PDEVICE_OBJECT AttachedDevice;
+    PAGED_CODE();
+
+    /* Initialize the pending IRP event */
+    KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
+
+    /* Get a reference to the root PDO (ACPI) */
+    AttachedDevice = IoGetAttachedDeviceReference(DeviceObject);
+    if (!AttachedDevice) return STATUS_INVALID_PARAMETER;
+
+    /* Build the requested IOCTL IRP */
+    Irp = IoBuildDeviceIoControlRequest(IoControlCode,
+                                        AttachedDevice,
+                                        InputBuffer,
+                                        InputBufferLength,
+                                        OutputBuffer,
+                                        OutputBufferLength,
+                                        0,
+                                        &Event,
+                                        &IoStatusBlock);
+    if (!Irp) return STATUS_INSUFFICIENT_RESOURCES;
+
+    /* Send the IOCTL to the driver */
+    Status = IoCallDriver(AttachedDevice, Irp);
+    if (Status == STATUS_PENDING)
+    {
+        /* Wait for a response */
+        KeWaitForSingleObject(&Event,
+                              Executive,
+                              KernelMode,
+                              FALSE,
+                              NULL);
+        Status = Irp->IoStatus.Status;
+    }
+
+    /* Take away the reference we took and return the result to the caller */
+    ObDereferenceObject(AttachedDevice);
+    return Status;
+}
+
+PPCI_SECONDARY_EXTENSION
+NTAPI
+PciFindNextSecondaryExtension(IN PSINGLE_LIST_ENTRY ListHead,
+                              IN PCI_SIGNATURE ExtensionType)
+{
+    PSINGLE_LIST_ENTRY NextEntry;
+    PPCI_SECONDARY_EXTENSION Extension;
+
+    /* Scan the list */
+    for (NextEntry = ListHead; NextEntry; NextEntry = NextEntry->Next)
+    {
+        /* Grab each extension and check if it's the one requested */
+        Extension = CONTAINING_RECORD(NextEntry, PCI_SECONDARY_EXTENSION, List);
+        if (Extension->ExtensionType == ExtensionType) return Extension;
+    }
+
+    /* Nothing was found */
+    return NULL;
+}
+
+ULONGLONG
+NTAPI
+PciGetHackFlags(IN USHORT VendorId,
+                IN USHORT DeviceId,
+                IN USHORT SubVendorId,
+                IN USHORT SubSystemId,
+                IN UCHAR RevisionId)
+{
+    PPCI_HACK_ENTRY HackEntry;
+    ULONGLONG HackFlags;
+    ULONG LastWeight, MatchWeight;
+    ULONG EntryFlags;
+    
+    /* ReactOS SetupLDR Hack */
+    if (!PciHackTable) return 0;
+
+    /* Initialize the variables before looping */
+    LastWeight = 0;
+    HackFlags = 0;
+    ASSERT(PciHackTable);
+
+    /* Scan the hack table */
+    for (HackEntry = PciHackTable;
+         HackEntry->VendorID != PCI_INVALID_VENDORID;
+         ++HackEntry)
+    {
+        /* Check if there's an entry for this device */
+        if ((HackEntry->DeviceID == DeviceId) &&
+            (HackEntry->VendorID == VendorId))
+        {
+            /* This is a basic match */
+            EntryFlags = HackEntry->Flags;
+            MatchWeight = 1;
+
+            /* Does the entry have revision information? */
+            if (EntryFlags & PCI_HACK_HAS_REVISION_INFO)
+            {
+                /* Check if the revision matches, if so, this is a better match */
+                if (HackEntry->RevisionID != RevisionId) continue;
+                MatchWeight = 3;
+            }
+
+            /* Does the netry have subsystem information? */
+            if (EntryFlags & PCI_HACK_HAS_SUBSYSTEM_INFO)
+            {
+                /* Check if it matches, if so, this is the best possible match */
+                if ((HackEntry->SubVendorID != SubVendorId) ||
+                    (HackEntry->SubSystemID != SubSystemId))
+                {
+                    continue;
+                }
+                MatchWeight += 4;
+            }
+
+            /* Is this the best match yet? */
+            if (MatchWeight > LastWeight)
+            {
+                /* This is the best match for now, use this as the hack flags */
+                HackFlags = HackEntry->HackFlags;
+                LastWeight = MatchWeight;
+            }
+        }
+    }
+
+    /* Return the best match */
+    return HackFlags;
+}
+
+BOOLEAN
+NTAPI
+PciIsCriticalDeviceClass(IN UCHAR BaseClass,
+                         IN UCHAR SubClass)
+{
+    /* Check for system or bridge devices */
+    if (BaseClass == PCI_CLASS_BASE_SYSTEM_DEV)
+    {
+        /* Interrupt controlers are critical */
+        return SubClass == PCI_SUBCLASS_SYS_INTERRUPT_CTLR;
+    }
+    else if (BaseClass == PCI_CLASS_BRIDGE_DEV)
+    {
+        /* ISA Bridges are critical */
+        return SubClass == PCI_SUBCLASS_BR_ISA;
+    }
+    else
+    {
+        /* All display controllers are critical */
+        return BaseClass == PCI_CLASS_DISPLAY_CTLR;
+    }
+}
+
+PPCI_PDO_EXTENSION
+NTAPI
+PciFindPdoByFunction(IN PPCI_FDO_EXTENSION DeviceExtension,
+                     IN ULONG FunctionNumber,
+                     IN PPCI_COMMON_HEADER PciData)
+{
+    KIRQL Irql;
+    PPCI_PDO_EXTENSION PdoExtension;
+
+    /* Get the current IRQL when this call was made */
+    Irql = KeGetCurrentIrql();
+
+    /* Is this a low-IRQL call? */
+    if (Irql < DISPATCH_LEVEL)
+    {
+        /* Acquire this device's lock */
+        KeEnterCriticalRegion();
+        KeWaitForSingleObject(&DeviceExtension->ChildListLock,
+                              Executive,
+                              KernelMode,
+                              FALSE,
+                              NULL);
+    }
+
+    /* Loop every child PDO */
+    for (PdoExtension = DeviceExtension->ChildPdoList;
+         PdoExtension;
+         PdoExtension = PdoExtension->Next)
+    {
+        /* Find only enumerated PDOs */
+        if (!PdoExtension->ReportedMissing)
+        {
+            /* Check if the function number and header data matches */
+            if ((FunctionNumber == PdoExtension->Slot.u.AsULONG) &&
+                (PdoExtension->VendorId == PciData->VendorID) &&
+                (PdoExtension->DeviceId == PciData->DeviceID) &&
+                (PdoExtension->RevisionId == PciData->RevisionID))
+            {
+                /* This is considered to be the same PDO */
+                break;
+            }
+        }
+    }
+
+    /* Was this a low-IRQL call? */
+    if (Irql < DISPATCH_LEVEL)
+    {
+        /* Release this device's lock */
+        KeSetEvent(&DeviceExtension->ChildListLock, IO_NO_INCREMENT, FALSE);
+        KeLeaveCriticalRegion();
+    }
+
+    /* If the search found something, this is non-NULL, otherwise it's NULL */
+    return PdoExtension;
+}
+
+BOOLEAN
+NTAPI
+PciIsDeviceOnDebugPath(IN PPCI_PDO_EXTENSION DeviceExtension)
+{
+    PAGED_CODE();
+
+    /* Check for too many, or no, debug ports */
+    ASSERT(PciDebugPortsCount <= MAX_DEBUGGING_DEVICES_SUPPORTED);
+    if (!PciDebugPortsCount) return FALSE;
+
+    /* eVb has not been able to test such devices yet */
+    UNIMPLEMENTED_DBGBREAK();
+    return FALSE;
+}
+
+NTSTATUS
+NTAPI
+PciGetBiosConfig(IN PPCI_PDO_EXTENSION DeviceExtension,
+                 OUT PPCI_COMMON_HEADER PciData)
+{
+    HANDLE KeyHandle, SubKeyHandle;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    UNICODE_STRING KeyName, KeyValue;
+    WCHAR Buffer[32];
+    WCHAR DataBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + PCI_COMMON_HDR_LENGTH];
+    PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PVOID)DataBuffer;
+    NTSTATUS Status;
+    ULONG ResultLength;
+    PAGED_CODE();
+
+    /* Open the PCI key */
+    Status = IoOpenDeviceRegistryKey(DeviceExtension->ParentFdoExtension->
+                                     PhysicalDeviceObject,
+                                     TRUE,
+                                     KEY_ALL_ACCESS,
+                                     &KeyHandle);
+    if (!NT_SUCCESS(Status)) return Status;
+
+    /* Create a volatile BIOS configuration key */
+    RtlInitUnicodeString(&KeyName, L"BiosConfig");
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &KeyName,
+                               OBJ_KERNEL_HANDLE,
+                               KeyHandle,
+                               NULL);
+    Status = ZwCreateKey(&SubKeyHandle,
+                         KEY_READ,
+                         &ObjectAttributes,
+                         0,
+                         NULL,
+                         REG_OPTION_VOLATILE,
+                         NULL);
+    ZwClose(KeyHandle);
+    if (!NT_SUCCESS(Status)) return Status;
+
+    /* Create the key value based on the device and function number */
+    swprintf(Buffer,
+             L"DEV_%02x&FUN_%02x",
+             DeviceExtension->Slot.u.bits.DeviceNumber,
+             DeviceExtension->Slot.u.bits.FunctionNumber);
+    RtlInitUnicodeString(&KeyValue, Buffer);
+
+    /* Query the value information (PCI BIOS configuration header) */
+    Status = ZwQueryValueKey(SubKeyHandle,
+                             &KeyValue,
+                             KeyValuePartialInformation,
+                             PartialInfo,
+                             sizeof(DataBuffer),
+                             &ResultLength);
+    ZwClose(SubKeyHandle);
+    if (!NT_SUCCESS(Status)) return Status;
+
+    /* If any information was returned, go ahead and copy its data */
+    ASSERT(PartialInfo->DataLength == PCI_COMMON_HDR_LENGTH);
+    RtlCopyMemory(PciData, PartialInfo->Data, PCI_COMMON_HDR_LENGTH);
+    return Status;
+}
+
+NTSTATUS
+NTAPI
+PciSaveBiosConfig(IN PPCI_PDO_EXTENSION DeviceExtension,
+                  IN PPCI_COMMON_HEADER PciData)
+{
+    HANDLE KeyHandle, SubKeyHandle;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    UNICODE_STRING KeyName, KeyValue;
+    WCHAR Buffer[32];
+    NTSTATUS Status;
+    PAGED_CODE();
+
+    /* Open the PCI key */
+    Status = IoOpenDeviceRegistryKey(DeviceExtension->ParentFdoExtension->
+                                     PhysicalDeviceObject,
+                                     TRUE,
+                                     KEY_READ | KEY_WRITE,
+                                     &KeyHandle);
+    if (!NT_SUCCESS(Status)) return Status;
+
+    /* Create a volatile BIOS configuration key */
+    RtlInitUnicodeString(&KeyName, L"BiosConfig");
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &KeyName,
+                               OBJ_KERNEL_HANDLE,
+                               KeyHandle,
+                               NULL);
+    Status = ZwCreateKey(&SubKeyHandle,
+                         KEY_READ | KEY_WRITE,
+                         &ObjectAttributes,
+                         0,
+                         NULL,
+                         REG_OPTION_VOLATILE,
+                         NULL);
+    ZwClose(KeyHandle);
+    if (!NT_SUCCESS(Status)) return Status;
+
+    /* Create the key value based on the device and function number */
+    swprintf(Buffer,
+             L"DEV_%02x&FUN_%02x",
+             DeviceExtension->Slot.u.bits.DeviceNumber,
+             DeviceExtension->Slot.u.bits.FunctionNumber);
+    RtlInitUnicodeString(&KeyValue, Buffer);
+
+    /* Set the value data (the PCI BIOS configuration header) */
+    Status = ZwSetValueKey(SubKeyHandle,
+                           &KeyValue,
+                           0,
+                           REG_BINARY,
+                           PciData,
+                           PCI_COMMON_HDR_LENGTH);
+    ZwClose(SubKeyHandle);
+    return Status;
+}
+
+UCHAR
+NTAPI
+PciReadDeviceCapability(IN PPCI_PDO_EXTENSION DeviceExtension,
+                        IN UCHAR Offset,
+                        IN ULONG CapabilityId,
+                        OUT PPCI_CAPABILITIES_HEADER Buffer,
+                        IN ULONG Length)
+{
+    ULONG CapabilityCount = 0;
+
+    /* If the device has no capabilility list, fail */
+    if (!Offset) return 0;
+
+    /* Validate a PDO with capabilities, a valid buffer, and a valid length */
+    ASSERT(DeviceExtension->ExtensionType == PciPdoExtensionType);
+    ASSERT(DeviceExtension->CapabilitiesPtr != 0);
+    ASSERT(Buffer);
+    ASSERT(Length >= sizeof(PCI_CAPABILITIES_HEADER));
+
+    /* Loop all capabilities */
+    while (Offset)
+    {
+        /* Make sure the pointer is spec-aligned and spec-sized */
+        ASSERT((Offset >= PCI_COMMON_HDR_LENGTH) && ((Offset & 0x3) == 0));
+
+        /* Read the capability header */
+        PciReadDeviceConfig(DeviceExtension,
+                            Buffer,
+                            Offset,
+                            sizeof(PCI_CAPABILITIES_HEADER));
+
+        /* Check if this is the capability being looked up */
+        if ((Buffer->CapabilityID == CapabilityId) || !(CapabilityId))
+        {
+            /* Check if was at a valid offset and length */
+            if ((Offset) && (Length > sizeof(PCI_CAPABILITIES_HEADER)))
+            {
+                /* Sanity check */
+                ASSERT(Length <= (sizeof(PCI_COMMON_CONFIG) - Offset));
+
+                /* Now read the whole capability data into the buffer */
+                PciReadDeviceConfig(DeviceExtension,
+                                    (PVOID)((ULONG_PTR)Buffer +
+                                    sizeof(PCI_CAPABILITIES_HEADER)),
+                                    Offset + sizeof(PCI_CAPABILITIES_HEADER),
+                                    Length - sizeof(PCI_CAPABILITIES_HEADER));
+            }
+
+            /* Return the offset where the capability was found */
+            return Offset;
+        }
+
+        /* Try the next capability instead */
+        CapabilityCount++;
+        Offset = Buffer->Next;
+
+        /* There can't be more than 48 capabilities (256 bytes max) */
+        if (CapabilityCount > 48)
+        {
+            /* Fail, since this is basically a broken PCI device */
+            DPRINT1("PCI device %p capabilities list is broken.\n", DeviceExtension);
+            return 0;
+        }
+    }
+
+    /* Capability wasn't found, fail */
+    return 0;
+}
+
+BOOLEAN
+NTAPI
+PciCanDisableDecodes(IN PPCI_PDO_EXTENSION DeviceExtension,
+                     IN PPCI_COMMON_HEADER Config,
+                     IN ULONGLONG HackFlags,
+                     IN BOOLEAN ForPowerDown)
+{
+    UCHAR BaseClass, SubClass;
+    BOOLEAN IsVga;
+
+    /* Is there a device extension or should the PCI header be used? */
+    if (DeviceExtension)
+    {
+        /* Never disable decodes for a debug PCI Device */
+        if (DeviceExtension->OnDebugPath) return FALSE;
+
+        /* Hack flags will be obtained from the extension, not the caller */
+        ASSERT(HackFlags == 0);
+
+        /* Get hacks and classification from the device extension */
+        HackFlags = DeviceExtension->HackFlags;
+        SubClass = DeviceExtension->SubClass;
+        BaseClass = DeviceExtension->BaseClass;
+    }
+    else
+    {
+        /* There must be a PCI header, go read the classification information */
+        ASSERT(Config != NULL);
+        BaseClass = Config->BaseClass;
+        SubClass = Config->SubClass;
+    }
+
+    /* Check for hack flags that prevent disabling the decodes */
+    if (HackFlags & (PCI_HACK_PRESERVE_COMMAND |
+                     PCI_HACK_CB_SHARE_CMD_BITS |
+                     PCI_HACK_DONT_DISABLE_DECODES))
+    {
+        /* Don't do it */
+        return FALSE;
+    }
+
+    /* Is this a VGA adapter? */
+    if ((BaseClass == PCI_CLASS_DISPLAY_CTLR) &&
+        (SubClass == PCI_SUBCLASS_VID_VGA_CTLR))
+    {
+        /* Never disable decodes if this is for power down */
+        return ForPowerDown;
+    }
+
+    /* Check for legacy devices */
+    if (BaseClass == PCI_CLASS_PRE_20)
+    {
+        /* Never disable video adapter cards if this is for power down */
+        if (SubClass == PCI_SUBCLASS_PRE_20_VGA) return ForPowerDown;
+    }
+    else if (BaseClass == PCI_CLASS_DISPLAY_CTLR)
+    {
+        /* Never disable VGA adapters if this is for power down */
+        if (SubClass == PCI_SUBCLASS_VID_VGA_CTLR) return ForPowerDown;
+    }
+    else if (BaseClass == PCI_CLASS_BRIDGE_DEV)
+    {
+        /* Check for legacy bridges */
+        if ((SubClass == PCI_SUBCLASS_BR_ISA) ||
+            (SubClass == PCI_SUBCLASS_BR_EISA) ||
+            (SubClass == PCI_SUBCLASS_BR_MCA) ||
+            (SubClass == PCI_SUBCLASS_BR_HOST) ||
+            (SubClass == PCI_SUBCLASS_BR_OTHER))
+        {
+            /* Never disable these */
+            return FALSE;
+        }
+        else if ((SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI) ||
+                 (SubClass == PCI_SUBCLASS_BR_CARDBUS))
+        {
+            /* This is a supported bridge, but does it have a VGA card? */
+            if (!DeviceExtension)
+            {
+                /* Read the bridge control flag from the PCI header */
+                IsVga = Config->u.type1.BridgeControl & PCI_ENABLE_BRIDGE_VGA;
+            }
+            else
+            {
+                /* Read the cached flag in the device extension */
+                IsVga = DeviceExtension->Dependent.type1.VgaBitSet;
+            }
+
+            /* Never disable VGA adapters if this is for power down */
+            if (IsVga) return ForPowerDown;
+        }
+    }
+
+    /* Finally, never disable decodes if there's no power management */
+    return !(HackFlags & PCI_HACK_NO_PM_CAPS);
+}
+
+PCI_DEVICE_TYPES
+NTAPI
+PciClassifyDeviceType(IN PPCI_PDO_EXTENSION PdoExtension)
+{
+    ASSERT(PdoExtension->ExtensionType == PciPdoExtensionType);
+
+    /* Differenriate between devices and bridges */
+    if (PdoExtension->BaseClass != PCI_CLASS_BRIDGE_DEV) return PciTypeDevice;
+
+    /* The PCI Bus driver handles only CardBus and PCI bridges (plus host) */
+    if (PdoExtension->SubClass == PCI_SUBCLASS_BR_HOST) return PciTypeHostBridge;
+    if (PdoExtension->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI) return PciTypePciBridge;
+    if (PdoExtension->SubClass == PCI_SUBCLASS_BR_CARDBUS) return PciTypeCardbusBridge;
+
+    /* Any other kind of bridge is treated like a device */
+    return PciTypeDevice;
+}
+
+ULONG_PTR
+NTAPI
+PciExecuteCriticalSystemRoutine(IN ULONG_PTR IpiContext)
+{
+    PPCI_IPI_CONTEXT Context = (PPCI_IPI_CONTEXT)IpiContext;
+
+    /* Check if the IPI is already running */
+    if (!InterlockedDecrement(&Context->RunCount))
+    {
+        /* Nope, this is the first instance, so execute the IPI function */
+        Context->Function(Context->DeviceExtension, Context->Context);
+
+        /* Notify anyone that was spinning that they can stop now */
+        Context->Barrier = 0;
+    }
+    else
+    {
+        /* Spin until it has finished running */
+        while (Context->Barrier);
+    }
+
+    /* Done */
+    return 0;
+}
+
+BOOLEAN
+NTAPI
+PciIsSlotPresentInParentMethod(IN PPCI_PDO_EXTENSION PdoExtension,
+                               IN ULONG Method)
+{
+    BOOLEAN FoundSlot;
+    PACPI_METHOD_ARGUMENT Argument;
+    ACPI_EVAL_INPUT_BUFFER InputBuffer;
+    PACPI_EVAL_OUTPUT_BUFFER OutputBuffer;
+    ULONG i, Length;
+    NTSTATUS Status;
+    PAGED_CODE();
+
+    /* Assume slot is not part of the parent method */
+    FoundSlot = FALSE;
+
+    /* Allocate a 2KB buffer for the method return parameters */
+    Length = sizeof(ACPI_EVAL_OUTPUT_BUFFER) + 2048;
+    OutputBuffer = ExAllocatePoolWithTag(PagedPool, Length, 'BicP');
+    if (OutputBuffer)
+    {
+        /* Clear out the output buffer */
+        RtlZeroMemory(OutputBuffer, Length);
+
+        /* Initialize the input buffer with the method requested */
+        InputBuffer.Signature = 0;
+        *(PULONG)InputBuffer.MethodName = Method;
+        InputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE;
+
+        /* Send it to the ACPI driver */
+        Status = PciSendIoctl(PdoExtension->ParentFdoExtension->PhysicalDeviceObject,
+                              IOCTL_ACPI_EVAL_METHOD,
+                              &InputBuffer,
+                              sizeof(ACPI_EVAL_INPUT_BUFFER),
+                              OutputBuffer,
+                              Length);
+        if (NT_SUCCESS(Status))
+        {
+            /* Scan all output arguments */
+            for (i = 0; i < OutputBuffer->Count; i++)
+            {
+                /* Make sure it's an integer */
+                Argument = &OutputBuffer->Argument[i];
+                if (Argument->Type != ACPI_METHOD_ARGUMENT_INTEGER) continue;
+
+                /* Check if the argument matches this PCI slot structure */
+                if (Argument->Argument == ((PdoExtension->Slot.u.bits.DeviceNumber) |
+                                           ((PdoExtension->Slot.u.bits.FunctionNumber) << 16)))
+                {
+                    /* This slot has been found, return it */
+                    FoundSlot = TRUE;
+                    break;
+                }
+            }
+        }
+
+        /* Finished with the buffer, free it */
+        ExFreePoolWithTag(OutputBuffer, 0);
+    }
+
+    /* Return if the slot was found */
+    return FoundSlot;
+}
+
+ULONG
+NTAPI
+PciGetLengthFromBar(IN ULONG Bar)
+{
+    ULONG Length;
+
+    /* I/O addresses vs. memory addresses start differently due to alignment */
+    Length = 1 << ((Bar & PCI_ADDRESS_IO_SPACE) ? 2 : 4);
+
+    /* Keep going until a set bit */
+    while (!(Length & Bar) && (Length)) Length <<= 1;
+
+    /* Return the length (might be 0 on 64-bit because it's the low-word) */
+    if ((Bar & PCI_ADDRESS_MEMORY_TYPE_MASK) != PCI_TYPE_64BIT) ASSERT(Length);
+    return Length;
+}
+
+BOOLEAN
+NTAPI
+PciCreateIoDescriptorFromBarLimit(PIO_RESOURCE_DESCRIPTOR ResourceDescriptor,
+                                  IN PULONG BarArray,
+                                  IN BOOLEAN Rom)
+{
+    ULONG CurrentBar, BarLength, BarMask;
+    BOOLEAN Is64BitBar = FALSE;
+
+    /* Check if the BAR is nor I/O nor memory */
+    CurrentBar = BarArray[0];
+    if (!(CurrentBar & ~PCI_ADDRESS_IO_SPACE))
+    {
+        /* Fail this descriptor */
+        ResourceDescriptor->Type = CmResourceTypeNull;
+        return FALSE;
+    }
+
+    /* Set default flag and clear high words */
+    ResourceDescriptor->Flags = 0;
+    ResourceDescriptor->u.Generic.MaximumAddress.HighPart = 0;
+    ResourceDescriptor->u.Generic.MinimumAddress.LowPart = 0;
+    ResourceDescriptor->u.Generic.MinimumAddress.HighPart = 0;
+
+    /* Check for ROM Address */
+    if (Rom)
+    {
+        /* Clean up the BAR to get just the address */
+        CurrentBar &= PCI_ADDRESS_ROM_ADDRESS_MASK;
+        if (!CurrentBar)
+        {
+            /* Invalid ar, fail this descriptor */
+            ResourceDescriptor->Type = CmResourceTypeNull;
+            return FALSE;
+        }
+
+        /* ROM Addresses are always read only */
+        ResourceDescriptor->Flags = CM_RESOURCE_MEMORY_READ_ONLY;
+    }
+
+    /* Compute the length, assume it's the alignment for now */
+    BarLength = PciGetLengthFromBar(CurrentBar);
+    ResourceDescriptor->u.Generic.Length = BarLength;
+    ResourceDescriptor->u.Generic.Alignment = BarLength;
+
+    /* Check what kind of BAR this is */
+    if (CurrentBar & PCI_ADDRESS_IO_SPACE)
+    {
+        /* Use correct mask to decode the address */
+        BarMask = PCI_ADDRESS_IO_ADDRESS_MASK;
+
+        /* Set this as an I/O Port descriptor */
+        ResourceDescriptor->Type = CmResourceTypePort;
+        ResourceDescriptor->Flags = CM_RESOURCE_PORT_IO;
+    }
+    else
+    {
+        /* Use correct mask to decode the address */
+        BarMask = PCI_ADDRESS_MEMORY_ADDRESS_MASK;
+
+        /* Set this as a memory descriptor */
+        ResourceDescriptor->Type = CmResourceTypeMemory;
+
+        /* Check if it's 64-bit or 20-bit decode */
+        if ((CurrentBar & PCI_ADDRESS_MEMORY_TYPE_MASK) == PCI_TYPE_64BIT)
+        {
+            /* The next BAR has the high word, read it */
+            ResourceDescriptor->u.Port.MaximumAddress.HighPart = BarArray[1];
+            Is64BitBar = TRUE;
+        }
+        else if ((CurrentBar & PCI_ADDRESS_MEMORY_TYPE_MASK) == PCI_TYPE_20BIT)
+        {
+            /* Use the correct mask to decode the address */
+            BarMask = ~0xFFF0000F;
+        }
+
+        /* Check if the BAR is listed as prefetchable memory */
+        if (CurrentBar & PCI_ADDRESS_MEMORY_PREFETCHABLE)
+        {
+            /* Mark the descriptor in the same way */
+            ResourceDescriptor->Flags |= CM_RESOURCE_MEMORY_PREFETCHABLE;
+        }
+    }
+
+    /* Now write down the maximum address based on the base + length */
+    ResourceDescriptor->u.Port.MaximumAddress.QuadPart = (CurrentBar & BarMask) +
+                                                         BarLength - 1;
+
+    /* Return if this is a 64-bit BAR, so the loop code knows to skip the next one */
+    return Is64BitBar;
+}
+
+VOID
+NTAPI
+PciDecodeEnable(IN PPCI_PDO_EXTENSION PdoExtension,
+                IN BOOLEAN Enable,
+                OUT PUSHORT Command)
+{
+    USHORT CommandValue;
+
+    /*
+     * If decodes are being disabled, make sure it's allowed, and in both cases,
+     * make sure that a hackflag isn't preventing touching the decodes at all.
+     */
+    if (((Enable) || (PciCanDisableDecodes(PdoExtension, 0, 0, 0))) &&
+        !(PdoExtension->HackFlags & PCI_HACK_PRESERVE_COMMAND))
+    {
+        /* Did the caller already have a command word? */
+        if (Command)
+        {
+            /* Use the caller's */
+            CommandValue = *Command;
+        }
+        else
+        {
+            /* Otherwise, read the current command */
+            PciReadDeviceConfig(PdoExtension,
+                                &Command,
+                                FIELD_OFFSET(PCI_COMMON_HEADER, Command),
+                                sizeof(USHORT));
+        }
+
+        /* Turn off decodes by default */
+        CommandValue &= ~(PCI_ENABLE_IO_SPACE |
+                          PCI_ENABLE_MEMORY_SPACE |
+                          PCI_ENABLE_BUS_MASTER);
+
+        /* If requested, enable the decodes that were enabled at init time */
+        if (Enable) CommandValue |= PdoExtension->CommandEnables &
+                                    (PCI_ENABLE_IO_SPACE |
+                                     PCI_ENABLE_MEMORY_SPACE |
+                                     PCI_ENABLE_BUS_MASTER);
+
+        /* Update the command word */
+        PciWriteDeviceConfig(PdoExtension,
+                             &CommandValue,
+                             FIELD_OFFSET(PCI_COMMON_HEADER, Command),
+                             sizeof(USHORT));
+    }
+}
+
+NTSTATUS
+NTAPI
+PciQueryBusInformation(IN PPCI_PDO_EXTENSION PdoExtension,
+                       IN PPNP_BUS_INFORMATION* Buffer)
+{
+    PPNP_BUS_INFORMATION BusInfo;
+
+    /* Allocate a structure for the bus information */
+    BusInfo = ExAllocatePoolWithTag(PagedPool,
+                                    sizeof(PNP_BUS_INFORMATION),
+                                    'BicP');
+    if (!BusInfo) return STATUS_INSUFFICIENT_RESOURCES;
+
+    /* Write the correct GUID and bus type identifier, and fill the bus number */
+    BusInfo->BusTypeGuid = GUID_BUS_TYPE_PCI;
+    BusInfo->LegacyBusType = PCIBus;
+    BusInfo->BusNumber = PdoExtension->ParentFdoExtension->BaseBus;
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+PciDetermineSlotNumber(IN PPCI_PDO_EXTENSION PdoExtension,
+                       OUT PULONG SlotNumber)
+{
+    PPCI_FDO_EXTENSION ParentExtension;
+    ULONG ResultLength;
+    NTSTATUS Status;
+    PSLOT_INFO SlotInfo;
+
+    /* Check if a $PIR from the BIOS is used (legacy IRQ routing) */
+    ParentExtension = PdoExtension->ParentFdoExtension;
+    DPRINT1("Slot lookup for %d.%d.%d\n",
+            ParentExtension ? ParentExtension->BaseBus : -1,
+            PdoExtension->Slot.u.bits.DeviceNumber,
+            PdoExtension->Slot.u.bits.FunctionNumber);
+    if ((PciIrqRoutingTable) && (ParentExtension))
+    {
+        /* Read every slot information entry */
+        SlotInfo = &PciIrqRoutingTable->Slot[0];
+        DPRINT1("PIR$ %p is %lx bytes, slot 0 is at: %lx\n",
+                PciIrqRoutingTable, PciIrqRoutingTable->TableSize, SlotInfo);
+        while (SlotInfo < (PSLOT_INFO)((ULONG_PTR)PciIrqRoutingTable +
+                                       PciIrqRoutingTable->TableSize))
+        {
+            DPRINT1("Slot Info: %d.%d->#%d\n",
+                    SlotInfo->BusNumber,
+                    SlotInfo->DeviceNumber,
+                    SlotInfo->SlotNumber);
+
+            /* Check if this slot information matches the PDO being queried */
+            if ((ParentExtension->BaseBus == SlotInfo->BusNumber) &&
+                (PdoExtension->Slot.u.bits.DeviceNumber == SlotInfo->DeviceNumber >> 3) &&
+                (SlotInfo->SlotNumber))
+            {
+                /* We found it, return it and return success */
+                *SlotNumber = SlotInfo->SlotNumber;
+                return STATUS_SUCCESS;
+            }
+
+            /* Try the next slot */
+            SlotInfo++;
+        }
+    }
+
+    /* Otherwise, grab the parent FDO and check if it's the root */
+    if (PCI_IS_ROOT_FDO(ParentExtension))
+    {
+        /* The root FDO doesn't have a slot number */
+        Status = STATUS_UNSUCCESSFUL;
+    }
+    else
+    {
+        /* Otherwise, query the slot/UI address/number as a device property */
+        Status = IoGetDeviceProperty(ParentExtension->PhysicalDeviceObject,
+                                     DevicePropertyUINumber,
+                                     sizeof(ULONG),
+                                     SlotNumber,
+                                     &ResultLength);
+    }
+
+    /* Return the status of this endeavour */
+    return Status;
+}
+
+NTSTATUS
+NTAPI
+PciGetDeviceCapabilities(IN PDEVICE_OBJECT DeviceObject,
+                         IN OUT PDEVICE_CAPABILITIES DeviceCapability)
+{
+    PIRP Irp;
+    NTSTATUS Status;
+    KEVENT Event;
+    PDEVICE_OBJECT AttachedDevice;
+    PIO_STACK_LOCATION IoStackLocation;
+    IO_STATUS_BLOCK IoStatusBlock;
+    PAGED_CODE();
+
+    /* Zero out capabilities and set undefined values to start with */
+    RtlZeroMemory(DeviceCapability, sizeof(DEVICE_CAPABILITIES));
+    DeviceCapability->Size = sizeof(DEVICE_CAPABILITIES);
+    DeviceCapability->Version = 1;
+    DeviceCapability->Address = -1;
+    DeviceCapability->UINumber = -1;
+
+    /* Build the wait event for the IOCTL */
+    KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
+
+    /* Find the device the PDO is attached to */
+    AttachedDevice = IoGetAttachedDeviceReference(DeviceObject);
+
+    /* And build an IRP for it */
+    Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
+                                       AttachedDevice,
+                                       NULL,
+                                       0,
+                                       NULL,
+                                       &Event,
+                                       &IoStatusBlock);
+    if (!Irp)
+    {
+        /* The IRP failed, fail the request as well */
+        ObDereferenceObject(AttachedDevice);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    /* Set default status */
+    Irp->IoStatus.Information = 0;
+    Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+
+    /* Get a stack location in this IRP */
+    IoStackLocation = IoGetNextIrpStackLocation(Irp);
+    ASSERT(IoStackLocation);
+
+    /* Initialize it as a query capabilities IRP, with no completion routine */
+    RtlZeroMemory(IoStackLocation, sizeof(IO_STACK_LOCATION));
+    IoStackLocation->MajorFunction = IRP_MJ_PNP;
+    IoStackLocation->MinorFunction = IRP_MN_QUERY_CAPABILITIES;
+    IoStackLocation->Parameters.DeviceCapabilities.Capabilities = DeviceCapability;
+    IoSetCompletionRoutine(Irp, NULL, NULL, FALSE, FALSE, FALSE);
+
+    /* Send the IOCTL to the driver */
+    Status = IoCallDriver(AttachedDevice, Irp);
+    if (Status == STATUS_PENDING)
+    {
+        /* Wait for a response and update the actual status */
+        KeWaitForSingleObject(&Event,
+                              Executive,
+                              KernelMode,
+                              FALSE,
+                              NULL);
+        Status = Irp->IoStatus.Status;
+    }
+
+    /* Done, dereference the attached device and return the final result */
+    ObDereferenceObject(AttachedDevice);
+    return Status;
+}
+
+NTSTATUS
+NTAPI
+PciQueryPowerCapabilities(IN PPCI_PDO_EXTENSION PdoExtension,
+                          IN PDEVICE_CAPABILITIES DeviceCapability)
+{
+    PDEVICE_OBJECT DeviceObject;
+    NTSTATUS Status;
+    DEVICE_CAPABILITIES AttachedCaps;
+    DEVICE_POWER_STATE NewPowerState, DevicePowerState, DeviceWakeLevel, DeviceWakeState;
+    SYSTEM_POWER_STATE SystemWakeState, DeepestWakeState, CurrentState;
+
+    /* Nothing is known at first */
+    DeviceWakeState = PowerDeviceUnspecified;
+    SystemWakeState = DeepestWakeState = PowerSystemUnspecified;
+
+    /* Get the PCI capabilities for the parent PDO */
+    DeviceObject = PdoExtension->ParentFdoExtension->PhysicalDeviceObject;
+    Status = PciGetDeviceCapabilities(DeviceObject, &AttachedCaps);
+    ASSERT(NT_SUCCESS(Status));
+    if (!NT_SUCCESS(Status)) return Status;
+
+    /* Check if there's not an existing device state for S0 */
+    if (!AttachedCaps.DeviceState[PowerSystemWorking])
+    {
+        /* Set D0<->S0 mapping */
+        AttachedCaps.DeviceState[PowerSystemWorking] = PowerDeviceD0;
+    }
+
+    /* Check if there's not an existing device state for S3 */
+    if (!AttachedCaps.DeviceState[PowerSystemShutdown])
+    {
+        /* Set D3<->S3 mapping */
+        AttachedCaps.DeviceState[PowerSystemShutdown] = PowerDeviceD3;
+    }
+
+    /* Check for a PDO with broken, or no, power capabilities */
+    if (PdoExtension->HackFlags & PCI_HACK_NO_PM_CAPS)
+    {
+        /* Unknown wake device states */
+        DeviceCapability->DeviceWake = PowerDeviceUnspecified;
+        DeviceCapability->SystemWake = PowerSystemUnspecified;
+
+        /* No device state support */
+        DeviceCapability->DeviceD1 = FALSE;
+        DeviceCapability->DeviceD2 = FALSE;
+
+        /* No waking from any low-power device state is supported */
+        DeviceCapability->WakeFromD0 = FALSE;
+        DeviceCapability->WakeFromD1 = FALSE;
+        DeviceCapability->WakeFromD2 = FALSE;
+        DeviceCapability->WakeFromD3 = FALSE;
+
+        /* For the rest, copy whatever the parent PDO had */
+        RtlCopyMemory(DeviceCapability->DeviceState,
+                      AttachedCaps.DeviceState,
+                      sizeof(DeviceCapability->DeviceState));
+        return STATUS_SUCCESS;
+    }
+
+    /* The PCI Device has power capabilities, so read which ones are supported */
+    DeviceCapability->DeviceD1 = PdoExtension->PowerCapabilities.Support.D1;
+    DeviceCapability->DeviceD2 = PdoExtension->PowerCapabilities.Support.D2;
+    DeviceCapability->WakeFromD0 = PdoExtension->PowerCapabilities.Support.PMED0;
+    DeviceCapability->WakeFromD1 = PdoExtension->PowerCapabilities.Support.PMED1;
+    DeviceCapability->WakeFromD2 = PdoExtension->PowerCapabilities.Support.PMED2;
+
+    /* Can the attached device wake from D3? */
+    if (AttachedCaps.DeviceWake != PowerDeviceD3)
+    {
+        /* It can't, so check if this PDO supports hot D3 wake */
+        DeviceCapability->WakeFromD3 = PdoExtension->PowerCapabilities.Support.PMED3Hot;
+    }
+    else
+    {
+        /* It can, is this the root bus? */
+        if (PCI_IS_ROOT_FDO(PdoExtension->ParentFdoExtension))
+        {
+            /* This is the root bus, so just check if it supports hot D3 wake */
+            DeviceCapability->WakeFromD3 = PdoExtension->PowerCapabilities.Support.PMED3Hot;
+        }
+        else
+        {
+            /* Take the minimums? -- need to check with briang at work */
+            UNIMPLEMENTED;
+        }
+    }
+
+    /* Now loop each system power state to determine its device state mapping */
+    for (CurrentState = PowerSystemWorking;
+         CurrentState < PowerSystemMaximum;
+         CurrentState++)
+    {
+        /* Read the current mapping from the attached device */
+        DevicePowerState = AttachedCaps.DeviceState[CurrentState];
+        NewPowerState = DevicePowerState;
+
+        /* The attachee suports D1, but this PDO does not */
+        if ((NewPowerState == PowerDeviceD1) &&
+            !(PdoExtension->PowerCapabilities.Support.D1))
+        {
+            /* Fall back to D2 */
+            NewPowerState = PowerDeviceD2;
+        }
+
+        /* The attachee supports D2, but this PDO does not */
+        if ((NewPowerState == PowerDeviceD2) &&
+            !(PdoExtension->PowerCapabilities.Support.D2))
+        {
+            /* Fall back to D3 */
+            NewPowerState = PowerDeviceD3;
+        }
+
+        /* Set the mapping based on the best state supported */
+        DeviceCapability->DeviceState[CurrentState] = NewPowerState;
+
+        /* Check if sleep states are being processed, and a mapping was found */
+        if ((CurrentState < PowerSystemHibernate) &&
+            (NewPowerState != PowerDeviceUnspecified))
+        {
+            /* Save this state as being the deepest one found until now */
+            DeepestWakeState = CurrentState;
+        }
+
+        /*
+         * Finally, check if the computed sleep state is within the states that
+         * this device can wake the system from, and if it's higher or equal to
+         * the sleep state mapping that came from the attachee, assuming that it
+         * had a valid mapping to begin with.
+         *
+         * It this is the case, then make sure that the computed sleep state is
+         * matched by the device's ability to actually wake from that state.
+         *
+         * For devices that support D3, the PCI device only needs Hot D3 as long
+         * as the attachee's state is less than D3. Otherwise, if the attachee
+         * might also be at D3, this would require a Cold D3 wake, so check that
+         * the device actually support this.
+         */
+        if ((CurrentState < AttachedCaps.SystemWake) &&
+            (NewPowerState >= DevicePowerState) &&
+            (DevicePowerState != PowerDeviceUnspecified) &&
+            (((NewPowerState == PowerDeviceD0) && (DeviceCapability->WakeFromD0)) ||
+             ((NewPowerState == PowerDeviceD1) && (DeviceCapability->WakeFromD1)) ||
+             ((NewPowerState == PowerDeviceD2) && (DeviceCapability->WakeFromD2)) ||
+             ((NewPowerState == PowerDeviceD3) &&
+              (PdoExtension->PowerCapabilities.Support.PMED3Hot) &&
+              ((DevicePowerState < PowerDeviceD3) ||
+               (PdoExtension->PowerCapabilities.Support.PMED3Cold)))))
+        {
+            /* The mapping is valid, so this will be the lowest wake state */
+            SystemWakeState = CurrentState;
+            DeviceWakeState = NewPowerState;
+        }
+    }
+
+    /* Read the current wake level */
+    DeviceWakeLevel = PdoExtension->PowerState.DeviceWakeLevel;
+
+    /* Check if the attachee's wake levels are valid, and the PDO's is higher */
+    if ((AttachedCaps.SystemWake != PowerSystemUnspecified) &&
+        (AttachedCaps.DeviceWake != PowerDeviceUnspecified) &&
+        (DeviceWakeLevel != PowerDeviceUnspecified) &&
+        (DeviceWakeLevel >= AttachedCaps.DeviceWake))
+    {
+        /* Inherit the system wake from the attachee, and this PDO's wake level */
+        DeviceCapability->SystemWake = AttachedCaps.SystemWake;
+        DeviceCapability->DeviceWake = DeviceWakeLevel;
+
+        /* Now check if the wake level is D0, but the PDO doesn't support it */
+        if ((DeviceCapability->DeviceWake == PowerDeviceD0) &&
+            !(DeviceCapability->WakeFromD0))
+        {
+            /* Bump to D1 */
+            DeviceCapability->DeviceWake = PowerDeviceD1;
+        }
+
+        /* Now check if the wake level is D1, but the PDO doesn't support it */
+        if ((DeviceCapability->DeviceWake == PowerDeviceD1) &&
+            !(DeviceCapability->WakeFromD1))
+        {
+            /* Bump to D2 */
+            DeviceCapability->DeviceWake = PowerDeviceD2;
+        }
+
+        /* Now check if the wake level is D2, but the PDO doesn't support it */
+        if ((DeviceCapability->DeviceWake == PowerDeviceD2) &&
+            !(DeviceCapability->WakeFromD2))
+        {
+            /* Bump it to D3 */
+            DeviceCapability->DeviceWake = PowerDeviceD3;
+        }
+
+        /* Now check if the wake level is D3, but the PDO doesn't support it */
+        if ((DeviceCapability->DeviceWake == PowerDeviceD3) &&
+            !(DeviceCapability->WakeFromD3))
+        {
+            /* Then no valid wake state exists */
+            DeviceCapability->DeviceWake = PowerDeviceUnspecified;
+            DeviceCapability->SystemWake = PowerSystemUnspecified;
+        }
+
+        /* Check if no valid wake state was found */
+        if ((DeviceCapability->DeviceWake == PowerDeviceUnspecified) ||
+            (DeviceCapability->SystemWake == PowerSystemUnspecified))
+        {
+            /* Check if one was computed earlier */
+            if ((SystemWakeState != PowerSystemUnspecified) &&
+                (DeviceWakeState != PowerDeviceUnspecified))
+            {
+                /* Use the wake state that had been computed earlier */
+                DeviceCapability->DeviceWake = DeviceWakeState;
+                DeviceCapability->SystemWake = SystemWakeState;
+
+                /* If that state was D3, then the device supports Hot/Cold D3 */
+                if (DeviceWakeState == PowerDeviceD3) DeviceCapability->WakeFromD3 = TRUE;
+            }
+        }
+
+        /*
+         * Finally, check for off states (lower than S3, such as hibernate) and
+         * make sure that the device both supports waking from D3 as well as
+         * supports a Cold wake
+         */
+        if ((DeviceCapability->SystemWake > PowerSystemSleeping3) &&
+            ((DeviceCapability->DeviceWake != PowerDeviceD3) ||
+             !(PdoExtension->PowerCapabilities.Support.PMED3Cold)))
+        {
+            /* It doesn't, so pick the computed lowest wake state from earlier */
+            DeviceCapability->SystemWake = DeepestWakeState;
+        }
+
+        /* Set the PCI Specification mandated maximum latencies for transitions */
+        DeviceCapability->D1Latency = 0;
+        DeviceCapability->D2Latency = 2;
+        DeviceCapability->D3Latency = 100;
+
+        /* Sanity check */
+        ASSERT(DeviceCapability->DeviceState[PowerSystemWorking] == PowerDeviceD0);
+    }
+    else
+    {
+        /* No valid sleep states, no latencies to worry about */
+        DeviceCapability->D1Latency = 0;
+        DeviceCapability->D2Latency = 0;
+        DeviceCapability->D3Latency = 0;
+    }
+
+    /* This function always succeeds, even without power management support */
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+PciQueryCapabilities(IN PPCI_PDO_EXTENSION PdoExtension,
+                     IN OUT PDEVICE_CAPABILITIES DeviceCapability)
+{
+    NTSTATUS Status;
+
+    /* A PDO ID is never unique, and its address is its function and device */
+    DeviceCapability->UniqueID = FALSE;
+    DeviceCapability->Address = PdoExtension->Slot.u.bits.FunctionNumber |
+                                (PdoExtension->Slot.u.bits.DeviceNumber << 16);
+
+    /* Check for host bridges */
+    if ((PdoExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
+        (PdoExtension->SubClass == PCI_SUBCLASS_BR_HOST))
+    {
+        /* Raw device opens to a host bridge are acceptable */
+        DeviceCapability->RawDeviceOK = TRUE;
+    }
+    else
+    {
+        /* Otherwise, other PDOs cannot be directly opened */
+        DeviceCapability->RawDeviceOK = FALSE;
+    }
+
+    /* PCI PDOs are pretty fixed things */
+    DeviceCapability->LockSupported = FALSE;
+    DeviceCapability->EjectSupported = FALSE;
+    DeviceCapability->Removable = FALSE;
+    DeviceCapability->DockDevice = FALSE;
+
+    /* The slot number is stored as a device property, go query it */
+    PciDetermineSlotNumber(PdoExtension, &DeviceCapability->UINumber);
+
+    /* Finally, query and power capabilities and convert them for PnP usage */
+    Status = PciQueryPowerCapabilities(PdoExtension, DeviceCapability);
+
+    /* Dump the capabilities if it all worked, and return the status */
+    if (NT_SUCCESS(Status)) PciDebugDumpQueryCapabilities(DeviceCapability);
+    return Status;
+}
+
+PCM_PARTIAL_RESOURCE_DESCRIPTOR
+NTAPI
+PciNextPartialDescriptor(PCM_PARTIAL_RESOURCE_DESCRIPTOR CmDescriptor)
+{
+    PCM_PARTIAL_RESOURCE_DESCRIPTOR NextDescriptor;
+
+    /* Assume the descriptors are the fixed size ones */
+    NextDescriptor = CmDescriptor + 1;
+
+    /* But check if this is actually a variable-sized descriptor */
+    if (CmDescriptor->Type == CmResourceTypeDeviceSpecific)
+    {
+        /* Add the size of the variable section as well */
+        NextDescriptor = (PVOID)((ULONG_PTR)NextDescriptor +
+                                 CmDescriptor->u.DeviceSpecificData.DataSize);
+    }
+
+    /* Now the correct pointer has been computed, return it */
+    return NextDescriptor;
+}
+
 /* EOF */