xref: /reactos/ntoskrnl/po/events.c (revision 34593d93)
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
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
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
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
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 
163     DPRINT("PopAddRemoveSysCapsCallback(%p %p)\n",
164         NotificationStructure, Context);
165 
166     Notification = (PDEVICE_INTERFACE_CHANGE_NOTIFICATION)NotificationStructure;
167     if (Notification->Version != 1)
168         return STATUS_REVISION_MISMATCH;
169     if (Notification->Size != sizeof(DEVICE_INTERFACE_CHANGE_NOTIFICATION))
170         return STATUS_INVALID_PARAMETER;
171     if (RtlCompareMemory(&Notification->Event, &GUID_DEVICE_INTERFACE_ARRIVAL, sizeof(GUID)) == sizeof(GUID))
172         Arrival = TRUE;
173     else if (RtlCompareMemory(&Notification->Event, &GUID_DEVICE_INTERFACE_REMOVAL, sizeof(GUID)) == sizeof(GUID))
174         Arrival = FALSE;
175     else
176         return STATUS_INVALID_PARAMETER;
177 
178     if (Arrival)
179     {
180         DPRINT("Arrival of %wZ\n", Notification->SymbolicLinkName);
181 
182         /* Open the device */
183         InitializeObjectAttributes(&ObjectAttributes,
184                                    Notification->SymbolicLinkName,
185                                    OBJ_KERNEL_HANDLE,
186                                    NULL,
187                                    NULL);
188         Status = ZwOpenFile(&FileHandle,
189                             FILE_READ_DATA,
190                             &ObjectAttributes,
191                             &IoStatusBlock,
192                             FILE_SHARE_READ | FILE_SHARE_WRITE,
193                             0);
194         if (!NT_SUCCESS(Status))
195         {
196             DPRINT1("ZwOpenFile() failed with status 0x%08lx\n", Status);
197             return Status;
198         }
199         Status = ObReferenceObjectByHandle(FileHandle,
200                                            FILE_READ_DATA,
201                                            IoFileObjectType,
202                                            KernelMode,
203                                            (PVOID*)&FileObject,
204                                            NULL);
205         if (!NT_SUCCESS(Status))
206         {
207             DPRINT1("ObReferenceObjectByHandle() failed with status 0x%08lx\n", Status);
208             ZwClose(FileHandle);
209             return Status;
210         }
211         DeviceObject = IoGetRelatedDeviceObject(FileObject);
212         ObDereferenceObject(FileObject);
213 
214         /* Get capabilities (IOCTL_GET_SYS_BUTTON_CAPS) */
215         KeInitializeEvent(&Event, NotificationEvent, FALSE);
216         Irp = IoBuildDeviceIoControlRequest(IOCTL_GET_SYS_BUTTON_CAPS,
217                                             DeviceObject,
218                                             NULL,
219                                             0,
220                                             &Caps,
221                                             sizeof(Caps),
222                                             FALSE,
223                                             &Event,
224                                             &IoStatusBlock);
225         if (!Irp)
226         {
227             DPRINT1("IoBuildDeviceIoControlRequest() failed\n");
228             ZwClose(FileHandle);
229             return STATUS_INSUFFICIENT_RESOURCES;
230         }
231         Status = IoCallDriver(DeviceObject, Irp);
232         if (Status == STATUS_PENDING)
233         {
234             DPRINT("IOCTL_GET_SYS_BUTTON_CAPS pending\n");
235             KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
236             Status = IoStatusBlock.Status;
237         }
238         if (!NT_SUCCESS(Status))
239         {
240             DPRINT1("Sending IOCTL_GET_SYS_BUTTON_CAPS failed with status 0x%08x\n", Status);
241             ZwClose(FileHandle);
242             return STATUS_INSUFFICIENT_RESOURCES;
243         }
244 
245         DPRINT("Device capabilities: 0x%x\n", Caps);
246         if (Caps & SYS_BUTTON_POWER)
247         {
248             DPRINT("POWER button present\n");
249             PopCapabilities.PowerButtonPresent = TRUE;
250         }
251 
252         if (Caps & SYS_BUTTON_SLEEP)
253         {
254             DPRINT("SLEEP button present\n");
255             PopCapabilities.SleepButtonPresent = TRUE;
256         }
257 
258         if (Caps & SYS_BUTTON_LID)
259         {
260             DPRINT("LID present\n");
261             PopCapabilities.LidPresent = TRUE;
262         }
263 
264         SysButtonContext = ExAllocatePoolWithTag(NonPagedPool,
265                                                  sizeof(SYS_BUTTON_CONTEXT),
266                                                  'IWOP');
267         if (!SysButtonContext)
268         {
269             DPRINT1("ExAllocatePoolWithTag() failed\n");
270             ZwClose(FileHandle);
271             return STATUS_INSUFFICIENT_RESOURCES;
272         }
273 
274         /* Queue a work item to get sys button event */
275         SysButtonContext->WorkItem = IoAllocateWorkItem(DeviceObject);
276         SysButtonContext->DeviceObject = DeviceObject;
277         if (!SysButtonContext->WorkItem)
278         {
279             DPRINT1("IoAllocateWorkItem() failed\n");
280             ZwClose(FileHandle);
281             ExFreePoolWithTag(SysButtonContext, 'IWOP');
282             return STATUS_INSUFFICIENT_RESOURCES;
283         }
284         IoQueueWorkItem(SysButtonContext->WorkItem,
285                         PopGetSysButton,
286                         DelayedWorkQueue,
287                         SysButtonContext);
288 
289         ZwClose(FileHandle);
290         return STATUS_SUCCESS;
291     }
292     else
293     {
294         DPRINT1("Removal of a power capable device not implemented\n");
295         return STATUS_NOT_IMPLEMENTED;
296     }
297 }
298