Sync with trunk r58033.
[reactos.git] / ntoskrnl / po / power.c
index e8d37b8..b174e20 100644 (file)
@@ -9,6 +9,7 @@
 
 /* INCLUDES ******************************************************************/
 
+#include "initguid.h"
 #include <ntoskrnl.h>
 #define NDEBUG
 #include <debug.h>
@@ -20,8 +21,16 @@ typedef struct _REQUEST_POWER_ITEM
     PREQUEST_POWER_COMPLETE CompletionRoutine;
     POWER_STATE PowerState;
     PVOID Context;
+    PDEVICE_OBJECT TopDeviceObject;
 } REQUEST_POWER_ITEM, *PREQUEST_POWER_ITEM;
 
+typedef struct _POWER_STATE_TRAVERSE_CONTEXT
+{
+    SYSTEM_POWER_STATE SystemPowerState;
+    POWER_ACTION PowerAction;
+    PDEVICE_OBJECT PowerDevice;
+} POWER_STATE_TRAVERSE_CONTEXT, *PPOWER_STATE_TRAVERSE_CONTEXT;
+
 PDEVICE_NODE PopSystemPowerDeviceNode = NULL;
 BOOLEAN PopAcpiPresent = FALSE;
 POP_POWER_ACTION PopAction;
@@ -48,11 +57,12 @@ PopRequestPowerIrpCompletion(IN PDEVICE_OBJECT DeviceObject,
                                         RequestPowerItem->Context,
                                         &Irp->IoStatus);
 
-    ExFreePool(Context);
-
     IoFreeIrp(Irp);
 
-    return STATUS_SUCCESS;
+    ObDereferenceObject(RequestPowerItem->TopDeviceObject);
+    ExFreePool(Context);
+
+    return STATUS_MORE_PROCESSING_REQUIRED;
 }
 
 VOID
@@ -63,53 +73,76 @@ PopCleanupPowerState(IN PPOWER_STATE PowerState)
 }
 
 NTSTATUS
-NTAPI
-PopSetSystemPowerState(SYSTEM_POWER_STATE PowerState)
+PopSendQuerySystemPowerState(PDEVICE_OBJECT DeviceObject, SYSTEM_POWER_STATE SystemState, POWER_ACTION PowerAction)
 {
+    KEVENT Event;
     IO_STATUS_BLOCK IoStatusBlock;
-    PDEVICE_OBJECT DeviceObject;
     PIO_STACK_LOCATION IrpSp;
-    PDEVICE_OBJECT Fdo;
-    NTSTATUS Status;
-    KEVENT Event;
     PIRP Irp;
+    NTSTATUS Status;
 
-    if (!PopAcpiPresent) return STATUS_NOT_IMPLEMENTED;
+    KeInitializeEvent(&Event,
+                      NotificationEvent,
+                      FALSE);
 
-    Status = IopGetSystemPowerDeviceObject(&DeviceObject);
-    if (!NT_SUCCESS(Status)) 
-    {
-        DPRINT1("No system power driver available\n");
-        return STATUS_UNSUCCESSFUL;
-    }
+    Irp = IoBuildSynchronousFsdRequest(IRP_MJ_POWER,
+                                       DeviceObject,
+                                       NULL,
+                                       0,
+                                       NULL,
+                                       &Event,
+                                       &IoStatusBlock);
+    if (!Irp) return STATUS_INSUFFICIENT_RESOURCES;
 
-    Fdo = IoGetAttachedDeviceReference(DeviceObject);
+    IrpSp = IoGetNextIrpStackLocation(Irp);
+    IrpSp->MinorFunction = IRP_MN_QUERY_POWER;
+    IrpSp->Parameters.Power.Type = SystemPowerState;
+    IrpSp->Parameters.Power.State.SystemState = SystemState;
+    IrpSp->Parameters.Power.ShutdownType = PowerAction;
 
-    if (Fdo == DeviceObject)
+    Status = PoCallDriver(DeviceObject, Irp);
+    if (Status == STATUS_PENDING)
     {
-        DPRINT("An FDO was not attached\n");
-        return STATUS_UNSUCCESSFUL;
+        KeWaitForSingleObject(&Event,
+                              Executive,
+                              KernelMode,
+                              FALSE,
+                              NULL);
+        Status = IoStatusBlock.Status;
     }
 
+    return Status;
+}
+
+NTSTATUS
+PopSendSetSystemPowerState(PDEVICE_OBJECT DeviceObject, SYSTEM_POWER_STATE SystemState, POWER_ACTION PowerAction)
+{
+    KEVENT Event;
+    IO_STATUS_BLOCK IoStatusBlock;
+    PIO_STACK_LOCATION IrpSp;
+    PIRP Irp;
+    NTSTATUS Status;
+
     KeInitializeEvent(&Event,
                       NotificationEvent,
                       FALSE);
 
     Irp = IoBuildSynchronousFsdRequest(IRP_MJ_POWER,
-                                       Fdo,
+                                       DeviceObject,
                                        NULL,
                                        0,
                                        NULL,
                                        &Event,
                                        &IoStatusBlock);
+    if (!Irp) return STATUS_INSUFFICIENT_RESOURCES;
 
     IrpSp = IoGetNextIrpStackLocation(Irp);
     IrpSp->MinorFunction = IRP_MN_SET_POWER;
     IrpSp->Parameters.Power.Type = SystemPowerState;
-    IrpSp->Parameters.Power.State.SystemState = PowerState;
+    IrpSp->Parameters.Power.State.SystemState = SystemState;
+    IrpSp->Parameters.Power.ShutdownType = PowerAction;
 
-    DPRINT("Calling ACPI driver");
-    Status = PoCallDriver(Fdo, Irp);
+    Status = PoCallDriver(DeviceObject, Irp);
     if (Status == STATUS_PENDING)
     {
         KeWaitForSingleObject(&Event,
@@ -117,16 +150,142 @@ PopSetSystemPowerState(SYSTEM_POWER_STATE PowerState)
                               KernelMode,
                               FALSE,
                               NULL);
-      Status = IoStatusBlock.Status;
+        Status = IoStatusBlock.Status;
     }
 
-    ObDereferenceObject(Fdo);
+    return Status;
+}
+
+NTSTATUS
+PopQuerySystemPowerStateTraverse(PDEVICE_NODE DeviceNode,
+                                 PVOID Context)
+{
+    PPOWER_STATE_TRAVERSE_CONTEXT PowerStateContext = Context;
+    NTSTATUS Status;
+    
+    DPRINT("PopQuerySystemPowerStateTraverse(%p, %p)\n", DeviceNode, Context);
+    
+    if (DeviceNode == IopRootDeviceNode)
+        return STATUS_SUCCESS;
+    
+    if (DeviceNode->Flags & DNF_LEGACY_DRIVER)
+        return STATUS_SUCCESS;
+
+    Status = PopSendQuerySystemPowerState(DeviceNode->PhysicalDeviceObject,
+                                          PowerStateContext->SystemPowerState,
+                                          PowerStateContext->PowerAction);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Device '%wZ' failed IRP_MN_QUERY_POWER\n", &DeviceNode->InstancePath);
+    }
+    
+#if 0
+    return Status;
+#else
+    return STATUS_SUCCESS;
+#endif
+}
+
+NTSTATUS
+PopSetSystemPowerStateTraverse(PDEVICE_NODE DeviceNode,
+                               PVOID Context)
+{
+    PPOWER_STATE_TRAVERSE_CONTEXT PowerStateContext = Context;
+    NTSTATUS Status;
+    
+    DPRINT("PopSetSystemPowerStateTraverse(%p, %p)\n", DeviceNode, Context);
+    
+    if (DeviceNode == IopRootDeviceNode)
+        return STATUS_SUCCESS;
+    
+    if (DeviceNode->PhysicalDeviceObject == PowerStateContext->PowerDevice)
+        return STATUS_SUCCESS;
+    
+    if (DeviceNode->Flags & DNF_LEGACY_DRIVER)
+        return STATUS_SUCCESS;
+
+    Status = PopSendSetSystemPowerState(DeviceNode->PhysicalDeviceObject,
+                                        PowerStateContext->SystemPowerState,
+                                        PowerStateContext->PowerAction);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Device '%wZ' failed IRP_MN_SET_POWER\n", &DeviceNode->InstancePath);
+    }
+    
+#if 0
+    return Status;
+#else
+    return STATUS_SUCCESS;
+#endif
+}
+
+NTSTATUS
+NTAPI
+PopSetSystemPowerState(SYSTEM_POWER_STATE PowerState, POWER_ACTION PowerAction)
+{
+    PDEVICE_OBJECT DeviceObject;
+    PDEVICE_OBJECT Fdo;
+    NTSTATUS Status;
+    DEVICETREE_TRAVERSE_CONTEXT Context;
+    POWER_STATE_TRAVERSE_CONTEXT PowerContext;
+    
+    Status = IopGetSystemPowerDeviceObject(&DeviceObject);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("No system power driver available\n");
+        Fdo = NULL;
+    }
+    else
+    {
+        Fdo = IoGetAttachedDeviceReference(DeviceObject);
+        if (Fdo == DeviceObject)
+        {
+            DPRINT("An FDO was not attached\n");
+            return STATUS_UNSUCCESSFUL;
+        }
+    }
+    
+    /* Set up context */
+    PowerContext.PowerAction = PowerAction;
+    PowerContext.SystemPowerState = PowerState;
+    PowerContext.PowerDevice = Fdo;
+    
+    /* Query for system power change */
+    IopInitDeviceTreeTraverseContext(&Context,
+                                     IopRootDeviceNode,
+                                     PopQuerySystemPowerStateTraverse,
+                                     &PowerContext);
+    
+    Status = IopTraverseDeviceTree(&Context);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Query system power state failed; changing state anyway\n");
+    }
+    
+    /* Set system power change */
+    IopInitDeviceTreeTraverseContext(&Context,
+                                     IopRootDeviceNode,
+                                     PopSetSystemPowerStateTraverse,
+                                     &PowerContext);
+    
+    IopTraverseDeviceTree(&Context);
+
+    if (!PopAcpiPresent) return STATUS_NOT_IMPLEMENTED;
+    
+    if (Fdo != NULL)
+    {
+        if (PowerAction != PowerActionShutdownReset)
+            PopSendSetSystemPowerState(Fdo, PowerState, PowerAction);
+
+        ObDereferenceObject(Fdo);
+    }
 
     return Status;
 }
 
 BOOLEAN
 NTAPI
+INIT_FUNCTION
 PoInitSystem(IN ULONG BootPhase)
 {
     PVOID NotificationEntry;
@@ -136,7 +295,7 @@ PoInitSystem(IN ULONG BootPhase)
     /* Check if this is phase 1 init */
     if (BootPhase == 1)
     {
-        /* Registry power button notification */
+        /* Register power button notification */
         IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange,
                                        PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
                                        (PVOID)&GUID_DEVICE_SYS_BUTTON,
@@ -145,6 +304,16 @@ PoInitSystem(IN ULONG BootPhase)
                                        PopAddRemoveSysCapsCallback,
                                        NULL,
                                        &NotificationEntry);
+        
+        /* Register lid notification */
+        IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange,
+                                       PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
+                                       (PVOID)&GUID_DEVICE_LID,
+                                       IopRootDeviceNode->
+                                       PhysicalDeviceObject->DriverObject,
+                                       PopAddRemoveSysCapsCallback,
+                                       NULL,
+                                       &NotificationEntry);
         return TRUE;
     }
 
@@ -206,6 +375,7 @@ PopIdle0(IN PPROCESSOR_POWER_STATE PowerState)
 
 VOID
 NTAPI
+INIT_FUNCTION
 PoInitializePrcb(IN PKPRCB Prcb)
 {
     /* Initialize the Power State */
@@ -360,7 +530,6 @@ PoRequestPowerIrp(IN PDEVICE_OBJECT DeviceObject,
     PIO_STACK_LOCATION Stack;
     PIRP Irp;
     PREQUEST_POWER_ITEM RequestPowerItem;
-    NTSTATUS Status;
   
     if (MinorFunction != IRP_MN_QUERY_POWER
         && MinorFunction != IRP_MN_SET_POWER
@@ -382,6 +551,7 @@ PoRequestPowerIrp(IN PDEVICE_OBJECT DeviceObject,
                                         NULL);
     if (!Irp)
     {
+        ObDereferenceObject(TopDeviceObject);
         ExFreePool(RequestPowerItem);
         return STATUS_INSUFFICIENT_RESOURCES;
     }
@@ -404,12 +574,13 @@ PoRequestPowerIrp(IN PDEVICE_OBJECT DeviceObject,
     RequestPowerItem->CompletionRoutine = CompletionFunction;
     RequestPowerItem->PowerState = PowerState;
     RequestPowerItem->Context = Context;
+    RequestPowerItem->TopDeviceObject = TopDeviceObject;
   
     if (pIrp != NULL)
         *pIrp = Irp;
   
     IoSetCompletionRoutine(Irp, PopRequestPowerIrpCompletion, RequestPowerItem, TRUE, TRUE, TRUE);
-    Status = IoCallDriver(TopDeviceObject, Irp);
+    PoCallDriver(TopDeviceObject, Irp);
   
     /* Always return STATUS_PENDING. The completion routine
      * will call CompletionFunction and complete the Irp.
@@ -622,7 +793,7 @@ NtSetThreadExecutionState(IN EXECUTION_STATE esFlags,
     PreviousState = Thread->PowerState | ES_CONTINUOUS;
 
     /* Check if we need to update the power state */
-    if (esFlags & ES_CONTINUOUS) Thread->PowerState = esFlags;
+    if (esFlags & ES_CONTINUOUS) Thread->PowerState = (UCHAR)esFlags;
 
     /* Protect the write back to user mode */
     _SEH2_TRY
@@ -650,18 +821,19 @@ NtSetSystemPowerState(IN POWER_ACTION SystemAction,
     KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
     POP_POWER_ACTION Action = {0};
     NTSTATUS Status;
+    ULONG Dummy;
 
     /* Check for invalid parameter combinations */
     if ((MinSystemState >= PowerSystemMaximum) ||
         (MinSystemState <= PowerSystemUnspecified) ||
         (SystemAction > PowerActionWarmEject) ||
         (SystemAction < PowerActionReserved) ||
-        (Flags & ~(POWER_ACTION_QUERY_ALLOWED  |  
-                   POWER_ACTION_UI_ALLOWED     | 
-                   POWER_ACTION_OVERRIDE_APPS  | 
-                   POWER_ACTION_LIGHTEST_FIRST | 
-                   POWER_ACTION_LOCK_CONSOLE   | 
-                   POWER_ACTION_DISABLE_WAKES  | 
+        (Flags & ~(POWER_ACTION_QUERY_ALLOWED  |
+                   POWER_ACTION_UI_ALLOWED     |
+                   POWER_ACTION_OVERRIDE_APPS  |
+                   POWER_ACTION_LIGHTEST_FIRST |
+                   POWER_ACTION_LOCK_CONSOLE   |
+                   POWER_ACTION_DISABLE_WAKES  |
                    POWER_ACTION_CRITICAL)))
     {
         DPRINT1("NtSetSystemPowerState: Bad parameters!\n");
@@ -733,9 +905,16 @@ NtSetSystemPowerState(IN POWER_ACTION SystemAction,
 
         /* Check if we're still in an invalid status */
         if (!NT_SUCCESS(Status)) break;
+        
+#ifndef NEWCC
+        /* Flush dirty cache pages */
+        CcRosFlushDirtyPages(-1, &Dummy, FALSE); //HACK: We really should wait here!
+#else
+        Dummy = 0;
+#endif
 
         /* Flush all volumes and the registry */
-        DPRINT1("Flushing volumes\n");
+        DPRINT1("Flushing volumes, cache flushed %d pages\n", Dummy);
         PopFlushVolumes(PopAction.Shutdown);
 
         /* Set IRP for drivers */