reactos/drivers/bus/acpi/cmbatt/cmexec.c
2024-08-12 11:56:18 +02:00

474 lines
16 KiB
C

/*
* PROJECT: ReactOS ACPI-Compliant Control Method Battery
* LICENSE: BSD - See COPYING.ARM in the top level directory
* FILE: boot/drivers/bus/acpi/cmbatt/cmexec.c
* PURPOSE: ACPI Method Execution/Evaluation Glue
* PROGRAMMERS: ReactOS Portable Systems Group
*/
/* INCLUDES *******************************************************************/
#include "cmbatt.h"
#include <acpiioct.h>
#include <debug.h>
typedef struct
{
LPSTR Name;
BOOLEAN IsString;
PVOID Data;
} ACPI_PACKAGE_FIELD, *PACPI_PACKAGE_FIELD;
/* FUNCTIONS ******************************************************************/
NTSTATUS
NTAPI
GetDwordElement(IN PACPI_METHOD_ARGUMENT Argument,
OUT PULONG Value)
{
NTSTATUS Status;
/* Must have an integer */
if (Argument->Type != ACPI_METHOD_ARGUMENT_INTEGER)
{
/* Not an integer, fail */
Status = STATUS_ACPI_INVALID_DATA;
if (CmBattDebug & 0x4C)
DbgPrint("GetDwordElement: Object contained wrong data type - %d\n",
Argument->Type);
}
else
{
/* Read the integer value */
*Value = Argument->Argument;
Status = STATUS_SUCCESS;
}
/* Return status */
return Status;
}
NTSTATUS
NTAPI
GetStringElement(IN PACPI_METHOD_ARGUMENT Argument,
OUT PCHAR Value)
{
NTSTATUS Status;
/* Must have a string of buffer */
if ((Argument->Type == ACPI_METHOD_ARGUMENT_STRING) ||
(Argument->Type == ACPI_METHOD_ARGUMENT_BUFFER))
{
/* String must be less than 256 characters */
if (Argument->DataLength < 256)
{
/* Copy the buffer */
RtlCopyMemory(Value, Argument->Data, Argument->DataLength);
Status = STATUS_SUCCESS;
}
else
{
/* The buffer is too small (the string is too large) */
Status = STATUS_BUFFER_TOO_SMALL;
if (CmBattDebug & 0x4C)
DbgPrint("GetStringElement: return buffer not big enough - %d\n", Argument->DataLength);
}
}
else
{
/* Not valid string data */
Status = STATUS_ACPI_INVALID_DATA;
if (CmBattDebug & 0x4C)
DbgPrint("GetStringElement: Object contained wrong data type - %d\n", Argument->Type);
}
/* Return the status */
return Status;
}
NTSTATUS
NTAPI
CmBattSendDownStreamIrp(IN PDEVICE_OBJECT DeviceObject,
IN ULONG IoControlCode,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
IN PACPI_EVAL_OUTPUT_BUFFER OutputBuffer,
IN ULONG OutputBufferLength)
{
PIRP Irp;
NTSTATUS Status;
KEVENT Event;
IO_STATUS_BLOCK IoStatusBlock;
PAGED_CODE();
/* Initialize our wait event */
KeInitializeEvent(&Event, SynchronizationEvent, 0);
/* Allocate the IRP */
Irp = IoBuildDeviceIoControlRequest(IoControlCode,
DeviceObject,
InputBuffer,
InputBufferLength,
OutputBuffer,
OutputBufferLength,
0,
&Event,
&IoStatusBlock);
if (!Irp)
{
/* No IRP, fail */
if (CmBattDebug & 0x4C)
DbgPrint("CmBattSendDownStreamIrp: Failed to allocate Irp\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Call ACPI */
if (CmBattDebug & 0x40)
DbgPrint("CmBattSendDownStreamIrp: Irp %x [Tid] %x\n",
Irp, KeGetCurrentThread());
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
{
/* Wait for completion */
KeWaitForSingleObject(&Event,
Executive,
KernelMode,
FALSE,
NULL);
Status = Irp->IoStatus.Status;
}
/* Check if caller wanted output */
if (OutputBuffer)
{
/* Make sure it's valid ACPI output buffer */
if ((OutputBuffer->Signature != ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE) ||
!(OutputBuffer->Count))
{
/* It isn't, so set failure code */
Status = STATUS_ACPI_INVALID_DATA;
}
}
/* Return status */
if (CmBattDebug & 0x40)
DbgPrint("CmBattSendDownStreamIrp: Irp %x completed %x! [Tid] %x\n",
Irp, Status, KeGetCurrentThread());
return Status;
}
static
NTSTATUS
CmBattCallAcpiPackage(
_In_ LPCSTR FunctionName,
_In_ PCMBATT_DEVICE_EXTENSION DeviceExtension,
_In_ ULONG PackageName,
_In_ ULONG OutputBufferSize,
_In_ PACPI_PACKAGE_FIELD PackageFields,
_In_ ULONG PackageFieldCount)
{
NTSTATUS Status;
PACPI_EVAL_OUTPUT_BUFFER OutputBuffer;
ACPI_EVAL_INPUT_BUFFER InputBuffer;
PACPI_METHOD_ARGUMENT Argument;
ULONG i;
PAGED_CODE();
OutputBuffer = ExAllocatePoolWithTag(PagedPool,
OutputBufferSize,
'MtaB');
if (!OutputBuffer)
{
if (CmBattDebug & (CMBATT_ACPI_ENTRY_EXIT | CMBATT_ACPI_WARNING | CMBATT_GENERIC_WARNING))
DbgPrint("%s: Failed to allocate Buffer\n", FunctionName);
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Initialize to zero */
RtlZeroMemory(OutputBuffer, OutputBufferSize);
/* Request the ACPI method */
*(PULONG)InputBuffer.MethodName = PackageName;
InputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE;
/* Send it to ACPI */
Status = CmBattSendDownStreamIrp(DeviceExtension->AttachedDevice,
IOCTL_ACPI_EVAL_METHOD,
&InputBuffer,
sizeof(InputBuffer),
OutputBuffer,
OutputBufferSize);
if (!NT_SUCCESS(Status))
{
if (CmBattDebug & (CMBATT_ACPI_ASSERT | CMBATT_ACPI_ENTRY_EXIT | CMBATT_ACPI_WARNING | CMBATT_GENERIC_WARNING))
DbgPrint("%s: Failed 0x%08x method on device %x - Status (0x%x)\n",
FunctionName, PackageName, DeviceExtension->DeviceId, Status);
ExFreePoolWithTag(OutputBuffer, 'MtaB');
return Status;
}
/* Check if we got the right number of elements */
if (OutputBuffer->Count != PackageFieldCount)
{
if (CmBattDebug & (CMBATT_ACPI_ASSERT | CMBATT_ACPI_ENTRY_EXIT | CMBATT_ACPI_WARNING | CMBATT_GENERIC_WARNING))
DbgPrint("%s: 0x%08x method returned %d elements (requires %d)\n",
FunctionName, PackageName, OutputBuffer->Count, PackageFieldCount);
ExFreePoolWithTag(OutputBuffer, 'MtaB');
return STATUS_ACPI_INVALID_DATA;
}
Argument = OutputBuffer->Argument;
for (i = 0; i < PackageFieldCount && NT_SUCCESS(Status); i++)
{
if (PackageFields[i].IsString)
Status = GetStringElement(Argument, PackageFields[i].Data);
else
Status = GetDwordElement(Argument, PackageFields[i].Data);
if (!NT_SUCCESS(Status))
{
if (CmBattDebug & (CMBATT_ACPI_ASSERT | CMBATT_ACPI_ENTRY_EXIT | CMBATT_ACPI_WARNING | CMBATT_GENERIC_WARNING))
DbgPrint("%s: Failed to get %s\n", FunctionName, PackageFields[i].Name);
break;
}
Argument = ACPI_METHOD_NEXT_ARGUMENT(Argument);
}
ExFreePoolWithTag(OutputBuffer, 'MtaB');
return Status;
}
NTSTATUS
NTAPI
CmBattGetPsrData(IN PDEVICE_OBJECT DeviceObject,
OUT PULONG PsrData)
{
NTSTATUS Status;
ACPI_EVAL_OUTPUT_BUFFER OutputBuffer;
ACPI_EVAL_INPUT_BUFFER InputBuffer;
PAGED_CODE();
if (CmBattDebug & 0x40)
DbgPrint("CmBattGetPsrData: Entered with Pdo %x Tid %x\n",
DeviceObject, KeGetCurrentThread());
/* Initialize to zero */
ASSERT(PsrData != NULL);
*PsrData = 0;
/* Request the _PSR method */
*(PULONG)InputBuffer.MethodName = 'RSP_';
InputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE;
/* Send it to ACPI */
Status = CmBattSendDownStreamIrp(DeviceObject,
IOCTL_ACPI_EVAL_METHOD,
&InputBuffer,
sizeof(InputBuffer),
&OutputBuffer,
sizeof(OutputBuffer));
if (NT_SUCCESS(Status))
{
/* Read the result */
Status = GetDwordElement(OutputBuffer.Argument, PsrData);
if (CmBattDebug & 0x440)
DbgPrint("CmBattGetPsrData: _PSR method returned %x \n", *PsrData);
}
else if (CmBattDebug & 0x44C)
{
/* Failure */
DbgPrint("CmBattGetPsrData: Failed _PSR method - Status (0x%x)\n", Status);
}
/* Return status */
return Status;
}
NTSTATUS
NTAPI
CmBattGetStaData(IN PDEVICE_OBJECT DeviceObject,
OUT PULONG StaData)
{
NTSTATUS Status;
ACPI_EVAL_OUTPUT_BUFFER OutputBuffer;
ACPI_EVAL_INPUT_BUFFER InputBuffer;
PAGED_CODE();
if (CmBattDebug & 0x40)
DbgPrint("CmBattGetStaData: Entered with Pdo %x Tid %x\n",
DeviceObject, KeGetCurrentThread());
/* Initialize to zero */
ASSERT(StaData != NULL);
*StaData = 0;
/* Request the _PSR method */
*(PULONG)InputBuffer.MethodName = 'ATS_';
InputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE;
/* Send it to ACPI */
Status = CmBattSendDownStreamIrp(DeviceObject,
IOCTL_ACPI_EVAL_METHOD,
&InputBuffer,
sizeof(InputBuffer),
&OutputBuffer,
sizeof(OutputBuffer));
if (NT_SUCCESS(Status))
{
/* Read the result */
Status = GetDwordElement(OutputBuffer.Argument, StaData);
if (CmBattDebug & 0x440)
DbgPrint("CmBattGetStaData: _STA method returned %x \n", *StaData);
}
else if (CmBattDebug & 0x44C)
{
/* Failure */
DbgPrint("CmBattGetStaData: Failed _STA method - Status (0x%x)\n", Status);
Status = STATUS_NO_SUCH_DEVICE;
}
/* Return status */
return Status;
}
NTSTATUS
NTAPI
CmBattGetUniqueId(IN PDEVICE_OBJECT DeviceObject,
OUT PULONG UniqueId)
{
NTSTATUS Status;
ACPI_EVAL_OUTPUT_BUFFER OutputBuffer;
ACPI_EVAL_INPUT_BUFFER InputBuffer;
PAGED_CODE();
if (CmBattDebug & 0x40)
DbgPrint("CmBattGetUniqueId: Entered with Pdo %x Tid %x\n",
DeviceObject, KeGetCurrentThread());
/* Initialize to zero */
ASSERT(UniqueId != NULL);
*UniqueId = 0;
/* Request the _PSR method */
*(PULONG)InputBuffer.MethodName = 'DIU_';
InputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE;
/* Send it to ACPI */
Status = CmBattSendDownStreamIrp(DeviceObject,
IOCTL_ACPI_EVAL_METHOD,
&InputBuffer,
sizeof(InputBuffer),
&OutputBuffer,
sizeof(OutputBuffer));
if (NT_SUCCESS(Status))
{
/* Read the result */
Status = GetDwordElement(OutputBuffer.Argument, UniqueId);
if (CmBattDebug & 0x440)
DbgPrint("CmBattGetUniqueId: _UID method returned %x \n", *UniqueId);
}
else if (CmBattDebug & 0x44C)
{
/* Failure */
DbgPrint("CmBattGetUniqueId: Failed _UID method - Status (0x%x)\n", Status);
Status = STATUS_NO_SUCH_DEVICE;
}
/* Return status */
return Status;
}
NTSTATUS
NTAPI
CmBattSetTripPpoint(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
IN ULONG AlarmValue)
{
NTSTATUS Status;
ACPI_EVAL_INPUT_BUFFER_SIMPLE_INTEGER InputBuffer;
PAGED_CODE();
if (CmBattDebug & 0x440)
DbgPrint("CmBattSetTripPpoint: _BTP Alarm Value %x Device %x Tid %x\n",
AlarmValue, DeviceExtension->DeviceId, KeGetCurrentThread);
/* Request the _BTP method */
*(PULONG)InputBuffer.MethodName = 'PTB_';
InputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIMPLE_INTEGER_SIGNATURE;
InputBuffer.IntegerArgument = AlarmValue;
/* Send it to ACPI */
Status = CmBattSendDownStreamIrp(DeviceExtension->AttachedDevice,
IOCTL_ACPI_EVAL_METHOD,
&InputBuffer,
sizeof(InputBuffer),
NULL,
0);
if (!(NT_SUCCESS(Status)) && (CmBattDebug & 0x440))
DbgPrint("CmBattSetTripPpoint: Failed _BTP method on device %x - Status (0x%x)\n",
DeviceExtension->DeviceId, Status);
/* Return status */
return Status;
}
NTSTATUS
NTAPI
CmBattGetBifData(PCMBATT_DEVICE_EXTENSION DeviceExtension,
PACPI_BIF_DATA BifData)
{
ACPI_PACKAGE_FIELD BifFields[] = {
{ "PowerUnit", FALSE, &BifData->PowerUnit },
{ "DesignCapacity", FALSE, &BifData->DesignCapacity },
{ "LastFullCapacity", FALSE, &BifData->LastFullCapacity },
{ "BatteryTechnology", FALSE, &BifData->BatteryTechnology },
{ "DesignVoltage", FALSE, &BifData->DesignVoltage },
{ "DesignCapacityWarning", FALSE, &BifData->DesignCapacityWarning },
{ "DesignCapacityLow", FALSE, &BifData->DesignCapacityLow },
{ "BatteryCapacityGranularity1", FALSE, &BifData->BatteryCapacityGranularity1 },
{ "BatteryCapacityGranularity2", FALSE, &BifData->BatteryCapacityGranularity2 },
{ "ModelNumber", TRUE, &BifData->ModelNumber },
{ "SerialNumber", TRUE, &BifData->SerialNumber },
{ "BatteryType", TRUE, &BifData->BatteryType },
{ "OemInfo", TRUE, &BifData->OemInfo },
};
PAGED_CODE();
if (CmBattDebug & CMBATT_ACPI_ENTRY_EXIT)
DbgPrint("CmBattGetBifData: Buffer (0x%x) Device %x Tid %x\n",
BifData, DeviceExtension->DeviceId, KeGetCurrentThread());
/* Request the _BIF method */
/* Note that _BIF method is deprecated since ACPI 4.0, and replaced by _BIX method.
* However, VirtualBox 7.0 only support _BIF method.
*/
return CmBattCallAcpiPackage("CmBattGetBifData",
DeviceExtension,
'FIB_',
512,
BifFields,
RTL_NUMBER_OF(BifFields));
}
NTSTATUS
NTAPI
CmBattGetBstData(PCMBATT_DEVICE_EXTENSION DeviceExtension,
PACPI_BST_DATA BstData)
{
ACPI_PACKAGE_FIELD BstFields[] = {
{ "State", FALSE, &BstData->State },
{ "PresentRate", FALSE, &BstData->PresentRate },
{ "RemainingCapacity", FALSE, &BstData->RemainingCapacity },
{ "PresentVoltage", FALSE, &BstData->PresentVoltage },
};
PAGED_CODE();
if (CmBattDebug & CMBATT_ACPI_ENTRY_EXIT)
DbgPrint("CmBattGetBstData: Buffer (0x%x) Device %x Tid %x\n",
BstData, DeviceExtension->DeviceId, KeGetCurrentThread());
return CmBattCallAcpiPackage("CmBattGetBstData",
DeviceExtension,
'TSB_',
512,
BstFields,
RTL_NUMBER_OF(BstFields));
}
/* EOF */