[BATTC] Fixed an oops!
[reactos.git] / drivers / battery / battc / battc.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: drivers/battery/battc/battc.c
5 * PURPOSE: Battery Class Driver
6 * PROGRAMMERS: Cameron Gutman (cameron.gutman@reactos.org)
7 */
8
9 #include <battc.h>
10
11 #define NDEBUG
12 #include <debug.h>
13
14 NTSTATUS
15 NTAPI
16 DriverEntry(PDRIVER_OBJECT DriverObject,
17 PUNICODE_STRING RegistryPath)
18 {
19 DPRINT("Battery class driver initialized\n");
20
21 return STATUS_SUCCESS;
22 }
23
24 BCLASSAPI
25 NTSTATUS
26 NTAPI
27 BatteryClassUnload(PVOID ClassData)
28 {
29 PBATTERY_CLASS_DATA BattClass;
30
31 DPRINT("Battery %p is being unloaded\n", ClassData);
32
33 BattClass = ClassData;
34 if (BattClass->InterfaceName.Length != 0)
35 {
36 IoSetDeviceInterfaceState(&BattClass->InterfaceName, FALSE);
37 RtlFreeUnicodeString(&BattClass->InterfaceName);
38 }
39
40 ExFreePoolWithTag(BattClass,
41 BATTERY_CLASS_DATA_TAG);
42
43 return STATUS_SUCCESS;
44 }
45
46 BCLASSAPI
47 NTSTATUS
48 NTAPI
49 BatteryClassSystemControl(PVOID ClassData,
50 PVOID WmiLibContext,
51 PDEVICE_OBJECT DeviceObject,
52 PIRP Irp,
53 PVOID Disposition)
54 {
55 UNIMPLEMENTED;
56
57 return STATUS_WMI_GUID_NOT_FOUND;
58 }
59
60 BCLASSAPI
61 NTSTATUS
62 NTAPI
63 BatteryClassQueryWmiDataBlock(PVOID ClassData,
64 PDEVICE_OBJECT DeviceObject,
65 PIRP Irp,
66 ULONG GuidIndex,
67 PULONG InstanceLengthArray,
68 ULONG OutBufferSize,
69 PUCHAR Buffer)
70 {
71 UNIMPLEMENTED;
72
73 return STATUS_WMI_GUID_NOT_FOUND;
74 }
75
76 BCLASSAPI
77 NTSTATUS
78 NTAPI
79 BatteryClassStatusNotify(PVOID ClassData)
80 {
81 PBATTERY_CLASS_DATA BattClass;
82 PBATTERY_WAIT_STATUS BattWait;
83 BATTERY_STATUS BattStatus;
84 NTSTATUS Status;
85
86 DPRINT("Received battery status notification from %p\n", ClassData);
87
88 BattClass = ClassData;
89 BattWait = BattClass->EventTriggerContext;
90
91 ExAcquireFastMutex(&BattClass->Mutex);
92 if (!BattClass->Waiting)
93 {
94 ExReleaseFastMutex(&BattClass->Mutex);
95 return STATUS_SUCCESS;
96 }
97
98 switch (BattClass->EventTrigger)
99 {
100 case EVENT_BATTERY_TAG:
101 ExReleaseFastMutex(&BattClass->Mutex);
102 DPRINT1("Waiting for battery is UNIMPLEMENTED!\n");
103 break;
104
105 case EVENT_BATTERY_STATUS:
106 ExReleaseFastMutex(&BattClass->Mutex);
107 Status = BattClass->MiniportInfo.QueryStatus(BattClass->MiniportInfo.Context,
108 BattWait->BatteryTag,
109 &BattStatus);
110 if (!NT_SUCCESS(Status))
111 return Status;
112
113 ExAcquireFastMutex(&BattClass->Mutex);
114
115 if (!(BattWait->PowerState & BattStatus.PowerState) ||
116 (BattWait->HighCapacity > BattStatus.Capacity) ||
117 (BattWait->LowCapacity < BattStatus.Capacity))
118 {
119 KeSetEvent(&BattClass->WaitEvent, IO_NO_INCREMENT, FALSE);
120 }
121
122 ExReleaseFastMutex(&BattClass->Mutex);
123 break;
124
125 default:
126 ExReleaseFastMutex(&BattClass->Mutex);
127 ASSERT(FALSE);
128 break;
129 }
130
131 return STATUS_SUCCESS;
132 }
133
134 BCLASSAPI
135 NTSTATUS
136 NTAPI
137 BatteryClassInitializeDevice(PBATTERY_MINIPORT_INFO MiniportInfo,
138 PVOID *ClassData)
139 {
140 NTSTATUS Status;
141 PBATTERY_CLASS_DATA BattClass;
142
143 BattClass = ExAllocatePoolWithTag(NonPagedPool,
144 sizeof(BATTERY_CLASS_DATA),
145 BATTERY_CLASS_DATA_TAG);
146 if (BattClass == NULL)
147 return STATUS_INSUFFICIENT_RESOURCES;
148
149 RtlZeroMemory(BattClass, sizeof(BATTERY_CLASS_DATA));
150
151 RtlCopyMemory(&BattClass->MiniportInfo,
152 MiniportInfo,
153 sizeof(BattClass->MiniportInfo));
154
155 KeInitializeEvent(&BattClass->WaitEvent, SynchronizationEvent, FALSE);
156
157 ExInitializeFastMutex(&BattClass->Mutex);
158
159 if (MiniportInfo->Pdo != NULL)
160 {
161 Status = IoRegisterDeviceInterface(MiniportInfo->Pdo,
162 &GUID_DEVICE_BATTERY,
163 NULL,
164 &BattClass->InterfaceName);
165 if (NT_SUCCESS(Status))
166 {
167 DPRINT("Initialized battery interface: %wZ\n", &BattClass->InterfaceName);
168 Status = IoSetDeviceInterfaceState(&BattClass->InterfaceName, TRUE);
169 if (Status == STATUS_OBJECT_NAME_EXISTS)
170 {
171 DPRINT1("Got STATUS_OBJECT_NAME_EXISTS for SetDeviceInterfaceState\n");
172 Status = STATUS_SUCCESS;
173 }
174 }
175 else
176 {
177 DPRINT1("IoRegisterDeviceInterface failed (0x%x)\n", Status);
178 }
179 }
180
181 *ClassData = BattClass;
182
183 return STATUS_SUCCESS;
184 }
185
186 BCLASSAPI
187 NTSTATUS
188 NTAPI
189 BatteryClassIoctl(PVOID ClassData,
190 PIRP Irp)
191 {
192 PBATTERY_CLASS_DATA BattClass;
193 PIO_STACK_LOCATION IrpSp;
194 NTSTATUS Status;
195 ULONG WaitTime;
196 PBATTERY_WAIT_STATUS BattWait;
197 PBATTERY_QUERY_INFORMATION BattQueryInfo;
198 PBATTERY_SET_INFORMATION BattSetInfo;
199 LARGE_INTEGER Timeout;
200 PBATTERY_STATUS BattStatus;
201 BATTERY_NOTIFY BattNotify;
202 ULONG ReturnedLength;
203
204 DPRINT("BatteryClassIoctl(%p %p)\n", ClassData, Irp);
205
206 BattClass = ClassData;
207
208 IrpSp = IoGetCurrentIrpStackLocation(Irp);
209 Irp->IoStatus.Information = 0;
210
211 DPRINT("Received IOCTL %x for %p\n", IrpSp->Parameters.DeviceIoControl.IoControlCode,
212 ClassData);
213
214 switch (IrpSp->Parameters.DeviceIoControl.IoControlCode)
215 {
216 case IOCTL_BATTERY_QUERY_TAG:
217 if (IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(ULONG) ||
218 IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG))
219 {
220 Status = STATUS_BUFFER_TOO_SMALL;
221 break;
222 }
223
224 WaitTime = *(PULONG)Irp->AssociatedIrp.SystemBuffer;
225
226 Timeout.QuadPart = Int32x32To64(WaitTime, -1000);
227
228 Status = BattClass->MiniportInfo.QueryTag(BattClass->MiniportInfo.Context,
229 (PULONG)Irp->AssociatedIrp.SystemBuffer);
230 if (!NT_SUCCESS(Status))
231 {
232 ExAcquireFastMutex(&BattClass->Mutex);
233 BattClass->EventTrigger = EVENT_BATTERY_TAG;
234 BattClass->Waiting = TRUE;
235 ExReleaseFastMutex(&BattClass->Mutex);
236
237 Status = KeWaitForSingleObject(&BattClass->WaitEvent,
238 Executive,
239 KernelMode,
240 FALSE,
241 WaitTime != -1 ? &Timeout : NULL);
242
243 ExAcquireFastMutex(&BattClass->Mutex);
244 BattClass->Waiting = FALSE;
245 ExReleaseFastMutex(&BattClass->Mutex);
246
247 if (Status == STATUS_SUCCESS)
248 {
249 Status = BattClass->MiniportInfo.QueryTag(BattClass->MiniportInfo.Context,
250 (PULONG)Irp->AssociatedIrp.SystemBuffer);
251 if (NT_SUCCESS(Status))
252 Irp->IoStatus.Information = sizeof(ULONG);
253 }
254 else
255 {
256 Status = STATUS_NO_SUCH_DEVICE;
257 }
258 }
259 else
260 {
261 Irp->IoStatus.Information = sizeof(ULONG);
262 }
263 break;
264
265 case IOCTL_BATTERY_QUERY_STATUS:
266 if (IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(*BattWait) ||
267 IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(BATTERY_STATUS))
268 {
269 Status = STATUS_BUFFER_TOO_SMALL;
270 break;
271 }
272
273 BattWait = Irp->AssociatedIrp.SystemBuffer;
274
275 Timeout.QuadPart = Int32x32To64(BattWait->Timeout, -1000);
276
277 Status = BattClass->MiniportInfo.QueryStatus(BattClass->MiniportInfo.Context,
278 BattWait->BatteryTag,
279 (PBATTERY_STATUS)Irp->AssociatedIrp.SystemBuffer);
280
281 BattStatus = Irp->AssociatedIrp.SystemBuffer;
282
283 if (!NT_SUCCESS(Status) ||
284 ((BattWait->PowerState & BattStatus->PowerState) &&
285 (BattWait->HighCapacity <= BattStatus->Capacity) &&
286 (BattWait->LowCapacity >= BattStatus->Capacity)))
287 {
288 BattNotify.PowerState = BattWait->PowerState;
289 BattNotify.HighCapacity = BattWait->HighCapacity;
290 BattNotify.LowCapacity = BattWait->LowCapacity;
291
292 BattClass->MiniportInfo.SetStatusNotify(BattClass->MiniportInfo.Context,
293 BattWait->BatteryTag,
294 &BattNotify);
295
296 ExAcquireFastMutex(&BattClass->Mutex);
297 BattClass->EventTrigger = EVENT_BATTERY_STATUS;
298 BattClass->EventTriggerContext = BattWait;
299 BattClass->Waiting = TRUE;
300 ExReleaseFastMutex(&BattClass->Mutex);
301
302 Status = KeWaitForSingleObject(&BattClass->WaitEvent,
303 Executive,
304 KernelMode,
305 FALSE,
306 BattWait->Timeout != -1 ? &Timeout : NULL);
307
308 ExAcquireFastMutex(&BattClass->Mutex);
309 BattClass->Waiting = FALSE;
310 ExReleaseFastMutex(&BattClass->Mutex);
311
312 BattClass->MiniportInfo.DisableStatusNotify(BattClass->MiniportInfo.Context);
313
314 if (Status == STATUS_SUCCESS)
315 {
316 Status = BattClass->MiniportInfo.QueryStatus(BattClass->MiniportInfo.Context,
317 BattWait->BatteryTag,
318 (PBATTERY_STATUS)Irp->AssociatedIrp.SystemBuffer);
319 if (NT_SUCCESS(Status))
320 Irp->IoStatus.Information = sizeof(ULONG);
321 }
322 else
323 {
324 Status = STATUS_NO_SUCH_DEVICE;
325 }
326 }
327 else
328 {
329 Irp->IoStatus.Information = sizeof(BATTERY_STATUS);
330 }
331 break;
332
333 case IOCTL_BATTERY_QUERY_INFORMATION:
334 if (IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(*BattQueryInfo))
335 {
336 Status = STATUS_BUFFER_TOO_SMALL;
337 break;
338 }
339
340 BattQueryInfo = Irp->AssociatedIrp.SystemBuffer;
341
342 Status = BattClass->MiniportInfo.QueryInformation(BattClass->MiniportInfo.Context,
343 BattQueryInfo->BatteryTag,
344 BattQueryInfo->InformationLevel,
345 BattQueryInfo->AtRate,
346 Irp->AssociatedIrp.SystemBuffer,
347 IrpSp->Parameters.DeviceIoControl.OutputBufferLength,
348 &ReturnedLength);
349 Irp->IoStatus.Information = ReturnedLength;
350 if (!NT_SUCCESS(Status))
351 {
352 DPRINT1("QueryInformation failed (0x%x)\n", Status);
353 }
354 break;
355
356 case IOCTL_BATTERY_SET_INFORMATION:
357 if (IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(*BattSetInfo))
358 {
359 Status = STATUS_BUFFER_TOO_SMALL;
360 break;
361 }
362
363 BattSetInfo = Irp->AssociatedIrp.SystemBuffer;
364
365 Status = BattClass->MiniportInfo.SetInformation(BattClass->MiniportInfo.Context,
366 BattSetInfo->BatteryTag,
367 BattSetInfo->InformationLevel,
368 BattSetInfo->Buffer);
369 if (!NT_SUCCESS(Status))
370 {
371 DPRINT1("SetInformation failed (0x%x)\n", Status);
372 }
373 break;
374
375 default:
376 DPRINT1("Received unsupported IRP %x\n", IrpSp->Parameters.DeviceIoControl.IoControlCode);
377 /* Do NOT complete the irp */
378 return STATUS_NOT_SUPPORTED;
379 }
380
381 Irp->IoStatus.Status = Status;
382 IoCompleteRequest(Irp, IO_NO_INCREMENT);
383
384 return Status;
385 }