xref: /reactos/ntoskrnl/po/events.c (revision 9e43518d)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/po/events.c
5  * PURPOSE:         Power Manager
6  * PROGRAMMERS:     Herv� Poussineau (hpoussin@reactos.org)
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14 
15 /* GLOBALS *******************************************************************/
16 
17 typedef struct _SYS_BUTTON_CONTEXT
18 {
19     PDEVICE_OBJECT DeviceObject;
20     PIO_WORKITEM WorkItem;
21     KEVENT Event;
22     IO_STATUS_BLOCK IoStatusBlock;
23     ULONG SysButton;
24 } SYS_BUTTON_CONTEXT, *PSYS_BUTTON_CONTEXT;
25 
26 static VOID
27 NTAPI
28 PopGetSysButton(
29     IN PDEVICE_OBJECT DeviceObject,
30     IN PVOID Context);
31 
32 PKWIN32_POWEREVENT_CALLOUT PopEventCallout;
33 extern PCALLBACK_OBJECT SetSystemTimeCallback;
34 
35 /* FUNCTIONS *****************************************************************/
36 
37 VOID
38 NTAPI
PoNotifySystemTimeSet(VOID)39 PoNotifySystemTimeSet(VOID)
40 {
41     KIRQL OldIrql;
42 
43     /* Check if Win32k registered a notification callback */
44     if (PopEventCallout)
45     {
46         /* Raise to dispatch */
47         KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
48 
49         /* Notify the callback */
50         ExNotifyCallback(SetSystemTimeCallback, NULL, NULL);
51 
52         /* Lower IRQL back */
53         KeLowerIrql(OldIrql);
54     }
55 }
56 
57 static NTSTATUS
58 NTAPI
PopGetSysButtonCompletion(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp,IN PVOID Context)59 PopGetSysButtonCompletion(
60     IN PDEVICE_OBJECT DeviceObject,
61     IN PIRP Irp,
62     IN PVOID Context)
63 {
64     PSYS_BUTTON_CONTEXT SysButtonContext = Context;
65     ULONG SysButton;
66 
67     /* The DeviceObject can be NULL, so use the one we stored */
68     DeviceObject = SysButtonContext->DeviceObject;
69 
70     /* FIXME: What do do with the sys button event? */
71     SysButton = *(PULONG)Irp->AssociatedIrp.SystemBuffer;
72     {
73         DPRINT1("A device reported the event 0x%x (", SysButton);
74         if (SysButton & SYS_BUTTON_POWER) DbgPrint(" POWER");
75         if (SysButton & SYS_BUTTON_SLEEP) DbgPrint(" SLEEP");
76         if (SysButton & SYS_BUTTON_LID) DbgPrint(" LID");
77         if (SysButton == 0) DbgPrint(" WAKE");
78         DbgPrint(" )\n");
79 
80         if (SysButton & SYS_BUTTON_POWER)
81         {
82             /* FIXME: Read registry for the action we should perform here */
83             DPRINT1("Initiating shutdown after power button event\n");
84 
85             ZwShutdownSystem(ShutdownNoReboot);
86         }
87     }
88 
89     /* Allocate a new workitem to send the next IOCTL_GET_SYS_BUTTON_EVENT */
90     SysButtonContext->WorkItem = IoAllocateWorkItem(DeviceObject);
91     if (!SysButtonContext->WorkItem)
92     {
93         DPRINT("IoAllocateWorkItem() failed\n");
94         ExFreePoolWithTag(SysButtonContext, 'IWOP');
95         return STATUS_SUCCESS;
96     }
97     IoQueueWorkItem(SysButtonContext->WorkItem,
98                     PopGetSysButton,
99                     DelayedWorkQueue,
100                     SysButtonContext);
101 
102     return STATUS_SUCCESS /* STATUS_CONTINUE_COMPLETION */;
103 }
104 
105 static VOID
106 NTAPI
PopGetSysButton(IN PDEVICE_OBJECT DeviceObject,IN PVOID Context)107 PopGetSysButton(
108     IN PDEVICE_OBJECT DeviceObject,
109     IN PVOID Context)
110 {
111     PSYS_BUTTON_CONTEXT SysButtonContext = Context;
112     PIO_WORKITEM CurrentWorkItem = SysButtonContext->WorkItem;
113     PIRP Irp;
114 
115     /* Get button pressed (IOCTL_GET_SYS_BUTTON_EVENT) */
116     KeInitializeEvent(&SysButtonContext->Event, NotificationEvent, FALSE);
117     Irp = IoBuildDeviceIoControlRequest(IOCTL_GET_SYS_BUTTON_EVENT,
118                                         DeviceObject,
119                                         NULL,
120                                         0,
121                                         &SysButtonContext->SysButton,
122                                         sizeof(SysButtonContext->SysButton),
123                                         FALSE,
124                                         &SysButtonContext->Event,
125                                         &SysButtonContext->IoStatusBlock);
126     if (Irp)
127     {
128         IoSetCompletionRoutine(Irp,
129                                PopGetSysButtonCompletion,
130                                SysButtonContext,
131                                TRUE,
132                                FALSE,
133                                FALSE);
134         IoCallDriver(DeviceObject, Irp);
135     }
136     else
137     {
138         DPRINT1("IoBuildDeviceIoControlRequest() failed\n");
139         ExFreePoolWithTag(SysButtonContext, 'IWOP');
140     }
141 
142     IoFreeWorkItem(CurrentWorkItem);
143 }
144 
145 NTSTATUS
146 NTAPI
PopAddRemoveSysCapsCallback(IN PVOID NotificationStructure,IN PVOID Context)147 PopAddRemoveSysCapsCallback(IN PVOID NotificationStructure,
148                             IN PVOID Context)
149 {
150     PDEVICE_INTERFACE_CHANGE_NOTIFICATION Notification;
151     PSYS_BUTTON_CONTEXT SysButtonContext;
152     OBJECT_ATTRIBUTES ObjectAttributes;
153     HANDLE FileHandle;
154     PDEVICE_OBJECT DeviceObject;
155     PFILE_OBJECT FileObject;
156     PIRP Irp;
157     IO_STATUS_BLOCK IoStatusBlock;
158     KEVENT Event;
159     BOOLEAN Arrival;
160     ULONG Caps;
161     NTSTATUS Status;
162     POP_POLICY_DEVICE_TYPE DeviceType = (POP_POLICY_DEVICE_TYPE)(ULONG_PTR)Context;
163 
164     DPRINT("PopAddRemoveSysCapsCallback(%p %p)\n",
165         NotificationStructure, Context);
166 
167     Notification = (PDEVICE_INTERFACE_CHANGE_NOTIFICATION)NotificationStructure;
168     if (Notification->Version != 1)
169         return STATUS_REVISION_MISMATCH;
170     if (Notification->Size != sizeof(DEVICE_INTERFACE_CHANGE_NOTIFICATION))
171         return STATUS_INVALID_PARAMETER;
172     if (RtlCompareMemory(&Notification->Event, &GUID_DEVICE_INTERFACE_ARRIVAL, sizeof(GUID)) == sizeof(GUID))
173         Arrival = TRUE;
174     else if (RtlCompareMemory(&Notification->Event, &GUID_DEVICE_INTERFACE_REMOVAL, sizeof(GUID)) == sizeof(GUID))
175         Arrival = FALSE;
176     else
177         return STATUS_INVALID_PARAMETER;
178 
179     if (Arrival && DeviceType == PolicyDeviceBattery)
180     {
181         PopCapabilities.SystemBatteriesPresent = TRUE;
182         return STATUS_SUCCESS;
183     }
184 
185     if (Arrival)
186     {
187         DPRINT("Arrival of %wZ\n", Notification->SymbolicLinkName);
188 
189         /* Open the device */
190         InitializeObjectAttributes(&ObjectAttributes,
191                                    Notification->SymbolicLinkName,
192                                    OBJ_KERNEL_HANDLE,
193                                    NULL,
194                                    NULL);
195         Status = ZwOpenFile(&FileHandle,
196                             FILE_READ_DATA,
197                             &ObjectAttributes,
198                             &IoStatusBlock,
199                             FILE_SHARE_READ | FILE_SHARE_WRITE,
200                             0);
201         if (!NT_SUCCESS(Status))
202         {
203             DPRINT1("ZwOpenFile() failed with status 0x%08lx\n", Status);
204             return Status;
205         }
206         Status = ObReferenceObjectByHandle(FileHandle,
207                                            FILE_READ_DATA,
208                                            IoFileObjectType,
209                                            KernelMode,
210                                            (PVOID*)&FileObject,
211                                            NULL);
212         if (!NT_SUCCESS(Status))
213         {
214             DPRINT1("ObReferenceObjectByHandle() failed with status 0x%08lx\n", Status);
215             ZwClose(FileHandle);
216             return Status;
217         }
218         DeviceObject = IoGetRelatedDeviceObject(FileObject);
219         ObDereferenceObject(FileObject);
220 
221         /* Get capabilities (IOCTL_GET_SYS_BUTTON_CAPS) */
222         KeInitializeEvent(&Event, NotificationEvent, FALSE);
223         Irp = IoBuildDeviceIoControlRequest(IOCTL_GET_SYS_BUTTON_CAPS,
224                                             DeviceObject,
225                                             NULL,
226                                             0,
227                                             &Caps,
228                                             sizeof(Caps),
229                                             FALSE,
230                                             &Event,
231                                             &IoStatusBlock);
232         if (!Irp)
233         {
234             DPRINT1("IoBuildDeviceIoControlRequest() failed\n");
235             ZwClose(FileHandle);
236             return STATUS_INSUFFICIENT_RESOURCES;
237         }
238         Status = IoCallDriver(DeviceObject, Irp);
239         if (Status == STATUS_PENDING)
240         {
241             DPRINT("IOCTL_GET_SYS_BUTTON_CAPS pending\n");
242             KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
243             Status = IoStatusBlock.Status;
244         }
245         if (!NT_SUCCESS(Status))
246         {
247             DPRINT1("Sending IOCTL_GET_SYS_BUTTON_CAPS failed with status 0x%08x\n", Status);
248             ZwClose(FileHandle);
249             return STATUS_INSUFFICIENT_RESOURCES;
250         }
251 
252         DPRINT("Device capabilities: 0x%x\n", Caps);
253         if (Caps & SYS_BUTTON_POWER)
254         {
255             DPRINT("POWER button present\n");
256             PopCapabilities.PowerButtonPresent = TRUE;
257         }
258 
259         if (Caps & SYS_BUTTON_SLEEP)
260         {
261             DPRINT("SLEEP button present\n");
262             PopCapabilities.SleepButtonPresent = TRUE;
263         }
264 
265         if (Caps & SYS_BUTTON_LID)
266         {
267             DPRINT("LID present\n");
268             PopCapabilities.LidPresent = TRUE;
269         }
270 
271         SysButtonContext = ExAllocatePoolWithTag(NonPagedPool,
272                                                  sizeof(SYS_BUTTON_CONTEXT),
273                                                  'IWOP');
274         if (!SysButtonContext)
275         {
276             DPRINT1("ExAllocatePoolWithTag() failed\n");
277             ZwClose(FileHandle);
278             return STATUS_INSUFFICIENT_RESOURCES;
279         }
280 
281         /* Queue a work item to get sys button event */
282         SysButtonContext->WorkItem = IoAllocateWorkItem(DeviceObject);
283         SysButtonContext->DeviceObject = DeviceObject;
284         if (!SysButtonContext->WorkItem)
285         {
286             DPRINT1("IoAllocateWorkItem() failed\n");
287             ZwClose(FileHandle);
288             ExFreePoolWithTag(SysButtonContext, 'IWOP');
289             return STATUS_INSUFFICIENT_RESOURCES;
290         }
291         IoQueueWorkItem(SysButtonContext->WorkItem,
292                         PopGetSysButton,
293                         DelayedWorkQueue,
294                         SysButtonContext);
295 
296         ZwClose(FileHandle);
297         return STATUS_SUCCESS;
298     }
299     else
300     {
301         DPRINT1("Removal of a power capable device not implemented\n");
302         return STATUS_NOT_IMPLEMENTED;
303     }
304 }
305