xref: /reactos/drivers/battery/battc/battc.c (revision b0680d65)
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
DriverEntry(PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegistryPath)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
BatteryClassUnload(PVOID ClassData)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
BatteryClassSystemControl(PVOID ClassData,PVOID WmiLibContext,PDEVICE_OBJECT DeviceObject,PIRP Irp,PVOID Disposition)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
BatteryClassQueryWmiDataBlock(PVOID ClassData,PDEVICE_OBJECT DeviceObject,PIRP Irp,ULONG GuidIndex,PULONG InstanceLengthArray,ULONG OutBufferSize,PUCHAR Buffer)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
BatteryClassStatusNotify(_In_ PVOID ClassData)79 BatteryClassStatusNotify(
80     _In_ PVOID ClassData)
81 {
82     PBATTERY_CLASS_DATA BattClass;
83     PBATTERY_WAIT_STATUS BattWait;
84     BATTERY_STATUS BattStatus;
85     ULONG Tag;
86     NTSTATUS Status;
87 
88     DPRINT("Received battery status notification from %p\n", ClassData);
89 
90     BattClass = ClassData;
91     BattWait = BattClass->EventTriggerContext;
92 
93     ExAcquireFastMutex(&BattClass->Mutex);
94     if (!BattClass->Waiting)
95     {
96         ExReleaseFastMutex(&BattClass->Mutex);
97         return STATUS_SUCCESS;
98     }
99 
100     switch (BattClass->EventTrigger)
101     {
102         case EVENT_BATTERY_TAG:
103             ExReleaseFastMutex(&BattClass->Mutex);
104             Status = BattClass->MiniportInfo.QueryTag(BattClass->MiniportInfo.Context,
105                                                       &Tag);
106             if (!NT_SUCCESS(Status))
107                 return Status;
108 
109             ExAcquireFastMutex(&BattClass->Mutex);
110             KeSetEvent(&BattClass->WaitEvent, IO_NO_INCREMENT, FALSE);
111             ExReleaseFastMutex(&BattClass->Mutex);
112             break;
113 
114         case EVENT_BATTERY_STATUS:
115             ExReleaseFastMutex(&BattClass->Mutex);
116             Status = BattClass->MiniportInfo.QueryStatus(BattClass->MiniportInfo.Context,
117                                                          BattWait->BatteryTag,
118                                                          &BattStatus);
119             if (!NT_SUCCESS(Status))
120                 return Status;
121 
122             ExAcquireFastMutex(&BattClass->Mutex);
123 
124             if (BattWait->PowerState != BattStatus.PowerState ||
125                 BattWait->HighCapacity < BattStatus.Capacity ||
126                 BattWait->LowCapacity > BattStatus.Capacity)
127             {
128                 KeSetEvent(&BattClass->WaitEvent, IO_NO_INCREMENT, FALSE);
129             }
130 
131             ExReleaseFastMutex(&BattClass->Mutex);
132             break;
133 
134         default:
135             ExReleaseFastMutex(&BattClass->Mutex);
136             ASSERT(FALSE);
137             break;
138     }
139 
140     return STATUS_SUCCESS;
141 }
142 
143 BCLASSAPI
144 NTSTATUS
145 NTAPI
BatteryClassInitializeDevice(PBATTERY_MINIPORT_INFO MiniportInfo,PVOID * ClassData)146 BatteryClassInitializeDevice(PBATTERY_MINIPORT_INFO MiniportInfo,
147                              PVOID *ClassData)
148 {
149     NTSTATUS Status;
150     PBATTERY_CLASS_DATA BattClass;
151 
152     BattClass = ExAllocatePoolWithTag(NonPagedPool,
153                                       sizeof(BATTERY_CLASS_DATA),
154                                       BATTERY_CLASS_DATA_TAG);
155     if (BattClass == NULL)
156         return STATUS_INSUFFICIENT_RESOURCES;
157 
158     RtlZeroMemory(BattClass, sizeof(BATTERY_CLASS_DATA));
159 
160     RtlCopyMemory(&BattClass->MiniportInfo,
161                   MiniportInfo,
162                   sizeof(BattClass->MiniportInfo));
163 
164     KeInitializeEvent(&BattClass->WaitEvent, SynchronizationEvent, FALSE);
165 
166     ExInitializeFastMutex(&BattClass->Mutex);
167 
168     if (MiniportInfo->Pdo != NULL)
169     {
170         Status = IoRegisterDeviceInterface(MiniportInfo->Pdo,
171                                            &GUID_DEVICE_BATTERY,
172                                            NULL,
173                                            &BattClass->InterfaceName);
174         if (NT_SUCCESS(Status))
175         {
176             DPRINT("Initialized battery interface: %wZ\n", &BattClass->InterfaceName);
177             Status = IoSetDeviceInterfaceState(&BattClass->InterfaceName, TRUE);
178             if (Status == STATUS_OBJECT_NAME_EXISTS)
179             {
180                 DPRINT1("Got STATUS_OBJECT_NAME_EXISTS for SetDeviceInterfaceState\n");
181                 Status = STATUS_SUCCESS;
182             }
183         }
184         else
185         {
186             DPRINT1("IoRegisterDeviceInterface failed (0x%x)\n", Status);
187         }
188     }
189 
190     *ClassData = BattClass;
191 
192     return STATUS_SUCCESS;
193 }
194 
195 BCLASSAPI
196 NTSTATUS
197 NTAPI
BatteryClassIoctl(PVOID ClassData,PIRP Irp)198 BatteryClassIoctl(PVOID ClassData,
199                   PIRP Irp)
200 {
201     PBATTERY_CLASS_DATA BattClass;
202     PIO_STACK_LOCATION IrpSp;
203     NTSTATUS Status;
204     ULONG WaitTime;
205     BATTERY_WAIT_STATUS BattWait;
206     PBATTERY_QUERY_INFORMATION BattQueryInfo;
207     PBATTERY_SET_INFORMATION BattSetInfo;
208     LARGE_INTEGER Timeout;
209     PBATTERY_STATUS BattStatus;
210     BATTERY_NOTIFY BattNotify;
211     ULONG ReturnedLength;
212 
213     DPRINT("BatteryClassIoctl(%p %p)\n", ClassData, Irp);
214 
215     BattClass = ClassData;
216 
217     IrpSp = IoGetCurrentIrpStackLocation(Irp);
218     Irp->IoStatus.Information = 0;
219 
220     DPRINT("Received IOCTL %x for %p\n", IrpSp->Parameters.DeviceIoControl.IoControlCode,
221            ClassData);
222 
223     switch (IrpSp->Parameters.DeviceIoControl.IoControlCode)
224     {
225         case IOCTL_BATTERY_QUERY_TAG:
226             if ((IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof(ULONG) && IrpSp->Parameters.DeviceIoControl.InputBufferLength != 0) ||
227                 IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG))
228             {
229                 Status = STATUS_BUFFER_TOO_SMALL;
230                 break;
231             }
232 
233             WaitTime = IrpSp->Parameters.DeviceIoControl.InputBufferLength == sizeof(ULONG) ? *(PULONG)Irp->AssociatedIrp.SystemBuffer : 0;
234 
235             Timeout.QuadPart = Int32x32To64(WaitTime, -10000);
236 
237             Status = BattClass->MiniportInfo.QueryTag(BattClass->MiniportInfo.Context,
238                                                       (PULONG)Irp->AssociatedIrp.SystemBuffer);
239             if (!NT_SUCCESS(Status))
240             {
241                 ExAcquireFastMutex(&BattClass->Mutex);
242                 BattClass->EventTrigger = EVENT_BATTERY_TAG;
243                 BattClass->Waiting = TRUE;
244                 ExReleaseFastMutex(&BattClass->Mutex);
245 
246                 Status = KeWaitForSingleObject(&BattClass->WaitEvent,
247                                                Executive,
248                                                KernelMode,
249                                                FALSE,
250                                                WaitTime != -1 ? &Timeout : NULL);
251 
252                 ExAcquireFastMutex(&BattClass->Mutex);
253                 BattClass->Waiting = FALSE;
254                 ExReleaseFastMutex(&BattClass->Mutex);
255 
256                 if (Status == STATUS_SUCCESS)
257                 {
258                     Status = BattClass->MiniportInfo.QueryTag(BattClass->MiniportInfo.Context,
259                                                               (PULONG)Irp->AssociatedIrp.SystemBuffer);
260                     if (NT_SUCCESS(Status))
261                         Irp->IoStatus.Information = sizeof(ULONG);
262                 }
263                 else
264                 {
265                     Status = STATUS_NO_SUCH_DEVICE;
266                 }
267             }
268             else
269             {
270                 Irp->IoStatus.Information = sizeof(ULONG);
271             }
272             break;
273 
274         case IOCTL_BATTERY_QUERY_STATUS:
275             if (IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(BattWait) ||
276                 IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(BATTERY_STATUS))
277             {
278                 Status = STATUS_BUFFER_TOO_SMALL;
279                 break;
280             }
281 
282             BattWait = *(PBATTERY_WAIT_STATUS)Irp->AssociatedIrp.SystemBuffer;
283 
284             Timeout.QuadPart = Int32x32To64(BattWait.Timeout, -10000);
285 
286             BattStatus = Irp->AssociatedIrp.SystemBuffer;
287             Status = BattClass->MiniportInfo.QueryStatus(BattClass->MiniportInfo.Context,
288                                                          BattWait.BatteryTag,
289                                                          BattStatus);
290 
291             if (!NT_SUCCESS(Status) ||
292                 (BattWait.PowerState == BattStatus->PowerState &&
293                  BattWait.HighCapacity >= BattStatus->Capacity &&
294                  BattWait.LowCapacity <= BattStatus->Capacity))
295             {
296                 BattNotify.PowerState = BattWait.PowerState;
297                 BattNotify.HighCapacity = BattWait.HighCapacity;
298                 BattNotify.LowCapacity = BattWait.LowCapacity;
299 
300                 BattClass->MiniportInfo.SetStatusNotify(BattClass->MiniportInfo.Context,
301                                                         BattWait.BatteryTag,
302                                                         &BattNotify);
303 
304                 ExAcquireFastMutex(&BattClass->Mutex);
305                 BattClass->EventTrigger = EVENT_BATTERY_STATUS;
306                 BattClass->EventTriggerContext = &BattWait;
307                 BattClass->Waiting = TRUE;
308                 ExReleaseFastMutex(&BattClass->Mutex);
309 
310                 Status = KeWaitForSingleObject(&BattClass->WaitEvent,
311                                                Executive,
312                                                KernelMode,
313                                                FALSE,
314                                                BattWait.Timeout != -1 ? &Timeout : NULL);
315                 if (Status == STATUS_TIMEOUT)
316                     Status = STATUS_SUCCESS;
317 
318                 ExAcquireFastMutex(&BattClass->Mutex);
319                 BattClass->Waiting = FALSE;
320                 ExReleaseFastMutex(&BattClass->Mutex);
321 
322                 BattClass->MiniportInfo.DisableStatusNotify(BattClass->MiniportInfo.Context);
323             }
324             else
325             {
326                 Irp->IoStatus.Information = sizeof(BATTERY_STATUS);
327             }
328             break;
329 
330         case IOCTL_BATTERY_QUERY_INFORMATION:
331             if (IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(*BattQueryInfo))
332             {
333                 Status = STATUS_BUFFER_TOO_SMALL;
334                 break;
335             }
336 
337             BattQueryInfo = Irp->AssociatedIrp.SystemBuffer;
338 
339             Status = BattClass->MiniportInfo.QueryInformation(BattClass->MiniportInfo.Context,
340                                                               BattQueryInfo->BatteryTag,
341                                                               BattQueryInfo->InformationLevel,
342                                                               BattQueryInfo->AtRate,
343                                                               Irp->AssociatedIrp.SystemBuffer,
344                                                               IrpSp->Parameters.DeviceIoControl.OutputBufferLength,
345                                                               &ReturnedLength);
346             Irp->IoStatus.Information = ReturnedLength;
347             if (!NT_SUCCESS(Status))
348             {
349                 DPRINT1("QueryInformation failed (0x%x)\n", Status);
350             }
351             break;
352 
353         case IOCTL_BATTERY_SET_INFORMATION:
354             if (IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(*BattSetInfo))
355             {
356                 Status = STATUS_BUFFER_TOO_SMALL;
357                 break;
358             }
359 
360             BattSetInfo = Irp->AssociatedIrp.SystemBuffer;
361 
362             Status = BattClass->MiniportInfo.SetInformation(BattClass->MiniportInfo.Context,
363                                                             BattSetInfo->BatteryTag,
364                                                             BattSetInfo->InformationLevel,
365                                                             BattSetInfo->Buffer);
366             if (!NT_SUCCESS(Status))
367             {
368                 DPRINT1("SetInformation failed (0x%x)\n", Status);
369             }
370             break;
371 
372         default:
373             DPRINT1("Received unsupported IRP %x\n", IrpSp->Parameters.DeviceIoControl.IoControlCode);
374             /* Do NOT complete the irp */
375             return STATUS_NOT_SUPPORTED;
376     }
377 
378     Irp->IoStatus.Status = Status;
379     IoCompleteRequest(Irp, IO_NO_INCREMENT);
380 
381     return Status;
382 }
383