[NTOSKRNL]
[reactos.git] / reactos / ntoskrnl / po / power.c
1 /*
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)
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include "initguid.h"
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <debug.h>
16
17 /* GLOBALS *******************************************************************/
18
19 typedef struct _REQUEST_POWER_ITEM
20 {
21 PREQUEST_POWER_COMPLETE CompletionRoutine;
22 POWER_STATE PowerState;
23 PVOID Context;
24 } REQUEST_POWER_ITEM, *PREQUEST_POWER_ITEM;
25
26 typedef struct _POWER_STATE_TRAVERSE_CONTEXT
27 {
28 SYSTEM_POWER_STATE SystemPowerState;
29 POWER_ACTION PowerAction;
30 PDEVICE_OBJECT PowerDevice;
31 } POWER_STATE_TRAVERSE_CONTEXT, *PPOWER_STATE_TRAVERSE_CONTEXT;
32
33 PDEVICE_NODE PopSystemPowerDeviceNode = NULL;
34 BOOLEAN PopAcpiPresent = FALSE;
35 POP_POWER_ACTION PopAction;
36 WORK_QUEUE_ITEM PopShutdownWorkItem;
37
38 /* PRIVATE FUNCTIONS *********************************************************/
39
40 static
41 NTSTATUS
42 NTAPI
43 PopRequestPowerIrpCompletion(IN PDEVICE_OBJECT DeviceObject,
44 IN PIRP Irp,
45 IN PVOID Context)
46 {
47 PIO_STACK_LOCATION Stack;
48 PREQUEST_POWER_ITEM RequestPowerItem;
49
50 Stack = IoGetNextIrpStackLocation(Irp);
51 RequestPowerItem = (PREQUEST_POWER_ITEM)Context;
52
53 RequestPowerItem->CompletionRoutine(DeviceObject,
54 Stack->MinorFunction,
55 RequestPowerItem->PowerState,
56 RequestPowerItem->Context,
57 &Irp->IoStatus);
58
59 ExFreePool(Context);
60
61 IoFreeIrp(Irp);
62 ObDereferenceObject(DeviceObject);
63
64 return STATUS_MORE_PROCESSING_REQUIRED;
65 }
66
67 VOID
68 NTAPI
69 PopCleanupPowerState(IN PPOWER_STATE PowerState)
70 {
71 //UNIMPLEMENTED;
72 }
73
74 NTSTATUS
75 PopSendQuerySystemPowerState(PDEVICE_OBJECT DeviceObject, SYSTEM_POWER_STATE SystemState, POWER_ACTION PowerAction)
76 {
77 KEVENT Event;
78 IO_STATUS_BLOCK IoStatusBlock;
79 PIO_STACK_LOCATION IrpSp;
80 PIRP Irp;
81 NTSTATUS Status;
82
83 KeInitializeEvent(&Event,
84 NotificationEvent,
85 FALSE);
86
87 Irp = IoBuildSynchronousFsdRequest(IRP_MJ_POWER,
88 DeviceObject,
89 NULL,
90 0,
91 NULL,
92 &Event,
93 &IoStatusBlock);
94 if (!Irp) return STATUS_INSUFFICIENT_RESOURCES;
95
96 IrpSp = IoGetNextIrpStackLocation(Irp);
97 IrpSp->MinorFunction = IRP_MN_QUERY_POWER;
98 IrpSp->Parameters.Power.Type = SystemPowerState;
99 IrpSp->Parameters.Power.State.SystemState = SystemState;
100 IrpSp->Parameters.Power.ShutdownType = PowerAction;
101
102 Status = PoCallDriver(DeviceObject, Irp);
103 if (Status == STATUS_PENDING)
104 {
105 KeWaitForSingleObject(&Event,
106 Executive,
107 KernelMode,
108 FALSE,
109 NULL);
110 Status = IoStatusBlock.Status;
111 }
112
113 return Status;
114 }
115
116 NTSTATUS
117 PopSendSetSystemPowerState(PDEVICE_OBJECT DeviceObject, SYSTEM_POWER_STATE SystemState, POWER_ACTION PowerAction)
118 {
119 KEVENT Event;
120 IO_STATUS_BLOCK IoStatusBlock;
121 PIO_STACK_LOCATION IrpSp;
122 PIRP Irp;
123 NTSTATUS Status;
124
125 KeInitializeEvent(&Event,
126 NotificationEvent,
127 FALSE);
128
129 Irp = IoBuildSynchronousFsdRequest(IRP_MJ_POWER,
130 DeviceObject,
131 NULL,
132 0,
133 NULL,
134 &Event,
135 &IoStatusBlock);
136 if (!Irp) return STATUS_INSUFFICIENT_RESOURCES;
137
138 IrpSp = IoGetNextIrpStackLocation(Irp);
139 IrpSp->MinorFunction = IRP_MN_SET_POWER;
140 IrpSp->Parameters.Power.Type = SystemPowerState;
141 IrpSp->Parameters.Power.State.SystemState = SystemState;
142 IrpSp->Parameters.Power.ShutdownType = PowerAction;
143
144 Status = PoCallDriver(DeviceObject, Irp);
145 if (Status == STATUS_PENDING)
146 {
147 KeWaitForSingleObject(&Event,
148 Executive,
149 KernelMode,
150 FALSE,
151 NULL);
152 Status = IoStatusBlock.Status;
153 }
154
155 return Status;
156 }
157
158 NTSTATUS
159 PopQuerySystemPowerStateTraverse(PDEVICE_NODE DeviceNode,
160 PVOID Context)
161 {
162 PPOWER_STATE_TRAVERSE_CONTEXT PowerStateContext = Context;
163 NTSTATUS Status;
164
165 DPRINT("PopQuerySystemPowerStateTraverse(%p, %p)\n", DeviceNode, Context);
166
167 if (DeviceNode == IopRootDeviceNode)
168 return STATUS_SUCCESS;
169
170 if (DeviceNode->Flags & DNF_LEGACY_DRIVER)
171 return STATUS_SUCCESS;
172
173 Status = PopSendQuerySystemPowerState(DeviceNode->PhysicalDeviceObject,
174 PowerStateContext->SystemPowerState,
175 PowerStateContext->PowerAction);
176 if (!NT_SUCCESS(Status))
177 {
178 DPRINT1("Device '%wZ' failed IRP_MN_QUERY_POWER\n", &DeviceNode->InstancePath);
179 }
180
181 #if 0
182 return Status;
183 #else
184 return STATUS_SUCCESS;
185 #endif
186 }
187
188 NTSTATUS
189 PopSetSystemPowerStateTraverse(PDEVICE_NODE DeviceNode,
190 PVOID Context)
191 {
192 PPOWER_STATE_TRAVERSE_CONTEXT PowerStateContext = Context;
193 NTSTATUS Status;
194
195 DPRINT("PopSetSystemPowerStateTraverse(%p, %p)\n", DeviceNode, Context);
196
197 if (DeviceNode == IopRootDeviceNode)
198 return STATUS_SUCCESS;
199
200 if (DeviceNode->PhysicalDeviceObject == PowerStateContext->PowerDevice)
201 return STATUS_SUCCESS;
202
203 if (DeviceNode->Flags & DNF_LEGACY_DRIVER)
204 return STATUS_SUCCESS;
205
206 Status = PopSendSetSystemPowerState(DeviceNode->PhysicalDeviceObject,
207 PowerStateContext->SystemPowerState,
208 PowerStateContext->PowerAction);
209 if (!NT_SUCCESS(Status))
210 {
211 DPRINT1("Device '%wZ' failed IRP_MN_SET_POWER\n", &DeviceNode->InstancePath);
212 }
213
214 #if 0
215 return Status;
216 #else
217 return STATUS_SUCCESS;
218 #endif
219 }
220
221 NTSTATUS
222 NTAPI
223 PopSetSystemPowerState(SYSTEM_POWER_STATE PowerState, POWER_ACTION PowerAction)
224 {
225 PDEVICE_OBJECT DeviceObject;
226 PDEVICE_OBJECT Fdo;
227 NTSTATUS Status;
228 DEVICETREE_TRAVERSE_CONTEXT Context;
229 POWER_STATE_TRAVERSE_CONTEXT PowerContext;
230
231 Status = IopGetSystemPowerDeviceObject(&DeviceObject);
232 if (!NT_SUCCESS(Status))
233 {
234 DPRINT1("No system power driver available\n");
235 Fdo = NULL;
236 }
237 else
238 {
239 Fdo = IoGetAttachedDeviceReference(DeviceObject);
240 if (Fdo == DeviceObject)
241 {
242 DPRINT("An FDO was not attached\n");
243 return STATUS_UNSUCCESSFUL;
244 }
245 }
246
247 /* Set up context */
248 PowerContext.PowerAction = PowerAction;
249 PowerContext.SystemPowerState = PowerState;
250 PowerContext.PowerDevice = Fdo;
251
252 /* Query for system power change */
253 IopInitDeviceTreeTraverseContext(&Context,
254 IopRootDeviceNode,
255 PopQuerySystemPowerStateTraverse,
256 &PowerContext);
257
258 Status = IopTraverseDeviceTree(&Context);
259 if (!NT_SUCCESS(Status))
260 {
261 DPRINT1("Query system power state failed; changing state anyway\n");
262 }
263
264 /* Set system power change */
265 IopInitDeviceTreeTraverseContext(&Context,
266 IopRootDeviceNode,
267 PopSetSystemPowerStateTraverse,
268 &PowerContext);
269
270 IopTraverseDeviceTree(&Context);
271
272 if (!PopAcpiPresent) return STATUS_NOT_IMPLEMENTED;
273
274 if (Fdo != NULL)
275 {
276 if (PowerAction != PowerActionShutdownReset)
277 PopSendSetSystemPowerState(Fdo, PowerState, PowerAction);
278
279 ObDereferenceObject(Fdo);
280 }
281
282 return Status;
283 }
284
285 BOOLEAN
286 NTAPI
287 INIT_FUNCTION
288 PoInitSystem(IN ULONG BootPhase)
289 {
290 PVOID NotificationEntry;
291 PCHAR CommandLine;
292 BOOLEAN ForceAcpiDisable = FALSE;
293
294 /* Check if this is phase 1 init */
295 if (BootPhase == 1)
296 {
297 /* Register power button notification */
298 IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange,
299 PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
300 (PVOID)&GUID_DEVICE_SYS_BUTTON,
301 IopRootDeviceNode->
302 PhysicalDeviceObject->DriverObject,
303 PopAddRemoveSysCapsCallback,
304 NULL,
305 &NotificationEntry);
306
307 /* Register lid notification */
308 IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange,
309 PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
310 (PVOID)&GUID_DEVICE_LID,
311 IopRootDeviceNode->
312 PhysicalDeviceObject->DriverObject,
313 PopAddRemoveSysCapsCallback,
314 NULL,
315 &NotificationEntry);
316 return TRUE;
317 }
318
319 /* Get the Command Line */
320 CommandLine = KeLoaderBlock->LoadOptions;
321
322 /* Upcase it */
323 _strupr(CommandLine);
324
325 /* Check for ACPI disable */
326 if (strstr(CommandLine, "NOACPI")) ForceAcpiDisable = TRUE;
327
328 if (ForceAcpiDisable)
329 {
330 /* Set the ACPI State to False if it's been forced that way */
331 PopAcpiPresent = FALSE;
332 }
333 else
334 {
335 /* Otherwise check if the LoaderBlock has a ACPI Table */
336 PopAcpiPresent = KeLoaderBlock->Extension->AcpiTable != NULL ? TRUE : FALSE;
337 }
338
339
340 /* Initialize volume support */
341 InitializeListHead(&PopVolumeDevices);
342 KeInitializeGuardedMutex(&PopVolumeLock);
343
344 /* Initialize support for dope */
345 KeInitializeSpinLock(&PopDopeGlobalLock);
346 return TRUE;
347 }
348
349 VOID
350 NTAPI
351 PopPerfIdle(PPROCESSOR_POWER_STATE PowerState)
352 {
353 DPRINT1("PerfIdle function: %p\n", PowerState);
354 }
355
356 VOID
357 NTAPI
358 PopPerfIdleDpc(IN PKDPC Dpc,
359 IN PVOID DeferredContext,
360 IN PVOID SystemArgument1,
361 IN PVOID SystemArgument2)
362 {
363 /* Call the Perf Idle function */
364 PopPerfIdle(&((PKPRCB)DeferredContext)->PowerState);
365 }
366
367 VOID
368 FASTCALL
369 PopIdle0(IN PPROCESSOR_POWER_STATE PowerState)
370 {
371 /* FIXME: Extremly naive implementation */
372 HalProcessorIdle();
373 }
374
375 VOID
376 NTAPI
377 INIT_FUNCTION
378 PoInitializePrcb(IN PKPRCB Prcb)
379 {
380 /* Initialize the Power State */
381 RtlZeroMemory(&Prcb->PowerState, sizeof(Prcb->PowerState));
382 Prcb->PowerState.Idle0KernelTimeLimit = 0xFFFFFFFF;
383 Prcb->PowerState.CurrentThrottle = 100;
384 Prcb->PowerState.CurrentThrottleIndex = 0;
385 Prcb->PowerState.IdleFunction = PopIdle0;
386
387 /* Initialize the Perf DPC and Timer */
388 KeInitializeDpc(&Prcb->PowerState.PerfDpc, PopPerfIdleDpc, Prcb);
389 KeSetTargetProcessorDpc(&Prcb->PowerState.PerfDpc, Prcb->Number);
390 KeInitializeTimerEx(&Prcb->PowerState.PerfTimer, SynchronizationTimer);
391 }
392
393 /* PUBLIC FUNCTIONS **********************************************************/
394
395 /*
396 * @unimplemented
397 */
398 NTSTATUS
399 NTAPI
400 PoCancelDeviceNotify(IN PVOID NotifyBlock)
401 {
402 UNIMPLEMENTED;
403 return STATUS_NOT_IMPLEMENTED;
404 }
405
406 /*
407 * @unimplemented
408 */
409 NTSTATUS
410 NTAPI
411 PoRegisterDeviceNotify(OUT PVOID Unknown0,
412 IN ULONG Unknown1,
413 IN ULONG Unknown2,
414 IN ULONG Unknown3,
415 IN PVOID Unknown4,
416 IN PVOID Unknown5)
417 {
418 UNIMPLEMENTED;
419 return STATUS_NOT_IMPLEMENTED;
420 }
421
422 /*
423 * @unimplemented
424 */
425 VOID
426 NTAPI
427 PoShutdownBugCheck(IN BOOLEAN LogError,
428 IN ULONG BugCheckCode,
429 IN ULONG_PTR BugCheckParameter1,
430 IN ULONG_PTR BugCheckParameter2,
431 IN ULONG_PTR BugCheckParameter3,
432 IN ULONG_PTR BugCheckParameter4)
433 {
434 DPRINT1("PoShutdownBugCheck called\n");
435
436 /* FIXME: Log error if requested */
437 /* FIXME: Initiate a shutdown */
438
439 /* Bugcheck the system */
440 KeBugCheckEx(BugCheckCode,
441 BugCheckParameter1,
442 BugCheckParameter2,
443 BugCheckParameter3,
444 BugCheckParameter4);
445 }
446
447 /*
448 * @unimplemented
449 */
450 NTSTATUS
451 NTAPI
452 PoRequestShutdownEvent(OUT PVOID *Event)
453 {
454 UNIMPLEMENTED;
455 return STATUS_NOT_IMPLEMENTED;
456 }
457
458 /*
459 * @unimplemented
460 */
461 VOID
462 NTAPI
463 PoSetHiberRange(IN PVOID HiberContext,
464 IN ULONG Flags,
465 IN OUT PVOID StartPage,
466 IN ULONG Length,
467 IN ULONG PageTag)
468 {
469 UNIMPLEMENTED;
470 return;
471 }
472
473 /*
474 * @implemented
475 */
476 NTSTATUS
477 NTAPI
478 PoCallDriver(IN PDEVICE_OBJECT DeviceObject,
479 IN OUT PIRP Irp)
480 {
481 NTSTATUS Status;
482
483 /* Forward to Io -- FIXME! */
484 Status = IoCallDriver(DeviceObject, Irp);
485
486 /* Return status */
487 return Status;
488 }
489
490 /*
491 * @unimplemented
492 */
493 PULONG
494 NTAPI
495 PoRegisterDeviceForIdleDetection(IN PDEVICE_OBJECT DeviceObject,
496 IN ULONG ConservationIdleTime,
497 IN ULONG PerformanceIdleTime,
498 IN DEVICE_POWER_STATE State)
499 {
500 UNIMPLEMENTED;
501 return NULL;
502 }
503
504 /*
505 * @unimplemented
506 */
507 PVOID
508 NTAPI
509 PoRegisterSystemState(IN PVOID StateHandle,
510 IN EXECUTION_STATE Flags)
511 {
512 UNIMPLEMENTED;
513 return NULL;
514 }
515
516 /*
517 * @implemented
518 */
519 NTSTATUS
520 NTAPI
521 PoRequestPowerIrp(IN PDEVICE_OBJECT DeviceObject,
522 IN UCHAR MinorFunction,
523 IN POWER_STATE PowerState,
524 IN PREQUEST_POWER_COMPLETE CompletionFunction,
525 IN PVOID Context,
526 OUT PIRP *pIrp OPTIONAL)
527 {
528 PDEVICE_OBJECT TopDeviceObject;
529 PIO_STACK_LOCATION Stack;
530 PIRP Irp;
531 PREQUEST_POWER_ITEM RequestPowerItem;
532
533 if (MinorFunction != IRP_MN_QUERY_POWER
534 && MinorFunction != IRP_MN_SET_POWER
535 && MinorFunction != IRP_MN_WAIT_WAKE)
536 return STATUS_INVALID_PARAMETER_2;
537
538 RequestPowerItem = ExAllocatePool(NonPagedPool, sizeof(REQUEST_POWER_ITEM));
539 if (!RequestPowerItem)
540 return STATUS_INSUFFICIENT_RESOURCES;
541
542 /* Always call the top of the device stack */
543 TopDeviceObject = IoGetAttachedDeviceReference(DeviceObject);
544
545 Irp = IoBuildAsynchronousFsdRequest(IRP_MJ_POWER,
546 TopDeviceObject,
547 NULL,
548 0,
549 NULL,
550 NULL);
551 if (!Irp)
552 {
553 ObDereferenceObject(TopDeviceObject);
554 ExFreePool(RequestPowerItem);
555 return STATUS_INSUFFICIENT_RESOURCES;
556 }
557
558 /* POWER IRPs are always initialized with a status code of
559 STATUS_NOT_IMPLEMENTED */
560 Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
561 Irp->IoStatus.Information = 0;
562
563 Stack = IoGetNextIrpStackLocation(Irp);
564 Stack->MinorFunction = MinorFunction;
565 if (MinorFunction == IRP_MN_WAIT_WAKE)
566 Stack->Parameters.WaitWake.PowerState = PowerState.SystemState;
567 else
568 {
569 Stack->Parameters.Power.Type = DevicePowerState;
570 Stack->Parameters.Power.State = PowerState;
571 }
572
573 RequestPowerItem->CompletionRoutine = CompletionFunction;
574 RequestPowerItem->PowerState = PowerState;
575 RequestPowerItem->Context = Context;
576
577 if (pIrp != NULL)
578 *pIrp = Irp;
579
580 IoSetCompletionRoutine(Irp, PopRequestPowerIrpCompletion, RequestPowerItem, TRUE, TRUE, TRUE);
581 PoCallDriver(TopDeviceObject, Irp);
582
583 /* Always return STATUS_PENDING. The completion routine
584 * will call CompletionFunction and complete the Irp.
585 */
586 return STATUS_PENDING;
587 }
588
589 /*
590 * @unimplemented
591 */
592 POWER_STATE
593 NTAPI
594 PoSetPowerState(IN PDEVICE_OBJECT DeviceObject,
595 IN POWER_STATE_TYPE Type,
596 IN POWER_STATE State)
597 {
598 POWER_STATE ps;
599
600 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
601
602 ps.SystemState = PowerSystemWorking; // Fully on
603 ps.DeviceState = PowerDeviceD0; // Fully on
604
605 return ps;
606 }
607
608 /*
609 * @unimplemented
610 */
611 VOID
612 NTAPI
613 PoSetSystemState(IN EXECUTION_STATE Flags)
614 {
615 UNIMPLEMENTED;
616 }
617
618 /*
619 * @unimplemented
620 */
621 VOID
622 NTAPI
623 PoStartNextPowerIrp(IN PIRP Irp)
624 {
625 UNIMPLEMENTED;
626 }
627
628 /*
629 * @unimplemented
630 */
631 VOID
632 NTAPI
633 PoUnregisterSystemState(IN PVOID StateHandle)
634 {
635 UNIMPLEMENTED;
636 }
637
638 /*
639 * @unimplemented
640 */
641 NTSTATUS
642 NTAPI
643 PoQueueShutdownWorkItem(IN PWORK_QUEUE_ITEM WorkItem)
644 {
645 PAGED_CODE();
646
647 UNIMPLEMENTED;
648 return STATUS_NOT_IMPLEMENTED;
649 }
650
651 /*
652 * @unimplemented
653 */
654 NTSTATUS
655 NTAPI
656 NtInitiatePowerAction (IN POWER_ACTION SystemAction,
657 IN SYSTEM_POWER_STATE MinSystemState,
658 IN ULONG Flags,
659 IN BOOLEAN Asynchronous)
660 {
661 UNIMPLEMENTED;
662 return STATUS_NOT_IMPLEMENTED;
663 }
664
665 /*
666 * @unimplemented
667 */
668 NTSTATUS
669 NTAPI
670 NtPowerInformation(IN POWER_INFORMATION_LEVEL PowerInformationLevel,
671 IN PVOID InputBuffer OPTIONAL,
672 IN ULONG InputBufferLength,
673 OUT PVOID OutputBuffer OPTIONAL,
674 IN ULONG OutputBufferLength)
675 {
676 NTSTATUS Status;
677
678 PAGED_CODE();
679
680 DPRINT("NtPowerInformation(PowerInformationLevel 0x%x, InputBuffer 0x%x, "
681 "InputBufferLength 0x%x, OutputBuffer 0x%x, OutputBufferLength 0x%x)\n",
682 PowerInformationLevel,
683 InputBuffer, InputBufferLength,
684 OutputBuffer, OutputBufferLength);
685
686 switch (PowerInformationLevel)
687 {
688 case SystemBatteryState:
689 {
690 PSYSTEM_BATTERY_STATE BatteryState = (PSYSTEM_BATTERY_STATE)OutputBuffer;
691
692 if (InputBuffer != NULL)
693 return STATUS_INVALID_PARAMETER;
694 if (OutputBufferLength < sizeof(SYSTEM_BATTERY_STATE))
695 return STATUS_BUFFER_TOO_SMALL;
696
697 /* Just zero the struct (and thus set BatteryState->BatteryPresent = FALSE) */
698 RtlZeroMemory(BatteryState, sizeof(SYSTEM_BATTERY_STATE));
699 BatteryState->EstimatedTime = MAXULONG;
700
701 Status = STATUS_SUCCESS;
702 break;
703 }
704 case SystemPowerCapabilities:
705 {
706 PSYSTEM_POWER_CAPABILITIES PowerCapabilities = (PSYSTEM_POWER_CAPABILITIES)OutputBuffer;
707
708 if (InputBuffer != NULL)
709 return STATUS_INVALID_PARAMETER;
710 if (OutputBufferLength < sizeof(SYSTEM_POWER_CAPABILITIES))
711 return STATUS_BUFFER_TOO_SMALL;
712
713 /* Just zero the struct (and thus set BatteryState->BatteryPresent = FALSE) */
714 RtlZeroMemory(PowerCapabilities, sizeof(SYSTEM_POWER_CAPABILITIES));
715 //PowerCapabilities->SystemBatteriesPresent = 0;
716
717 Status = STATUS_SUCCESS;
718 break;
719 }
720
721 default:
722 Status = STATUS_NOT_IMPLEMENTED;
723 DPRINT1("PowerInformationLevel 0x%x is UNIMPLEMENTED! Have a nice day.\n",
724 PowerInformationLevel);
725 break;
726 }
727
728 return Status;
729 }
730
731 NTSTATUS
732 NTAPI
733 NtGetDevicePowerState(IN HANDLE Device,
734 IN PDEVICE_POWER_STATE PowerState)
735 {
736 UNIMPLEMENTED;
737 return STATUS_NOT_IMPLEMENTED;
738 }
739
740 BOOLEAN
741 NTAPI
742 NtIsSystemResumeAutomatic(VOID)
743 {
744 UNIMPLEMENTED;
745 return FALSE;
746 }
747
748 NTSTATUS
749 NTAPI
750 NtRequestWakeupLatency(IN LATENCY_TIME Latency)
751 {
752 UNIMPLEMENTED;
753 return STATUS_NOT_IMPLEMENTED;
754 }
755
756 NTSTATUS
757 NTAPI
758 NtSetThreadExecutionState(IN EXECUTION_STATE esFlags,
759 OUT EXECUTION_STATE *PreviousFlags)
760 {
761 PKTHREAD Thread = KeGetCurrentThread();
762 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
763 EXECUTION_STATE PreviousState;
764 PAGED_CODE();
765
766 /* Validate flags */
767 if (esFlags & ~(ES_CONTINUOUS | ES_USER_PRESENT))
768 {
769 /* Fail the request */
770 return STATUS_INVALID_PARAMETER;
771 }
772
773 /* Check for user parameters */
774 if (PreviousMode != KernelMode)
775 {
776 /* Protect the probes */
777 _SEH2_TRY
778 {
779 /* Check if the pointer is valid */
780 ProbeForWriteUlong(PreviousFlags);
781 }
782 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
783 {
784 /* It isn't -- fail */
785 _SEH2_YIELD(return _SEH2_GetExceptionCode());
786 }
787 _SEH2_END;
788 }
789
790 /* Save the previous state, always masking in the continous flag */
791 PreviousState = Thread->PowerState | ES_CONTINUOUS;
792
793 /* Check if we need to update the power state */
794 if (esFlags & ES_CONTINUOUS) Thread->PowerState = (UCHAR)esFlags;
795
796 /* Protect the write back to user mode */
797 _SEH2_TRY
798 {
799 /* Return the previous flags */
800 *PreviousFlags = PreviousState;
801 }
802 _SEH2_EXCEPT(ExSystemExceptionFilter())
803 {
804 /* Something's wrong, fail */
805 _SEH2_YIELD(return _SEH2_GetExceptionCode());
806 }
807 _SEH2_END;
808
809 /* All is good */
810 return STATUS_SUCCESS;
811 }
812
813 NTSTATUS
814 NTAPI
815 NtSetSystemPowerState(IN POWER_ACTION SystemAction,
816 IN SYSTEM_POWER_STATE MinSystemState,
817 IN ULONG Flags)
818 {
819 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
820 POP_POWER_ACTION Action = {0};
821 NTSTATUS Status;
822 ULONG Dummy;
823
824 /* Check for invalid parameter combinations */
825 if ((MinSystemState >= PowerSystemMaximum) ||
826 (MinSystemState <= PowerSystemUnspecified) ||
827 (SystemAction > PowerActionWarmEject) ||
828 (SystemAction < PowerActionReserved) ||
829 (Flags & ~(POWER_ACTION_QUERY_ALLOWED |
830 POWER_ACTION_UI_ALLOWED |
831 POWER_ACTION_OVERRIDE_APPS |
832 POWER_ACTION_LIGHTEST_FIRST |
833 POWER_ACTION_LOCK_CONSOLE |
834 POWER_ACTION_DISABLE_WAKES |
835 POWER_ACTION_CRITICAL)))
836 {
837 DPRINT1("NtSetSystemPowerState: Bad parameters!\n");
838 DPRINT1(" SystemAction: 0x%x\n", SystemAction);
839 DPRINT1(" MinSystemState: 0x%x\n", MinSystemState);
840 DPRINT1(" Flags: 0x%x\n", Flags);
841 return STATUS_INVALID_PARAMETER;
842 }
843
844 /* Check for user caller */
845 if (PreviousMode != KernelMode)
846 {
847 /* Check for shutdown permission */
848 if (!SeSinglePrivilegeCheck(SeShutdownPrivilege, PreviousMode))
849 {
850 /* Not granted */
851 DPRINT1("ERROR: Privilege not held for shutdown\n");
852 //return STATUS_PRIVILEGE_NOT_HELD; HACK!
853 }
854
855 /* Do it as a kernel-mode caller for consistency with system state */
856 return ZwSetSystemPowerState (SystemAction, MinSystemState, Flags);
857 }
858
859 /* Read policy settings (partial shutdown vs. full shutdown) */
860 if (SystemAction == PowerActionShutdown) PopReadShutdownPolicy();
861
862 /* Disable lazy flushing of registry */
863 DPRINT1("Stopping lazy flush\n");
864 CmSetLazyFlushState(FALSE);
865
866 /* Setup the power action */
867 Action.Action = SystemAction;
868 Action.Flags = Flags;
869
870 /* Notify callbacks */
871 DPRINT1("Notifying callbacks\n");
872 ExNotifyCallback(PowerStateCallback, (PVOID)3, NULL);
873
874 /* Swap in any worker thread stacks */
875 DPRINT1("Swapping worker threads\n");
876 ExSwapinWorkerThreads(FALSE);
877
878 /* Make our action global */
879 PopAction = Action;
880
881 /* Start power loop */
882 Status = STATUS_CANCELLED;
883 while (TRUE)
884 {
885 /* Break out if there's nothing to do */
886 if (Action.Action == PowerActionNone) break;
887
888 /* Check for first-pass or restart */
889 if (Status == STATUS_CANCELLED)
890 {
891 /* Check for shutdown action */
892 if ((PopAction.Action == PowerActionShutdown) ||
893 (PopAction.Action == PowerActionShutdownReset) ||
894 (PopAction.Action == PowerActionShutdownOff))
895 {
896 /* Set the action */
897 PopAction.Shutdown = TRUE;
898 }
899
900 /* Now we are good to go */
901 Status = STATUS_SUCCESS;
902 }
903
904 /* Check if we're still in an invalid status */
905 if (!NT_SUCCESS(Status)) break;
906
907 #ifndef NEWCC
908 /* Flush dirty cache pages */
909 CcRosFlushDirtyPages(-1, &Dummy, FALSE); //HACK: We really should wait here!
910 #else
911 Dummy = 0;
912 #endif
913
914 /* Flush all volumes and the registry */
915 DPRINT1("Flushing volumes, cache flushed %d pages\n", Dummy);
916 PopFlushVolumes(PopAction.Shutdown);
917
918 /* Set IRP for drivers */
919 PopAction.IrpMinor = IRP_MN_SET_POWER;
920 if (PopAction.Shutdown)
921 {
922 DPRINT1("Queueing shutdown thread\n");
923 /* Check if we are running in the system context */
924 if (PsGetCurrentProcess() != PsInitialSystemProcess)
925 {
926 /* We're not, so use a worker thread for shutdown */
927 ExInitializeWorkItem(&PopShutdownWorkItem,
928 &PopGracefulShutdown,
929 NULL);
930
931 ExQueueWorkItem(&PopShutdownWorkItem, CriticalWorkQueue);
932
933 /* Spend us -- when we wake up, the system is good to go down */
934 KeSuspendThread(KeGetCurrentThread());
935 Status = STATUS_SYSTEM_SHUTDOWN;
936 goto Exit;
937
938 }
939 else
940 {
941 /* Do the shutdown inline */
942 PopGracefulShutdown(NULL);
943 }
944 }
945
946 /* You should not have made it this far */
947 ASSERT(FALSE && "System is still up and running?!");
948 break;
949 }
950
951 Exit:
952 /* We're done, return */
953 return Status;
954 }