/*++ Copyright (c) 1990 Microsoft Corporation Module Name: battc.c Abstract: Battery Class Driver Author: Ken Reneris Environment: Notes: Revision History: --*/ #include "battcp.h" #include #include WMIGUIDREGINFO BattWmiGuidList[BattWmiTotalGuids] = { { &BATTERY_STATUS_WMI_GUID, 1, WMIREG_FLAG_INSTANCE_PDO }, { &BATTERY_RUNTIME_WMI_GUID, 1, WMIREG_FLAG_INSTANCE_PDO }, { &BATTERY_TEMPERATURE_WMI_GUID, 1, WMIREG_FLAG_INSTANCE_PDO }, { &BATTERY_FULL_CHARGED_CAPACITY_WMI_GUID, 1, WMIREG_FLAG_INSTANCE_PDO }, { &BATTERY_CYCLE_COUNT_WMI_GUID, 1, WMIREG_FLAG_INSTANCE_PDO }, { &BATTERY_STATIC_DATA_WMI_GUID, 1, WMIREG_FLAG_INSTANCE_PDO }, { &BATTERY_STATUS_CHANGE_WMI_GUID, 1, WMIREG_FLAG_INSTANCE_PDO }, { &BATTERY_TAG_CHANGE_WMI_GUID, 1, WMIREG_FLAG_INSTANCE_PDO } }; // // Prototypes // NTSTATUS DriverEntry ( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE,DriverEntry) #pragma alloc_text(PAGE,BatteryClassInitializeDevice) #pragma alloc_text(PAGE,BatteryClassUnload) #pragma alloc_text(PAGE,BatteryClassIoctl) #endif #if DEBUG #if DBG ULONG BattDebug = BATT_ERROR|BATT_WARN; #else ULONG BattDebug = 0x0; #endif ULONG NextDeviceNum = 0; // Used to assign a unique number to each device for debugging. #endif NTSTATUS DriverEntry ( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) { return STATUS_SUCCESS; } NTSTATUS BATTERYCLASSAPI BatteryClassInitializeDevice ( IN PBATTERY_MINIPORT_INFO MiniportInfo, IN PVOID *ClassData ) /*++ Routine Description: Initializes a new battery class device. N.B. The caller needs to reserve 1 IRP stack location for the battery class driver Arguments: MiniportInfo - Pointer to registration structure for driver registering as a battery miniport ClassData - Returned battery class handle for use by the miniport when invoking furture battery class functions Return Value: On sucess the battery has been registered. --*/ { PBATT_NP_INFO BattNPInfo; PBATT_INFO BattInfo; NTSTATUS status = STATUS_SUCCESS; PAGED_CODE(); #if DEBUG if (MiniportInfo->DeviceName && MiniportInfo->DeviceName->Buffer) { BattPrint ((BATT_TRACE), ("BattC (%d): InitializeDevice (Pdo = 0x%08lx) (DeviceName = %ws)\n", NextDeviceNum, MiniportInfo->Pdo, MiniportInfo->DeviceName->Buffer)); } else { BattPrint ((BATT_TRACE), ("BattC (%d): InitializeDevice (Pdo = 0x%08lx)\n", NextDeviceNum, MiniportInfo->Pdo)); } #endif if (MiniportInfo->MajorVersion != BATTERY_CLASS_MAJOR_VERSION) { return STATUS_REVISION_MISMATCH; } // // Allocate space for the class info to be kept with this device instance // BattNPInfo = ExAllocatePoolWithTag(NonPagedPool, sizeof(BATT_NP_INFO), 'ttaB'); if (!BattNPInfo) { return STATUS_INSUFFICIENT_RESOURCES; } BattInfo = ExAllocatePoolWithTag(PagedPool, sizeof(BATT_INFO), 'ttaB'); if (!BattInfo) { ExFreePool (BattNPInfo); return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory (BattNPInfo, sizeof(*BattNPInfo)); RtlZeroMemory (BattInfo, sizeof(*BattInfo)); // // Capture Miniport info // RtlCopyMemory (&BattInfo->Mp, MiniportInfo, sizeof(*MiniportInfo)); // // Initilize class driver values // KeInitializeTimer (&BattNPInfo->WorkerTimer); KeInitializeTimer (&BattNPInfo->TagTimer); KeInitializeDpc (&BattNPInfo->WorkerDpc, BattCWorkerDpc, BattNPInfo); KeInitializeDpc (&BattNPInfo->TagDpc, BattCTagDpc, BattNPInfo); ExInitializeWorkItem (&BattNPInfo->WorkerThread, BattCWorkerThread, BattNPInfo); ExInitializeFastMutex (&BattNPInfo->Mutex); BattNPInfo->TagNotified = TRUE; BattNPInfo->StatusNotified = TRUE; BattNPInfo->BattInfo = BattInfo; #if DEBUG BattInfo->BattNPInfo = BattNPInfo; #endif BattInfo->Tag = BATTERY_TAG_INVALID; InitializeListHead (&BattInfo->IoQueue); InitializeListHead (&BattInfo->StatusQueue); InitializeListHead (&BattInfo->TagQueue); InitializeListHead (&BattInfo->WmiQueue); // // Removal lock initialization // BattNPInfo->WantToRemove = FALSE; // // InUseCount is set to 2. 1 lock is always held until the removal time. // 1 additional lock is held for the worker thread which only releases it // at removal time. Rather than aquiring and releasing each time, it just // checks WantToRemove to determine if it should release the lock. // BattNPInfo->InUseCount = 2; KeInitializeEvent(&BattNPInfo->ReadyToRemove, SynchronizationEvent, FALSE); #if DEBUG // Set device Number for debug prints. BattNPInfo->DeviceNum = NextDeviceNum; NextDeviceNum++; #endif *ClassData = BattNPInfo; // // Check to see if this is a battery other than the composite // if (MiniportInfo->Pdo) { // Blank UNICODE_STRING so IoRegisterDeviceInterface will allocate space. RtlInitUnicodeString (&BattInfo->SymbolicLinkName, NULL); // // Create the symbolic link // status = IoRegisterDeviceInterface( MiniportInfo->Pdo, (LPGUID)&GUID_DEVICE_BATTERY, NULL, &BattInfo->SymbolicLinkName); if (NT_SUCCESS(status)) { // // Now set the symbolic link for the association and store it.. // BattPrint ((BATT_NOTE), ("BattC (%d): Making SetDeviceInterfaceState call.\n", BattNPInfo->DeviceNum)); status = IoSetDeviceInterfaceState(&BattInfo->SymbolicLinkName, TRUE); if (status == STATUS_OBJECT_NAME_EXISTS) { // The device interface was already enabled. Continue anyway. BattPrint ((BATT_WARN), ("BattC (%d): Got STATUS_OBJECT_NAME_EXISTS for SetDeviceInterfaceState\n", BattNPInfo->DeviceNum)); status = STATUS_SUCCESS; } } } BattPrint ((BATT_TRACE), ("BattC (%d): BatteryClassInitializeDevice (status = 0x%08lx).\n", BattNPInfo->DeviceNum, status)); return status; } // BatteryClassInitializeDevice NTSTATUS BATTERYCLASSAPI BatteryClassUnload ( IN PVOID ClassData ) /*++ Routine Description: Called by the miniport when it has received a remove request. The miniclass driver must syncronize itself so that this API is not called while any of the others are not yet completed. Arguments: ClassData - Handle to class driver Return Value: This routine must not fail. It returns STATUS_SUCCESS --*/ { NTSTATUS status; PBATT_INFO BattInfo; PBATT_NP_INFO BattNPInfo; PAGED_CODE(); BattNPInfo = (PBATT_NP_INFO) ClassData; BattInfo = BattNPInfo->BattInfo; BattPrint ((BATT_TRACE), ("BattC (%d): BatteryClassUnload called.\n", BattNPInfo->DeviceNum)); // // Disable the symbolic link // ASSERT(BattInfo->SymbolicLinkName.Buffer); status = IoSetDeviceInterfaceState(&BattInfo->SymbolicLinkName, FALSE); if (!NT_SUCCESS(status)) { BattPrint (BATT_ERROR, ("BattC (%d) Unload: IoSetDeviceInterface returned 0x%08lx\n", BattNPInfo->DeviceNum, status)); } // // Syncronization with the worker thread. // We can't return because the worker may be in the middle of something. // By returning, the battery class driver gives up the right to call miniport routines. // // This needs to be done before canceling the timers so that the worker // thread doesn't reset them. // BattNPInfo->WantToRemove = TRUE; // // Cancel timers // If a timer had been waiting,we need to release the remove lock that was // aquired before the timer was set since it will not be released in the DPC. // if (KeCancelTimer (&BattNPInfo->WorkerTimer)) { // Release Removal Lock // "InUseCount can never be 0 after this operation. InterlockedDecrement(&BattNPInfo->InUseCount); BattPrint ((BATT_LOCK), ("BatteryClassUnload: Released WorkerTimer remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount)); } if (KeCancelTimer (&BattNPInfo->TagTimer)) { // Release Removal Lock // "InUseCount can never be 0 after this operation. InterlockedDecrement(&BattNPInfo->InUseCount); BattPrint ((BATT_LOCK), ("BatteryClassUnload: Released TagTimer remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount)); } // // Queue the worker thread once more to make sure that the remove lock for // the worker thread is released. // BattCQueueWorker (BattNPInfo, FALSE); // Finish syncronization if (InterlockedDecrement (&BattNPInfo->InUseCount) > 0) { KeWaitForSingleObject (&BattNPInfo->ReadyToRemove, Executive, KernelMode, FALSE, NULL ); } BattPrint ((BATT_LOCK), ("BatteryClassUnload: Done waiting for remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount)); // // Free Structures // ExFreePool (BattInfo->SymbolicLinkName.Buffer); ExFreePool (BattInfo); ExFreePool (BattNPInfo); BattPrint ((BATT_TRACE), ("BattC (%d): BatteryClassUnload returning.\n", BattNPInfo->DeviceNum)); return STATUS_SUCCESS; } NTSTATUS BATTERYCLASSAPI BatteryClassIoctl ( IN PVOID ClassData, IN PIRP Irp ) /*++ Routine Description: Called by the miniport to handle battery ioctl requests. If handled, the battery class driver owns the IRP. If not handled, it belongs to the caller. Arguments: ClassData - Handle to class driver Irp - ICOTL irp to check Return Value: If handled, the battery class driver owns the IRP and will complete it once its handled; otherwise, the error STATUS_NOT_SUPPORTED is returned. --*/ { NTSTATUS status; PBATT_INFO BattInfo; PBATT_NP_INFO BattNPInfo; PIO_STACK_LOCATION IrpSp; PAGED_CODE(); BattNPInfo = (PBATT_NP_INFO) ClassData; BattInfo = BattNPInfo->BattInfo; BattPrint ((BATT_TRACE), ("BattC (%d): BatteryClassIoctl called.\n", BattNPInfo->DeviceNum)); IrpSp = IoGetCurrentIrpStackLocation(Irp); // // Assume it's not our IRP // status = STATUS_NOT_SUPPORTED; // // Check IOCTL code to see if it's our IRP // switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) { case IOCTL_BATTERY_QUERY_TAG: case IOCTL_BATTERY_QUERY_INFORMATION: case IOCTL_BATTERY_SET_INFORMATION: case IOCTL_BATTERY_QUERY_STATUS: // // Acquire remove lock. // We don't want to queue anything more if we're being removed. // InterlockedIncrement (&BattNPInfo->InUseCount); BattPrint ((BATT_LOCK), ("BatteryClassIoctl: Aqcuired remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount)); if (BattNPInfo->WantToRemove == TRUE) { if (0 == InterlockedDecrement(&BattNPInfo->InUseCount)) { KeSetEvent (&BattNPInfo->ReadyToRemove, IO_NO_INCREMENT, FALSE); } BattPrint ((BATT_LOCK), ("BatteryClassIoctl: Released remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount)); status = STATUS_DEVICE_REMOVED; } else { // // Irp to handle. Put it in the queue worker threads list // status = STATUS_PENDING; Irp->IoStatus.Status = STATUS_PENDING; IoMarkIrpPending (Irp); ExAcquireFastMutex (&BattNPInfo->Mutex); InsertTailList (&BattInfo->IoQueue, &Irp->Tail.Overlay.ListEntry); ExReleaseFastMutex (&BattNPInfo->Mutex); BattCQueueWorker (BattNPInfo, FALSE); // // Release Remove Lock. // if (0 == InterlockedDecrement(&BattNPInfo->InUseCount)) { KeSetEvent (&BattNPInfo->ReadyToRemove, IO_NO_INCREMENT, FALSE); } BattPrint ((BATT_LOCK), ("BatteryClassIoctl: Released remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount)); } break; default: BattPrint ((BATT_ERROR|BATT_IOCTL), ("BattC (%d): unknown battery ioctl - %x\n", BattNPInfo->DeviceNum, IrpSp->Parameters.DeviceIoControl.IoControlCode)); break; } if ((status != STATUS_PENDING) && (status != STATUS_NOT_SUPPORTED)) { Irp->IoStatus.Status = status; IoCompleteRequest (Irp, IO_NO_INCREMENT); } BattPrint ((BATT_TRACE), ("BattC (%d): BatteryClassIoctl returning (status = 0x%08lx).\n", BattNPInfo->DeviceNum, status)); return status; } NTSTATUS BATTERYCLASSAPI BatteryClassStatusNotify ( IN PVOID ClassData ) /*++ Routine Description: Called by the miniport to signify that something interesting concerning the battery status has occured. Calling this function will cause the battery class driver to obtain the battery status if there are any pending status requests pending. If the miniport supports SetNotify from the class driver, then the miniport only needs to call this function once when the notification crtierea is met. If the miniport does not support SetNotify from the class driver, then the class driver will poll (at a slow rate) but the miniport should still call this function at least when the batteries power status changes such that timely updates of at least the power status will occur in the UI. Arguments: ClassData - Handle to class driver Return Value: Status --*/ { PBATT_NP_INFO BattNPInfo; BattNPInfo = (PBATT_NP_INFO) ClassData; BattPrint ((BATT_TRACE), ("BattC (%d): BatteryClassStatusNotify called\n", BattNPInfo->DeviceNum)); InterlockedExchange (&BattNPInfo->StatusNotified, 1); InterlockedExchange (&BattNPInfo->TagNotified, 1); BattCQueueWorker (BattNPInfo, TRUE); return STATUS_SUCCESS; } NTSTATUS BATTERYCLASSAPI BatteryClassSystemControl ( IN PVOID ClassData, IN PWMILIB_CONTEXT WmiLibContext, IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, OUT PSYSCTL_IRP_DISPOSITION Disposition ) /*++ Routine Description: The miniport driver calls this instead of WmiSystemControl. Arguments: ClassData - Handle to class driver The other parameters are the parameters for WmiSystemControl. Return Value: STATUS_SUCCESS or one of the following error codes: STATUS_INVALID_DEVICE_REQUEST STATUS_WMI_GUID_NOT_FOUND STATUS_WMI_INSTANCE_NOT_FOUND --*/ { NTSTATUS status = STATUS_NOT_SUPPORTED; PBATT_INFO BattInfo; PBATT_NP_INFO BattNPInfo; PIO_STACK_LOCATION IrpSp; PAGED_CODE(); BattNPInfo = (PBATT_NP_INFO) ClassData; BattInfo = BattNPInfo->BattInfo; IrpSp = IoGetCurrentIrpStackLocation (Irp); BattPrint ((BATT_TRACE), ("BattC (%d): BatteryClassSystemControl called.\n", BattNPInfo->DeviceNum)); // // Initialize the WmiLibContext structure if this is the first time. // if (BattInfo->WmiLibContext.GuidCount == 0) { RtlCopyMemory(&BattInfo->WmiLibContext, WmiLibContext, sizeof(*WmiLibContext)); BattInfo->WmiLibContext.GuidCount = WmiLibContext->GuidCount + BattWmiTotalGuids; BattInfo->WmiGuidIndex = WmiLibContext->GuidCount; BattInfo->WmiLibContext.GuidList = ExAllocatePoolWithTag(PagedPool, BattInfo->WmiLibContext.GuidCount * sizeof(WMIGUIDREGINFO), 'ttaB'); if (!BattInfo->WmiLibContext.GuidList) { // // Figure out how to fail gracefully. // } RtlCopyMemory(BattInfo->WmiLibContext.GuidList, WmiLibContext->GuidList, WmiLibContext->GuidCount * sizeof(WMIGUIDREGINFO)); RtlCopyMemory(&BattInfo->WmiLibContext.GuidList [WmiLibContext->GuidCount], BattWmiGuidList, BattWmiTotalGuids * sizeof(WMIGUIDREGINFO)); } // // Acquire remove lock. // We don't want to queue anything more if we're being removed. // InterlockedIncrement (&BattNPInfo->InUseCount); BattPrint ((BATT_LOCK), ("BatteryClassSystemControl: Aqcuired remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount)); if (BattNPInfo->WantToRemove == TRUE) { if (0 == InterlockedDecrement(&BattNPInfo->InUseCount)) { KeSetEvent (&BattNPInfo->ReadyToRemove, IO_NO_INCREMENT, FALSE); } BattPrint ((BATT_LOCK), ("BatteryClassSystemControl: Released remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount)); status = STATUS_DEVICE_REMOVED; } else { status = WmiSystemControl(&BattInfo->WmiLibContext, DeviceObject, Irp, Disposition); BattPrint ((BATT_DEBUG), ("BattC (%d): BatteryClassSystemControl Returned from WmiSystemControl (status = 0x%08x).\n", BattNPInfo->DeviceNum, status)); // // For IRP_MN_REGINFO BattC needs to add additional data to the IRP // about the battery class MOF resource. // if ((*Disposition == IrpNotCompleted) && ((IrpSp->MinorFunction == IRP_MN_REGINFO) || (IrpSp->MinorFunction == IRP_MN_REGINFO_EX)) && (IrpSp->Parameters.WMI.DataPath == WMIREGISTER)) { // // Original structure // PWMIREGINFO regInfoPtr = IrpSp->Parameters.WMI.Buffer; BattPrint ((BATT_DEBUG), ("BattC (%d): BatteryClassSystemControl Adding Resource.\n", BattNPInfo->DeviceNum)); // // If WmiSystemControl returned STATUS_BUFFER_TOO_SMALL or entered // the correct size as a ULONG in IoStatus.Information. // Increase the required size to accomodate battery class data // before returning // if (Irp->IoStatus.Information == sizeof(ULONG) || Irp->IoStatus.Status == STATUS_BUFFER_TOO_SMALL) { // // Aditional battery class data includes one WMIREGINFO structure // Followed strings for the regstry path and resource name. // (Plus two WCHARS because these need to be counted strings.) // Round this up to the nearest 8 bytes. // regInfoPtr->BufferSize = (regInfoPtr->BufferSize + sizeof(WMIREGINFO) + sizeof(MOFREGISTRYPATH) + sizeof(MOFRESOURCENAME) + 2 * sizeof(WCHAR) + 7) & 0xFFFFFFF8; BattPrint ((BATT_DEBUG), ("BattC (%d): BatteryClassSystemControl Buffer Too Small:\n" " Information = %08x\n" " BufferSize = %08x\n" " NewSize = %08x\n", BattNPInfo->DeviceNum, Irp->IoStatus.Information, IrpSp->Parameters.WMI.BufferSize, regInfoPtr->BufferSize)); // // Make sure IRP is set up to fail correctly. // Irp->IoStatus.Information = sizeof(ULONG); Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; status = STATUS_BUFFER_TOO_SMALL; } else { ULONG size; PWCHAR tempString; // // Assume that there is only one WmiRegInfo Structure so far. // ASSERT (regInfoPtr->NextWmiRegInfo == 0); regInfoPtr->NextWmiRegInfo = (regInfoPtr->BufferSize + 7) & 0xFFFFFFF8; size = regInfoPtr->NextWmiRegInfo + sizeof(WMIREGINFO) + sizeof(MOFRESOURCENAME) + sizeof(MOFREGISTRYPATH) + 2 * sizeof(WCHAR); // // Set BufferSize Whether we succeed or not. // ((PWMIREGINFO)IrpSp->Parameters.WMI.Buffer)->BufferSize = size; if (size > IrpSp->Parameters.WMI.BufferSize) { // // If WmiSystemControl was successful, but there isnt room // for the extra data, this request needs to fail // Irp->IoStatus.Information = sizeof(ULONG); Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; status = STATUS_BUFFER_TOO_SMALL; BattPrint ((BATT_DEBUG), ("BattC (%d): BatteryClassSystemControl Buffer Too Small.\n" " BufferSize = %08x\n" " Size = %08x\n", BattNPInfo->DeviceNum, IrpSp->Parameters.WMI.BufferSize, size)); } else { Irp->IoStatus.Information = size; BattPrint ((BATT_DEBUG), ("BattC (%d): BatteryClassSystemControl Munging Structures:\n" " Buffer = %08x\n" " temp = %08x\n", BattNPInfo->DeviceNum, (ULONG_PTR) IrpSp->Parameters.WMI.Buffer, (ULONG_PTR) regInfoPtr)); // // Intialize new structure // // Set teporary pointer to point at data structure we are adding. (ULONG_PTR)regInfoPtr += (ULONG_PTR)regInfoPtr->NextWmiRegInfo; regInfoPtr->BufferSize = sizeof(WMIREGINFO) + sizeof(MOFRESOURCENAME) + sizeof(MOFREGISTRYPATH) + 2 * sizeof(WCHAR); regInfoPtr->NextWmiRegInfo = 0; // Initialize RegistryPath counted string. regInfoPtr->RegistryPath = sizeof(WMIREGINFO); tempString = (PWCHAR)((ULONG_PTR)regInfoPtr + sizeof(WMIREGINFO)); *tempString++ = sizeof(MOFREGISTRYPATH); RtlCopyMemory(tempString, MOFREGISTRYPATH, sizeof(MOFREGISTRYPATH)); // Initialize MofResourceName counted string. regInfoPtr->MofResourceName = sizeof(WMIREGINFO) + sizeof(MOFREGISTRYPATH) + sizeof(WCHAR); tempString = (PWCHAR)((ULONG_PTR)regInfoPtr + regInfoPtr->MofResourceName); *tempString++ = sizeof(MOFRESOURCENAME); RtlCopyMemory(tempString, MOFRESOURCENAME, sizeof(MOFRESOURCENAME)); regInfoPtr->GuidCount = 0; } } } // // Release Remove Lock. // if (0 == InterlockedDecrement(&BattNPInfo->InUseCount)) { KeSetEvent (&BattNPInfo->ReadyToRemove, IO_NO_INCREMENT, FALSE); } BattPrint ((BATT_LOCK), ("BatteryClassSystemControl: Released remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount)); } return status; } NTSTATUS BATTERYCLASSAPI BatteryClassQueryWmiDataBlock( IN PVOID ClassData, IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN ULONG GuidIndex, IN OUT PULONG InstanceLengthArray, IN ULONG OutBufferSize, OUT PUCHAR Buffer ) /*++ Routine Description: This routine is a callback into the driver to query for the contents of a data block. When the driver has finished filling the data block it must call WmiCompleteRequest to complete the irp. The driver can return STATUS_PENDING if the irp cannot be completed immediately. Arguments: DeviceObject is the device whose data block is being queried Irp is the Irp that makes this request GuidIndex is the index into the list of guids provided when the device registered InstanceLengthArray is a pointer to an array of ULONG that returns the lengths of each instance of the data block. If this is NULL then there was not enough space in the output buffer to fulfill the request so the irp should be completed with the buffer needed. BufferAvail on has the maximum size available to write the data block. Buffer on return is filled with the returned data block Return Value: status --*/ { PBATT_NP_INFO BattNPInfo = (PBATT_NP_INFO) ClassData; PBATT_INFO BattInfo = BattNPInfo->BattInfo; PBATT_WMI_REQUEST WmiRequest; NTSTATUS status = STATUS_SUCCESS; ULONG size = 0; PAGED_CODE(); BattPrint ((BATT_TRACE|BATT_WMI), ("Entered BatteryClassQueryWmiDataBlock\n")); // // Don't need to acquire remove lock. It is already head by SystemControl. // switch (GuidIndex - BattInfo->WmiGuidIndex) { case BattWmiStatusId: size = sizeof (BATTERY_WMI_STATUS); break; case BattWmiRuntimeId: size = sizeof (BATTERY_WMI_RUNTIME); break; case BattWmiTemperatureId: size = sizeof (BATTERY_WMI_TEMPERATURE); break; case BattWmiFullChargedCapacityId: size = sizeof (BATTERY_WMI_FULL_CHARGED_CAPACITY); break; case BattWmiCycleCountId: size = sizeof (BATTERY_WMI_CYCLE_COUNT); break; case BattWmiStaticDataId: size = sizeof(BATTERY_WMI_STATIC_DATA)+4*MAX_BATTERY_STRING_SIZE*sizeof(WCHAR); // data plus 4 strings break; default: status = STATUS_WMI_GUID_NOT_FOUND; } if (status != STATUS_WMI_GUID_NOT_FOUND) { if (OutBufferSize < size ) { status = STATUS_BUFFER_TOO_SMALL; *InstanceLengthArray = size; status = WmiCompleteRequest( DeviceObject, Irp, status, size, IO_NO_INCREMENT); return status; } WmiRequest = ExAllocatePoolWithTag (PagedPool, sizeof(BATT_WMI_REQUEST), 'ttaB'); if (!WmiRequest) { BattPrint((BATT_ERROR), ("Failed to allocate memory for WMI request\n")); status = STATUS_INSUFFICIENT_RESOURCES; status = WmiCompleteRequest( DeviceObject, Irp, status, size, IO_NO_INCREMENT); return status; } WmiRequest->DeviceObject = DeviceObject; WmiRequest->Irp = Irp; WmiRequest->GuidIndex = GuidIndex - BattInfo->WmiGuidIndex; WmiRequest->InstanceLengthArray = InstanceLengthArray; WmiRequest->OutBufferSize = OutBufferSize; WmiRequest->Buffer = Buffer; ExAcquireFastMutex (&BattNPInfo->Mutex); InsertTailList (&BattInfo->WmiQueue, &WmiRequest->ListEntry); ExReleaseFastMutex (&BattNPInfo->Mutex); BattCQueueWorker (BattNPInfo, FALSE); status = STATUS_PENDING; } return status; }