reactos/drivers/battery/battc/battc.c
Hervé Poussineau 6b5ee5ccc7 [BATTC] Make input buffer on IOCTL_BATTERY_QUERY_TAG optional
In that case, don't wait to get battery tag.
2024-02-01 10:08:51 +01:00

385 lines
13 KiB
C

/*
* PROJECT: ReactOS Kernel
* LICENSE: GPL - See COPYING in the top level directory
* FILE: drivers/battery/battc/battc.c
* PURPOSE: Battery Class Driver
* PROGRAMMERS: Cameron Gutman (cameron.gutman@reactos.org)
*/
#include <battc.h>
#define NDEBUG
#include <debug.h>
NTSTATUS
NTAPI
DriverEntry(PDRIVER_OBJECT DriverObject,
PUNICODE_STRING RegistryPath)
{
DPRINT("Battery class driver initialized\n");
return STATUS_SUCCESS;
}
BCLASSAPI
NTSTATUS
NTAPI
BatteryClassUnload(PVOID ClassData)
{
PBATTERY_CLASS_DATA BattClass;
DPRINT("Battery %p is being unloaded\n", ClassData);
BattClass = ClassData;
if (BattClass->InterfaceName.Length != 0)
{
IoSetDeviceInterfaceState(&BattClass->InterfaceName, FALSE);
RtlFreeUnicodeString(&BattClass->InterfaceName);
}
ExFreePoolWithTag(BattClass,
BATTERY_CLASS_DATA_TAG);
return STATUS_SUCCESS;
}
BCLASSAPI
NTSTATUS
NTAPI
BatteryClassSystemControl(PVOID ClassData,
PVOID WmiLibContext,
PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PVOID Disposition)
{
UNIMPLEMENTED;
return STATUS_WMI_GUID_NOT_FOUND;
}
BCLASSAPI
NTSTATUS
NTAPI
BatteryClassQueryWmiDataBlock(PVOID ClassData,
PDEVICE_OBJECT DeviceObject,
PIRP Irp,
ULONG GuidIndex,
PULONG InstanceLengthArray,
ULONG OutBufferSize,
PUCHAR Buffer)
{
UNIMPLEMENTED;
return STATUS_WMI_GUID_NOT_FOUND;
}
BCLASSAPI
NTSTATUS
NTAPI
BatteryClassStatusNotify(PVOID ClassData)
{
PBATTERY_CLASS_DATA BattClass;
PBATTERY_WAIT_STATUS BattWait;
BATTERY_STATUS BattStatus;
NTSTATUS Status;
DPRINT("Received battery status notification from %p\n", ClassData);
BattClass = ClassData;
BattWait = BattClass->EventTriggerContext;
ExAcquireFastMutex(&BattClass->Mutex);
if (!BattClass->Waiting)
{
ExReleaseFastMutex(&BattClass->Mutex);
return STATUS_SUCCESS;
}
switch (BattClass->EventTrigger)
{
case EVENT_BATTERY_TAG:
ExReleaseFastMutex(&BattClass->Mutex);
DPRINT1("Waiting for battery is UNIMPLEMENTED!\n");
break;
case EVENT_BATTERY_STATUS:
ExReleaseFastMutex(&BattClass->Mutex);
Status = BattClass->MiniportInfo.QueryStatus(BattClass->MiniportInfo.Context,
BattWait->BatteryTag,
&BattStatus);
if (!NT_SUCCESS(Status))
return Status;
ExAcquireFastMutex(&BattClass->Mutex);
if (!(BattWait->PowerState & BattStatus.PowerState) ||
(BattWait->HighCapacity > BattStatus.Capacity) ||
(BattWait->LowCapacity < BattStatus.Capacity))
{
KeSetEvent(&BattClass->WaitEvent, IO_NO_INCREMENT, FALSE);
}
ExReleaseFastMutex(&BattClass->Mutex);
break;
default:
ExReleaseFastMutex(&BattClass->Mutex);
ASSERT(FALSE);
break;
}
return STATUS_SUCCESS;
}
BCLASSAPI
NTSTATUS
NTAPI
BatteryClassInitializeDevice(PBATTERY_MINIPORT_INFO MiniportInfo,
PVOID *ClassData)
{
NTSTATUS Status;
PBATTERY_CLASS_DATA BattClass;
BattClass = ExAllocatePoolWithTag(NonPagedPool,
sizeof(BATTERY_CLASS_DATA),
BATTERY_CLASS_DATA_TAG);
if (BattClass == NULL)
return STATUS_INSUFFICIENT_RESOURCES;
RtlZeroMemory(BattClass, sizeof(BATTERY_CLASS_DATA));
RtlCopyMemory(&BattClass->MiniportInfo,
MiniportInfo,
sizeof(BattClass->MiniportInfo));
KeInitializeEvent(&BattClass->WaitEvent, SynchronizationEvent, FALSE);
ExInitializeFastMutex(&BattClass->Mutex);
if (MiniportInfo->Pdo != NULL)
{
Status = IoRegisterDeviceInterface(MiniportInfo->Pdo,
&GUID_DEVICE_BATTERY,
NULL,
&BattClass->InterfaceName);
if (NT_SUCCESS(Status))
{
DPRINT("Initialized battery interface: %wZ\n", &BattClass->InterfaceName);
Status = IoSetDeviceInterfaceState(&BattClass->InterfaceName, TRUE);
if (Status == STATUS_OBJECT_NAME_EXISTS)
{
DPRINT1("Got STATUS_OBJECT_NAME_EXISTS for SetDeviceInterfaceState\n");
Status = STATUS_SUCCESS;
}
}
else
{
DPRINT1("IoRegisterDeviceInterface failed (0x%x)\n", Status);
}
}
*ClassData = BattClass;
return STATUS_SUCCESS;
}
BCLASSAPI
NTSTATUS
NTAPI
BatteryClassIoctl(PVOID ClassData,
PIRP Irp)
{
PBATTERY_CLASS_DATA BattClass;
PIO_STACK_LOCATION IrpSp;
NTSTATUS Status;
ULONG WaitTime;
PBATTERY_WAIT_STATUS BattWait;
PBATTERY_QUERY_INFORMATION BattQueryInfo;
PBATTERY_SET_INFORMATION BattSetInfo;
LARGE_INTEGER Timeout;
PBATTERY_STATUS BattStatus;
BATTERY_NOTIFY BattNotify;
ULONG ReturnedLength;
DPRINT("BatteryClassIoctl(%p %p)\n", ClassData, Irp);
BattClass = ClassData;
IrpSp = IoGetCurrentIrpStackLocation(Irp);
Irp->IoStatus.Information = 0;
DPRINT("Received IOCTL %x for %p\n", IrpSp->Parameters.DeviceIoControl.IoControlCode,
ClassData);
switch (IrpSp->Parameters.DeviceIoControl.IoControlCode)
{
case IOCTL_BATTERY_QUERY_TAG:
if ((IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof(ULONG) && IrpSp->Parameters.DeviceIoControl.InputBufferLength != 0) ||
IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG))
{
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
WaitTime = IrpSp->Parameters.DeviceIoControl.InputBufferLength == sizeof(ULONG) ? *(PULONG)Irp->AssociatedIrp.SystemBuffer : 0;
Timeout.QuadPart = Int32x32To64(WaitTime, -1000);
Status = BattClass->MiniportInfo.QueryTag(BattClass->MiniportInfo.Context,
(PULONG)Irp->AssociatedIrp.SystemBuffer);
if (!NT_SUCCESS(Status))
{
ExAcquireFastMutex(&BattClass->Mutex);
BattClass->EventTrigger = EVENT_BATTERY_TAG;
BattClass->Waiting = TRUE;
ExReleaseFastMutex(&BattClass->Mutex);
Status = KeWaitForSingleObject(&BattClass->WaitEvent,
Executive,
KernelMode,
FALSE,
WaitTime != -1 ? &Timeout : NULL);
ExAcquireFastMutex(&BattClass->Mutex);
BattClass->Waiting = FALSE;
ExReleaseFastMutex(&BattClass->Mutex);
if (Status == STATUS_SUCCESS)
{
Status = BattClass->MiniportInfo.QueryTag(BattClass->MiniportInfo.Context,
(PULONG)Irp->AssociatedIrp.SystemBuffer);
if (NT_SUCCESS(Status))
Irp->IoStatus.Information = sizeof(ULONG);
}
else
{
Status = STATUS_NO_SUCH_DEVICE;
}
}
else
{
Irp->IoStatus.Information = sizeof(ULONG);
}
break;
case IOCTL_BATTERY_QUERY_STATUS:
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(*BattWait) ||
IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(BATTERY_STATUS))
{
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
BattWait = Irp->AssociatedIrp.SystemBuffer;
Timeout.QuadPart = Int32x32To64(BattWait->Timeout, -1000);
Status = BattClass->MiniportInfo.QueryStatus(BattClass->MiniportInfo.Context,
BattWait->BatteryTag,
(PBATTERY_STATUS)Irp->AssociatedIrp.SystemBuffer);
BattStatus = Irp->AssociatedIrp.SystemBuffer;
if (!NT_SUCCESS(Status) ||
((BattWait->PowerState & BattStatus->PowerState) &&
(BattWait->HighCapacity <= BattStatus->Capacity) &&
(BattWait->LowCapacity >= BattStatus->Capacity)))
{
BattNotify.PowerState = BattWait->PowerState;
BattNotify.HighCapacity = BattWait->HighCapacity;
BattNotify.LowCapacity = BattWait->LowCapacity;
BattClass->MiniportInfo.SetStatusNotify(BattClass->MiniportInfo.Context,
BattWait->BatteryTag,
&BattNotify);
ExAcquireFastMutex(&BattClass->Mutex);
BattClass->EventTrigger = EVENT_BATTERY_STATUS;
BattClass->EventTriggerContext = BattWait;
BattClass->Waiting = TRUE;
ExReleaseFastMutex(&BattClass->Mutex);
Status = KeWaitForSingleObject(&BattClass->WaitEvent,
Executive,
KernelMode,
FALSE,
BattWait->Timeout != -1 ? &Timeout : NULL);
ExAcquireFastMutex(&BattClass->Mutex);
BattClass->Waiting = FALSE;
ExReleaseFastMutex(&BattClass->Mutex);
BattClass->MiniportInfo.DisableStatusNotify(BattClass->MiniportInfo.Context);
if (Status == STATUS_SUCCESS)
{
Status = BattClass->MiniportInfo.QueryStatus(BattClass->MiniportInfo.Context,
BattWait->BatteryTag,
(PBATTERY_STATUS)Irp->AssociatedIrp.SystemBuffer);
if (NT_SUCCESS(Status))
Irp->IoStatus.Information = sizeof(ULONG);
}
else
{
Status = STATUS_NO_SUCH_DEVICE;
}
}
else
{
Irp->IoStatus.Information = sizeof(BATTERY_STATUS);
}
break;
case IOCTL_BATTERY_QUERY_INFORMATION:
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(*BattQueryInfo))
{
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
BattQueryInfo = Irp->AssociatedIrp.SystemBuffer;
Status = BattClass->MiniportInfo.QueryInformation(BattClass->MiniportInfo.Context,
BattQueryInfo->BatteryTag,
BattQueryInfo->InformationLevel,
BattQueryInfo->AtRate,
Irp->AssociatedIrp.SystemBuffer,
IrpSp->Parameters.DeviceIoControl.OutputBufferLength,
&ReturnedLength);
Irp->IoStatus.Information = ReturnedLength;
if (!NT_SUCCESS(Status))
{
DPRINT1("QueryInformation failed (0x%x)\n", Status);
}
break;
case IOCTL_BATTERY_SET_INFORMATION:
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(*BattSetInfo))
{
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
BattSetInfo = Irp->AssociatedIrp.SystemBuffer;
Status = BattClass->MiniportInfo.SetInformation(BattClass->MiniportInfo.Context,
BattSetInfo->BatteryTag,
BattSetInfo->InformationLevel,
BattSetInfo->Buffer);
if (!NT_SUCCESS(Status))
{
DPRINT1("SetInformation failed (0x%x)\n", Status);
}
break;
default:
DPRINT1("Received unsupported IRP %x\n", IrpSp->Parameters.DeviceIoControl.IoControlCode);
/* Do NOT complete the irp */
return STATUS_NOT_SUPPORTED;
}
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}