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!


svn path=/trunk/; revision=48107
This commit is contained in:
evb 2010-07-18 18:58:33 +00:00
parent a6017cd9b2
commit 8091e98e96
6 changed files with 605 additions and 2 deletions

View file

@ -124,6 +124,126 @@ PciSkipThisFunction(IN PPCI_COMMON_HEADER PciData,
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
NTAPI
PciScanBus(IN PPCI_FDO_EXTENSION DeviceExtension)
@ -374,6 +494,12 @@ PciScanBus(IN PPCI_FDO_EXTENSION DeviceExtension)
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;

View file

@ -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
//
@ -375,6 +380,17 @@ typedef struct PCI_ARBITER_INSTANCE
//ARBITER_INSTANCE CommonInstance; FIXME: Need Arbiter Headers
} 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
//
@ -442,6 +458,14 @@ PciFdoIrpQueryPower(
IN PPCI_FDO_EXTENSION DeviceExtension
);
NTSTATUS
NTAPI
PciSetPowerManagedDevicePowerState(
IN PPCI_PDO_EXTENSION DeviceExtension,
IN DEVICE_POWER_STATE DeviceState,
IN BOOLEAN IrpSet
);
//
// Bus FDO Routines
//
@ -778,6 +802,12 @@ PciVerifierInit(
IN PDRIVER_OBJECT DriverObject
);
PPCI_VERIFIER_DATA
NTAPI
PciVerifierRetrieveFailureData(
IN ULONG FailureCode
);
//
// Utility Routines
//
@ -925,6 +955,25 @@ PciSaveBiosConfig(
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
//
@ -953,6 +1002,15 @@ PciWriteDeviceConfig(
IN ULONG Length
);
VOID
NTAPI
PciReadDeviceConfig(
IN PPCI_PDO_EXTENSION DeviceExtension,
IN PVOID Buffer,
IN ULONG Offset,
IN ULONG Length
);
UCHAR
NTAPI
PciGetAdjustedInterruptLine(

View file

@ -24,7 +24,7 @@ PciGetAdjustedInterruptLine(IN PPCI_PDO_EXTENSION PdoExtension)
{
UCHAR InterruptLine = 0, PciInterruptLine;
ULONG Length;
/* Does the device have an interrupt pin? */
if (PdoExtension->InterruptPin)
{
@ -102,6 +102,22 @@ PciWriteDeviceConfig(IN PPCI_PDO_EXTENSION DeviceExtension,
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
NTAPI
PciReadSlotConfig(IN PPCI_FDO_EXTENSION DeviceExtension,

View file

@ -17,8 +17,62 @@
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
PciVerifierProfileChangeCallback(IN PVOID NotificationStructure,

View file

@ -14,8 +14,192 @@
/* 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
PciFdoWaitWake(IN PIRP Irp,

View file

@ -864,7 +864,7 @@ PciSaveBiosConfig(IN PPCI_PDO_EXTENSION DeviceExtension,
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 @@ PciSaveBiosConfig(IN PPCI_PDO_EXTENSION DeviceExtension,
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 */