Author: evb
Date: Sun Jul 18 18:58:33 2010
New Revision: 48107
URL:
http://svn.reactos.org/svn/reactos?rev=48107&view=rev
Log:
Can't sleep so write more source codes! add scan bus functions to get power caps
(PciGetEnhancedCapabilities) and AGP caps, use PciReadDeviceCapability util function, now
can get wake levels, PCI power state plus target AGP ID. Now can find PCI Device that
can't sleep neither! Ha-ha!
Support PCI_HACK_NO_PM_CAPS, PCI_HACK_PRESERVE_COMMAND, PCI_HACK_DONT_DISABLE_DECOES
Add scan bus function to set power for PCI, for now to power up
(PciSetPowerManagedDevicePowerState), with support for device that is critical/broken
(PciCanDisableDecodes)
Check spec-correct with PciStallForPowerChange after define PciPowerDelayTable for
D0<->D3 crossmatrix spec timings (add PciReadDeviceConfig for support)
If bad spec timing use PCI verifier support (PciVerifierRetrieveFailureData) +
STATUS_DEVICE_PROTOCOL_ERROR
Add PciVerifierFailureTable with all failure type
Almost the time for resource discovery of BARs!
Modified:
trunk/reactos/drivers/bus/pcix/enum.c
trunk/reactos/drivers/bus/pcix/pci.h
trunk/reactos/drivers/bus/pcix/pci/config.c
trunk/reactos/drivers/bus/pcix/pcivrify.c
trunk/reactos/drivers/bus/pcix/power.c
trunk/reactos/drivers/bus/pcix/utils.c
Modified: trunk/reactos/drivers/bus/pcix/enum.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/drivers/bus/pcix/enum.c?re…
==============================================================================
--- trunk/reactos/drivers/bus/pcix/enum.c [iso-8859-1] (original)
+++ trunk/reactos/drivers/bus/pcix/enum.c [iso-8859-1] Sun Jul 18 18:58:33 2010
@@ -122,6 +122,126 @@
/* Hit one of the known bugs/hackflags, or this is a new kind of PCI unit */
DPRINT1(" Device skipped (not enumerated).\n");
return TRUE;
+}
+
+VOID
+NTAPI
+PciGetEnhancedCapabilities(IN PPCI_PDO_EXTENSION PdoExtension,
+ IN PPCI_COMMON_HEADER PciData)
+{
+ ULONG HeaderType, CapPtr, TargetAgpCapabilityId;
+ DEVICE_POWER_STATE WakeLevel;
+ PCI_CAPABILITIES_HEADER AgpCapability;
+ PCI_PM_CAPABILITY PowerCapabilities;
+ PAGED_CODE();
+
+ /* Assume no known wake level */
+ PdoExtension->PowerState.DeviceWakeLevel = PowerDeviceUnspecified;
+
+ /* Make sure the device has capabilities */
+ if (!(PciData->Status & PCI_STATUS_CAPABILITIES_LIST))
+ {
+ /* If it doesn't, there will be no power management */
+ PdoExtension->CapabilitiesPtr = 0;
+ PdoExtension->HackFlags |= PCI_HACK_NO_PM_CAPS;
+ }
+ else
+ {
+ /* There's capabilities, need to figure out where to get the offset */
+ HeaderType = PCI_CONFIGURATION_TYPE(PciData);
+ if (HeaderType == PCI_CARDBUS_BRIDGE_TYPE)
+ {
+ /* Use the bridge's header */
+ CapPtr = PciData->u.type2.CapabilitiesPtr;
+ }
+ else
+ {
+ /* Use the device header */
+ ASSERT(HeaderType <= PCI_CARDBUS_BRIDGE_TYPE);
+ CapPtr = PciData->u.type0.CapabilitiesPtr;
+ }
+
+ /* Make sure the pointer is spec-aligned and located, and save it */
+ DPRINT1("Device has capabilities at: %lx\n", CapPtr);
+ ASSERT(((CapPtr & 0x3) == 0) && (CapPtr >=
PCI_COMMON_HDR_LENGTH));
+ PdoExtension->CapabilitiesPtr = CapPtr;
+
+ /* Check for PCI-to-PCI Bridges and AGP bridges */
+ if ((PdoExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
+ ((PdoExtension->SubClass == PCI_SUBCLASS_BR_HOST) ||
+ (PdoExtension->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI)))
+ {
+ /* Query either the raw AGP capabilitity, or the Target AGP one */
+ TargetAgpCapabilityId = (PdoExtension->SubClass ==
+ PCI_SUBCLASS_BR_PCI_TO_PCI) ?
+ PCI_CAPABILITY_ID_AGP_TARGET :
+ PCI_CAPABILITY_ID_AGP;
+ if (PciReadDeviceCapability(PdoExtension,
+ PdoExtension->CapabilitiesPtr,
+ TargetAgpCapabilityId,
+ &AgpCapability,
+ sizeof(PCI_CAPABILITIES_HEADER)))
+ {
+ /* AGP target ID was found, store it */
+ DPRINT1("AGP ID: %lx\n", TargetAgpCapabilityId);
+ PdoExtension->TargetAgpCapabilityId = TargetAgpCapabilityId;
+ }
+ }
+
+ /* Check for devices that are known not to have proper power management */
+ if (!(PdoExtension->HackFlags & PCI_HACK_NO_PM_CAPS))
+ {
+ /* Query if this device supports power management */
+ if (!PciReadDeviceCapability(PdoExtension,
+ PdoExtension->CapabilitiesPtr,
+ PCI_CAPABILITY_ID_POWER_MANAGEMENT,
+ &PowerCapabilities.Header,
+ sizeof(PCI_PM_CAPABILITY)))
+ {
+ /* No power management, so act as if it had the hackflag set */
+ DPRINT1("No PM caps, disabling PM\n");
+ PdoExtension->HackFlags |= PCI_HACK_NO_PM_CAPS;
+ }
+ else
+ {
+ /* Otherwise, pick the highest wake level that is supported */
+ WakeLevel = PowerDeviceUnspecified;
+ if (PowerCapabilities.PMC.Capabilities.Support.PMED0)
+ WakeLevel = PowerDeviceD0;
+ if (PowerCapabilities.PMC.Capabilities.Support.PMED1)
+ WakeLevel = PowerDeviceD1;
+ if (PowerCapabilities.PMC.Capabilities.Support.PMED2)
+ WakeLevel = PowerDeviceD2;
+ if (PowerCapabilities.PMC.Capabilities.Support.PMED3Hot)
+ WakeLevel = PowerDeviceD3;
+ if (PowerCapabilities.PMC.Capabilities.Support.PMED3Cold)
+ WakeLevel = PowerDeviceD3;
+ PdoExtension->PowerState.DeviceWakeLevel = WakeLevel;
+
+ /* Convert the PCI power state to the NT power state */
+ PdoExtension->PowerState.CurrentDeviceState =
+ PowerCapabilities.PMCSR.ControlStatus.PowerState + 1;
+
+ /* Save all the power capabilities */
+ PdoExtension->PowerCapabilities = PowerCapabilities.PMC.Capabilities;
+ DPRINT1("PM Caps Found! Wake Level: %d Power State: %d\n",
+ WakeLevel, PdoExtension->PowerState.CurrentDeviceState);
+ }
+ }
+ }
+
+ /* At the very end of all this, does this device not have power management? */
+ if (PdoExtension->HackFlags & PCI_HACK_NO_PM_CAPS)
+ {
+ /* Then guess the current state based on whether the decodes are on */
+ PdoExtension->PowerState.CurrentDeviceState =
+ PciData->Command & (PCI_ENABLE_IO_SPACE |
+ PCI_ENABLE_MEMORY_SPACE |
+ PCI_ENABLE_BUS_MASTER) ?
+ PowerDeviceD0: PowerDeviceD3;
+ DPRINT1("PM is off, so assumed device is: %d based on enables\n",
+ PdoExtension->PowerState.CurrentDeviceState);
+ }
}
NTSTATUS
@@ -374,6 +494,12 @@
NewExtension->CommandEnables = PciData->Command;
NewExtension->HackFlags = HackFlags;
+ /* Get power, AGP, and other capability data */
+ PciGetEnhancedCapabilities(NewExtension, PciData);
+
+ /* Power up the device */
+ PciSetPowerManagedDevicePowerState(NewExtension, PowerDeviceD0, FALSE);
+
/* Save interrupt pin */
NewExtension->InterruptPin = PciData->u.type0.InterruptPin;
Modified: trunk/reactos/drivers/bus/pcix/pci.h
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/drivers/bus/pcix/pci.h?rev…
==============================================================================
--- trunk/reactos/drivers/bus/pcix/pci.h [iso-8859-1] (original)
+++ trunk/reactos/drivers/bus/pcix/pci.h [iso-8859-1] Sun Jul 18 18:58:33 2010
@@ -67,6 +67,11 @@
//
#define MAX_DEBUGGING_DEVICES_SUPPORTED 0x04
+//
+// PCI Driver Verifier Failures
+//
+#define PCI_VERIFIER_CODES 0x04
+
//
// Device Extension, Interface, Translator and Arbiter Signatures
//
@@ -376,6 +381,17 @@
} PCI_ARBITER_INSTANCE, *PPCI_ARBITER_INSTANCE;
//
+// PCI Verifier Data
+//
+typedef struct _PCI_VERIFIER_DATA
+{
+ ULONG FailureCode;
+ VF_FAILURE_CLASS FailureClass;
+ ULONG AssertionControl;
+ PCHAR DebuggerMessageText;
+} PCI_VERIFIER_DATA, *PPCI_VERIFIER_DATA;
+
+//
// IRP Dispatch Routines
//
NTSTATUS
@@ -440,6 +456,14 @@
IN PIRP Irp,
IN PIO_STACK_LOCATION IoStackLocation,
IN PPCI_FDO_EXTENSION DeviceExtension
+);
+
+NTSTATUS
+NTAPI
+PciSetPowerManagedDevicePowerState(
+ IN PPCI_PDO_EXTENSION DeviceExtension,
+ IN DEVICE_POWER_STATE DeviceState,
+ IN BOOLEAN IrpSet
);
//
@@ -776,6 +800,12 @@
NTAPI
PciVerifierInit(
IN PDRIVER_OBJECT DriverObject
+);
+
+PPCI_VERIFIER_DATA
+NTAPI
+PciVerifierRetrieveFailureData(
+ IN ULONG FailureCode
);
//
@@ -925,6 +955,25 @@
OUT PPCI_COMMON_HEADER PciData
);
+UCHAR
+NTAPI
+PciReadDeviceCapability(
+ IN PPCI_PDO_EXTENSION DeviceExtension,
+ IN UCHAR Offset,
+ IN ULONG CapabilityId,
+ OUT PPCI_CAPABILITIES_HEADER Buffer,
+ IN ULONG Length
+);
+
+BOOLEAN
+NTAPI
+PciCanDisableDecodes(
+ IN PPCI_PDO_EXTENSION DeviceExtension,
+ IN PPCI_COMMON_HEADER Config,
+ IN ULONGLONG HackFlags,
+ IN BOOLEAN ForPowerDown
+);
+
//
// Configuration Routines
//
@@ -947,6 +996,15 @@
VOID
NTAPI
PciWriteDeviceConfig(
+ IN PPCI_PDO_EXTENSION DeviceExtension,
+ IN PVOID Buffer,
+ IN ULONG Offset,
+ IN ULONG Length
+);
+
+VOID
+NTAPI
+PciReadDeviceConfig(
IN PPCI_PDO_EXTENSION DeviceExtension,
IN PVOID Buffer,
IN ULONG Offset,
Modified: trunk/reactos/drivers/bus/pcix/pci/config.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/drivers/bus/pcix/pci/confi…
==============================================================================
--- trunk/reactos/drivers/bus/pcix/pci/config.c [iso-8859-1] (original)
+++ trunk/reactos/drivers/bus/pcix/pci/config.c [iso-8859-1] Sun Jul 18 18:58:33 2010
@@ -24,7 +24,7 @@
{
UCHAR InterruptLine = 0, PciInterruptLine;
ULONG Length;
-
+
/* Does the device have an interrupt pin? */
if (PdoExtension->InterruptPin)
{
@@ -100,6 +100,22 @@
Offset,
Length,
FALSE);
+}
+
+VOID
+NTAPI
+PciReadDeviceConfig(IN PPCI_PDO_EXTENSION DeviceExtension,
+ IN PVOID Buffer,
+ IN ULONG Offset,
+ IN ULONG Length)
+{
+ /* Call the generic worker function */
+ PciReadWriteConfigSpace(DeviceExtension->ParentFdoExtension,
+ DeviceExtension->Slot,
+ Buffer,
+ Offset,
+ Length,
+ TRUE);
}
VOID
Modified: trunk/reactos/drivers/bus/pcix/pcivrify.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/drivers/bus/pcix/pcivrify.…
==============================================================================
--- trunk/reactos/drivers/bus/pcix/pcivrify.c [iso-8859-1] (original)
+++ trunk/reactos/drivers/bus/pcix/pcivrify.c [iso-8859-1] Sun Jul 18 18:58:33 2010
@@ -17,7 +17,61 @@
BOOLEAN PciVerifierRegistered;
PVOID PciVerifierNotificationHandle;
+PCI_VERIFIER_DATA PciVerifierFailureTable[PCI_VERIFIER_CODES] =
+{
+ {
+ 1,
+ VFFAILURE_FAIL_LOGO,
+ 0,
+ "The BIOS has reprogrammed the bus numbers of an active PCI device "
+ "(!devstack %DevObj) during a dock or undock!"
+ },
+ {
+ 2,
+ VFFAILURE_FAIL_LOGO,
+ 0,
+ "A device in the system did not update it's PMCSR register in the spec
"
+ "mandated time (!devstack %DevObj, Power state D%Ulong)"
+ },
+ {
+ 3,
+ VFFAILURE_FAIL_LOGO,
+ 0,
+ "A driver controlling a PCI device has tried to access OS controlled "
+ "configuration space registers (!devstack %DevObj, Offset 0x%Ulong1, "
+ "Length 0x%Ulong2)"
+ },
+ {
+ 4,
+ VFFAILURE_FAIL_UNDER_DEBUGGER,
+ 0,
+ "A driver controlling a PCI device has tried to read or write from an
"
+ "invalid space using IRP_MN_READ/WRITE_CONFIG or via
BUS_INTERFACE_STANDARD."
+ " NB: These functions take WhichSpace parameters of the form
PCI_WHICHSPACE_*"
+ " and not a BUS_DATA_TYPE (!devstack %DevObj, WhichSpace 0x%Ulong1)"
+ },
+};
+
/* FUNCTIONS ******************************************************************/
+
+PPCI_VERIFIER_DATA
+NTAPI
+PciVerifierRetrieveFailureData(IN ULONG FailureCode)
+{
+ PPCI_VERIFIER_DATA VerifierData;
+
+ /* Scan the verifier failure table for this code */
+ VerifierData = PciVerifierFailureTable;
+ while (VerifierData->FailureCode != FailureCode)
+ {
+ /* Keep searching */
+ ++VerifierData;
+ ASSERT(VerifierData < &PciVerifierFailureTable[PCI_VERIFIER_CODES]);
+ }
+
+ /* Return the entry for this code */
+ return VerifierData;
+}
NTSTATUS
NTAPI
Modified: trunk/reactos/drivers/bus/pcix/power.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/drivers/bus/pcix/power.c?r…
==============================================================================
--- trunk/reactos/drivers/bus/pcix/power.c [iso-8859-1] (original)
+++ trunk/reactos/drivers/bus/pcix/power.c [iso-8859-1] Sun Jul 18 18:58:33 2010
@@ -14,7 +14,191 @@
/* GLOBALS ********************************************************************/
+ULONG PciPowerDelayTable[PowerDeviceD3 * PowerDeviceD3] =
+{
+ 0, // D0 -> D0
+ 0, // D1 -> D0
+ 200, // D2 -> D0
+ 10000, // D3 -> D0
+
+ 0, // D0 -> D1
+ 0, // D1 -> D1
+ 200, // D2 -> D1
+ 10000, // D3 -> D1
+
+ 200, // D0 -> D2
+ 200, // D1 -> D2
+ 0, // D2 -> D2
+ 10000, // D3 -> D2
+
+ 10000, // D0 -> D3
+ 10000, // D1 -> D3
+ 10000, // D2 -> D3
+ 0 // D3 -> D3
+};
+
/* FUNCTIONS ******************************************************************/
+
+NTSTATUS
+NTAPI
+PciStallForPowerChange(IN PPCI_PDO_EXTENSION PdoExtension,
+ IN DEVICE_POWER_STATE PowerState,
+ IN ULONG_PTR CapOffset)
+{
+ ULONG PciState, TimeoutEntry, PmcsrOffset, TryCount;
+ PPCI_VERIFIER_DATA VerifierData;
+ LARGE_INTEGER Interval;
+ PCI_PMCSR Pmcsr;
+ KIRQL Irql;
+
+ /* Make sure the power state is valid, and the device can support it */
+ ASSERT((PdoExtension->PowerState.CurrentDeviceState >= PowerDeviceD0)
&&
+ (PdoExtension->PowerState.CurrentDeviceState <= PowerDeviceD3));
+ ASSERT((PowerState >= PowerDeviceD0) && (PowerState <=
PowerDeviceD3));
+ ASSERT(!(PdoExtension->HackFlags & PCI_HACK_NO_PM_CAPS));
+
+ /* Save the current IRQL */
+ Irql = KeGetCurrentIrql();
+
+ /* Pick the expected timeout for this transition */
+ TimeoutEntry = PciPowerDelayTable[PowerState *
PdoExtension->PowerState.CurrentDeviceState];
+
+ /* PCI power states are one less than NT power states */
+ PciState = PowerState - 1;
+
+ /* The state status is stored in the PMCSR offset */
+ PmcsrOffset = CapOffset + FIELD_OFFSET(PCI_PM_CAPABILITY, PMCSR);
+
+ /* Try changing the power state up to 100 times */
+ TryCount = 100;
+ while (--TryCount)
+ {
+ /* Check if this state transition will take time */
+ if (TimeoutEntry > 0)
+ {
+ /* Check if this is happening at high IRQL */
+ if (Irql >= DISPATCH_LEVEL)
+ {
+ /* Can't wait at high IRQL, stall the processor */
+ KeStallExecutionProcessor(TimeoutEntry);
+ }
+ else
+ {
+ /* Do a wait for the timeout specified instead */
+ Interval.QuadPart = -10 * TimeoutEntry;
+ Interval.QuadPart -= KeQueryTimeIncrement() - 1;
+ KeDelayExecutionThread(0, 0, &Interval);
+ }
+ }
+
+ /* Read the PMCSR and see if the state has changed */
+ PciReadDeviceConfig(PdoExtension, &Pmcsr, PmcsrOffset, sizeof(PCI_PMCSR));
+ if (Pmcsr.PowerState == PciState) return STATUS_SUCCESS;
+
+ /* Try again, forcing a timeout of 1ms */
+ TimeoutEntry = 1000;
+ }
+
+ /* Call verifier with this error */
+ VerifierData = PciVerifierRetrieveFailureData(2);
+ ASSERT(VerifierData);
+ VfFailDeviceNode(PdoExtension->PhysicalDeviceObject,
+ PCI_VERIFIER_DETECTED_VIOLATION,
+ 2, // The PMCSR register was not updated within the spec-mandated
time.
+ VerifierData->FailureClass,
+ &VerifierData->AssertionControl,
+ VerifierData->DebuggerMessageText,
+ "%DevObj%Ulong",
+ PdoExtension->PhysicalDeviceObject,
+ PciState);
+
+ return STATUS_DEVICE_PROTOCOL_ERROR;
+}
+
+NTSTATUS
+NTAPI
+PciSetPowerManagedDevicePowerState(IN PPCI_PDO_EXTENSION DeviceExtension,
+ IN DEVICE_POWER_STATE DeviceState,
+ IN BOOLEAN IrpSet)
+{
+ NTSTATUS Status;
+ PCI_PM_CAPABILITY PmCaps;
+ ULONG CapsOffset;
+
+ /* Assume success */
+ Status = STATUS_SUCCESS;
+
+ /* Check if this device can support low power states */
+ if (!(PciCanDisableDecodes(DeviceExtension, NULL, 0, TRUE)) &&
+ (DeviceState != PowerDeviceD0))
+ {
+ /* Simply return success, ignoring this request */
+ DPRINT1("Cannot disable decodes on this device, ignoring PM
request...\n");
+ return Status;
+ }
+
+ /* Does the device support power management at all? */
+ if (!(DeviceExtension->HackFlags & PCI_HACK_NO_PM_CAPS))
+ {
+ /* Get the PM capabailities register */
+ CapsOffset = PciReadDeviceCapability(DeviceExtension,
+ DeviceExtension->CapabilitiesPtr,
+ PCI_CAPABILITY_ID_POWER_MANAGEMENT,
+ &PmCaps.Header,
+ sizeof(PCI_PM_CAPABILITY));
+ ASSERT(CapsOffset);
+ ASSERT(DeviceState != PowerDeviceUnspecified);
+
+ /* Check if the device is being powered up */
+ if (DeviceState == PowerDeviceD0)
+ {
+ /* Set full power state */
+ PmCaps.PMCSR.ControlStatus.PowerState = 0;
+
+ /* Check if the device supports Cold-D3 poweroff */
+ if (PmCaps.PMC.Capabilities.Support.PMED3Cold)
+ {
+ /* If there was a pending PME, clear it */
+ PmCaps.PMCSR.ControlStatus.PMEStatus = 1;
+ }
+ }
+ else
+ {
+ /* Otherwise, just set the new power state, converting from NT */
+ PmCaps.PMCSR.ControlStatus.PowerState = DeviceState - 1;
+ }
+
+ /* Write the new power state in the PMCSR */
+ PciWriteDeviceConfig(DeviceExtension,
+ &PmCaps.PMCSR,
+ CapsOffset + FIELD_OFFSET(PCI_PM_CAPABILITY, PMCSR),
+ sizeof(PCI_PMCSR));
+
+ /* Now wait for the change to "stick" based on the spec-mandated time
*/
+ Status = PciStallForPowerChange(DeviceExtension, DeviceState, CapsOffset);
+ if (!NT_SUCCESS(Status)) return Status;
+ }
+ else
+ {
+ /* Nothing to do! */
+ DPRINT1("No PM on this device, ignoring request\n");
+ }
+
+ /* Check if new resources have to be assigned */
+ if (IrpSet)
+ {
+ /* Check if the new device state is lower (higher power) than now */
+ if (DeviceState < DeviceExtension->PowerState.CurrentDeviceState)
+ {
+ /* We would normally re-assign resources after powerup */
+ UNIMPLEMENTED;
+ while (TRUE);
+ }
+ }
+
+ /* Return the power state change status */
+ return Status;
+}
NTSTATUS
NTAPI
Modified: trunk/reactos/drivers/bus/pcix/utils.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/drivers/bus/pcix/utils.c?r…
==============================================================================
--- trunk/reactos/drivers/bus/pcix/utils.c [iso-8859-1] (original)
+++ trunk/reactos/drivers/bus/pcix/utils.c [iso-8859-1] Sun Jul 18 18:58:33 2010
@@ -864,7 +864,7 @@
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,
@@ -876,4 +876,169 @@
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);
+}
+
/* EOF */