xref: /reactos/drivers/battery/battc/battc.c (revision 40462c92)
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 }
386