Reactos
1/*
2 * PROJECT: ReactOS ACPI-Compliant Control Method Battery
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: boot/drivers/bus/acpi/cmbatt/cmexec.c
5 * PURPOSE: ACPI Method Execution/Evaluation Glue
6 * PROGRAMMERS: ReactOS Portable Systems Group
7 */
8
9/* INCLUDES *******************************************************************/
10
11#include "cmbatt.h"
12
13#include <acpiioct.h>
14
15typedef struct _ACPI_PACKAGE_FIELD
16{
17 PSTR Name;
18 BOOLEAN IsString;
19 PVOID Data;
20} ACPI_PACKAGE_FIELD, *PACPI_PACKAGE_FIELD;
21
22/* FUNCTIONS ******************************************************************/
23
24NTSTATUS
25NTAPI
26GetDwordElement(IN PACPI_METHOD_ARGUMENT Argument,
27 OUT PULONG Value)
28{
29 NTSTATUS Status;
30
31 /* Must have an integer */
32 if (Argument->Type != ACPI_METHOD_ARGUMENT_INTEGER)
33 {
34 /* Not an integer, fail */
35 Status = STATUS_ACPI_INVALID_DATA;
36 if (CmBattDebug & 0x4C)
37 DbgPrint("GetDwordElement: Object contained wrong data type - %d\n",
38 Argument->Type);
39 }
40 else
41 {
42 /* Read the integer value */
43 *Value = Argument->Argument;
44 Status = STATUS_SUCCESS;
45 }
46
47 /* Return status */
48 return Status;
49}
50
51NTSTATUS
52NTAPI
53GetStringElement(IN PACPI_METHOD_ARGUMENT Argument,
54 OUT PCHAR Value)
55{
56 NTSTATUS Status;
57
58 /* Must have a string of buffer */
59 if ((Argument->Type == ACPI_METHOD_ARGUMENT_STRING) ||
60 (Argument->Type == ACPI_METHOD_ARGUMENT_BUFFER))
61 {
62 /* String must be less than 256 characters */
63 if (Argument->DataLength < 256)
64 {
65 /* Copy the buffer */
66 RtlCopyMemory(Value, Argument->Data, Argument->DataLength);
67 Status = STATUS_SUCCESS;
68 }
69 else
70 {
71 /* The buffer is too small (the string is too large) */
72 Status = STATUS_BUFFER_TOO_SMALL;
73 if (CmBattDebug & 0x4C)
74 DbgPrint("GetStringElement: return buffer not big enough - %d\n", Argument->DataLength);
75 }
76 }
77 else
78 {
79 /* Not valid string data */
80 Status = STATUS_ACPI_INVALID_DATA;
81 if (CmBattDebug & 0x4C)
82 DbgPrint("GetStringElement: Object contained wrong data type - %d\n", Argument->Type);
83 }
84
85 /* Return the status */
86 return Status;
87}
88
89NTSTATUS
90NTAPI
91CmBattSendDownStreamIrp(IN PDEVICE_OBJECT DeviceObject,
92 IN ULONG IoControlCode,
93 IN PVOID InputBuffer,
94 IN ULONG InputBufferLength,
95 IN PACPI_EVAL_OUTPUT_BUFFER OutputBuffer,
96 IN ULONG OutputBufferLength)
97{
98 PIRP Irp;
99 NTSTATUS Status;
100 KEVENT Event;
101 IO_STATUS_BLOCK IoStatusBlock;
102 PAGED_CODE();
103
104 /* Initialize our wait event */
105 KeInitializeEvent(&Event, SynchronizationEvent, 0);
106
107 /* Allocate the IRP */
108 Irp = IoBuildDeviceIoControlRequest(IoControlCode,
109 DeviceObject,
110 InputBuffer,
111 InputBufferLength,
112 OutputBuffer,
113 OutputBufferLength,
114 0,
115 &Event,
116 &IoStatusBlock);
117 if (!Irp)
118 {
119 /* No IRP, fail */
120 if (CmBattDebug & 0x4C)
121 DbgPrint("CmBattSendDownStreamIrp: Failed to allocate Irp\n");
122 return STATUS_INSUFFICIENT_RESOURCES;
123 }
124
125 /* Call ACPI */
126 if (CmBattDebug & 0x40)
127 DbgPrint("CmBattSendDownStreamIrp: Irp %x [Tid] %x\n",
128 Irp, KeGetCurrentThread());
129 Status = IoCallDriver(DeviceObject, Irp);
130 if (Status == STATUS_PENDING)
131 {
132 /* Wait for completion */
133 KeWaitForSingleObject(&Event,
134 Executive,
135 KernelMode,
136 FALSE,
137 NULL);
138 Status = Irp->IoStatus.Status;
139 }
140
141 /* Check if caller wanted output */
142 if (OutputBuffer)
143 {
144 /* Make sure it's valid ACPI output buffer */
145 if ((OutputBuffer->Signature != ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE) ||
146 !(OutputBuffer->Count))
147 {
148 /* It isn't, so set failure code */
149 Status = STATUS_ACPI_INVALID_DATA;
150 }
151 }
152
153 /* Return status */
154 if (CmBattDebug & 0x40)
155 DbgPrint("CmBattSendDownStreamIrp: Irp %x completed %x! [Tid] %x\n",
156 Irp, Status, KeGetCurrentThread());
157 return Status;
158}
159
160static
161NTSTATUS
162CmBattCallAcpiPackage(
163 _In_ LPCSTR FunctionName,
164 _In_ PCMBATT_DEVICE_EXTENSION DeviceExtension,
165 _In_ ULONG PackageName,
166 _In_ ULONG OutputBufferSize,
167 _In_ PACPI_PACKAGE_FIELD PackageFields,
168 _In_ ULONG PackageFieldCount)
169{
170 NTSTATUS Status;
171 PACPI_EVAL_OUTPUT_BUFFER OutputBuffer;
172 ACPI_EVAL_INPUT_BUFFER InputBuffer;
173 PACPI_METHOD_ARGUMENT Argument;
174 ULONG i;
175 PAGED_CODE();
176
177 OutputBuffer = ExAllocatePoolWithTag(PagedPool,
178 OutputBufferSize,
179 'MtaB');
180 if (!OutputBuffer)
181 {
182 if (CmBattDebug & (CMBATT_ACPI_ENTRY_EXIT | CMBATT_ACPI_WARNING | CMBATT_GENERIC_WARNING))
183 DbgPrint("%s: Failed to allocate Buffer\n", FunctionName);
184 return STATUS_INSUFFICIENT_RESOURCES;
185 }
186
187 /* Initialize to zero */
188 RtlZeroMemory(OutputBuffer, OutputBufferSize);
189
190 /* Request the ACPI method */
191 *(PULONG)InputBuffer.MethodName = PackageName;
192 InputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE;
193
194 /* Send it to ACPI */
195 Status = CmBattSendDownStreamIrp(DeviceExtension->AttachedDevice,
196 IOCTL_ACPI_EVAL_METHOD,
197 &InputBuffer,
198 sizeof(InputBuffer),
199 OutputBuffer,
200 OutputBufferSize);
201 if (!NT_SUCCESS(Status))
202 {
203 if (CmBattDebug & (CMBATT_ACPI_ASSERT | CMBATT_ACPI_ENTRY_EXIT | CMBATT_ACPI_WARNING | CMBATT_GENERIC_WARNING))
204 DbgPrint("%s: Failed 0x%08x method on device %x - Status (0x%x)\n",
205 FunctionName, PackageName, DeviceExtension->DeviceId, Status);
206 ExFreePoolWithTag(OutputBuffer, 'MtaB');
207 return Status;
208 }
209
210 /* Check if we got the right number of elements */
211 if (OutputBuffer->Count != PackageFieldCount)
212 {
213 if (CmBattDebug & (CMBATT_ACPI_ASSERT | CMBATT_ACPI_ENTRY_EXIT | CMBATT_ACPI_WARNING | CMBATT_GENERIC_WARNING))
214 DbgPrint("%s: 0x%08x method returned %d elements (requires %d)\n",
215 FunctionName, PackageName, OutputBuffer->Count, PackageFieldCount);
216 ExFreePoolWithTag(OutputBuffer, 'MtaB');
217 return STATUS_ACPI_INVALID_DATA;
218 }
219
220 Argument = OutputBuffer->Argument;
221 for (i = 0; i < PackageFieldCount && NT_SUCCESS(Status); i++)
222 {
223 if (PackageFields[i].IsString)
224 Status = GetStringElement(Argument, PackageFields[i].Data);
225 else
226 Status = GetDwordElement(Argument, PackageFields[i].Data);
227 if (!NT_SUCCESS(Status))
228 {
229 if (CmBattDebug & (CMBATT_ACPI_ASSERT | CMBATT_ACPI_ENTRY_EXIT | CMBATT_ACPI_WARNING | CMBATT_GENERIC_WARNING))
230 DbgPrint("%s: Failed to get %s\n", FunctionName, PackageFields[i].Name);
231 break;
232 }
233 Argument = ACPI_METHOD_NEXT_ARGUMENT(Argument);
234 }
235
236 ExFreePoolWithTag(OutputBuffer, 'MtaB');
237 return Status;
238}
239
240NTSTATUS
241NTAPI
242CmBattGetPsrData(IN PDEVICE_OBJECT DeviceObject,
243 OUT PULONG PsrData)
244{
245 NTSTATUS Status;
246 ACPI_EVAL_OUTPUT_BUFFER OutputBuffer;
247 ACPI_EVAL_INPUT_BUFFER InputBuffer;
248 PAGED_CODE();
249 if (CmBattDebug & 0x40)
250 DbgPrint("CmBattGetPsrData: Entered with Pdo %x Tid %x\n",
251 DeviceObject, KeGetCurrentThread());
252
253 /* Initialize to zero */
254 ASSERT(PsrData != NULL);
255 *PsrData = 0;
256
257 /* Request the _PSR method */
258 *(PULONG)InputBuffer.MethodName = 'RSP_';
259 InputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE;
260
261 /* Send it to ACPI */
262 Status = CmBattSendDownStreamIrp(DeviceObject,
263 IOCTL_ACPI_EVAL_METHOD,
264 &InputBuffer,
265 sizeof(InputBuffer),
266 &OutputBuffer,
267 sizeof(OutputBuffer));
268 if (NT_SUCCESS(Status))
269 {
270 /* Read the result */
271 Status = GetDwordElement(OutputBuffer.Argument, PsrData);
272 if (CmBattDebug & 0x440)
273 DbgPrint("CmBattGetPsrData: _PSR method returned %x \n", *PsrData);
274 }
275 else if (CmBattDebug & 0x44C)
276 {
277 /* Failure */
278 DbgPrint("CmBattGetPsrData: Failed _PSR method - Status (0x%x)\n", Status);
279 }
280
281 /* Return status */
282 return Status;
283}
284
285NTSTATUS
286NTAPI
287CmBattGetStaData(IN PDEVICE_OBJECT DeviceObject,
288 OUT PULONG StaData)
289{
290 NTSTATUS Status;
291 ACPI_EVAL_OUTPUT_BUFFER OutputBuffer;
292 ACPI_EVAL_INPUT_BUFFER InputBuffer;
293 PAGED_CODE();
294 if (CmBattDebug & 0x40)
295 DbgPrint("CmBattGetStaData: Entered with Pdo %x Tid %x\n",
296 DeviceObject, KeGetCurrentThread());
297
298 /* Initialize to zero */
299 ASSERT(StaData != NULL);
300 *StaData = 0;
301
302 /* Request the _STA method */
303 *(PULONG)InputBuffer.MethodName = 'ATS_';
304 InputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE;
305
306 /* Send it to ACPI */
307 Status = CmBattSendDownStreamIrp(DeviceObject,
308 IOCTL_ACPI_EVAL_METHOD,
309 &InputBuffer,
310 sizeof(InputBuffer),
311 &OutputBuffer,
312 sizeof(OutputBuffer));
313 if (NT_SUCCESS(Status))
314 {
315 /* Read the result */
316 Status = GetDwordElement(OutputBuffer.Argument, StaData);
317 if (CmBattDebug & 0x440)
318 DbgPrint("CmBattGetStaData: _STA method returned %x \n", *StaData);
319 }
320 else if (CmBattDebug & 0x44C)
321 {
322 /* Failure */
323 DbgPrint("CmBattGetStaData: Failed _STA method - Status (0x%x)\n", Status);
324 Status = STATUS_NO_SUCH_DEVICE;
325 }
326
327 /* Return status */
328 return Status;
329}
330
331NTSTATUS
332NTAPI
333CmBattGetUniqueId(IN PDEVICE_OBJECT DeviceObject,
334 OUT PULONG UniqueId)
335{
336 NTSTATUS Status;
337 ACPI_EVAL_OUTPUT_BUFFER OutputBuffer;
338 ACPI_EVAL_INPUT_BUFFER InputBuffer;
339 PAGED_CODE();
340 if (CmBattDebug & 0x40)
341 DbgPrint("CmBattGetUniqueId: Entered with Pdo %x Tid %x\n",
342 DeviceObject, KeGetCurrentThread());
343
344 /* Initialize to zero */
345 ASSERT(UniqueId != NULL);
346 *UniqueId = 0;
347
348 /* Request the _UID method */
349 *(PULONG)InputBuffer.MethodName = 'DIU_';
350 InputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE;
351
352 /* Send it to ACPI */
353 Status = CmBattSendDownStreamIrp(DeviceObject,
354 IOCTL_ACPI_EVAL_METHOD,
355 &InputBuffer,
356 sizeof(InputBuffer),
357 &OutputBuffer,
358 sizeof(OutputBuffer));
359 if (NT_SUCCESS(Status))
360 {
361 /* Read the result */
362 Status = GetDwordElement(OutputBuffer.Argument, UniqueId);
363 if (CmBattDebug & 0x440)
364 DbgPrint("CmBattGetUniqueId: _UID method returned %x \n", *UniqueId);
365 }
366 else if (CmBattDebug & 0x44C)
367 {
368 /* Failure */
369 DbgPrint("CmBattGetUniqueId: Failed _UID method - Status (0x%x)\n", Status);
370 Status = STATUS_NO_SUCH_DEVICE;
371 }
372
373 /* Return status */
374 return Status;
375}
376
377NTSTATUS
378NTAPI
379CmBattSetTripPoint(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
380 IN ULONG AlarmValue)
381{
382 NTSTATUS Status;
383 ACPI_EVAL_INPUT_BUFFER_SIMPLE_INTEGER InputBuffer;
384 PAGED_CODE();
385 if (CmBattDebug & 0x440)
386 DbgPrint("CmBattSetTripPoint: _BTP Alarm Value %x Device %x Tid %x\n",
387 AlarmValue, DeviceExtension->DeviceId, KeGetCurrentThread());
388
389 /* Request the _BTP method */
390 *(PULONG)InputBuffer.MethodName = 'PTB_';
391 InputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIMPLE_INTEGER_SIGNATURE;
392 InputBuffer.IntegerArgument = AlarmValue;
393
394 /* Send it to ACPI */
395 Status = CmBattSendDownStreamIrp(DeviceExtension->AttachedDevice,
396 IOCTL_ACPI_EVAL_METHOD,
397 &InputBuffer,
398 sizeof(InputBuffer),
399 NULL,
400 0);
401 if (!(NT_SUCCESS(Status)) && (CmBattDebug & 0x440))
402 DbgPrint("CmBattSetTripPoint: Failed _BTP method on device %x - Status (0x%x)\n",
403 DeviceExtension->DeviceId, Status);
404
405 /* Return status */
406 return Status;
407}
408
409NTSTATUS
410NTAPI
411CmBattGetBifData(PCMBATT_DEVICE_EXTENSION DeviceExtension,
412 PACPI_BIF_DATA BifData)
413{
414 ACPI_PACKAGE_FIELD BifFields[] = {
415 { "PowerUnit", FALSE, &BifData->PowerUnit },
416 { "DesignCapacity", FALSE, &BifData->DesignCapacity },
417 { "LastFullCapacity", FALSE, &BifData->LastFullCapacity },
418 { "BatteryTechnology", FALSE, &BifData->BatteryTechnology },
419 { "DesignVoltage", FALSE, &BifData->DesignVoltage },
420 { "DesignCapacityWarning", FALSE, &BifData->DesignCapacityWarning },
421 { "DesignCapacityLow", FALSE, &BifData->DesignCapacityLow },
422 { "BatteryCapacityGranularity1", FALSE, &BifData->BatteryCapacityGranularity1 },
423 { "BatteryCapacityGranularity2", FALSE, &BifData->BatteryCapacityGranularity2 },
424 { "ModelNumber", TRUE, &BifData->ModelNumber },
425 { "SerialNumber", TRUE, &BifData->SerialNumber },
426 { "BatteryType", TRUE, &BifData->BatteryType },
427 { "OemInfo", TRUE, &BifData->OemInfo },
428 };
429 PAGED_CODE();
430
431 if (CmBattDebug & CMBATT_ACPI_ENTRY_EXIT)
432 DbgPrint("CmBattGetBifData: Buffer (0x%x) Device %x Tid %x\n",
433 BifData, DeviceExtension->DeviceId, KeGetCurrentThread());
434
435 /* Request the _BIF method */
436 /* Note that _BIF method is deprecated since ACPI 4.0, and replaced by _BIX method.
437 * However, VirtualBox 7.0 only support _BIF method.
438 */
439 return CmBattCallAcpiPackage("CmBattGetBifData",
440 DeviceExtension,
441 'FIB_',
442 512,
443 BifFields,
444 RTL_NUMBER_OF(BifFields));
445}
446
447/**
448 * @brief
449 * Retrieves the eXtended static battery information from the
450 * ACPI _BIX method.
451 *
452 * @param[in] DeviceExtension
453 * A pointer to a Control Method (CM) battery device extension.
454 * It is used to send the ACPI method evaluation operation
455 * to the ACPI driver of which it is attached to this CM battery.
456 *
457 * @param[out] BixData
458 * A pointer to a structure that contains the _BIX data fields,
459 * returned to caller.
460 *
461 * @return
462 * Returns STATUS_SUCCESS if the operation has succeeded successfully,
463 * otherwise a failure NTSTATUS code is returned.
464 */
465NTSTATUS
466NTAPI
467CmBattGetBixData(
468 _In_ PCMBATT_DEVICE_EXTENSION DeviceExtension,
469 _Out_ PACPI_BIX_DATA BixData)
470{
471 ACPI_PACKAGE_FIELD BixFields[] = {
472 { "Revision", FALSE, &BixData->Revision },
473 { "PowerUnit", FALSE, &BixData->PowerUnit },
474 { "DesignCapacity", FALSE, &BixData->DesignCapacity },
475 { "LastFullCapacity", FALSE, &BixData->LastFullCapacity },
476 { "BatteryTechnology", FALSE, &BixData->BatteryTechnology },
477 { "DesignVoltage", FALSE, &BixData->DesignVoltage },
478 { "DesignCapacityWarning", FALSE, &BixData->DesignCapacityWarning },
479 { "DesignCapacityLow", FALSE, &BixData->DesignCapacityLow },
480 { "CycleCount", FALSE, &BixData->CycleCount },
481 { "Accuracy", FALSE, &BixData->Accuracy },
482 { "MaxSampleTime", FALSE, &BixData->MaxSampleTime },
483 { "MinSampleTime", FALSE, &BixData->MinSampleTime },
484 { "MaxAverageInterval", FALSE, &BixData->MaxAverageInterval },
485 { "MinAverageInterval", FALSE, &BixData->MinAverageInterval },
486 { "BatteryCapacityGranularity1", FALSE, &BixData->BatteryCapacityGranularity1 },
487 { "BatteryCapacityGranularity2", FALSE, &BixData->BatteryCapacityGranularity2 },
488 { "ModelNumber", TRUE, &BixData->ModelNumber },
489 { "SerialNumber", TRUE, &BixData->SerialNumber },
490 { "BatteryType", TRUE, &BixData->BatteryType },
491 { "OemInfo", TRUE, &BixData->OemInfo },
492 { "SwapCapability", FALSE, &BixData->SwapCapability },
493 };
494 PAGED_CODE();
495
496 if (CmBattDebug & CMBATT_ACPI_ENTRY_EXIT)
497 {
498 DbgPrint("CmBattGetBixData: Buffer (0x%x) Device %x Tid %x\n",
499 BixData, DeviceExtension->DeviceId, KeGetCurrentThread());
500 }
501
502 /* Request the ACPI driver to get the _BIX data for us */
503 return CmBattCallAcpiPackage("CmBattGetBifData",
504 DeviceExtension,
505 'XIB_',
506 512,
507 BixFields,
508 RTL_NUMBER_OF(BixFields));
509}
510
511NTSTATUS
512NTAPI
513CmBattGetBstData(PCMBATT_DEVICE_EXTENSION DeviceExtension,
514 PACPI_BST_DATA BstData)
515{
516 ACPI_PACKAGE_FIELD BstFields[] = {
517 { "State", FALSE, &BstData->State },
518 { "PresentRate", FALSE, &BstData->PresentRate },
519 { "RemainingCapacity", FALSE, &BstData->RemainingCapacity },
520 { "PresentVoltage", FALSE, &BstData->PresentVoltage },
521 };
522 PAGED_CODE();
523
524 if (CmBattDebug & CMBATT_ACPI_ENTRY_EXIT)
525 DbgPrint("CmBattGetBstData: Buffer (0x%x) Device %x Tid %x\n",
526 BstData, DeviceExtension->DeviceId, KeGetCurrentThread());
527
528
529 return CmBattCallAcpiPackage("CmBattGetBstData",
530 DeviceExtension,
531 'TSB_',
532 512,
533 BstFields,
534 RTL_NUMBER_OF(BstFields));
535}
536
537/* EOF */