reactos/drivers/bus/acpi/compbatt/comppnp.c

496 lines
17 KiB
C

/*
* PROJECT: ReactOS Composite Battery Driver
* LICENSE: BSD - See COPYING.ARM in the top level directory
* FILE: boot/drivers/bus/acpi/compbatt/comppnp.c
* PURPOSE: Plug-and-Play IOCTL/IRP Handling
* PROGRAMMERS: ReactOS Portable Systems Group
*/
/* INCLUDES *******************************************************************/
#include "compbatt.h"
#include <wdmguid.h>
/* FUNCTIONS ******************************************************************/
NTSTATUS
NTAPI
CompBattPowerDispatch(IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
PCOMPBATT_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
if (CompBattDebug & 1) DbgPrint("CompBatt: PowerDispatch received power IRP.\n");
/* Start the next IRP */
PoStartNextPowerIrp(Irp);
/* Call the next driver in the stack */
IoSkipCurrentIrpStackLocation(Irp);
return PoCallDriver(DeviceExtension->AttachedDevice, Irp);
}
PCOMPBATT_BATTERY_DATA
NTAPI
RemoveBatteryFromList(IN PCUNICODE_STRING BatteryName,
IN PCOMPBATT_DEVICE_EXTENSION DeviceExtension)
{
PLIST_ENTRY ListHead, NextEntry;
PCOMPBATT_BATTERY_DATA BatteryData;
if (CompBattDebug & 1)
DbgPrint("CompBatt: ENTERING RemoveBatteryFromList\n");
/* Loop the battery list */
ExAcquireFastMutex(&DeviceExtension->Lock);
ListHead = &DeviceExtension->BatteryList;
NextEntry = ListHead->Flink;
while (NextEntry != ListHead)
{
/* Get the battery information and compare the name */
BatteryData = CONTAINING_RECORD(NextEntry, COMPBATT_BATTERY_DATA, BatteryLink);
if (!RtlCompareUnicodeString(BatteryName, &BatteryData->BatteryName, TRUE))
{
/* Flush pending deletes and any lock waiters */
IoAcquireRemoveLock(&BatteryData->RemoveLock, 0);
ExReleaseFastMutex(&DeviceExtension->Lock);
IoReleaseRemoveLockAndWait(&BatteryData->RemoveLock, 0);
/* Remove the entry from the list */
ExAcquireFastMutex(&DeviceExtension->Lock);
RemoveEntryList(&BatteryData->BatteryLink);
ExReleaseFastMutex(&DeviceExtension->Lock);
return BatteryData;
}
/* Next */
NextEntry = NextEntry->Flink;
}
/* Done */
ExReleaseFastMutex(&DeviceExtension->Lock);
if (CompBattDebug & 1) DbgPrint("CompBatt: EXITING RemoveBatteryFromList\n");
return NULL;
}
BOOLEAN
NTAPI
IsBatteryAlreadyOnList(IN PCUNICODE_STRING BatteryName,
IN PCOMPBATT_DEVICE_EXTENSION DeviceExtension)
{
PLIST_ENTRY ListHead, NextEntry;
PCOMPBATT_BATTERY_DATA BatteryData;
BOOLEAN Found = FALSE;
if (CompBattDebug & 1)
DbgPrint("CompBatt: ENTERING IsBatteryAlreadyOnList\n");
/* Loop the battery list */
ExAcquireFastMutex(&DeviceExtension->Lock);
ListHead = &DeviceExtension->BatteryList;
NextEntry = ListHead->Flink;
while (NextEntry != ListHead)
{
/* Get the battery information and compare the name */
BatteryData = CONTAINING_RECORD(NextEntry, COMPBATT_BATTERY_DATA, BatteryLink);
if (!RtlCompareUnicodeString(BatteryName, &BatteryData->BatteryName, TRUE))
{
/* Got it */
Found = TRUE;
break;
}
/* Next */
NextEntry = NextEntry->Flink;
}
/* Release the lock and return search status */
ExReleaseFastMutex(&DeviceExtension->Lock);
if (CompBattDebug & 1) DbgPrint("CompBatt: EXITING IsBatteryAlreadyOnList\n");
return Found;
}
NTSTATUS
NTAPI
CompBattAddNewBattery(IN PUNICODE_STRING BatteryName,
IN PCOMPBATT_DEVICE_EXTENSION DeviceExtension)
{
NTSTATUS Status = STATUS_SUCCESS;
PCOMPBATT_BATTERY_DATA BatteryData;
PIRP Irp;
PIO_STACK_LOCATION IoStackLocation;
PFILE_OBJECT FileObject;
PAGED_CODE();
if (CompBattDebug & 1)
DbgPrint("CompBatt: ENTERING AddNewBattery \"%w\" \n", BatteryName->Buffer);
/* Is this a new battery? */
if (!IsBatteryAlreadyOnList(BatteryName, DeviceExtension))
{
/* Allocate battery data */
BatteryData = ExAllocatePoolWithTag(NonPagedPool,
sizeof(COMPBATT_BATTERY_DATA) +
BatteryName->Length,
'CtaB');
if (BatteryData)
{
/* Initialize the data and write the battery name */
RtlZeroMemory(BatteryData, sizeof(COMPBATT_BATTERY_DATA));
BatteryData->Tag = 0;
BatteryData->BatteryName.MaximumLength = BatteryName->Length;
BatteryData->BatteryName.Buffer = (PWCHAR)(BatteryData + 1);
RtlCopyUnicodeString(&BatteryData->BatteryName, BatteryName);
/* Get the device object */
Status = CompBattGetDeviceObjectPointer(BatteryName,
FILE_ALL_ACCESS,
&FileObject,
&BatteryData->DeviceObject);
if (NT_SUCCESS(Status))
{
/* Reference the DO and drop the FO */
ObReferenceObject(BatteryData->DeviceObject);
ObDereferenceObject(FileObject);
/* Allocate the battery IRP */
Irp = IoAllocateIrp(BatteryData->DeviceObject->StackSize + 1, 0);
if (Irp)
{
/* Save it */
BatteryData->Irp = Irp;
/* Setup the stack location */
IoStackLocation = IoGetNextIrpStackLocation(Irp);
IoStackLocation->Parameters.Others.Argument1 = DeviceExtension;
IoStackLocation->Parameters.Others.Argument2 = BatteryData;
/* Set IRP data */
IoSetNextIrpStackLocation(Irp);
Irp->IoStatus.Status = STATUS_DEVICE_NOT_CONNECTED;
BatteryData->WaitFlag = 0;
/* Insert this battery in the list */
ExAcquireFastMutex(&DeviceExtension->Lock);
InsertTailList(&DeviceExtension->BatteryList,
&BatteryData->BatteryLink);
ExReleaseFastMutex(&DeviceExtension->Lock);
/* Initialize the work item and delete lock */
IoInitializeRemoveLock(&BatteryData->RemoveLock, 0, 0, 0);
ExInitializeWorkItem(&BatteryData->WorkItem,
(PVOID)CompBattMonitorIrpCompleteWorker,
BatteryData);
/* Setup the IRP work entry */
CompBattMonitorIrpComplete(BatteryData->DeviceObject, Irp, 0);
Status = STATUS_SUCCESS;
}
else
{
/* Fail, no memory */
if (CompBattDebug & 8)
DbgPrint("CompBatt: Couldn't allocate new battery Irp\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
ObDereferenceObject(BatteryData->DeviceObject);
}
}
else if (CompBattDebug & 8)
{
/* Fail */
DbgPrint("CompBattAddNewBattery: Failed to get device Object. status = %lx\n",
Status);
}
/* Free the battery data */
ExFreePool(BatteryData);
}
else
{
/* Fail, no memory */
if (CompBattDebug & 8)
DbgPrint("CompBatt: Couldn't allocate new battery node\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
/* We're done */
if (CompBattDebug & 1) DbgPrint("CompBatt: EXITING AddNewBattery\n");
return Status;
}
NTSTATUS
NTAPI
CompBattRemoveBattery(IN PCUNICODE_STRING BatteryName,
IN PCOMPBATT_DEVICE_EXTENSION DeviceExtension)
{
PCOMPBATT_BATTERY_DATA BatteryData;
if (CompBattDebug & 1) DbgPrint("CompBatt: RemoveBattery\n");
/* Remove the entry */
BatteryData = RemoveBatteryFromList(BatteryName, DeviceExtension);
if (BatteryData)
{
/* Dereference and free it */
ObDereferenceObject(BatteryData->DeviceObject);
ExFreePool(BatteryData);
/* Notify class driver */
DeviceExtension->Flags = 0;
BatteryClassStatusNotify(DeviceExtension->ClassData);
}
/* It's done */
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
CompBattGetBatteries(IN PCOMPBATT_DEVICE_EXTENSION DeviceExtension)
{
PWCHAR p;
NTSTATUS Status;
PWCHAR LinkList;
UNICODE_STRING LinkString;
if (CompBattDebug & 1) DbgPrint("CompBatt: ENTERING GetBatteries\n");
/* Get all battery links */
Status = IoGetDeviceInterfaces(&GUID_DEVICE_BATTERY, NULL, 0, &LinkList);
p = LinkList;
if (NT_SUCCESS(Status))
{
/* Loop all strings inside */
while (TRUE)
{
/* Create the string */
RtlInitUnicodeString(&LinkString, p);
if (!LinkString.Length) break;
/* Add this battery and move on */
Status = CompBattAddNewBattery(&LinkString, DeviceExtension);
p += (LinkString.Length / sizeof(WCHAR)) + sizeof(UNICODE_NULL);
}
/* Parsing complete, clean up buffer */
ExFreePool(LinkList);
}
else if (CompBattDebug & 8)
{
/* Fail */
DbgPrint("CompBatt: Couldn't get list of batteries\n");
}
/* Done */
if (CompBattDebug & 1) DbgPrint("CompBatt: EXITING GetBatteries\n");
return Status;
}
NTSTATUS
NTAPI
CompBattPnpEventHandler(IN PDEVICE_INTERFACE_CHANGE_NOTIFICATION Notification,
IN PCOMPBATT_DEVICE_EXTENSION DeviceExtension)
{
if (CompBattDebug & 1) DbgPrint("CompBatt: ENTERING PnpEventHandler\n");
if (CompBattDebug & 2) DbgPrint("CompBatt: Received device interface change notification\n");
/* Check what happened */
if (IsEqualGUIDAligned(&Notification->Event, &GUID_DEVICE_INTERFACE_ARRIVAL))
{
/* Add the new battery */
if (CompBattDebug & 2)
DbgPrint("CompBatt: Received notification of battery arrival\n");
CompBattAddNewBattery(Notification->SymbolicLinkName, DeviceExtension);
}
else if (IsEqualGUIDAligned(&Notification->Event, &GUID_DEVICE_INTERFACE_REMOVAL))
{
/* Don't do anything */
if (CompBattDebug & 2)
DbgPrint("CompBatt: Received notification of battery removal\n");
}
else
{
/* Shouldn't happen */
if (CompBattDebug & 2) DbgPrint("CompBatt: Received unhandled PnP event\n");
}
/* Done, return success */
if (CompBattDebug & 1) DbgPrint("CompBatt: EXITING PnpEventHandler\n");
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
CompBattAddDevice(IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PdoDeviceObject)
{
NTSTATUS Status;
UNICODE_STRING DeviceName;
PCOMPBATT_DEVICE_EXTENSION DeviceExtension;
PDEVICE_OBJECT DeviceObject;
UNICODE_STRING SymbolicLinkName;
BATTERY_MINIPORT_INFO MiniportInfo;
if (CompBattDebug & 2) DbgPrint("CompBatt: Got an AddDevice - %x\n", PdoDeviceObject);
/* Create the device */
RtlInitUnicodeString(&DeviceName, L"\\Device\\CompositeBattery");
Status = IoCreateDevice(DriverObject,
sizeof(COMPBATT_DEVICE_EXTENSION),
&DeviceName,
FILE_DEVICE_BATTERY,
FILE_DEVICE_SECURE_OPEN,
FALSE,
&DeviceObject);
if (!NT_SUCCESS(Status)) return Status;
/* Setup symbolic link for Win32 access */
RtlInitUnicodeString(&SymbolicLinkName, L"\\DosDevices\\CompositeBattery");
IoCreateSymbolicLink(&SymbolicLinkName, &DeviceName);
/* Initialize the device extension */
DeviceExtension = DeviceObject->DeviceExtension;
RtlZeroMemory(DeviceExtension, sizeof(COMPBATT_DEVICE_EXTENSION));
/* Attach to device stack and set DO pointers */
DeviceExtension->AttachedDevice = IoAttachDeviceToDeviceStack(DeviceObject,
PdoDeviceObject);
DeviceExtension->DeviceObject = DeviceObject;
if (!DeviceExtension->AttachedDevice)
{
/* Fail */
if (CompBattDebug & 8)
DbgPrint("CompBattAddDevice: Could not attach to LowerDevice.\n");
IoDeleteDevice(DeviceObject);
return STATUS_UNSUCCESSFUL;
}
/* Set device object flags */
DeviceObject->Flags |= (DO_POWER_PAGABLE | DO_BUFFERED_IO);
DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
/* Setup the device extension */
ExInitializeFastMutex(&DeviceExtension->Lock);
InitializeListHead(&DeviceExtension->BatteryList);
DeviceExtension->Flags = 0;
DeviceExtension->NextTag = 1;
/* Setup the miniport data */
RtlZeroMemory(&MiniportInfo, sizeof(MiniportInfo));
MiniportInfo.MajorVersion = BATTERY_CLASS_MAJOR_VERSION;
MiniportInfo.MinorVersion = BATTERY_CLASS_MINOR_VERSION;
MiniportInfo.Context = DeviceExtension;
MiniportInfo.DeviceName = &DeviceName;
MiniportInfo.QueryTag = (BCLASS_QUERY_TAG)CompBattQueryTag;
MiniportInfo.QueryInformation = (BCLASS_QUERY_INFORMATION)CompBattQueryInformation;
MiniportInfo.SetInformation = NULL;
MiniportInfo.QueryStatus = (BCLASS_QUERY_STATUS)CompBattQueryStatus;
MiniportInfo.SetStatusNotify = (BCLASS_SET_STATUS_NOTIFY)CompBattSetStatusNotify;
MiniportInfo.DisableStatusNotify = (BCLASS_DISABLE_STATUS_NOTIFY)CompBattDisableStatusNotify;
MiniportInfo.Pdo = NULL;
/* Register with the class driver */
Status = BatteryClassInitializeDevice(&MiniportInfo,
&DeviceExtension->ClassData);
if (!NT_SUCCESS(Status))
{
/* Undo everything */
IoDetachDevice(DeviceExtension->AttachedDevice);
IoDeleteDevice(DeviceObject);
}
/* Return status */
return Status;
}
NTSTATUS
NTAPI
CompBattPnpDispatch(IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
PIO_STACK_LOCATION IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
NTSTATUS Status;
PCOMPBATT_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
if (CompBattDebug & 1) DbgPrint("CompBatt: ENTERING PnpDispatch\n");
/* Set default error */
Status = STATUS_NOT_SUPPORTED;
/* Check what kind of PnP function this is */
switch (IoStackLocation->MinorFunction)
{
case IRP_MN_START_DEVICE:
/* Device is starting, register for new batteries and pick up current ones */
Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange,
0,
(PVOID)&GUID_DEVICE_BATTERY,
DeviceObject->DriverObject,
(PDRIVER_NOTIFICATION_CALLBACK_ROUTINE)CompBattPnpEventHandler,
DeviceExtension,
&DeviceExtension->NotificationEntry);
if (NT_SUCCESS(Status))
{
/* Now go get the batteries */
if (CompBattDebug & 2)
DbgPrint("CompBatt: Successfully registered for PnP notification\n");
Status = CompBattGetBatteries(DeviceExtension);
}
else
{
/* We failed */
if (CompBattDebug & 8)
DbgPrint("CompBatt: Couldn't register for PnP notification - %x\n",
Status);
}
break;
case IRP_MN_CANCEL_STOP_DEVICE:
/* Explicitly say ok */
Status = STATUS_SUCCESS;
break;
case IRP_MN_CANCEL_REMOVE_DEVICE:
/* Explicitly say ok */
Status = STATUS_SUCCESS;
break;
case IRP_MN_SURPRISE_REMOVAL:
/* Explicitly say ok */
Status = STATUS_SUCCESS;
break;
case IRP_MN_QUERY_PNP_DEVICE_STATE:
/* Add this in */
Irp->IoStatus.Information |= PNP_DEVICE_NOT_DISABLEABLE;
Status = STATUS_SUCCESS;
break;
default:
/* Not supported */
Status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
/* Set IRP status if we have one */
if (Status != STATUS_NOT_SUPPORTED) Irp->IoStatus.Status = Status;
/* Did someone pick it up? */
if ((NT_SUCCESS(Status)) || (Status == STATUS_NOT_SUPPORTED))
{
/* Still unsupported, try ACPI */
IoSkipCurrentIrpStackLocation(Irp);
Status = IoCallDriver(DeviceExtension->AttachedDevice, Irp);
}
else
{
/* Complete the request */
Status = Irp->IoStatus.Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
/* Release the remove lock and return status */
if (CompBattDebug & 1) DbgPrint("CompBatt: EXITING PnpDispatch\n");
return Status;
}
/* EOF */