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
,
51 ExFreePool(&Irp
->IoStatus
);
54 return STATUS_SUCCESS
;
59 PopCleanupPowerState(IN PPOWER_STATE PowerState
)
66 PopSetSystemPowerState(SYSTEM_POWER_STATE PowerState
)
68 IO_STATUS_BLOCK IoStatusBlock
;
69 PDEVICE_OBJECT DeviceObject
;
70 PIO_STACK_LOCATION IrpSp
;
76 if (!PopAcpiPresent
) return STATUS_NOT_IMPLEMENTED
;
78 Status
= IopGetSystemPowerDeviceObject(&DeviceObject
);
79 if (!NT_SUCCESS(Status
))
81 DPRINT1("No system power driver available\n");
82 return STATUS_UNSUCCESSFUL
;
85 Fdo
= IoGetAttachedDeviceReference(DeviceObject
);
87 if (Fdo
== DeviceObject
)
89 DPRINT("An FDO was not attached\n");
90 return STATUS_UNSUCCESSFUL
;
93 KeInitializeEvent(&Event
,
97 Irp
= IoBuildSynchronousFsdRequest(IRP_MJ_POWER
,
105 IrpSp
= IoGetNextIrpStackLocation(Irp
);
106 IrpSp
->MinorFunction
= IRP_MN_SET_POWER
;
107 IrpSp
->Parameters
.Power
.Type
= SystemPowerState
;
108 IrpSp
->Parameters
.Power
.State
.SystemState
= PowerState
;
110 DPRINT("Calling ACPI driver");
111 Status
= PoCallDriver(Fdo
, Irp
);
112 if (Status
== STATUS_PENDING
)
114 KeWaitForSingleObject(&Event
,
119 Status
= IoStatusBlock
.Status
;
122 ObDereferenceObject(Fdo
);
129 PoInitSystem(IN ULONG BootPhase
)
131 PVOID NotificationEntry
;
133 BOOLEAN ForceAcpiDisable
= FALSE
;
135 /* Check if this is phase 1 init */
138 /* Registry power button notification */
139 IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange
,
140 0, /* The registry has not been initialized yet */
141 (PVOID
)&GUID_DEVICE_SYS_BUTTON
,
143 PhysicalDeviceObject
->DriverObject
,
144 PopAddRemoveSysCapsCallback
,
150 /* Get the Command Line */
151 CommandLine
= KeLoaderBlock
->LoadOptions
;
154 _strupr(CommandLine
);
156 /* Check for ACPI disable */
157 if (strstr(CommandLine
, "NOACPI")) ForceAcpiDisable
= TRUE
;
159 if (ForceAcpiDisable
)
161 /* Set the ACPI State to False if it's been forced that way */
162 PopAcpiPresent
= FALSE
;
166 /* Otherwise check if the LoaderBlock has a ACPI Table */
167 PopAcpiPresent
= KeLoaderBlock
->Extension
->AcpiTable
!= NULL
? TRUE
: FALSE
;
171 /* Initialize volume support */
172 InitializeListHead(&PopVolumeDevices
);
173 KeInitializeGuardedMutex(&PopVolumeLock
);
175 /* Initialize support for dope */
176 KeInitializeSpinLock(&PopDopeGlobalLock
);
182 PopPerfIdle(PPROCESSOR_POWER_STATE PowerState
)
184 DPRINT1("PerfIdle function: %p\n", PowerState
);
189 PopPerfIdleDpc(IN PKDPC Dpc
,
190 IN PVOID DeferredContext
,
191 IN PVOID SystemArgument1
,
192 IN PVOID SystemArgument2
)
194 /* Call the Perf Idle function */
195 PopPerfIdle(&((PKPRCB
)DeferredContext
)->PowerState
);
200 PopIdle0(IN PPROCESSOR_POWER_STATE PowerState
)
202 /* FIXME: Extremly naive implementation */
208 PoInitializePrcb(IN PKPRCB Prcb
)
210 /* Initialize the Power State */
211 RtlZeroMemory(&Prcb
->PowerState
, sizeof(Prcb
->PowerState
));
212 Prcb
->PowerState
.Idle0KernelTimeLimit
= 0xFFFFFFFF;
213 Prcb
->PowerState
.CurrentThrottle
= 100;
214 Prcb
->PowerState
.CurrentThrottleIndex
= 0;
215 Prcb
->PowerState
.IdleFunction
= PopIdle0
;
217 /* Initialize the Perf DPC and Timer */
218 KeInitializeDpc(&Prcb
->PowerState
.PerfDpc
, PopPerfIdleDpc
, Prcb
);
219 KeSetTargetProcessorDpc(&Prcb
->PowerState
.PerfDpc
, Prcb
->Number
);
220 KeInitializeTimerEx(&Prcb
->PowerState
.PerfTimer
, SynchronizationTimer
);
223 /* PUBLIC FUNCTIONS **********************************************************/
230 PoCancelDeviceNotify(IN PVOID NotifyBlock
)
233 return STATUS_NOT_IMPLEMENTED
;
241 PoRegisterDeviceNotify(OUT PVOID Unknown0
,
249 return STATUS_NOT_IMPLEMENTED
;
257 PoShutdownBugCheck(IN BOOLEAN LogError
,
258 IN ULONG BugCheckCode
,
259 IN ULONG_PTR BugCheckParameter1
,
260 IN ULONG_PTR BugCheckParameter2
,
261 IN ULONG_PTR BugCheckParameter3
,
262 IN ULONG_PTR BugCheckParameter4
)
264 DPRINT1("PoShutdownBugCheck called\n");
266 /* FIXME: Log error if requested */
267 /* FIXME: Initiate a shutdown */
269 /* Bugcheck the system */
270 KeBugCheckEx(BugCheckCode
,
282 PoRequestShutdownEvent(OUT PVOID
*Event
)
285 return STATUS_NOT_IMPLEMENTED
;
293 PoSetHiberRange(IN PVOID HiberContext
,
295 IN OUT PVOID StartPage
,
308 PoCallDriver(IN PDEVICE_OBJECT DeviceObject
,
313 /* Forward to Io -- FIXME! */
314 Status
= IoCallDriver(DeviceObject
, Irp
);
325 PoRegisterDeviceForIdleDetection(IN PDEVICE_OBJECT DeviceObject
,
326 IN ULONG ConservationIdleTime
,
327 IN ULONG PerformanceIdleTime
,
328 IN DEVICE_POWER_STATE State
)
339 PoRegisterSystemState(IN PVOID StateHandle
,
340 IN EXECUTION_STATE Flags
)
351 PoRequestPowerIrp(IN PDEVICE_OBJECT DeviceObject
,
352 IN UCHAR MinorFunction
,
353 IN POWER_STATE PowerState
,
354 IN PREQUEST_POWER_COMPLETE CompletionFunction
,
356 OUT PIRP
*pIrp OPTIONAL
)
358 PDEVICE_OBJECT TopDeviceObject
;
359 PIO_STACK_LOCATION Stack
;
361 PIO_STATUS_BLOCK IoStatusBlock
;
362 PREQUEST_POWER_ITEM RequestPowerItem
;
365 if (MinorFunction
!= IRP_MN_QUERY_POWER
366 && MinorFunction
!= IRP_MN_SET_POWER
367 && MinorFunction
!= IRP_MN_WAIT_WAKE
)
368 return STATUS_INVALID_PARAMETER_2
;
370 RequestPowerItem
= ExAllocatePool(NonPagedPool
, sizeof(REQUEST_POWER_ITEM
));
371 if (!RequestPowerItem
)
372 return STATUS_INSUFFICIENT_RESOURCES
;
373 IoStatusBlock
= ExAllocatePool(NonPagedPool
, sizeof(IO_STATUS_BLOCK
));
376 ExFreePool(RequestPowerItem
);
377 return STATUS_INSUFFICIENT_RESOURCES
;
380 /* Always call the top of the device stack */
381 TopDeviceObject
= IoGetAttachedDeviceReference(DeviceObject
);
383 Irp
= IoBuildSynchronousFsdRequest(IRP_MJ_PNP
,
392 ExFreePool(RequestPowerItem
);
393 ExFreePool(IoStatusBlock
);
394 return STATUS_INSUFFICIENT_RESOURCES
;
397 /* POWER IRPs are always initialized with a status code of
398 STATUS_NOT_IMPLEMENTED */
399 Irp
->IoStatus
.Status
= STATUS_NOT_IMPLEMENTED
;
400 Irp
->IoStatus
.Information
= 0;
402 Stack
= IoGetNextIrpStackLocation(Irp
);
403 Stack
->MinorFunction
= MinorFunction
;
404 if (MinorFunction
== IRP_MN_WAIT_WAKE
)
405 Stack
->Parameters
.WaitWake
.PowerState
= PowerState
.SystemState
;
407 Stack
->Parameters
.WaitWake
.PowerState
= PowerState
.DeviceState
;
409 RequestPowerItem
->CompletionRoutine
= CompletionFunction
;
410 RequestPowerItem
->PowerState
= PowerState
;
411 RequestPowerItem
->Context
= Context
;
416 IoSetCompletionRoutine(Irp
, PopRequestPowerIrpCompletion
, RequestPowerItem
, TRUE
, TRUE
, TRUE
);
417 Status
= IoCallDriver(TopDeviceObject
, Irp
);
419 /* Always return STATUS_PENDING. The completion routine
420 * will call CompletionFunction and complete the Irp.
422 return STATUS_PENDING
;
430 PoSetPowerState(IN PDEVICE_OBJECT DeviceObject
,
431 IN POWER_STATE_TYPE Type
,
432 IN POWER_STATE State
)
436 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
438 ps
.SystemState
= PowerSystemWorking
; // Fully on
439 ps
.DeviceState
= PowerDeviceD0
; // Fully on
449 PoSetSystemState(IN EXECUTION_STATE Flags
)
459 PoStartNextPowerIrp(IN PIRP Irp
)
469 PoUnregisterSystemState(IN PVOID StateHandle
)
479 PoQueueShutdownWorkItem(IN PWORK_QUEUE_ITEM WorkItem
)
484 return STATUS_NOT_IMPLEMENTED
;
492 NtInitiatePowerAction (IN POWER_ACTION SystemAction
,
493 IN SYSTEM_POWER_STATE MinSystemState
,
495 IN BOOLEAN Asynchronous
)
498 return STATUS_NOT_IMPLEMENTED
;
506 NtPowerInformation(IN POWER_INFORMATION_LEVEL PowerInformationLevel
,
507 IN PVOID InputBuffer OPTIONAL
,
508 IN ULONG InputBufferLength
,
509 OUT PVOID OutputBuffer OPTIONAL
,
510 IN ULONG OutputBufferLength
)
516 DPRINT("NtPowerInformation(PowerInformationLevel 0x%x, InputBuffer 0x%x, "
517 "InputBufferLength 0x%x, OutputBuffer 0x%x, OutputBufferLength 0x%x)\n",
518 PowerInformationLevel
,
519 InputBuffer
, InputBufferLength
,
520 OutputBuffer
, OutputBufferLength
);
522 switch (PowerInformationLevel
)
524 case SystemBatteryState
:
526 PSYSTEM_BATTERY_STATE BatteryState
= (PSYSTEM_BATTERY_STATE
)OutputBuffer
;
528 if (InputBuffer
!= NULL
)
529 return STATUS_INVALID_PARAMETER
;
530 if (OutputBufferLength
< sizeof(SYSTEM_BATTERY_STATE
))
531 return STATUS_BUFFER_TOO_SMALL
;
533 /* Just zero the struct (and thus set BatteryState->BatteryPresent = FALSE) */
534 RtlZeroMemory(BatteryState
, sizeof(SYSTEM_BATTERY_STATE
));
535 BatteryState
->EstimatedTime
= MAXULONG
;
537 Status
= STATUS_SUCCESS
;
540 case SystemPowerCapabilities
:
542 PSYSTEM_POWER_CAPABILITIES PowerCapabilities
= (PSYSTEM_POWER_CAPABILITIES
)OutputBuffer
;
544 if (InputBuffer
!= NULL
)
545 return STATUS_INVALID_PARAMETER
;
546 if (OutputBufferLength
< sizeof(SYSTEM_POWER_CAPABILITIES
))
547 return STATUS_BUFFER_TOO_SMALL
;
549 /* Just zero the struct (and thus set BatteryState->BatteryPresent = FALSE) */
550 RtlZeroMemory(PowerCapabilities
, sizeof(SYSTEM_POWER_CAPABILITIES
));
551 //PowerCapabilities->SystemBatteriesPresent = 0;
553 Status
= STATUS_SUCCESS
;
558 Status
= STATUS_NOT_IMPLEMENTED
;
559 DPRINT1("PowerInformationLevel 0x%x is UNIMPLEMENTED! Have a nice day.\n",
560 PowerInformationLevel
);
569 NtGetDevicePowerState(IN HANDLE Device
,
570 IN PDEVICE_POWER_STATE PowerState
)
573 return STATUS_NOT_IMPLEMENTED
;
578 NtIsSystemResumeAutomatic(VOID
)
586 NtRequestWakeupLatency(IN LATENCY_TIME Latency
)
589 return STATUS_NOT_IMPLEMENTED
;
594 NtSetThreadExecutionState(IN EXECUTION_STATE esFlags
,
595 OUT EXECUTION_STATE
*PreviousFlags
)
597 PKTHREAD Thread
= KeGetCurrentThread();
598 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
599 EXECUTION_STATE PreviousState
;
603 if (esFlags
& ~(ES_CONTINUOUS
| ES_USER_PRESENT
))
605 /* Fail the request */
606 return STATUS_INVALID_PARAMETER
;
609 /* Check for user parameters */
610 if (PreviousMode
!= KernelMode
)
612 /* Protect the probes */
615 /* Check if the pointer is valid */
616 ProbeForWriteUlong(PreviousFlags
);
618 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
620 /* It isn't -- fail */
621 _SEH2_YIELD(return _SEH2_GetExceptionCode());
626 /* Save the previous state, always masking in the continous flag */
627 PreviousState
= Thread
->PowerState
| ES_CONTINUOUS
;
629 /* Check if we need to update the power state */
630 if (esFlags
& ES_CONTINUOUS
) Thread
->PowerState
= esFlags
;
632 /* Protect the write back to user mode */
635 /* Return the previous flags */
636 *PreviousFlags
= PreviousState
;
638 _SEH2_EXCEPT(ExSystemExceptionFilter())
640 /* Something's wrong, fail */
641 _SEH2_YIELD(return _SEH2_GetExceptionCode());
646 return STATUS_SUCCESS
;
651 NtSetSystemPowerState(IN POWER_ACTION SystemAction
,
652 IN SYSTEM_POWER_STATE MinSystemState
,
655 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
656 POP_POWER_ACTION Action
= {0};
659 /* Check for invalid parameter combinations */
660 if ((MinSystemState
>= PowerSystemMaximum
) ||
661 (MinSystemState
<= PowerSystemUnspecified
) ||
662 (SystemAction
> PowerActionWarmEject
) ||
663 (SystemAction
< PowerActionReserved
) ||
664 (Flags
& ~(POWER_ACTION_QUERY_ALLOWED
|
665 POWER_ACTION_UI_ALLOWED
|
666 POWER_ACTION_OVERRIDE_APPS
|
667 POWER_ACTION_LIGHTEST_FIRST
|
668 POWER_ACTION_LOCK_CONSOLE
|
669 POWER_ACTION_DISABLE_WAKES
|
670 POWER_ACTION_CRITICAL
)))
672 DPRINT1("NtSetSystemPowerState: Bad parameters!\n");
673 DPRINT1(" SystemAction: 0x%x\n", SystemAction
);
674 DPRINT1(" MinSystemState: 0x%x\n", MinSystemState
);
675 DPRINT1(" Flags: 0x%x\n", Flags
);
676 return STATUS_INVALID_PARAMETER
;
679 /* Check for user caller */
680 if (PreviousMode
!= KernelMode
)
682 /* Check for shutdown permission */
683 if (!SeSinglePrivilegeCheck(SeShutdownPrivilege
, PreviousMode
))
686 DPRINT1("ERROR: Privilege not held for shutdown\n");
687 //return STATUS_PRIVILEGE_NOT_HELD; HACK!
690 /* Do it as a kernel-mode caller for consistency with system state */
691 return ZwSetSystemPowerState (SystemAction
, MinSystemState
, Flags
);
694 /* Read policy settings (partial shutdown vs. full shutdown) */
695 if (SystemAction
== PowerActionShutdown
) PopReadShutdownPolicy();
697 /* Disable lazy flushing of registry */
698 DPRINT1("Stopping lazy flush\n");
699 CmSetLazyFlushState(FALSE
);
701 /* Setup the power action */
702 Action
.Action
= SystemAction
;
703 Action
.Flags
= Flags
;
705 /* Notify callbacks */
706 DPRINT1("Notifying callbacks\n");
707 ExNotifyCallback(PowerStateCallback
, (PVOID
)3, NULL
);
709 /* Swap in any worker thread stacks */
710 DPRINT1("Swapping worker threads\n");
711 ExSwapinWorkerThreads(FALSE
);
713 /* Make our action global */
716 /* Start power loop */
717 Status
= STATUS_CANCELLED
;
720 /* Break out if there's nothing to do */
721 if (Action
.Action
== PowerActionNone
) break;
723 /* Check for first-pass or restart */
724 if (Status
== STATUS_CANCELLED
)
726 /* Check for shutdown action */
727 if ((PopAction
.Action
== PowerActionShutdown
) ||
728 (PopAction
.Action
== PowerActionShutdownReset
) ||
729 (PopAction
.Action
== PowerActionShutdownOff
))
732 PopAction
.Shutdown
= TRUE
;
735 /* Now we are good to go */
736 Status
= STATUS_SUCCESS
;
739 /* Check if we're still in an invalid status */
740 if (!NT_SUCCESS(Status
)) break;
742 /* Flush all volumes and the registry */
743 DPRINT1("Flushing volumes\n");
744 PopFlushVolumes(PopAction
.Shutdown
);
746 /* Set IRP for drivers */
747 PopAction
.IrpMinor
= IRP_MN_SET_POWER
;
748 if (PopAction
.Shutdown
)
750 DPRINT1("Queueing shutdown thread\n");
751 /* Check if we are running in the system context */
752 if (PsGetCurrentProcess() != PsInitialSystemProcess
)
754 /* We're not, so use a worker thread for shutdown */
755 ExInitializeWorkItem(&PopShutdownWorkItem
,
756 &PopGracefulShutdown
,
759 ExQueueWorkItem(&PopShutdownWorkItem
, CriticalWorkQueue
);
761 /* Spend us -- when we wake up, the system is good to go down */
762 KeSuspendThread(KeGetCurrentThread());
763 Status
= STATUS_SYSTEM_SHUTDOWN
;
769 /* Do the shutdown inline */
770 PopGracefulShutdown(NULL
);
774 /* You should not have made it this far */
775 ASSERT(FALSE
&& "System is still up and running?!");
780 /* We're done, return */