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
);
131 PoInitSystem(IN ULONG BootPhase
)
133 PVOID NotificationEntry
;
135 BOOLEAN ForceAcpiDisable
= FALSE
;
137 /* Check if this is phase 1 init */
140 /* Register power button notification */
141 IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange
,
142 PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES
,
143 (PVOID
)&GUID_DEVICE_SYS_BUTTON
,
145 PhysicalDeviceObject
->DriverObject
,
146 PopAddRemoveSysCapsCallback
,
150 /* Register lid notification */
151 IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange
,
152 PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES
,
153 (PVOID
)&GUID_DEVICE_LID
,
155 PhysicalDeviceObject
->DriverObject
,
156 PopAddRemoveSysCapsCallback
,
162 /* Get the Command Line */
163 CommandLine
= KeLoaderBlock
->LoadOptions
;
166 _strupr(CommandLine
);
168 /* Check for ACPI disable */
169 if (strstr(CommandLine
, "NOACPI")) ForceAcpiDisable
= TRUE
;
171 if (ForceAcpiDisable
)
173 /* Set the ACPI State to False if it's been forced that way */
174 PopAcpiPresent
= FALSE
;
178 /* Otherwise check if the LoaderBlock has a ACPI Table */
179 PopAcpiPresent
= KeLoaderBlock
->Extension
->AcpiTable
!= NULL
? TRUE
: FALSE
;
183 /* Initialize volume support */
184 InitializeListHead(&PopVolumeDevices
);
185 KeInitializeGuardedMutex(&PopVolumeLock
);
187 /* Initialize support for dope */
188 KeInitializeSpinLock(&PopDopeGlobalLock
);
194 PopPerfIdle(PPROCESSOR_POWER_STATE PowerState
)
196 DPRINT1("PerfIdle function: %p\n", PowerState
);
201 PopPerfIdleDpc(IN PKDPC Dpc
,
202 IN PVOID DeferredContext
,
203 IN PVOID SystemArgument1
,
204 IN PVOID SystemArgument2
)
206 /* Call the Perf Idle function */
207 PopPerfIdle(&((PKPRCB
)DeferredContext
)->PowerState
);
212 PopIdle0(IN PPROCESSOR_POWER_STATE PowerState
)
214 /* FIXME: Extremly naive implementation */
221 PoInitializePrcb(IN PKPRCB Prcb
)
223 /* Initialize the Power State */
224 RtlZeroMemory(&Prcb
->PowerState
, sizeof(Prcb
->PowerState
));
225 Prcb
->PowerState
.Idle0KernelTimeLimit
= 0xFFFFFFFF;
226 Prcb
->PowerState
.CurrentThrottle
= 100;
227 Prcb
->PowerState
.CurrentThrottleIndex
= 0;
228 Prcb
->PowerState
.IdleFunction
= PopIdle0
;
230 /* Initialize the Perf DPC and Timer */
231 KeInitializeDpc(&Prcb
->PowerState
.PerfDpc
, PopPerfIdleDpc
, Prcb
);
232 KeSetTargetProcessorDpc(&Prcb
->PowerState
.PerfDpc
, Prcb
->Number
);
233 KeInitializeTimerEx(&Prcb
->PowerState
.PerfTimer
, SynchronizationTimer
);
236 /* PUBLIC FUNCTIONS **********************************************************/
243 PoCancelDeviceNotify(IN PVOID NotifyBlock
)
246 return STATUS_NOT_IMPLEMENTED
;
254 PoRegisterDeviceNotify(OUT PVOID Unknown0
,
262 return STATUS_NOT_IMPLEMENTED
;
270 PoShutdownBugCheck(IN BOOLEAN LogError
,
271 IN ULONG BugCheckCode
,
272 IN ULONG_PTR BugCheckParameter1
,
273 IN ULONG_PTR BugCheckParameter2
,
274 IN ULONG_PTR BugCheckParameter3
,
275 IN ULONG_PTR BugCheckParameter4
)
277 DPRINT1("PoShutdownBugCheck called\n");
279 /* FIXME: Log error if requested */
280 /* FIXME: Initiate a shutdown */
282 /* Bugcheck the system */
283 KeBugCheckEx(BugCheckCode
,
295 PoRequestShutdownEvent(OUT PVOID
*Event
)
298 return STATUS_NOT_IMPLEMENTED
;
306 PoSetHiberRange(IN PVOID HiberContext
,
308 IN OUT PVOID StartPage
,
321 PoCallDriver(IN PDEVICE_OBJECT DeviceObject
,
326 /* Forward to Io -- FIXME! */
327 Status
= IoCallDriver(DeviceObject
, Irp
);
338 PoRegisterDeviceForIdleDetection(IN PDEVICE_OBJECT DeviceObject
,
339 IN ULONG ConservationIdleTime
,
340 IN ULONG PerformanceIdleTime
,
341 IN DEVICE_POWER_STATE State
)
352 PoRegisterSystemState(IN PVOID StateHandle
,
353 IN EXECUTION_STATE Flags
)
364 PoRequestPowerIrp(IN PDEVICE_OBJECT DeviceObject
,
365 IN UCHAR MinorFunction
,
366 IN POWER_STATE PowerState
,
367 IN PREQUEST_POWER_COMPLETE CompletionFunction
,
369 OUT PIRP
*pIrp OPTIONAL
)
371 PDEVICE_OBJECT TopDeviceObject
;
372 PIO_STACK_LOCATION Stack
;
374 PREQUEST_POWER_ITEM RequestPowerItem
;
376 if (MinorFunction
!= IRP_MN_QUERY_POWER
377 && MinorFunction
!= IRP_MN_SET_POWER
378 && MinorFunction
!= IRP_MN_WAIT_WAKE
)
379 return STATUS_INVALID_PARAMETER_2
;
381 RequestPowerItem
= ExAllocatePool(NonPagedPool
, sizeof(REQUEST_POWER_ITEM
));
382 if (!RequestPowerItem
)
383 return STATUS_INSUFFICIENT_RESOURCES
;
385 /* Always call the top of the device stack */
386 TopDeviceObject
= IoGetAttachedDeviceReference(DeviceObject
);
388 Irp
= IoBuildAsynchronousFsdRequest(IRP_MJ_POWER
,
396 ExFreePool(RequestPowerItem
);
397 return STATUS_INSUFFICIENT_RESOURCES
;
400 /* POWER IRPs are always initialized with a status code of
401 STATUS_NOT_IMPLEMENTED */
402 Irp
->IoStatus
.Status
= STATUS_NOT_IMPLEMENTED
;
403 Irp
->IoStatus
.Information
= 0;
405 Stack
= IoGetNextIrpStackLocation(Irp
);
406 Stack
->MinorFunction
= MinorFunction
;
407 if (MinorFunction
== IRP_MN_WAIT_WAKE
)
408 Stack
->Parameters
.WaitWake
.PowerState
= PowerState
.SystemState
;
411 Stack
->Parameters
.Power
.Type
= DevicePowerState
;
412 Stack
->Parameters
.Power
.State
= PowerState
;
415 RequestPowerItem
->CompletionRoutine
= CompletionFunction
;
416 RequestPowerItem
->PowerState
= PowerState
;
417 RequestPowerItem
->Context
= Context
;
422 IoSetCompletionRoutine(Irp
, PopRequestPowerIrpCompletion
, RequestPowerItem
, TRUE
, TRUE
, TRUE
);
423 IoCallDriver(TopDeviceObject
, Irp
);
425 /* Always return STATUS_PENDING. The completion routine
426 * will call CompletionFunction and complete the Irp.
428 return STATUS_PENDING
;
436 PoSetPowerState(IN PDEVICE_OBJECT DeviceObject
,
437 IN POWER_STATE_TYPE Type
,
438 IN POWER_STATE State
)
442 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
444 ps
.SystemState
= PowerSystemWorking
; // Fully on
445 ps
.DeviceState
= PowerDeviceD0
; // Fully on
455 PoSetSystemState(IN EXECUTION_STATE Flags
)
465 PoStartNextPowerIrp(IN PIRP Irp
)
475 PoUnregisterSystemState(IN PVOID StateHandle
)
485 PoQueueShutdownWorkItem(IN PWORK_QUEUE_ITEM WorkItem
)
490 return STATUS_NOT_IMPLEMENTED
;
498 NtInitiatePowerAction (IN POWER_ACTION SystemAction
,
499 IN SYSTEM_POWER_STATE MinSystemState
,
501 IN BOOLEAN Asynchronous
)
504 return STATUS_NOT_IMPLEMENTED
;
512 NtPowerInformation(IN POWER_INFORMATION_LEVEL PowerInformationLevel
,
513 IN PVOID InputBuffer OPTIONAL
,
514 IN ULONG InputBufferLength
,
515 OUT PVOID OutputBuffer OPTIONAL
,
516 IN ULONG OutputBufferLength
)
522 DPRINT("NtPowerInformation(PowerInformationLevel 0x%x, InputBuffer 0x%x, "
523 "InputBufferLength 0x%x, OutputBuffer 0x%x, OutputBufferLength 0x%x)\n",
524 PowerInformationLevel
,
525 InputBuffer
, InputBufferLength
,
526 OutputBuffer
, OutputBufferLength
);
528 switch (PowerInformationLevel
)
530 case SystemBatteryState
:
532 PSYSTEM_BATTERY_STATE BatteryState
= (PSYSTEM_BATTERY_STATE
)OutputBuffer
;
534 if (InputBuffer
!= NULL
)
535 return STATUS_INVALID_PARAMETER
;
536 if (OutputBufferLength
< sizeof(SYSTEM_BATTERY_STATE
))
537 return STATUS_BUFFER_TOO_SMALL
;
539 /* Just zero the struct (and thus set BatteryState->BatteryPresent = FALSE) */
540 RtlZeroMemory(BatteryState
, sizeof(SYSTEM_BATTERY_STATE
));
541 BatteryState
->EstimatedTime
= MAXULONG
;
543 Status
= STATUS_SUCCESS
;
546 case SystemPowerCapabilities
:
548 PSYSTEM_POWER_CAPABILITIES PowerCapabilities
= (PSYSTEM_POWER_CAPABILITIES
)OutputBuffer
;
550 if (InputBuffer
!= NULL
)
551 return STATUS_INVALID_PARAMETER
;
552 if (OutputBufferLength
< sizeof(SYSTEM_POWER_CAPABILITIES
))
553 return STATUS_BUFFER_TOO_SMALL
;
555 /* Just zero the struct (and thus set BatteryState->BatteryPresent = FALSE) */
556 RtlZeroMemory(PowerCapabilities
, sizeof(SYSTEM_POWER_CAPABILITIES
));
557 //PowerCapabilities->SystemBatteriesPresent = 0;
559 Status
= STATUS_SUCCESS
;
564 Status
= STATUS_NOT_IMPLEMENTED
;
565 DPRINT1("PowerInformationLevel 0x%x is UNIMPLEMENTED! Have a nice day.\n",
566 PowerInformationLevel
);
575 NtGetDevicePowerState(IN HANDLE Device
,
576 IN PDEVICE_POWER_STATE PowerState
)
579 return STATUS_NOT_IMPLEMENTED
;
584 NtIsSystemResumeAutomatic(VOID
)
592 NtRequestWakeupLatency(IN LATENCY_TIME Latency
)
595 return STATUS_NOT_IMPLEMENTED
;
600 NtSetThreadExecutionState(IN EXECUTION_STATE esFlags
,
601 OUT EXECUTION_STATE
*PreviousFlags
)
603 PKTHREAD Thread
= KeGetCurrentThread();
604 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
605 EXECUTION_STATE PreviousState
;
609 if (esFlags
& ~(ES_CONTINUOUS
| ES_USER_PRESENT
))
611 /* Fail the request */
612 return STATUS_INVALID_PARAMETER
;
615 /* Check for user parameters */
616 if (PreviousMode
!= KernelMode
)
618 /* Protect the probes */
621 /* Check if the pointer is valid */
622 ProbeForWriteUlong(PreviousFlags
);
624 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
626 /* It isn't -- fail */
627 _SEH2_YIELD(return _SEH2_GetExceptionCode());
632 /* Save the previous state, always masking in the continous flag */
633 PreviousState
= Thread
->PowerState
| ES_CONTINUOUS
;
635 /* Check if we need to update the power state */
636 if (esFlags
& ES_CONTINUOUS
) Thread
->PowerState
= esFlags
;
638 /* Protect the write back to user mode */
641 /* Return the previous flags */
642 *PreviousFlags
= PreviousState
;
644 _SEH2_EXCEPT(ExSystemExceptionFilter())
646 /* Something's wrong, fail */
647 _SEH2_YIELD(return _SEH2_GetExceptionCode());
652 return STATUS_SUCCESS
;
657 NtSetSystemPowerState(IN POWER_ACTION SystemAction
,
658 IN SYSTEM_POWER_STATE MinSystemState
,
661 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
662 POP_POWER_ACTION Action
= {0};
666 /* Check for invalid parameter combinations */
667 if ((MinSystemState
>= PowerSystemMaximum
) ||
668 (MinSystemState
<= PowerSystemUnspecified
) ||
669 (SystemAction
> PowerActionWarmEject
) ||
670 (SystemAction
< PowerActionReserved
) ||
671 (Flags
& ~(POWER_ACTION_QUERY_ALLOWED
|
672 POWER_ACTION_UI_ALLOWED
|
673 POWER_ACTION_OVERRIDE_APPS
|
674 POWER_ACTION_LIGHTEST_FIRST
|
675 POWER_ACTION_LOCK_CONSOLE
|
676 POWER_ACTION_DISABLE_WAKES
|
677 POWER_ACTION_CRITICAL
)))
679 DPRINT1("NtSetSystemPowerState: Bad parameters!\n");
680 DPRINT1(" SystemAction: 0x%x\n", SystemAction
);
681 DPRINT1(" MinSystemState: 0x%x\n", MinSystemState
);
682 DPRINT1(" Flags: 0x%x\n", Flags
);
683 return STATUS_INVALID_PARAMETER
;
686 /* Check for user caller */
687 if (PreviousMode
!= KernelMode
)
689 /* Check for shutdown permission */
690 if (!SeSinglePrivilegeCheck(SeShutdownPrivilege
, PreviousMode
))
693 DPRINT1("ERROR: Privilege not held for shutdown\n");
694 //return STATUS_PRIVILEGE_NOT_HELD; HACK!
697 /* Do it as a kernel-mode caller for consistency with system state */
698 return ZwSetSystemPowerState (SystemAction
, MinSystemState
, Flags
);
701 /* Read policy settings (partial shutdown vs. full shutdown) */
702 if (SystemAction
== PowerActionShutdown
) PopReadShutdownPolicy();
704 /* Disable lazy flushing of registry */
705 DPRINT1("Stopping lazy flush\n");
706 CmSetLazyFlushState(FALSE
);
708 /* Setup the power action */
709 Action
.Action
= SystemAction
;
710 Action
.Flags
= Flags
;
712 /* Notify callbacks */
713 DPRINT1("Notifying callbacks\n");
714 ExNotifyCallback(PowerStateCallback
, (PVOID
)3, NULL
);
716 /* Swap in any worker thread stacks */
717 DPRINT1("Swapping worker threads\n");
718 ExSwapinWorkerThreads(FALSE
);
720 /* Make our action global */
723 /* Start power loop */
724 Status
= STATUS_CANCELLED
;
727 /* Break out if there's nothing to do */
728 if (Action
.Action
== PowerActionNone
) break;
730 /* Check for first-pass or restart */
731 if (Status
== STATUS_CANCELLED
)
733 /* Check for shutdown action */
734 if ((PopAction
.Action
== PowerActionShutdown
) ||
735 (PopAction
.Action
== PowerActionShutdownReset
) ||
736 (PopAction
.Action
== PowerActionShutdownOff
))
739 PopAction
.Shutdown
= TRUE
;
742 /* Now we are good to go */
743 Status
= STATUS_SUCCESS
;
746 /* Check if we're still in an invalid status */
747 if (!NT_SUCCESS(Status
)) break;
750 /* Flush dirty cache pages */
751 CcRosFlushDirtyPages(-1, &Dummy
);
754 /* Flush all volumes and the registry */
755 DPRINT1("Flushing volumes, cache flushed %d pages\n", Dummy
);
756 PopFlushVolumes(PopAction
.Shutdown
);
758 /* Set IRP for drivers */
759 PopAction
.IrpMinor
= IRP_MN_SET_POWER
;
760 if (PopAction
.Shutdown
)
762 DPRINT1("Queueing shutdown thread\n");
763 /* Check if we are running in the system context */
764 if (PsGetCurrentProcess() != PsInitialSystemProcess
)
766 /* We're not, so use a worker thread for shutdown */
767 ExInitializeWorkItem(&PopShutdownWorkItem
,
768 &PopGracefulShutdown
,
771 ExQueueWorkItem(&PopShutdownWorkItem
, CriticalWorkQueue
);
773 /* Spend us -- when we wake up, the system is good to go down */
774 KeSuspendThread(KeGetCurrentThread());
775 Status
= STATUS_SYSTEM_SHUTDOWN
;
781 /* Do the shutdown inline */
782 PopGracefulShutdown(NULL
);
786 /* You should not have made it this far */
787 ASSERT(FALSE
&& "System is still up and running?!");
792 /* We're done, return */