Can't sleep so write more source codes! add scan bus functions to get power caps...
[reactos.git] / reactos / drivers / bus / pcix / enum.c
index 3551a3b..4af77b3 100644 (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;