/* * PROJECT: ReactOS PCI Bus Driver * LICENSE: BSD - See COPYING.ARM in the top level directory * FILE: drivers/bus/pci/utils.c * PURPOSE: Utility/Helper Support Code * PROGRAMMERS: ReactOS Portable Systems Group */ /* INCLUDES *******************************************************************/ #include #define NDEBUG #include /* 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("PCI Successfully did the impossible!\n", FALSE); break; } /* Allocate the required buffer */ Buffer = ExAllocatePoolWithTag(PagedPool, BufferLength, 'BicP'); if (!Buffer) { /* No memory, fail the request */ DPRINT1("PCI - Failed to allocate DeviceProperty buffer (%u 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 controllers 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(); UNREFERENCED_PARAMETER(DeviceExtension); /* 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); /* Differentiate 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; UNREFERENCED_PARAMETER(Buffer); /* 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.%u.%u\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: %p\n", PciIrqRoutingTable, PciIrqRoutingTable->TableSize, SlotInfo); while (SlotInfo < (PSLOT_INFO)((ULONG_PTR)PciIrqRoutingTable + PciIrqRoutingTable->TableSize)) { DPRINT1("Slot Info: %u.%u->#%u\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 supports 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; } /* EOF */