Sync with trunk r58033.
[reactos.git] / ntoskrnl / po / events.c
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 ExFreePool(SysButtonContext);
95 return STATUS_SUCCESS;
96 }
97 IoQueueWorkItem(
98 SysButtonContext->WorkItem,
99 PopGetSysButton,
100 DelayedWorkQueue,
101 SysButtonContext);
102
103 return STATUS_SUCCESS /* STATUS_CONTINUE_COMPLETION */;
104 }
105
106 static VOID
107 NTAPI
108 PopGetSysButton(
109 IN PDEVICE_OBJECT DeviceObject,
110 IN PVOID Context)
111 {
112 PSYS_BUTTON_CONTEXT SysButtonContext = Context;
113 PIO_WORKITEM CurrentWorkItem = SysButtonContext->WorkItem;
114 PIRP Irp;
115
116 /* Get button pressed (IOCTL_GET_SYS_BUTTON_EVENT) */
117 KeInitializeEvent(&SysButtonContext->Event, NotificationEvent, FALSE);
118 Irp = IoBuildDeviceIoControlRequest(
119 IOCTL_GET_SYS_BUTTON_EVENT,
120 DeviceObject,
121 NULL,
122 0,
123 &SysButtonContext->SysButton,
124 sizeof(SysButtonContext->SysButton),
125 FALSE,
126 &SysButtonContext->Event,
127 &SysButtonContext->IoStatusBlock);
128 if (Irp)
129 {
130 IoSetCompletionRoutine(
131 Irp,
132 PopGetSysButtonCompletion,
133 SysButtonContext,
134 TRUE,
135 FALSE,
136 FALSE);
137 IoCallDriver(DeviceObject, Irp);
138 }
139 else
140 {
141 DPRINT1("IoBuildDeviceIoControlRequest() failed\n");
142 ExFreePool(SysButtonContext);
143 }
144
145 IoFreeWorkItem(CurrentWorkItem);
146 }
147
148 NTSTATUS
149 NTAPI
150 PopAddRemoveSysCapsCallback(IN PVOID NotificationStructure,
151 IN PVOID Context)
152 {
153 PDEVICE_INTERFACE_CHANGE_NOTIFICATION Notification;
154 PSYS_BUTTON_CONTEXT SysButtonContext;
155 OBJECT_ATTRIBUTES ObjectAttributes;
156 HANDLE FileHandle;
157 PDEVICE_OBJECT DeviceObject;
158 PFILE_OBJECT FileObject;
159 PIRP Irp;
160 IO_STATUS_BLOCK IoStatusBlock;
161 KEVENT Event;
162 BOOLEAN Arrival;
163 ULONG Caps;
164 NTSTATUS Status;
165 UNICODE_STRING DeviceName;
166 UNICODE_STRING DeviceNamePrefix = RTL_CONSTANT_STRING(L"\\??\\");
167
168 DPRINT("PopAddRemoveSysCapsCallback(%p %p)\n",
169 NotificationStructure, Context);
170
171 Notification = (PDEVICE_INTERFACE_CHANGE_NOTIFICATION)NotificationStructure;
172 if (Notification->Version != 1)
173 return STATUS_REVISION_MISMATCH;
174 if (Notification->Size != sizeof(DEVICE_INTERFACE_CHANGE_NOTIFICATION))
175 return STATUS_INVALID_PARAMETER;
176 if (RtlCompareMemory(&Notification->Event, &GUID_DEVICE_INTERFACE_ARRIVAL, sizeof(GUID)) == sizeof(GUID))
177 Arrival = TRUE;
178 else if (RtlCompareMemory(&Notification->Event, &GUID_DEVICE_INTERFACE_REMOVAL, sizeof(GUID)) == sizeof(GUID))
179 Arrival = FALSE;
180 else
181 return STATUS_INVALID_PARAMETER;
182
183 if (Arrival)
184 {
185 DPRINT("Arrival of %wZ\n", Notification->SymbolicLinkName);
186
187 DeviceName.Length = 0;
188 DeviceName.MaximumLength = Notification->SymbolicLinkName->MaximumLength + DeviceNamePrefix.MaximumLength;
189 DeviceName.Buffer = ExAllocatePool(PagedPool, DeviceName.MaximumLength);
190 if (!DeviceName.Buffer) return STATUS_INSUFFICIENT_RESOURCES;
191
192 RtlCopyUnicodeString(&DeviceName, &DeviceNamePrefix);
193 RtlAppendUnicodeStringToString(&DeviceName, Notification->SymbolicLinkName);
194
195 DPRINT("Opening handle to %wZ\n", &DeviceName);
196
197 /* Open the device */
198 InitializeObjectAttributes(
199 &ObjectAttributes,
200 &DeviceName,
201 OBJ_KERNEL_HANDLE,
202 NULL,
203 NULL);
204 Status = ZwOpenFile(
205 &FileHandle,
206 FILE_READ_DATA,
207 &ObjectAttributes,
208 &IoStatusBlock,
209 FILE_SHARE_READ | FILE_SHARE_WRITE,
210 0);
211 RtlFreeUnicodeString(&DeviceName);
212 if (!NT_SUCCESS(Status))
213 {
214 DPRINT1("ZwOpenFile() failed with status 0x%08lx\n", Status);
215 return Status;
216 }
217 Status = ObReferenceObjectByHandle(
218 FileHandle,
219 FILE_READ_DATA,
220 IoFileObjectType,
221 KernelMode,
222 (PVOID*)&FileObject,
223 NULL);
224 if (!NT_SUCCESS(Status))
225 {
226 DPRINT1("ObReferenceObjectByHandle() failed with status 0x%08lx\n", Status);
227 ZwClose(FileHandle);
228 return Status;
229 }
230 DeviceObject = IoGetRelatedDeviceObject(FileObject);
231 ObDereferenceObject(FileObject);
232
233 /* Get capabilities (IOCTL_GET_SYS_BUTTON_CAPS) */
234 KeInitializeEvent(&Event, NotificationEvent, FALSE);
235 Irp = IoBuildDeviceIoControlRequest(
236 IOCTL_GET_SYS_BUTTON_CAPS,
237 DeviceObject,
238 NULL,
239 0,
240 &Caps,
241 sizeof(Caps),
242 FALSE,
243 &Event,
244 &IoStatusBlock);
245 if (!Irp)
246 {
247 DPRINT1("IoBuildDeviceIoControlRequest() failed\n");
248 ZwClose(FileHandle);
249 return STATUS_INSUFFICIENT_RESOURCES;
250 }
251 Status = IoCallDriver(DeviceObject, Irp);
252 if (Status == STATUS_PENDING)
253 {
254 DPRINT("IOCTL_GET_SYS_BUTTON_CAPS pending\n");
255 KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
256 Status = IoStatusBlock.Status;
257 }
258 if (!NT_SUCCESS(Status))
259 {
260 DPRINT1("Sending IOCTL_GET_SYS_BUTTON_CAPS failed with status 0x%08x\n", Status);
261 ZwClose(FileHandle);
262 return STATUS_INSUFFICIENT_RESOURCES;
263 }
264
265 /* FIXME: What do do with the capabilities? */
266 {
267 DPRINT1("Device capabilities: 0x%x (", Caps);
268 if (Caps & SYS_BUTTON_POWER) DbgPrint(" POWER");
269 if (Caps & SYS_BUTTON_SLEEP) DbgPrint(" SLEEP");
270 if (Caps & SYS_BUTTON_LID) DbgPrint(" LID");
271 DbgPrint(" )\n");
272 }
273
274 SysButtonContext = ExAllocatePool(NonPagedPool, sizeof(SYS_BUTTON_CONTEXT));
275 if (!SysButtonContext)
276 {
277 DPRINT1("ExAllocatePool() failed\n");
278 ZwClose(FileHandle);
279 return STATUS_INSUFFICIENT_RESOURCES;
280 }
281
282 /* Queue a work item to get sys button event */
283 SysButtonContext->WorkItem = IoAllocateWorkItem(DeviceObject);
284 SysButtonContext->DeviceObject = DeviceObject;
285 if (!SysButtonContext->WorkItem)
286 {
287 DPRINT1("IoAllocateWorkItem() failed\n");
288 ZwClose(FileHandle);
289 ExFreePool(SysButtonContext);
290 return STATUS_INSUFFICIENT_RESOURCES;
291 }
292 IoQueueWorkItem(
293 SysButtonContext->WorkItem,
294 PopGetSysButton,
295 DelayedWorkQueue,
296 SysButtonContext);
297
298 ZwClose(FileHandle);
299 return STATUS_SUCCESS;
300 }
301 else
302 {
303 DPRINT1("Removal of a power capable device not implemented\n");
304 return STATUS_NOT_IMPLEMENTED;
305 }
306 }