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 ******************************************************************/
17 /* GLOBALS *******************************************************************/
19 typedef struct _REQUEST_POWER_ITEM
21 PREQUEST_POWER_COMPLETE CompletionRoutine
;
22 POWER_STATE PowerState
;
24 } REQUEST_POWER_ITEM
, *PREQUEST_POWER_ITEM
;
26 PDEVICE_NODE PopSystemPowerDeviceNode
= NULL
;
27 BOOLEAN PopAcpiPresent
= FALSE
;
28 POP_POWER_ACTION PopAction
;
29 WORK_QUEUE_ITEM PopShutdownWorkItem
;
31 /* PRIVATE FUNCTIONS *********************************************************/
36 PopRequestPowerIrpCompletion(IN PDEVICE_OBJECT DeviceObject
,
40 PIO_STACK_LOCATION Stack
;
41 PREQUEST_POWER_ITEM RequestPowerItem
;
43 Stack
= IoGetNextIrpStackLocation(Irp
);
44 RequestPowerItem
= (PREQUEST_POWER_ITEM
)Context
;
46 RequestPowerItem
->CompletionRoutine(DeviceObject
,
48 RequestPowerItem
->PowerState
,
49 RequestPowerItem
->Context
,
56 return STATUS_MORE_PROCESSING_REQUIRED
;
61 PopCleanupPowerState(IN PPOWER_STATE PowerState
)
68 PopSetSystemPowerState(SYSTEM_POWER_STATE PowerState
)
70 IO_STATUS_BLOCK IoStatusBlock
;
71 PDEVICE_OBJECT DeviceObject
;
72 PIO_STACK_LOCATION IrpSp
;
78 if (!PopAcpiPresent
) return STATUS_NOT_IMPLEMENTED
;
80 Status
= IopGetSystemPowerDeviceObject(&DeviceObject
);
81 if (!NT_SUCCESS(Status
))
83 DPRINT1("No system power driver available\n");
84 return STATUS_UNSUCCESSFUL
;
87 Fdo
= IoGetAttachedDeviceReference(DeviceObject
);
89 if (Fdo
== DeviceObject
)
91 DPRINT("An FDO was not attached\n");
92 return STATUS_UNSUCCESSFUL
;
95 KeInitializeEvent(&Event
,
99 Irp
= IoBuildSynchronousFsdRequest(IRP_MJ_POWER
,
107 IrpSp
= IoGetNextIrpStackLocation(Irp
);
108 IrpSp
->MinorFunction
= IRP_MN_SET_POWER
;
109 IrpSp
->Parameters
.Power
.Type
= SystemPowerState
;
110 IrpSp
->Parameters
.Power
.State
.SystemState
= PowerState
;
112 DPRINT("Calling ACPI driver");
113 Status
= PoCallDriver(Fdo
, Irp
);
114 if (Status
== STATUS_PENDING
)
116 KeWaitForSingleObject(&Event
,
121 Status
= IoStatusBlock
.Status
;
124 ObDereferenceObject(Fdo
);
132 PoInitSystem(IN ULONG BootPhase
)
134 PVOID NotificationEntry
;
136 BOOLEAN ForceAcpiDisable
= FALSE
;
138 /* Check if this is phase 1 init */
141 /* Register power button notification */
142 IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange
,
143 PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES
,
144 (PVOID
)&GUID_DEVICE_SYS_BUTTON
,
146 PhysicalDeviceObject
->DriverObject
,
147 PopAddRemoveSysCapsCallback
,
151 /* Register lid notification */
152 IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange
,
153 PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES
,
154 (PVOID
)&GUID_DEVICE_LID
,
156 PhysicalDeviceObject
->DriverObject
,
157 PopAddRemoveSysCapsCallback
,
163 /* Get the Command Line */
164 CommandLine
= KeLoaderBlock
->LoadOptions
;
167 _strupr(CommandLine
);
169 /* Check for ACPI disable */
170 if (strstr(CommandLine
, "NOACPI")) ForceAcpiDisable
= TRUE
;
172 if (ForceAcpiDisable
)
174 /* Set the ACPI State to False if it's been forced that way */
175 PopAcpiPresent
= FALSE
;
179 /* Otherwise check if the LoaderBlock has a ACPI Table */
180 PopAcpiPresent
= KeLoaderBlock
->Extension
->AcpiTable
!= NULL
? TRUE
: FALSE
;
184 /* Initialize volume support */
185 InitializeListHead(&PopVolumeDevices
);
186 KeInitializeGuardedMutex(&PopVolumeLock
);
188 /* Initialize support for dope */
189 KeInitializeSpinLock(&PopDopeGlobalLock
);
195 PopPerfIdle(PPROCESSOR_POWER_STATE PowerState
)
197 DPRINT1("PerfIdle function: %p\n", PowerState
);
202 PopPerfIdleDpc(IN PKDPC Dpc
,
203 IN PVOID DeferredContext
,
204 IN PVOID SystemArgument1
,
205 IN PVOID SystemArgument2
)
207 /* Call the Perf Idle function */
208 PopPerfIdle(&((PKPRCB
)DeferredContext
)->PowerState
);
213 PopIdle0(IN PPROCESSOR_POWER_STATE PowerState
)
215 /* FIXME: Extremly naive implementation */
222 PoInitializePrcb(IN PKPRCB Prcb
)
224 /* Initialize the Power State */
225 RtlZeroMemory(&Prcb
->PowerState
, sizeof(Prcb
->PowerState
));
226 Prcb
->PowerState
.Idle0KernelTimeLimit
= 0xFFFFFFFF;
227 Prcb
->PowerState
.CurrentThrottle
= 100;
228 Prcb
->PowerState
.CurrentThrottleIndex
= 0;
229 Prcb
->PowerState
.IdleFunction
= PopIdle0
;
231 /* Initialize the Perf DPC and Timer */
232 KeInitializeDpc(&Prcb
->PowerState
.PerfDpc
, PopPerfIdleDpc
, Prcb
);
233 KeSetTargetProcessorDpc(&Prcb
->PowerState
.PerfDpc
, Prcb
->Number
);
234 KeInitializeTimerEx(&Prcb
->PowerState
.PerfTimer
, SynchronizationTimer
);
237 /* PUBLIC FUNCTIONS **********************************************************/
244 PoCancelDeviceNotify(IN PVOID NotifyBlock
)
247 return STATUS_NOT_IMPLEMENTED
;
255 PoRegisterDeviceNotify(OUT PVOID Unknown0
,
263 return STATUS_NOT_IMPLEMENTED
;
271 PoShutdownBugCheck(IN BOOLEAN LogError
,
272 IN ULONG BugCheckCode
,
273 IN ULONG_PTR BugCheckParameter1
,
274 IN ULONG_PTR BugCheckParameter2
,
275 IN ULONG_PTR BugCheckParameter3
,
276 IN ULONG_PTR BugCheckParameter4
)
278 DPRINT1("PoShutdownBugCheck called\n");
280 /* FIXME: Log error if requested */
281 /* FIXME: Initiate a shutdown */
283 /* Bugcheck the system */
284 KeBugCheckEx(BugCheckCode
,
296 PoRequestShutdownEvent(OUT PVOID
*Event
)
299 return STATUS_NOT_IMPLEMENTED
;
307 PoSetHiberRange(IN PVOID HiberContext
,
309 IN OUT PVOID StartPage
,
322 PoCallDriver(IN PDEVICE_OBJECT DeviceObject
,
327 /* Forward to Io -- FIXME! */
328 Status
= IoCallDriver(DeviceObject
, Irp
);
339 PoRegisterDeviceForIdleDetection(IN PDEVICE_OBJECT DeviceObject
,
340 IN ULONG ConservationIdleTime
,
341 IN ULONG PerformanceIdleTime
,
342 IN DEVICE_POWER_STATE State
)
353 PoRegisterSystemState(IN PVOID StateHandle
,
354 IN EXECUTION_STATE Flags
)
365 PoRequestPowerIrp(IN PDEVICE_OBJECT DeviceObject
,
366 IN UCHAR MinorFunction
,
367 IN POWER_STATE PowerState
,
368 IN PREQUEST_POWER_COMPLETE CompletionFunction
,
370 OUT PIRP
*pIrp OPTIONAL
)
372 PDEVICE_OBJECT TopDeviceObject
;
373 PIO_STACK_LOCATION Stack
;
375 PREQUEST_POWER_ITEM RequestPowerItem
;
377 if (MinorFunction
!= IRP_MN_QUERY_POWER
378 && MinorFunction
!= IRP_MN_SET_POWER
379 && MinorFunction
!= IRP_MN_WAIT_WAKE
)
380 return STATUS_INVALID_PARAMETER_2
;
382 RequestPowerItem
= ExAllocatePool(NonPagedPool
, sizeof(REQUEST_POWER_ITEM
));
383 if (!RequestPowerItem
)
384 return STATUS_INSUFFICIENT_RESOURCES
;
386 /* Always call the top of the device stack */
387 TopDeviceObject
= IoGetAttachedDeviceReference(DeviceObject
);
389 Irp
= IoBuildAsynchronousFsdRequest(IRP_MJ_POWER
,
397 ExFreePool(RequestPowerItem
);
398 return STATUS_INSUFFICIENT_RESOURCES
;
401 /* POWER IRPs are always initialized with a status code of
402 STATUS_NOT_IMPLEMENTED */
403 Irp
->IoStatus
.Status
= STATUS_NOT_IMPLEMENTED
;
404 Irp
->IoStatus
.Information
= 0;
406 Stack
= IoGetNextIrpStackLocation(Irp
);
407 Stack
->MinorFunction
= MinorFunction
;
408 if (MinorFunction
== IRP_MN_WAIT_WAKE
)
409 Stack
->Parameters
.WaitWake
.PowerState
= PowerState
.SystemState
;
412 Stack
->Parameters
.Power
.Type
= DevicePowerState
;
413 Stack
->Parameters
.Power
.State
= PowerState
;
416 RequestPowerItem
->CompletionRoutine
= CompletionFunction
;
417 RequestPowerItem
->PowerState
= PowerState
;
418 RequestPowerItem
->Context
= Context
;
423 IoSetCompletionRoutine(Irp
, PopRequestPowerIrpCompletion
, RequestPowerItem
, TRUE
, TRUE
, TRUE
);
424 IoCallDriver(TopDeviceObject
, Irp
);
426 /* Always return STATUS_PENDING. The completion routine
427 * will call CompletionFunction and complete the Irp.
429 return STATUS_PENDING
;
437 PoSetPowerState(IN PDEVICE_OBJECT DeviceObject
,
438 IN POWER_STATE_TYPE Type
,
439 IN POWER_STATE State
)
443 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
445 ps
.SystemState
= PowerSystemWorking
; // Fully on
446 ps
.DeviceState
= PowerDeviceD0
; // Fully on
456 PoSetSystemState(IN EXECUTION_STATE Flags
)
466 PoStartNextPowerIrp(IN PIRP Irp
)
476 PoUnregisterSystemState(IN PVOID StateHandle
)
486 PoQueueShutdownWorkItem(IN PWORK_QUEUE_ITEM WorkItem
)
491 return STATUS_NOT_IMPLEMENTED
;
499 NtInitiatePowerAction (IN POWER_ACTION SystemAction
,
500 IN SYSTEM_POWER_STATE MinSystemState
,
502 IN BOOLEAN Asynchronous
)
505 return STATUS_NOT_IMPLEMENTED
;
513 NtPowerInformation(IN POWER_INFORMATION_LEVEL PowerInformationLevel
,
514 IN PVOID InputBuffer OPTIONAL
,
515 IN ULONG InputBufferLength
,
516 OUT PVOID OutputBuffer OPTIONAL
,
517 IN ULONG OutputBufferLength
)
523 DPRINT("NtPowerInformation(PowerInformationLevel 0x%x, InputBuffer 0x%x, "
524 "InputBufferLength 0x%x, OutputBuffer 0x%x, OutputBufferLength 0x%x)\n",
525 PowerInformationLevel
,
526 InputBuffer
, InputBufferLength
,
527 OutputBuffer
, OutputBufferLength
);
529 switch (PowerInformationLevel
)
531 case SystemBatteryState
:
533 PSYSTEM_BATTERY_STATE BatteryState
= (PSYSTEM_BATTERY_STATE
)OutputBuffer
;
535 if (InputBuffer
!= NULL
)
536 return STATUS_INVALID_PARAMETER
;
537 if (OutputBufferLength
< sizeof(SYSTEM_BATTERY_STATE
))
538 return STATUS_BUFFER_TOO_SMALL
;
540 /* Just zero the struct (and thus set BatteryState->BatteryPresent = FALSE) */
541 RtlZeroMemory(BatteryState
, sizeof(SYSTEM_BATTERY_STATE
));
542 BatteryState
->EstimatedTime
= MAXULONG
;
544 Status
= STATUS_SUCCESS
;
547 case SystemPowerCapabilities
:
549 PSYSTEM_POWER_CAPABILITIES PowerCapabilities
= (PSYSTEM_POWER_CAPABILITIES
)OutputBuffer
;
551 if (InputBuffer
!= NULL
)
552 return STATUS_INVALID_PARAMETER
;
553 if (OutputBufferLength
< sizeof(SYSTEM_POWER_CAPABILITIES
))
554 return STATUS_BUFFER_TOO_SMALL
;
556 /* Just zero the struct (and thus set BatteryState->BatteryPresent = FALSE) */
557 RtlZeroMemory(PowerCapabilities
, sizeof(SYSTEM_POWER_CAPABILITIES
));
558 //PowerCapabilities->SystemBatteriesPresent = 0;
560 Status
= STATUS_SUCCESS
;
565 Status
= STATUS_NOT_IMPLEMENTED
;
566 DPRINT1("PowerInformationLevel 0x%x is UNIMPLEMENTED! Have a nice day.\n",
567 PowerInformationLevel
);
576 NtGetDevicePowerState(IN HANDLE Device
,
577 IN PDEVICE_POWER_STATE PowerState
)
580 return STATUS_NOT_IMPLEMENTED
;
585 NtIsSystemResumeAutomatic(VOID
)
593 NtRequestWakeupLatency(IN LATENCY_TIME Latency
)
596 return STATUS_NOT_IMPLEMENTED
;
601 NtSetThreadExecutionState(IN EXECUTION_STATE esFlags
,
602 OUT EXECUTION_STATE
*PreviousFlags
)
604 PKTHREAD Thread
= KeGetCurrentThread();
605 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
606 EXECUTION_STATE PreviousState
;
610 if (esFlags
& ~(ES_CONTINUOUS
| ES_USER_PRESENT
))
612 /* Fail the request */
613 return STATUS_INVALID_PARAMETER
;
616 /* Check for user parameters */
617 if (PreviousMode
!= KernelMode
)
619 /* Protect the probes */
622 /* Check if the pointer is valid */
623 ProbeForWriteUlong(PreviousFlags
);
625 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
627 /* It isn't -- fail */
628 _SEH2_YIELD(return _SEH2_GetExceptionCode());
633 /* Save the previous state, always masking in the continous flag */
634 PreviousState
= Thread
->PowerState
| ES_CONTINUOUS
;
636 /* Check if we need to update the power state */
637 if (esFlags
& ES_CONTINUOUS
) Thread
->PowerState
= esFlags
;
639 /* Protect the write back to user mode */
642 /* Return the previous flags */
643 *PreviousFlags
= PreviousState
;
645 _SEH2_EXCEPT(ExSystemExceptionFilter())
647 /* Something's wrong, fail */
648 _SEH2_YIELD(return _SEH2_GetExceptionCode());
653 return STATUS_SUCCESS
;
658 NtSetSystemPowerState(IN POWER_ACTION SystemAction
,
659 IN SYSTEM_POWER_STATE MinSystemState
,
662 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
663 POP_POWER_ACTION Action
= {0};
667 /* Check for invalid parameter combinations */
668 if ((MinSystemState
>= PowerSystemMaximum
) ||
669 (MinSystemState
<= PowerSystemUnspecified
) ||
670 (SystemAction
> PowerActionWarmEject
) ||
671 (SystemAction
< PowerActionReserved
) ||
672 (Flags
& ~(POWER_ACTION_QUERY_ALLOWED
|
673 POWER_ACTION_UI_ALLOWED
|
674 POWER_ACTION_OVERRIDE_APPS
|
675 POWER_ACTION_LIGHTEST_FIRST
|
676 POWER_ACTION_LOCK_CONSOLE
|
677 POWER_ACTION_DISABLE_WAKES
|
678 POWER_ACTION_CRITICAL
)))
680 DPRINT1("NtSetSystemPowerState: Bad parameters!\n");
681 DPRINT1(" SystemAction: 0x%x\n", SystemAction
);
682 DPRINT1(" MinSystemState: 0x%x\n", MinSystemState
);
683 DPRINT1(" Flags: 0x%x\n", Flags
);
684 return STATUS_INVALID_PARAMETER
;
687 /* Check for user caller */
688 if (PreviousMode
!= KernelMode
)
690 /* Check for shutdown permission */
691 if (!SeSinglePrivilegeCheck(SeShutdownPrivilege
, PreviousMode
))
694 DPRINT1("ERROR: Privilege not held for shutdown\n");
695 //return STATUS_PRIVILEGE_NOT_HELD; HACK!
698 /* Do it as a kernel-mode caller for consistency with system state */
699 return ZwSetSystemPowerState (SystemAction
, MinSystemState
, Flags
);
702 /* Read policy settings (partial shutdown vs. full shutdown) */
703 if (SystemAction
== PowerActionShutdown
) PopReadShutdownPolicy();
705 /* Disable lazy flushing of registry */
706 DPRINT1("Stopping lazy flush\n");
707 CmSetLazyFlushState(FALSE
);
709 /* Setup the power action */
710 Action
.Action
= SystemAction
;
711 Action
.Flags
= Flags
;
713 /* Notify callbacks */
714 DPRINT1("Notifying callbacks\n");
715 ExNotifyCallback(PowerStateCallback
, (PVOID
)3, NULL
);
717 /* Swap in any worker thread stacks */
718 DPRINT1("Swapping worker threads\n");
719 ExSwapinWorkerThreads(FALSE
);
721 /* Make our action global */
724 /* Start power loop */
725 Status
= STATUS_CANCELLED
;
728 /* Break out if there's nothing to do */
729 if (Action
.Action
== PowerActionNone
) break;
731 /* Check for first-pass or restart */
732 if (Status
== STATUS_CANCELLED
)
734 /* Check for shutdown action */
735 if ((PopAction
.Action
== PowerActionShutdown
) ||
736 (PopAction
.Action
== PowerActionShutdownReset
) ||
737 (PopAction
.Action
== PowerActionShutdownOff
))
740 PopAction
.Shutdown
= TRUE
;
743 /* Now we are good to go */
744 Status
= STATUS_SUCCESS
;
747 /* Check if we're still in an invalid status */
748 if (!NT_SUCCESS(Status
)) break;
751 /* Flush dirty cache pages */
752 CcRosFlushDirtyPages(-1, &Dummy
);
757 /* Flush all volumes and the registry */
758 DPRINT1("Flushing volumes, cache flushed %d pages\n", Dummy
);
759 PopFlushVolumes(PopAction
.Shutdown
);
761 /* Set IRP for drivers */
762 PopAction
.IrpMinor
= IRP_MN_SET_POWER
;
763 if (PopAction
.Shutdown
)
765 DPRINT1("Queueing shutdown thread\n");
766 /* Check if we are running in the system context */
767 if (PsGetCurrentProcess() != PsInitialSystemProcess
)
769 /* We're not, so use a worker thread for shutdown */
770 ExInitializeWorkItem(&PopShutdownWorkItem
,
771 &PopGracefulShutdown
,
774 ExQueueWorkItem(&PopShutdownWorkItem
, CriticalWorkQueue
);
776 /* Spend us -- when we wake up, the system is good to go down */
777 KeSuspendThread(KeGetCurrentThread());
778 Status
= STATUS_SYSTEM_SHUTDOWN
;
784 /* Do the shutdown inline */
785 PopGracefulShutdown(NULL
);
789 /* You should not have made it this far */
790 ASSERT(FALSE
&& "System is still up and running?!");
795 /* We're done, return */