Sync with trunk.
[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
166 DPRINT("PopAddRemoveSysCapsCallback(%p %p)\n",
167 NotificationStructure, Context);
168
169 Notification = (PDEVICE_INTERFACE_CHANGE_NOTIFICATION)NotificationStructure;
170 if (Notification->Version != 1)
171 return STATUS_REVISION_MISMATCH;
172 if (Notification->Size != sizeof(DEVICE_INTERFACE_CHANGE_NOTIFICATION))
173 return STATUS_INVALID_PARAMETER;
174 if (RtlCompareMemory(&Notification->Event, &GUID_DEVICE_INTERFACE_ARRIVAL, sizeof(GUID)) == sizeof(GUID))
175 Arrival = TRUE;
176 else if (RtlCompareMemory(&Notification->Event, &GUID_DEVICE_INTERFACE_REMOVAL, sizeof(GUID)) == sizeof(GUID))
177 Arrival = FALSE;
178 else
179 return STATUS_INVALID_PARAMETER;
180
181 if (Arrival)
182 {
183 DPRINT("Arrival of %wZ\n", Notification->SymbolicLinkName);
184
185 /* Open the device */
186 InitializeObjectAttributes(
187 &ObjectAttributes,
188 Notification->SymbolicLinkName,
189 OBJ_KERNEL_HANDLE,
190 NULL,
191 NULL);
192 Status = ZwOpenFile(
193 &FileHandle,
194 FILE_READ_DATA,
195 &ObjectAttributes,
196 &IoStatusBlock,
197 FILE_SHARE_READ | FILE_SHARE_WRITE,
198 0);
199 if (!NT_SUCCESS(Status))
200 {
201 DPRINT1("ZwOpenFile() failed with status 0x%08lx\n", Status);
202 return Status;
203 }
204 Status = ObReferenceObjectByHandle(
205 FileHandle,
206 FILE_READ_DATA,
207 IoFileObjectType,
208 KernelMode,
209 (PVOID*)&FileObject,
210 NULL);
211 if (!NT_SUCCESS(Status))
212 {
213 DPRINT1("ObReferenceObjectByHandle() failed with status 0x%08lx\n", Status);
214 ZwClose(FileHandle);
215 return Status;
216 }
217 DeviceObject = IoGetRelatedDeviceObject(FileObject);
218 ObDereferenceObject(FileObject);
219
220 /* Get capabilities (IOCTL_GET_SYS_BUTTON_CAPS) */
221 KeInitializeEvent(&Event, NotificationEvent, FALSE);
222 Irp = IoBuildDeviceIoControlRequest(
223 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 /* FIXME: What do do with the capabilities? */
253 {
254 DPRINT("Device capabilities: 0x%x (", Caps);
255 if (Caps & SYS_BUTTON_POWER) DPRINT(" POWER");
256 if (Caps & SYS_BUTTON_SLEEP) DPRINT(" SLEEP");
257 if (Caps & SYS_BUTTON_LID) DPRINT(" LID");
258 DPRINT(" )\n");
259 }
260
261 SysButtonContext = ExAllocatePool(NonPagedPool, sizeof(SYS_BUTTON_CONTEXT));
262 if (!SysButtonContext)
263 {
264 DPRINT1("ExAllocatePool() failed\n");
265 ZwClose(FileHandle);
266 return STATUS_INSUFFICIENT_RESOURCES;
267 }
268
269 /* Queue a work item to get sys button event */
270 SysButtonContext->WorkItem = IoAllocateWorkItem(DeviceObject);
271 SysButtonContext->DeviceObject = DeviceObject;
272 if (!SysButtonContext->WorkItem)
273 {
274 DPRINT1("IoAllocateWorkItem() failed\n");
275 ZwClose(FileHandle);
276 ExFreePool(SysButtonContext);
277 return STATUS_INSUFFICIENT_RESOURCES;
278 }
279 IoQueueWorkItem(
280 SysButtonContext->WorkItem,
281 PopGetSysButton,
282 DelayedWorkQueue,
283 SysButtonContext);
284
285 ZwClose(FileHandle);
286 return STATUS_SUCCESS;
287 }
288 else
289 {
290 DPRINT1("Removal of a power capable device not implemented\n");
291 return STATUS_NOT_IMPLEMENTED;
292 }
293 }