2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/po/power.c
5 * PURPOSE: Power Manager
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * Hervé Poussineau (hpoussin@reactos.com)
10 /* INCLUDES ******************************************************************/
16 /* GLOBALS *******************************************************************/
18 typedef struct _REQUEST_POWER_ITEM
20 PREQUEST_POWER_COMPLETE CompletionRoutine
;
21 POWER_STATE PowerState
;
23 } REQUEST_POWER_ITEM
, *PREQUEST_POWER_ITEM
;
25 PDEVICE_NODE PopSystemPowerDeviceNode
= NULL
;
26 BOOLEAN PopAcpiPresent
= FALSE
;
27 POP_POWER_ACTION PopAction
;
28 WORK_QUEUE_ITEM PopShutdownWorkItem
;
30 /* PRIVATE FUNCTIONS *********************************************************/
35 PopRequestPowerIrpCompletion(IN PDEVICE_OBJECT DeviceObject
,
39 PIO_STACK_LOCATION Stack
;
40 PREQUEST_POWER_ITEM RequestPowerItem
;
42 Stack
= IoGetNextIrpStackLocation(Irp
);
43 RequestPowerItem
= (PREQUEST_POWER_ITEM
)Context
;
45 RequestPowerItem
->CompletionRoutine(DeviceObject
,
47 RequestPowerItem
->PowerState
,
48 RequestPowerItem
->Context
,
55 return STATUS_MORE_PROCESSING_REQUIRED
;
60 PopCleanupPowerState(IN PPOWER_STATE PowerState
)
67 PopSetSystemPowerState(SYSTEM_POWER_STATE PowerState
)
69 IO_STATUS_BLOCK IoStatusBlock
;
70 PDEVICE_OBJECT DeviceObject
;
71 PIO_STACK_LOCATION IrpSp
;
77 if (!PopAcpiPresent
) return STATUS_NOT_IMPLEMENTED
;
79 Status
= IopGetSystemPowerDeviceObject(&DeviceObject
);
80 if (!NT_SUCCESS(Status
))
82 DPRINT1("No system power driver available\n");
83 return STATUS_UNSUCCESSFUL
;
86 Fdo
= IoGetAttachedDeviceReference(DeviceObject
);
88 if (Fdo
== DeviceObject
)
90 DPRINT("An FDO was not attached\n");
91 return STATUS_UNSUCCESSFUL
;
94 KeInitializeEvent(&Event
,
98 Irp
= IoBuildSynchronousFsdRequest(IRP_MJ_POWER
,
106 IrpSp
= IoGetNextIrpStackLocation(Irp
);
107 IrpSp
->MinorFunction
= IRP_MN_SET_POWER
;
108 IrpSp
->Parameters
.Power
.Type
= SystemPowerState
;
109 IrpSp
->Parameters
.Power
.State
.SystemState
= PowerState
;
111 DPRINT("Calling ACPI driver");
112 Status
= PoCallDriver(Fdo
, Irp
);
113 if (Status
== STATUS_PENDING
)
115 KeWaitForSingleObject(&Event
,
120 Status
= IoStatusBlock
.Status
;
123 ObDereferenceObject(Fdo
);
130 PoInitSystem(IN ULONG BootPhase
)
132 PVOID NotificationEntry
;
134 BOOLEAN ForceAcpiDisable
= FALSE
;
136 /* Check if this is phase 1 init */
139 /* Register power button notification */
140 IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange
,
141 PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES
,
142 (PVOID
)&GUID_DEVICE_SYS_BUTTON
,
144 PhysicalDeviceObject
->DriverObject
,
145 PopAddRemoveSysCapsCallback
,
149 /* Register lid notification */
150 IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange
,
151 PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES
,
152 (PVOID
)&GUID_DEVICE_LID
,
154 PhysicalDeviceObject
->DriverObject
,
155 PopAddRemoveSysCapsCallback
,
161 /* Get the Command Line */
162 CommandLine
= KeLoaderBlock
->LoadOptions
;
165 _strupr(CommandLine
);
167 /* Check for ACPI disable */
168 if (strstr(CommandLine
, "NOACPI")) ForceAcpiDisable
= TRUE
;
170 if (ForceAcpiDisable
)
172 /* Set the ACPI State to False if it's been forced that way */
173 PopAcpiPresent
= FALSE
;
177 /* Otherwise check if the LoaderBlock has a ACPI Table */
178 PopAcpiPresent
= KeLoaderBlock
->Extension
->AcpiTable
!= NULL
? TRUE
: FALSE
;
182 /* Initialize volume support */
183 InitializeListHead(&PopVolumeDevices
);
184 KeInitializeGuardedMutex(&PopVolumeLock
);
186 /* Initialize support for dope */
187 KeInitializeSpinLock(&PopDopeGlobalLock
);
193 PopPerfIdle(PPROCESSOR_POWER_STATE PowerState
)
195 DPRINT1("PerfIdle function: %p\n", PowerState
);
200 PopPerfIdleDpc(IN PKDPC Dpc
,
201 IN PVOID DeferredContext
,
202 IN PVOID SystemArgument1
,
203 IN PVOID SystemArgument2
)
205 /* Call the Perf Idle function */
206 PopPerfIdle(&((PKPRCB
)DeferredContext
)->PowerState
);
211 PopIdle0(IN PPROCESSOR_POWER_STATE PowerState
)
213 /* FIXME: Extremly naive implementation */
219 PoInitializePrcb(IN PKPRCB Prcb
)
221 /* Initialize the Power State */
222 RtlZeroMemory(&Prcb
->PowerState
, sizeof(Prcb
->PowerState
));
223 Prcb
->PowerState
.Idle0KernelTimeLimit
= 0xFFFFFFFF;
224 Prcb
->PowerState
.CurrentThrottle
= 100;
225 Prcb
->PowerState
.CurrentThrottleIndex
= 0;
226 Prcb
->PowerState
.IdleFunction
= PopIdle0
;
228 /* Initialize the Perf DPC and Timer */
229 KeInitializeDpc(&Prcb
->PowerState
.PerfDpc
, PopPerfIdleDpc
, Prcb
);
230 KeSetTargetProcessorDpc(&Prcb
->PowerState
.PerfDpc
, Prcb
->Number
);
231 KeInitializeTimerEx(&Prcb
->PowerState
.PerfTimer
, SynchronizationTimer
);
234 /* PUBLIC FUNCTIONS **********************************************************/
241 PoCancelDeviceNotify(IN PVOID NotifyBlock
)
244 return STATUS_NOT_IMPLEMENTED
;
252 PoRegisterDeviceNotify(OUT PVOID Unknown0
,
260 return STATUS_NOT_IMPLEMENTED
;
268 PoShutdownBugCheck(IN BOOLEAN LogError
,
269 IN ULONG BugCheckCode
,
270 IN ULONG_PTR BugCheckParameter1
,
271 IN ULONG_PTR BugCheckParameter2
,
272 IN ULONG_PTR BugCheckParameter3
,
273 IN ULONG_PTR BugCheckParameter4
)
275 DPRINT1("PoShutdownBugCheck called\n");
277 /* FIXME: Log error if requested */
278 /* FIXME: Initiate a shutdown */
280 /* Bugcheck the system */
281 KeBugCheckEx(BugCheckCode
,
293 PoRequestShutdownEvent(OUT PVOID
*Event
)
296 return STATUS_NOT_IMPLEMENTED
;
304 PoSetHiberRange(IN PVOID HiberContext
,
306 IN OUT PVOID StartPage
,
319 PoCallDriver(IN PDEVICE_OBJECT DeviceObject
,
324 /* Forward to Io -- FIXME! */
325 Status
= IoCallDriver(DeviceObject
, Irp
);
336 PoRegisterDeviceForIdleDetection(IN PDEVICE_OBJECT DeviceObject
,
337 IN ULONG ConservationIdleTime
,
338 IN ULONG PerformanceIdleTime
,
339 IN DEVICE_POWER_STATE State
)
350 PoRegisterSystemState(IN PVOID StateHandle
,
351 IN EXECUTION_STATE Flags
)
362 PoRequestPowerIrp(IN PDEVICE_OBJECT DeviceObject
,
363 IN UCHAR MinorFunction
,
364 IN POWER_STATE PowerState
,
365 IN PREQUEST_POWER_COMPLETE CompletionFunction
,
367 OUT PIRP
*pIrp OPTIONAL
)
369 PDEVICE_OBJECT TopDeviceObject
;
370 PIO_STACK_LOCATION Stack
;
372 PREQUEST_POWER_ITEM RequestPowerItem
;
374 if (MinorFunction
!= IRP_MN_QUERY_POWER
375 && MinorFunction
!= IRP_MN_SET_POWER
376 && MinorFunction
!= IRP_MN_WAIT_WAKE
)
377 return STATUS_INVALID_PARAMETER_2
;
379 RequestPowerItem
= ExAllocatePool(NonPagedPool
, sizeof(REQUEST_POWER_ITEM
));
380 if (!RequestPowerItem
)
381 return STATUS_INSUFFICIENT_RESOURCES
;
383 /* Always call the top of the device stack */
384 TopDeviceObject
= IoGetAttachedDeviceReference(DeviceObject
);
386 Irp
= IoBuildAsynchronousFsdRequest(IRP_MJ_POWER
,
394 ExFreePool(RequestPowerItem
);
395 return STATUS_INSUFFICIENT_RESOURCES
;
398 /* POWER IRPs are always initialized with a status code of
399 STATUS_NOT_IMPLEMENTED */
400 Irp
->IoStatus
.Status
= STATUS_NOT_IMPLEMENTED
;
401 Irp
->IoStatus
.Information
= 0;
403 Stack
= IoGetNextIrpStackLocation(Irp
);
404 Stack
->MinorFunction
= MinorFunction
;
405 if (MinorFunction
== IRP_MN_WAIT_WAKE
)
406 Stack
->Parameters
.WaitWake
.PowerState
= PowerState
.SystemState
;
409 Stack
->Parameters
.Power
.Type
= DevicePowerState
;
410 Stack
->Parameters
.Power
.State
= PowerState
;
413 RequestPowerItem
->CompletionRoutine
= CompletionFunction
;
414 RequestPowerItem
->PowerState
= PowerState
;
415 RequestPowerItem
->Context
= Context
;
420 IoSetCompletionRoutine(Irp
, PopRequestPowerIrpCompletion
, RequestPowerItem
, TRUE
, TRUE
, TRUE
);
421 IoCallDriver(TopDeviceObject
, Irp
);
423 /* Always return STATUS_PENDING. The completion routine
424 * will call CompletionFunction and complete the Irp.
426 return STATUS_PENDING
;
434 PoSetPowerState(IN PDEVICE_OBJECT DeviceObject
,
435 IN POWER_STATE_TYPE Type
,
436 IN POWER_STATE State
)
440 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
442 ps
.SystemState
= PowerSystemWorking
; // Fully on
443 ps
.DeviceState
= PowerDeviceD0
; // Fully on
453 PoSetSystemState(IN EXECUTION_STATE Flags
)
463 PoStartNextPowerIrp(IN PIRP Irp
)
473 PoUnregisterSystemState(IN PVOID StateHandle
)
483 PoQueueShutdownWorkItem(IN PWORK_QUEUE_ITEM WorkItem
)
488 return STATUS_NOT_IMPLEMENTED
;
496 NtInitiatePowerAction (IN POWER_ACTION SystemAction
,
497 IN SYSTEM_POWER_STATE MinSystemState
,
499 IN BOOLEAN Asynchronous
)
502 return STATUS_NOT_IMPLEMENTED
;
510 NtPowerInformation(IN POWER_INFORMATION_LEVEL PowerInformationLevel
,
511 IN PVOID InputBuffer OPTIONAL
,
512 IN ULONG InputBufferLength
,
513 OUT PVOID OutputBuffer OPTIONAL
,
514 IN ULONG OutputBufferLength
)
520 DPRINT("NtPowerInformation(PowerInformationLevel 0x%x, InputBuffer 0x%x, "
521 "InputBufferLength 0x%x, OutputBuffer 0x%x, OutputBufferLength 0x%x)\n",
522 PowerInformationLevel
,
523 InputBuffer
, InputBufferLength
,
524 OutputBuffer
, OutputBufferLength
);
526 switch (PowerInformationLevel
)
528 case SystemBatteryState
:
530 PSYSTEM_BATTERY_STATE BatteryState
= (PSYSTEM_BATTERY_STATE
)OutputBuffer
;
532 if (InputBuffer
!= NULL
)
533 return STATUS_INVALID_PARAMETER
;
534 if (OutputBufferLength
< sizeof(SYSTEM_BATTERY_STATE
))
535 return STATUS_BUFFER_TOO_SMALL
;
537 /* Just zero the struct (and thus set BatteryState->BatteryPresent = FALSE) */
538 RtlZeroMemory(BatteryState
, sizeof(SYSTEM_BATTERY_STATE
));
539 BatteryState
->EstimatedTime
= MAXULONG
;
541 Status
= STATUS_SUCCESS
;
544 case SystemPowerCapabilities
:
546 PSYSTEM_POWER_CAPABILITIES PowerCapabilities
= (PSYSTEM_POWER_CAPABILITIES
)OutputBuffer
;
548 if (InputBuffer
!= NULL
)
549 return STATUS_INVALID_PARAMETER
;
550 if (OutputBufferLength
< sizeof(SYSTEM_POWER_CAPABILITIES
))
551 return STATUS_BUFFER_TOO_SMALL
;
553 /* Just zero the struct (and thus set BatteryState->BatteryPresent = FALSE) */
554 RtlZeroMemory(PowerCapabilities
, sizeof(SYSTEM_POWER_CAPABILITIES
));
555 //PowerCapabilities->SystemBatteriesPresent = 0;
557 Status
= STATUS_SUCCESS
;
562 Status
= STATUS_NOT_IMPLEMENTED
;
563 DPRINT1("PowerInformationLevel 0x%x is UNIMPLEMENTED! Have a nice day.\n",
564 PowerInformationLevel
);
573 NtGetDevicePowerState(IN HANDLE Device
,
574 IN PDEVICE_POWER_STATE PowerState
)
577 return STATUS_NOT_IMPLEMENTED
;
582 NtIsSystemResumeAutomatic(VOID
)
590 NtRequestWakeupLatency(IN LATENCY_TIME Latency
)
593 return STATUS_NOT_IMPLEMENTED
;
598 NtSetThreadExecutionState(IN EXECUTION_STATE esFlags
,
599 OUT EXECUTION_STATE
*PreviousFlags
)
601 PKTHREAD Thread
= KeGetCurrentThread();
602 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
603 EXECUTION_STATE PreviousState
;
607 if (esFlags
& ~(ES_CONTINUOUS
| ES_USER_PRESENT
))
609 /* Fail the request */
610 return STATUS_INVALID_PARAMETER
;
613 /* Check for user parameters */
614 if (PreviousMode
!= KernelMode
)
616 /* Protect the probes */
619 /* Check if the pointer is valid */
620 ProbeForWriteUlong(PreviousFlags
);
622 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
624 /* It isn't -- fail */
625 _SEH2_YIELD(return _SEH2_GetExceptionCode());
630 /* Save the previous state, always masking in the continous flag */
631 PreviousState
= Thread
->PowerState
| ES_CONTINUOUS
;
633 /* Check if we need to update the power state */
634 if (esFlags
& ES_CONTINUOUS
) Thread
->PowerState
= esFlags
;
636 /* Protect the write back to user mode */
639 /* Return the previous flags */
640 *PreviousFlags
= PreviousState
;
642 _SEH2_EXCEPT(ExSystemExceptionFilter())
644 /* Something's wrong, fail */
645 _SEH2_YIELD(return _SEH2_GetExceptionCode());
650 return STATUS_SUCCESS
;
655 NtSetSystemPowerState(IN POWER_ACTION SystemAction
,
656 IN SYSTEM_POWER_STATE MinSystemState
,
659 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
660 POP_POWER_ACTION Action
= {0};
664 /* Check for invalid parameter combinations */
665 if ((MinSystemState
>= PowerSystemMaximum
) ||
666 (MinSystemState
<= PowerSystemUnspecified
) ||
667 (SystemAction
> PowerActionWarmEject
) ||
668 (SystemAction
< PowerActionReserved
) ||
669 (Flags
& ~(POWER_ACTION_QUERY_ALLOWED
|
670 POWER_ACTION_UI_ALLOWED
|
671 POWER_ACTION_OVERRIDE_APPS
|
672 POWER_ACTION_LIGHTEST_FIRST
|
673 POWER_ACTION_LOCK_CONSOLE
|
674 POWER_ACTION_DISABLE_WAKES
|
675 POWER_ACTION_CRITICAL
)))
677 DPRINT1("NtSetSystemPowerState: Bad parameters!\n");
678 DPRINT1(" SystemAction: 0x%x\n", SystemAction
);
679 DPRINT1(" MinSystemState: 0x%x\n", MinSystemState
);
680 DPRINT1(" Flags: 0x%x\n", Flags
);
681 return STATUS_INVALID_PARAMETER
;
684 /* Check for user caller */
685 if (PreviousMode
!= KernelMode
)
687 /* Check for shutdown permission */
688 if (!SeSinglePrivilegeCheck(SeShutdownPrivilege
, PreviousMode
))
691 DPRINT1("ERROR: Privilege not held for shutdown\n");
692 //return STATUS_PRIVILEGE_NOT_HELD; HACK!
695 /* Do it as a kernel-mode caller for consistency with system state */
696 return ZwSetSystemPowerState (SystemAction
, MinSystemState
, Flags
);
699 /* Read policy settings (partial shutdown vs. full shutdown) */
700 if (SystemAction
== PowerActionShutdown
) PopReadShutdownPolicy();
702 /* Disable lazy flushing of registry */
703 DPRINT1("Stopping lazy flush\n");
704 CmSetLazyFlushState(FALSE
);
706 /* Setup the power action */
707 Action
.Action
= SystemAction
;
708 Action
.Flags
= Flags
;
710 /* Notify callbacks */
711 DPRINT1("Notifying callbacks\n");
712 ExNotifyCallback(PowerStateCallback
, (PVOID
)3, NULL
);
714 /* Swap in any worker thread stacks */
715 DPRINT1("Swapping worker threads\n");
716 ExSwapinWorkerThreads(FALSE
);
718 /* Make our action global */
721 /* Start power loop */
722 Status
= STATUS_CANCELLED
;
725 /* Break out if there's nothing to do */
726 if (Action
.Action
== PowerActionNone
) break;
728 /* Check for first-pass or restart */
729 if (Status
== STATUS_CANCELLED
)
731 /* Check for shutdown action */
732 if ((PopAction
.Action
== PowerActionShutdown
) ||
733 (PopAction
.Action
== PowerActionShutdownReset
) ||
734 (PopAction
.Action
== PowerActionShutdownOff
))
737 PopAction
.Shutdown
= TRUE
;
740 /* Now we are good to go */
741 Status
= STATUS_SUCCESS
;
744 /* Check if we're still in an invalid status */
745 if (!NT_SUCCESS(Status
)) break;
747 /* Flush dirty cache pages */
748 CcRosFlushDirtyPages(-1, &Dummy
);
750 /* Flush all volumes and the registry */
751 DPRINT1("Flushing volumes, cache flushed %d pages\n", Dummy
);
752 PopFlushVolumes(PopAction
.Shutdown
);
754 /* Set IRP for drivers */
755 PopAction
.IrpMinor
= IRP_MN_SET_POWER
;
756 if (PopAction
.Shutdown
)
758 DPRINT1("Queueing shutdown thread\n");
759 /* Check if we are running in the system context */
760 if (PsGetCurrentProcess() != PsInitialSystemProcess
)
762 /* We're not, so use a worker thread for shutdown */
763 ExInitializeWorkItem(&PopShutdownWorkItem
,
764 &PopGracefulShutdown
,
767 ExQueueWorkItem(&PopShutdownWorkItem
, CriticalWorkQueue
);
769 /* Spend us -- when we wake up, the system is good to go down */
770 KeSuspendThread(KeGetCurrentThread());
771 Status
= STATUS_SYSTEM_SHUTDOWN
;
777 /* Do the shutdown inline */
778 PopGracefulShutdown(NULL
);
782 /* You should not have made it this far */
783 ASSERT(FALSE
&& "System is still up and running?!");
788 /* We're done, return */