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