/*++ Copyright (c) 1990 Microsoft Corporation Module Name: CompBatt.c Abstract: Composite Battery device functions The purpose of the composite battery device is to open all batteries in the system which supply system power and provide a logical sumation of the information under one battery device. Author: Ken Reneris Environment: Notes: Revision History: 07/02/97: Local cache timestamps/timeouts --*/ #include "compbatt.h" #if DEBUG #if DBG ULONG CompBattDebug = BATT_ERRORS; #else ULONG CompBattDebug = 0; #endif #endif #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT, DriverEntry) #pragma alloc_text(PAGE, CompBattUnload) #pragma alloc_text(PAGE, CompBattIoctl) #pragma alloc_text(PAGE, CompBattQueryTag) #pragma alloc_text(PAGE, CompBattQueryInformation) #pragma alloc_text(PAGE, CompBattQueryStatus) #pragma alloc_text(PAGE, CompBattSetStatusNotify) #pragma alloc_text(PAGE, CompBattDisableStatusNotify) #pragma alloc_text(PAGE, CompBattGetBatteryInformation) #pragma alloc_text(PAGE, CompBattGetBatteryGranularity) #pragma alloc_text(PAGE, CompBattGetEstimatedTime) #endif NTSTATUS DriverEntry ( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) /*++ Routine Description: The first time the battery class driver is loaded it will check to see if the composite battery has been created. If not, it will create a driver object with this routine as the DriverEntry. This routine then does the necessary things to initialize the composite battery. Arguments: DriverObject - Driver object for newly created driver RegistryPath - Not used Return Value: Status --*/ { // DbgBreakPoint (); // // Initialize the driver entry points // //DriverObject->DriverUnload = CompBattUnload; DriverObject->DriverExtension->AddDevice = CompBattAddDevice; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = CompBattIoctl; DriverObject->MajorFunction[IRP_MJ_CREATE] = CompBattOpenClose; DriverObject->MajorFunction[IRP_MJ_CLOSE] = CompBattOpenClose; DriverObject->MajorFunction[IRP_MJ_PNP] = CompBattPnpDispatch; DriverObject->MajorFunction[IRP_MJ_POWER] = CompBattPowerDispatch; DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = CompBattSystemControl; return STATUS_SUCCESS; } NTSTATUS CompBattAddDevice ( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PDO ) /*++ Routine Description: Arguments: DriverObject - Pointer to driver object created by system. PDO - PDO for the new device(s) Return Value: Status --*/ { PDEVICE_OBJECT fdo; BATTERY_MINIPORT_INFO BattInit; UNICODE_STRING UnicodeString; NTSTATUS Status; UNICODE_STRING DosLinkName; PCOMPOSITE_BATTERY compBatt; BattPrint (BATT_NOTE, ("CompBatt: Got an AddDevice - %x\n", PDO)); // // Build the composite battery device and register it to the // battery class driver (i.e., ourselves) // RtlInitUnicodeString(&UnicodeString, L"\\Device\\CompositeBattery"); Status = IoCreateDevice( DriverObject, sizeof (COMPOSITE_BATTERY), &UnicodeString, FILE_DEVICE_BATTERY, // DeviceType 0, FALSE, &fdo ); if (!NT_SUCCESS(Status)) { return Status; } RtlInitUnicodeString(&DosLinkName, L"\\DosDevices\\CompositeBattery"); IoCreateSymbolicLink(&DosLinkName, &UnicodeString); // // Layer our FDO on top of the PDO. // compBatt = (PCOMPOSITE_BATTERY) fdo->DeviceExtension; RtlZeroMemory (compBatt, sizeof(COMPOSITE_BATTERY)); compBatt->LowerDevice = IoAttachDeviceToDeviceStack (fdo,PDO); compBatt->DeviceObject = fdo; // // No status. Do the best we can. // if (!compBatt->LowerDevice) { BattPrint (BATT_ERROR, ("CompBattAddDevice: Could not attach to LowerDevice.\n")); return STATUS_UNSUCCESSFUL; } // // Initialize composite battery info // fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE; fdo->Flags &= ~DO_DEVICE_INITIALIZING; InitializeListHead (&compBatt->Batteries); ExInitializeFastMutex (&compBatt->ListMutex); compBatt->NextTag = 1; // first valid battery tag for composite compBatt->Info.Valid = 0; RtlZeroMemory (&BattInit, sizeof(BattInit)); BattInit.MajorVersion = BATTERY_CLASS_MAJOR_VERSION; BattInit.MinorVersion = BATTERY_CLASS_MINOR_VERSION; BattInit.Context = compBatt; BattInit.QueryTag = CompBattQueryTag; BattInit.QueryInformation = CompBattQueryInformation; BattInit.SetInformation = NULL; BattInit.QueryStatus = CompBattQueryStatus; BattInit.SetStatusNotify = CompBattSetStatusNotify; BattInit.DisableStatusNotify = CompBattDisableStatusNotify; BattInit.Pdo = NULL; BattInit.DeviceName = &UnicodeString; // // Register myself with the battery class driver // Status = BatteryClassInitializeDevice (&BattInit, &compBatt->Class); if (!NT_SUCCESS(Status)) { IoDeleteDevice(fdo); } return Status; } VOID CompBattUnload( IN PDRIVER_OBJECT DriverObject ) /*++ Routine Description: Cleanup all devices and unload the driver Arguments: DriverObject - Driver object for unload Return Value: Status --*/ { DbgBreakPoint(); // // Unloading the composite battery is not supported. // If it were implemented, we would // need to call the class driver's unload and then // delete all nodes in the battery list, clean up // then delete our FDO. // } NTSTATUS CompBattOpenClose( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PAGED_CODE(); BattPrint (BATT_TRACE, ("CompBatt: ENTERING OpenClose\n")); // // Complete the request and return status. // Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); BattPrint (BATT_TRACE, ("CompBatt: Exiting OpenClose\n")); return(STATUS_SUCCESS); } NTSTATUS CompBattIoctl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: IOCTL handler. As this is an exclusive battery device, send the Irp to the battery class driver to handle battery IOCTLs. Arguments: DeviceObject - Battery for request Irp - IO request Return Value: Status of request --*/ { PCOMPOSITE_BATTERY compBatt; NTSTATUS status; PAGED_CODE(); BattPrint (BATT_TRACE, ("CompBatt: ENTERING Ioctl\n")); compBatt = (PCOMPOSITE_BATTERY) DeviceObject->DeviceExtension; status = BatteryClassIoctl (compBatt->Class, Irp); if (status == STATUS_NOT_SUPPORTED) { // // Not for the battery, pass it down the stack. // Irp->IoStatus.Status = status; IoSkipCurrentIrpStackLocation(Irp); status = IoCallDriver(compBatt->LowerDevice, Irp); } BattPrint (BATT_TRACE, ("CompBatt: EXITING Ioctl\n")); return status; } NTSTATUS CompBattSystemControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine forwards System Control requests down the stack Arguments: DeviceObject - the device object in question Irp - the request to forward Return Value: NTSTATUS --*/ { PCOMPOSITE_BATTERY compBatt; NTSTATUS status; PAGED_CODE(); BattPrint (BATT_TRACE, ("CompBatt: ENTERING System Control\n")); compBatt = (PCOMPOSITE_BATTERY) DeviceObject->DeviceExtension; if (compBatt->LowerDevice != NULL) { IoSkipCurrentIrpStackLocation( Irp ); status = IoCallDriver( compBatt->LowerDevice, Irp ); } else { Irp->IoStatus.Status = status = STATUS_NOT_SUPPORTED; IoCompleteRequest( Irp, IO_NO_INCREMENT ); } return status; } NTSTATUS CompBattQueryTag ( IN PVOID Context, OUT PULONG BatteryTag ) /*++ Routine Description: Called by the class driver to retrieve the batteries current tag value Arguments: Context - Miniport context value for battery BatteryTag - Pointer to return current tag Return Value: Success if there is a battery currently installed, else no such device. --*/ { PCOMPOSITE_BATTERY compBatt; NTSTATUS status = STATUS_SUCCESS; PAGED_CODE(); BattPrint (BATT_TRACE, ("CompBatt: ENTERING QueryTag\n")); compBatt = (PCOMPOSITE_BATTERY) Context; if (!(compBatt->Info.Valid & VALID_TAG)) { // // Recalculate the composite's tag. // CompBattRecalculateTag(compBatt); } if ((compBatt->Info.Valid & VALID_TAG) && (compBatt->Info.Tag != BATTERY_TAG_INVALID)) { *BatteryTag = compBatt->Info.Tag; status = STATUS_SUCCESS; } else { *BatteryTag = BATTERY_TAG_INVALID; status = STATUS_NO_SUCH_DEVICE; } BattPrint (BATT_TRACE, ("CompBatt: EXITING QueryTag\n")); return status; } NTSTATUS CompBattQueryInformation ( IN PVOID Context, IN ULONG BatteryTag, IN BATTERY_QUERY_INFORMATION_LEVEL Level, IN LONG AtRate, OUT PVOID Buffer, IN ULONG BufferLength, OUT PULONG ReturnedLength ) { ULONG resultData; NTSTATUS status; PVOID returnBuffer; ULONG returnBufferLength; PCOMPOSITE_BATTERY compBatt; BATTERY_INFORMATION totalBattInfo; BATTERY_REPORTING_SCALE granularity[4]; BATTERY_MANUFACTURE_DATE date; WCHAR compositeName[] = L"Composite Battery"; PAGED_CODE(); BattPrint (BATT_TRACE, ("CompBatt: ENTERING QueryInformation\n")); compBatt = (PCOMPOSITE_BATTERY) Context; if ((BatteryTag != compBatt->Info.Tag) || !(compBatt->Info.Valid & VALID_TAG)) { return STATUS_NO_SUCH_DEVICE; } returnBuffer = NULL; returnBufferLength = 0; status = STATUS_SUCCESS; // // Get the info requested // switch (Level) { case BatteryInformation: RtlZeroMemory (&totalBattInfo, sizeof(totalBattInfo)); status = CompBattGetBatteryInformation (&totalBattInfo, compBatt); if (NT_SUCCESS(status)) { returnBuffer = &totalBattInfo; returnBufferLength = sizeof(totalBattInfo); } break; case BatteryGranularityInformation: RtlZeroMemory (&granularity[0], sizeof(granularity)); status = CompBattGetBatteryGranularity (&granularity[0], compBatt); if (NT_SUCCESS(status)) { returnBuffer = &granularity[0]; returnBufferLength = sizeof(granularity); } break; case BatteryTemperature: resultData = 0; returnBuffer = &resultData; returnBufferLength = sizeof (resultData); break; case BatteryEstimatedTime: RtlZeroMemory (&resultData, sizeof(resultData)); status = CompBattGetEstimatedTime (&resultData, compBatt); if (NT_SUCCESS(status)) { returnBuffer = &resultData; returnBufferLength = sizeof(resultData); } break; case BatteryDeviceName: returnBuffer = compositeName; returnBufferLength = sizeof (compositeName); break; case BatteryManufactureDate: date.Day = 26; date.Month = 6; date.Year = 1997; returnBuffer = &date; returnBufferLength = sizeof (date); break; case BatteryManufactureName: returnBuffer = compositeName; returnBufferLength = sizeof (compositeName); break; case BatteryUniqueID: resultData = 0; returnBuffer = &resultData; returnBufferLength = sizeof (resultData); break; default: status = STATUS_INVALID_PARAMETER; break; } // // Make sure nothing changed while reading batteries. // if ((BatteryTag != compBatt->Info.Tag) || !(compBatt->Info.Valid & VALID_TAG)) { return STATUS_NO_SUCH_DEVICE; } // // Done, return buffer if needed // *ReturnedLength = returnBufferLength; if (BufferLength < returnBufferLength) { status = STATUS_BUFFER_TOO_SMALL; } if (NT_SUCCESS(status) && returnBuffer) { memcpy (Buffer, returnBuffer, returnBufferLength); } BattPrint (BATT_TRACE, ("CompBatt: EXITING QueryInformation\n")); return status; } NTSTATUS CompBattQueryStatus ( IN PVOID Context, IN ULONG BatteryTag, OUT PBATTERY_STATUS BatteryStatus ) /*++ Routine Description: Called by the class driver to retrieve the batteries current status. This routine loops through all of the batteries in the system and reports a composite battery. Arguments: Context - Miniport context value for battery BatteryTag - Tag of current battery BatteryStatus - Pointer to structure to return the current battery status Return Value: Success if there is a battery currently installed, else no such device. --*/ { NTSTATUS status = STATUS_SUCCESS; PCOMPOSITE_ENTRY batt; PLIST_ENTRY entry; PBATTERY_STATUS localBatteryStatus; PCOMPOSITE_BATTERY compBatt; BATTERY_WAIT_STATUS batteryWaitStatus; ULONGLONG wallClockTime; BattPrint (BATT_TRACE, ("CompBatt: ENTERING QueryStatus\n")); compBatt = (PCOMPOSITE_BATTERY) Context; if ((BatteryTag != compBatt->Info.Tag) || !(compBatt->Info.Valid & VALID_TAG)) { return STATUS_NO_SUCH_DEVICE; } // // Initialize Composite data structure. // BatteryStatus->Rate = BATTERY_UNKNOWN_RATE; BatteryStatus->Voltage = BATTERY_UNKNOWN_VOLTAGE; BatteryStatus->Capacity = BATTERY_UNKNOWN_CAPACITY; // Composite battery will only report POWER_ON_LINE if all batteries report // this flag. BatteryStatus->PowerState = BATTERY_POWER_ON_LINE; // // Set up the local battery status structure for calls to the batteries // RtlZeroMemory (&batteryWaitStatus, sizeof (BATTERY_WAIT_STATUS)); // // Get current time for timestamps // wallClockTime = KeQueryInterruptTime (); // // If cache is fresh, no need to do anything // if ((wallClockTime - compBatt->Info.StatusTimeStamp) <= CACHE_STATUS_TIMEOUT) { BattPrint (BATT_NOTE, ("CompBattQueryStatus: Composite battery status cache is [valid]\n")); // // Copy status info to caller's buffer // RtlCopyMemory (BatteryStatus, &compBatt->Info.Status, sizeof (BATTERY_STATUS)); return STATUS_SUCCESS; } BattPrint (BATT_NOTE, ("CompBattQueryStatus: Composite battery status cache is [stale] - refreshing\n")); // // Walk the list of batteries, getting status of each // ExAcquireFastMutex (&compBatt->ListMutex); for (entry = compBatt->Batteries.Flink; entry != &compBatt->Batteries; entry = entry->Flink) { batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries); if (!NT_SUCCESS (CompbattAcquireDeleteLock(&batt->DeleteLock))) { continue; } ExReleaseFastMutex (&compBatt->ListMutex); batteryWaitStatus.BatteryTag = batt->Info.Tag; localBatteryStatus = &batt->Info.Status; if (batt->Info.Valid & VALID_TAG) { // // If cached status for this battery is stale, refresh it // if ((wallClockTime - batt->Info.StatusTimeStamp) > CACHE_STATUS_TIMEOUT) { BattPrint (BATT_NOTE, ("CompBattQueryStatus: Battery status cache is [stale] - refreshing\n")); // // issue IOCTL to device // RtlZeroMemory (localBatteryStatus, sizeof(BATTERY_STATUS)); status = BatteryIoctl (IOCTL_BATTERY_QUERY_STATUS, batt->DeviceObject, &batteryWaitStatus, sizeof (BATTERY_WAIT_STATUS), localBatteryStatus, sizeof (BATTERY_STATUS), FALSE); if (!NT_SUCCESS(status)) { // // In case of failure, this function should simply return the // status code. Invalidating of data is now performed only // in MonitorIrpComplete. // // This raises the slight possibility that the sender of this // request could retry before the data is properly invalidated, // but worst case, they would again get this same error condition // until the data is properly invalidated by MonitorIrpComplete. // if (status == STATUS_DEVICE_REMOVED) { // // This battery is being removed. // The composite battery tag is or will soon be // invalidated by MonitorIrpComplete. // status = STATUS_NO_SUCH_DEVICE; } // // Return failure code. // ExAcquireFastMutex (&compBatt->ListMutex); CompbattReleaseDeleteLock(&batt->DeleteLock); break; } // Set new timestamp batt->Info.StatusTimeStamp = wallClockTime; } else { BattPrint (BATT_NOTE, ("CompBattQueryStatus: Battery status cache is [valid]\n")); } // // Accumulate data. // // // Combine the power states. // // Logical OR CHARGING and DISCHARGING BatteryStatus->PowerState |= (localBatteryStatus->PowerState & (BATTERY_CHARGING | BATTERY_DISCHARGING)); // Logical AND POWER_ON_LINE BatteryStatus->PowerState &= (localBatteryStatus->PowerState | ~BATTERY_POWER_ON_LINE); // Compbatt is critical if one battery is critical and discharging if ((localBatteryStatus->PowerState & BATTERY_CRITICAL) && (localBatteryStatus->PowerState & BATTERY_DISCHARGING)) { BatteryStatus->PowerState |= BATTERY_CRITICAL; } // // The Capacity could possibly be "Unknown" for CMBatt, and if so // we should not add it to the total capacity. // if (BatteryStatus->Capacity == BATTERY_UNKNOWN_CAPACITY) { BatteryStatus->Capacity = localBatteryStatus->Capacity; } else if (localBatteryStatus->Capacity != BATTERY_UNKNOWN_CAPACITY) { BatteryStatus->Capacity += localBatteryStatus->Capacity; } // // The Voltage should just be the greatest one encountered. // if (BatteryStatus->Voltage == BATTERY_UNKNOWN_VOLTAGE) { BatteryStatus->Voltage = localBatteryStatus->Voltage; } else if ((localBatteryStatus->Voltage > BatteryStatus->Voltage) && (localBatteryStatus->Voltage != BATTERY_UNKNOWN_VOLTAGE)) { BatteryStatus->Voltage = localBatteryStatus->Voltage; } // // The Current should just be total of all currents encountered. This could // also possibly be "Unknown" for CMBatt, and if so we should not use it // in the calculation. // if (BatteryStatus->Rate == BATTERY_UNKNOWN_RATE) { BatteryStatus->Rate = localBatteryStatus->Rate; } else if (localBatteryStatus->Rate != BATTERY_UNKNOWN_RATE) { BatteryStatus->Rate += localBatteryStatus->Rate; } } // if (batt->Tag != BATTERY_TAG_INVALID) ExAcquireFastMutex (&compBatt->ListMutex); CompbattReleaseDeleteLock(&batt->DeleteLock); } // for (entry = gBatteries.Flink; entry != &gBatteries; entry = entry->Flink) ExReleaseFastMutex (&compBatt->ListMutex); // // If one battery was discharging while another was charging // Assume that it is discharging. (This could happen with a UPS attached) // if ((BatteryStatus->PowerState & BATTERY_CHARGING) && (BatteryStatus->PowerState & BATTERY_DISCHARGING)) { BatteryStatus->PowerState &= ~BATTERY_CHARGING; } // // Make sure nothing changed while reading batteries. // if ((BatteryTag != compBatt->Info.Tag) || !(compBatt->Info.Valid & VALID_TAG)) { return STATUS_NO_SUCH_DEVICE; } // // Save the status in the composites cache // if (NT_SUCCESS(status)) { RtlCopyMemory (&compBatt->Info.Status, BatteryStatus, sizeof (BATTERY_STATUS)); compBatt->Info.StatusTimeStamp = wallClockTime; BattPrint (BATT_DATA, ("CompBatt: Composite's new Status\n" "-------- PowerState = %x\n" "-------- Capacity = %x\n" "-------- Voltage = %x\n" "-------- Rate = %x\n", compBatt->Info.Status.PowerState, compBatt->Info.Status.Capacity, compBatt->Info.Status.Voltage, compBatt->Info.Status.Rate) ); } BattPrint (BATT_TRACE, ("CompBatt: EXITING QueryStatus\n")); return status; } NTSTATUS CompBattSetStatusNotify ( IN PVOID Context, IN ULONG BatteryTag, IN PBATTERY_NOTIFY BatteryNotify ) /*++ Routine Description: Called by the class driver to set the batteries current notification setting. When the battery trips the notification, one call to BatteryClassStatusNotify is issued. If an error is returned, the class driver will poll the battery status - primarily for capacity changes. Which is to say the miniport should still issue BatteryClass- StatusNotify whenever the power state changes. Arguments: Context - Miniport context value for battery BatteryTag - Tag of current battery BatteryNotify - The notification setting Return Value: Status --*/ { PCOMPOSITE_ENTRY batt; PLIST_ENTRY entry; PCOMPOSITE_BATTERY compBatt; BATTERY_STATUS batteryStatus; LONG totalRate = 0; ULONG delta; ULONG highCapacityDelta; ULONG lowCapacityDelta; NTSTATUS status; BOOLEAN inconsistent = FALSE; ULONG battCount = 0; BattPrint (BATT_TRACE, ("CompBatt: ENTERING SetStatusNotify\n")); compBatt = (PCOMPOSITE_BATTERY) Context; // // Check to see if this is the right battery // if ((BatteryTag != compBatt->Info.Tag) || !(compBatt->Info.Valid & VALID_TAG)) { return STATUS_NO_SUCH_DEVICE; } // // Refresh the composite battery status cache if necessary. // status = CompBattQueryStatus (compBatt, BatteryTag, &batteryStatus); if (!NT_SUCCESS(status)) { return status; } // // Save away the composite notification parameters for future reference // compBatt->Wait.PowerState = BatteryNotify->PowerState; compBatt->Wait.LowCapacity = BatteryNotify->LowCapacity; compBatt->Wait.HighCapacity = BatteryNotify->HighCapacity; compBatt->Info.Valid |= VALID_NOTIFY; BattPrint (BATT_DATA, ("CompBatt: Got SetStatusNotify\n" "-------- PowerState = %x\n" "-------- LowCapacity = %x\n" "-------- HighCapacity = %x\n", compBatt->Wait.PowerState, compBatt->Wait.LowCapacity, compBatt->Wait.HighCapacity) ); // // Compute capacity deltas based on the total system capacity // lowCapacityDelta = compBatt->Info.Status.Capacity - BatteryNotify->LowCapacity; highCapacityDelta = BatteryNotify->HighCapacity - compBatt->Info.Status.Capacity; // // Run through the list of batteries and add up the total rate // // // Hold Mutex for this entire loop, since this loop doesn't call any drivers, etc // ExAcquireFastMutex (&compBatt->ListMutex); for (entry = compBatt->Batteries.Flink; entry != &compBatt->Batteries; entry = entry->Flink) { batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries); if (!NT_SUCCESS (CompbattAcquireDeleteLock(&batt->DeleteLock))) { continue; } if (!(batt->Info.Valid & VALID_TAG) || (batt->Info.Status.Rate == BATTERY_UNKNOWN_RATE)) { CompbattReleaseDeleteLock(&batt->DeleteLock); continue; } battCount++; if (((batt->Info.Status.PowerState & BATTERY_DISCHARGING) && (batt->Info.Status.Rate >= 0)) || ((batt->Info.Status.PowerState & BATTERY_CHARGING) && (batt->Info.Status.Rate <= 0)) || (((batt->Info.Status.PowerState & (BATTERY_CHARGING | BATTERY_DISCHARGING)) == 0) && (batt->Info.Status.Rate != 0))) { inconsistent = TRUE; BattPrint (BATT_ERROR, ("CompBatt: PowerState 0x%08x does not match Rate 0x%08x\n", batt->Info.Status.PowerState, batt->Info.Status.Rate)); } if (((batt->Info.Status.Rate < 0) ^ (totalRate < 0)) && (batt->Info.Status.Rate != 0) && (totalRate != 0)) { inconsistent = TRUE; BattPrint (BATT_ERROR, ("CompBatt: It appears that one battery is charging while another is discharging.\n" " This situation is not handled correctly.\n")); } totalRate += batt->Info.Status.Rate; CompbattReleaseDeleteLock(&batt->DeleteLock); } ExReleaseFastMutex (&compBatt->ListMutex); // // Run through the list of batteries and update the new wait status params // ExAcquireFastMutex (&compBatt->ListMutex); for (entry = compBatt->Batteries.Flink; entry != &compBatt->Batteries; entry = entry->Flink) { batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries); if (!NT_SUCCESS (CompbattAcquireDeleteLock(&batt->DeleteLock))) { continue; } ExReleaseFastMutex (&compBatt->ListMutex); if (!(batt->Info.Valid & VALID_TAG) || (batt->Info.Status.Capacity == BATTERY_UNKNOWN_CAPACITY)) { batt->Wait.LowCapacity = BATTERY_UNKNOWN_CAPACITY; batt->Wait.HighCapacity = BATTERY_UNKNOWN_CAPACITY; #if DEBUG if (batt->Info.Status.Capacity == BATTERY_UNKNOWN_CAPACITY) { BattPrint (BATT_DEBUG, ("CompBattSetStatusNotify: Unknown Capacity encountered.\n")); } #endif ExAcquireFastMutex (&compBatt->ListMutex); CompbattReleaseDeleteLock(&batt->DeleteLock); continue; } // // Adjust the LowCapacity alarm // // // Calculate the portion of the composite battery delta that belongs to // this battery. // if (inconsistent) { // // If data returned from batteries was inconsistent, don't do anything intelligent. // Just divide the notifications evenly between the batteries as if they were // draining at the same rate. This will most likely result in early notification, // but by that time the data on the batteries ought to have settled down. // delta = lowCapacityDelta/battCount; } else if (totalRate != 0) { delta = (ULONG) (((LONGLONG) lowCapacityDelta * batt->Info.Status.Rate) / totalRate); } else { // // If total rate is zero, we would expect no change in battery // capacity, so we should get notified of any. // delta = 0; } // // Check for underflow on low capacity // if (batt->Info.Status.Capacity > delta) { batt->Wait.LowCapacity = batt->Info.Status.Capacity - delta; } else { // // If there is still some charge in the battery set the LowCapacity // alarm to 1, else to 0. // // No need to do that. If this battery runs out, it doesn't // need to notify. One of the other batteries will be notifying // right away. If there isn't another battery, this shouldn't // happen. BattPrint (BATT_NOTE, ("CompBatt: Unexpectedly huge delta encountered. \n" " Capacity = %08x\n" " LowCapcityDelta = %08x\n" " Rate = %08x\n" " TotalRate = %08x\n", batt->Info.Status.Capacity, lowCapacityDelta, batt->Info.Status.Rate, totalRate)); batt->Wait.LowCapacity = 0; } // // Adjust the HighCapacity alarm for charging batteries only // // // Calculate the portion of the composite battery delta that belongs to // this battery. // if (inconsistent) { delta = highCapacityDelta/battCount; } else if (totalRate != 0) { delta = (ULONG) (((LONGLONG) highCapacityDelta * batt->Info.Status.Rate) / totalRate); } else { // // If total rate is zero, we would expect no change in battery // capacity, so we should get notified of any. // delta = 0; } // // Check for overflow on high capacity. // Allow setting the percentage above full charged capacity // since some batteries do that when new. // if ((MAX_HIGH_CAPACITY - delta) < batt->Wait.HighCapacity) { batt->Wait.HighCapacity = MAX_HIGH_CAPACITY; } else { batt->Wait.HighCapacity = batt->Info.Status.Capacity + delta; } // // If we're currently waiting, and the parameters are in // conflict, get the irp back to reset it // if (batt->State == CB_ST_GET_STATUS && (batt->Wait.PowerState != batt->IrpBuffer.Wait.PowerState || batt->Wait.LowCapacity != batt->IrpBuffer.Wait.LowCapacity || batt->Wait.HighCapacity != batt->IrpBuffer.Wait.HighCapacity)) { IoCancelIrp (batt->StatusIrp); } ExAcquireFastMutex (&compBatt->ListMutex); CompbattReleaseDeleteLock(&batt->DeleteLock); } ExReleaseFastMutex (&compBatt->ListMutex); // // Make sure nothing changed while reading batteries. // if ((BatteryTag != compBatt->Info.Tag) || !(compBatt->Info.Valid & VALID_TAG)) { return STATUS_NO_SUCH_DEVICE; } BattPrint (BATT_TRACE, ("CompBatt: EXITING SetStatusNotify\n")); return STATUS_SUCCESS; } NTSTATUS CompBattDisableStatusNotify ( IN PVOID Context ) /*++ Routine Description: Called by the class driver to disable the notification setting for the battery supplied by Context. Note, to disable a setting does not require the battery tag. Any notification is to be masked off until a subsequent call to SmbBattSetStatusNotify. Arguments: Context - Miniport context value for battery Return Value: Status --*/ { PCOMPOSITE_ENTRY batt; PLIST_ENTRY entry; PCOMPOSITE_BATTERY compBatt; BattPrint (BATT_TRACE, ("CompBatt: ENTERING DisableStatusNotify\n")); compBatt = (PCOMPOSITE_BATTERY) Context; // // Run through the list of batteries and disable the wait status params // Hold mutex for entire loop, since loop doesn't make any calls. ExAcquireFastMutex (&compBatt->ListMutex); for (entry = compBatt->Batteries.Flink; entry != &compBatt->Batteries; entry = entry->Flink) { batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries); batt->Wait.LowCapacity = MIN_LOW_CAPACITY; batt->Wait.HighCapacity = MAX_HIGH_CAPACITY; } ExReleaseFastMutex (&compBatt->ListMutex); BattPrint (BATT_TRACE, ("CompBatt: EXITING DisableStatusNotify\n")); return STATUS_SUCCESS; } NTSTATUS CompBattGetBatteryInformation ( IN PBATTERY_INFORMATION TotalBattInfo, IN PCOMPOSITE_BATTERY CompBatt ) /*++ Routine Description: The routine loops through the batteries in the system and queries them for information. It then forms a composite representation of this information to send back to the caller. Arguments: TotalBattInfo - Buffer to place the composite battery information in Return Value: STATUS_SUCCESS or the status returned by the Ioctl to the battery. --*/ { NTSTATUS status; PBATTERY_INFORMATION battInfo; PCOMPOSITE_ENTRY batt; PLIST_ENTRY entry; BATTERY_QUERY_INFORMATION bInfo; BattPrint (BATT_TRACE, ("CompBatt: ENTERING GetBatteryInformation\n")); TotalBattInfo->DefaultAlert1 = 0; TotalBattInfo->DefaultAlert2 = 0; TotalBattInfo->CriticalBias = 0; status = STATUS_SUCCESS; // // Run through the list of batteries getting the information // ExAcquireFastMutex (&CompBatt->ListMutex); for (entry = CompBatt->Batteries.Flink; entry != &CompBatt->Batteries; entry = entry->Flink) { batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries); if (!NT_SUCCESS (CompbattAcquireDeleteLock(&batt->DeleteLock))) { continue; } ExReleaseFastMutex (&CompBatt->ListMutex); bInfo.BatteryTag = batt->Info.Tag; bInfo.InformationLevel = BatteryInformation; bInfo.AtRate = 0; battInfo = &batt->Info.Info; if (batt->Info.Tag != BATTERY_TAG_INVALID) { if (!(batt->Info.Valid & VALID_INFO)) { // // issue IOCTL to device // RtlZeroMemory (battInfo, sizeof(BATTERY_INFORMATION)); status = BatteryIoctl (IOCTL_BATTERY_QUERY_INFORMATION, batt->DeviceObject, &bInfo, sizeof (bInfo), battInfo, sizeof (BATTERY_INFORMATION), FALSE); if (!NT_SUCCESS(status)) { if (status == STATUS_DEVICE_REMOVED) { // // If one device is removed, that invalidates the tag. // status = STATUS_NO_SUCH_DEVICE; } ExAcquireFastMutex (&CompBatt->ListMutex); CompbattReleaseDeleteLock(&batt->DeleteLock); break; } BattPrint (BATT_DATA, ("CompBattGetBatteryInformation: Read individual BATTERY_INFORMATION\n" "-------- Capabilities = %x\n" "-------- Technology = %x\n" "-------- Chemistry[4] = %x\n" "-------- DesignedCapacity = %x\n" "-------- FullChargedCapacity = %x\n" "-------- DefaultAlert1 = %x\n" "-------- DefaultAlert2 = %x\n" "-------- CriticalBias = %x\n" "-------- CycleCount = %x\n", battInfo->Capabilities, battInfo->Technology, battInfo->Chemistry[4], battInfo->DesignedCapacity, battInfo->FullChargedCapacity, battInfo->DefaultAlert1, battInfo->DefaultAlert2, battInfo->CriticalBias, battInfo->CycleCount) ); batt->Info.Valid |= VALID_INFO; } // if (!(batt->Info.Valid & VALID_INFO)) // // Logically OR the capabilities // TotalBattInfo->Capabilities |= battInfo->Capabilities; // // Add the designed capacities. If this is UNKNOWN (possible // with the control method batteries, don't add them in. // if (battInfo->DesignedCapacity != BATTERY_UNKNOWN_CAPACITY) { TotalBattInfo->DesignedCapacity += battInfo->DesignedCapacity; } if (battInfo->FullChargedCapacity != BATTERY_UNKNOWN_CAPACITY) { TotalBattInfo->FullChargedCapacity += battInfo->FullChargedCapacity; } if (TotalBattInfo->DefaultAlert1 < battInfo->DefaultAlert1) { TotalBattInfo->DefaultAlert1 = battInfo->DefaultAlert1; } if (TotalBattInfo->DefaultAlert2 < battInfo->DefaultAlert2) { TotalBattInfo->DefaultAlert2 = battInfo->DefaultAlert2; } if (TotalBattInfo->CriticalBias < battInfo->CriticalBias) { TotalBattInfo->CriticalBias = battInfo->CriticalBias; } } // if (batt->Tag != BATTERY_TAG_INVALID) ExAcquireFastMutex (&CompBatt->ListMutex); CompbattReleaseDeleteLock(&batt->DeleteLock); } // for (entry = gBatteries.Flink; entry != &gBatteries; entry = entry->Flink) ExReleaseFastMutex (&CompBatt->ListMutex); // // Save the battery information in the composite battery cache // if (NT_SUCCESS(status)) { // // Check to see if we have an UNKNOWN full charge capacity. If so, set this // to the design capacity. // if (TotalBattInfo->FullChargedCapacity == 0) { TotalBattInfo->FullChargedCapacity = TotalBattInfo->DesignedCapacity; } BattPrint (BATT_DATA, ("CompBattGetBatteryInformation: Returning BATTERY_INFORMATION\n" "-------- Capabilities = %x\n" "-------- Technology = %x\n" "-------- Chemistry[4] = %x\n" "-------- DesignedCapacity = %x\n" "-------- FullChargedCapacity = %x\n" "-------- DefaultAlert1 = %x\n" "-------- DefaultAlert2 = %x\n" "-------- CriticalBias = %x\n" "-------- CycleCount = %x\n", TotalBattInfo->Capabilities, TotalBattInfo->Technology, TotalBattInfo->Chemistry[4], TotalBattInfo->DesignedCapacity, TotalBattInfo->FullChargedCapacity, TotalBattInfo->DefaultAlert1, TotalBattInfo->DefaultAlert2, TotalBattInfo->CriticalBias, TotalBattInfo->CycleCount) ); RtlCopyMemory (&CompBatt->Info.Info, TotalBattInfo, sizeof(BATTERY_INFORMATION)); CompBatt->Info.Valid |= VALID_INFO; } BattPrint (BATT_TRACE, ("CompBatt: EXITING GetBatteryInformation\n")); return status; } NTSTATUS CompBattGetBatteryGranularity ( IN PBATTERY_REPORTING_SCALE GranularityBuffer, IN PCOMPOSITE_BATTERY CompBatt ) /*++ Routine Description: The routine queries all the batteries in the system to get their granularity settings. It then returns the setting that has the finest granularity in each range. Arguments: GranularityBuffer - Buffer for containing the results of the query Return Value: STATUS_SUCCESS or the status returned by the Ioctl to the battery. --*/ { NTSTATUS status; BATTERY_REPORTING_SCALE localGranularity[4]; PCOMPOSITE_ENTRY batt; PLIST_ENTRY entry; ULONG i; BATTERY_QUERY_INFORMATION bInfo; BattPrint (BATT_TRACE, ("CompBatt: ENTERING GetBatteryGranularity\n")); GranularityBuffer[0].Granularity = 0xFFFFFFFF; GranularityBuffer[1].Granularity = 0xFFFFFFFF; GranularityBuffer[2].Granularity = 0xFFFFFFFF; GranularityBuffer[3].Granularity = 0xFFFFFFFF; // // Run through the list of batteries getting the granularity // ExAcquireFastMutex (&CompBatt->ListMutex); for (entry = CompBatt->Batteries.Flink; entry != &CompBatt->Batteries; entry = entry->Flink) { batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries); if (!NT_SUCCESS (CompbattAcquireDeleteLock(&batt->DeleteLock))) { continue; } ExReleaseFastMutex (&CompBatt->ListMutex); bInfo.BatteryTag = batt->Info.Tag; bInfo.InformationLevel = BatteryGranularityInformation; if (batt->Info.Tag != BATTERY_TAG_INVALID) { // // issue IOCTL to device // RtlZeroMemory (&localGranularity[0], sizeof(localGranularity)); status = BatteryIoctl (IOCTL_BATTERY_QUERY_INFORMATION, batt->DeviceObject, &bInfo, sizeof (bInfo), &localGranularity, sizeof (localGranularity), FALSE); if (!NT_SUCCESS(status)) { if (status == STATUS_DEVICE_REMOVED) { // // If one device is removed, that invalidates the tag. // status = STATUS_NO_SUCH_DEVICE; } ExAcquireFastMutex (&CompBatt->ListMutex); CompbattReleaseDeleteLock(&batt->DeleteLock); break; } // // Check for the best granularity in each range. // for (i = 0; i < 4; i++) { if (localGranularity[i].Granularity) { if (localGranularity[i].Granularity < GranularityBuffer[i].Granularity) { GranularityBuffer[i].Granularity = localGranularity[i].Granularity; } GranularityBuffer[i].Capacity = localGranularity[i].Capacity; } } } // if (batt->Tag != BATTERY_TAG_INVALID) ExAcquireFastMutex (&CompBatt->ListMutex); CompbattReleaseDeleteLock(&batt->DeleteLock); } // for (entry = gBatteries.Flink; entry != &gBatteries; entry = entry->Flink) ExReleaseFastMutex (&CompBatt->ListMutex); BattPrint (BATT_TRACE, ("CompBatt: EXITING GetBatteryGranularity\n")); return STATUS_SUCCESS; } NTSTATUS CompBattGetEstimatedTime ( IN PULONG TimeBuffer, IN PCOMPOSITE_BATTERY CompBatt ) /*++ Routine Description: The routine queries all the batteries in the system to get their estimated time left. If one of the batteries in the system does not support this function then an error is returned. Arguments: TimeBuffer - Buffer for containing cumulative time left Return Value: STATUS_SUCCESS or the status returned by the Ioctl to the battery. --*/ { NTSTATUS status; LONG localBuffer = 0; PCOMPOSITE_ENTRY batt; PLIST_ENTRY entry; BATTERY_QUERY_INFORMATION bInfo; BATTERY_STATUS batteryStatus; LONG atRate = 0; BattPrint (BATT_TRACE, ("CompBatt: ENTERING GetEstimatedTime\n")); *TimeBuffer = BATTERY_UNKNOWN_TIME; // // Refresh the composite battery status cache if necessary. // status = CompBattQueryStatus (CompBatt, CompBatt->Info.Tag, &batteryStatus); if (!NT_SUCCESS(status)) { return status; } // // If we're on AC then our estimated run time is invalid. // if (CompBatt->Info.Status.PowerState & BATTERY_POWER_ON_LINE) { return STATUS_SUCCESS; } // // We are on battery power and may have more than one battery in the system. // // We need to find the total rate of power being drawn from all batteries // then we need to ask how long each battery would last at that rate (as // if they were being discharged one at a time). This should give us a fairly // good measure of how long it will last. // // To find the power being drawn we read the devide the remaining capacity by // the estimated time rather than simply reading the rate. This is because the // rate is theoretically the instantanious current wereas the estimated time // should be based on average usage. This isn't the case for control method // batteries, but it is for smart batteries, and could be for others as well. // ExAcquireFastMutex (&CompBatt->ListMutex); for (entry = CompBatt->Batteries.Flink; entry != &CompBatt->Batteries; entry = entry->Flink) { batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries); if (!NT_SUCCESS (CompbattAcquireDeleteLock(&batt->DeleteLock))) { continue; } ExReleaseFastMutex (&CompBatt->ListMutex); if (batt->Info.Valid & VALID_TAG) { bInfo.BatteryTag = batt->Info.Tag; bInfo.InformationLevel = BatteryEstimatedTime; bInfo.AtRate = 0; // // issue IOCTL to device // status = BatteryIoctl (IOCTL_BATTERY_QUERY_INFORMATION, batt->DeviceObject, &bInfo, sizeof (bInfo), &localBuffer, sizeof (localBuffer), FALSE); if ((localBuffer != BATTERY_UNKNOWN_TIME) && (localBuffer != 0) && (NT_SUCCESS(status))) { atRate -= ((long)batt->Info.Status.Capacity)*3600 / localBuffer; BattPrint (BATT_NOTE, ("CompBattGetEstimatedTime: EstTime: %08x, Capacity: %08x, cumulative AtRate: %08x\n", localBuffer, batt->Info.Status.Capacity, atRate)); } else { BattPrint (BATT_NOTE, ("CompBattGetEstimatedTime: Bad Estimated time. Status: %08x, localBuffer: %08x, Capacity: %08x, cumulative AtRate: %08x\n", status, localBuffer, batt->Info.Status.Capacity, atRate)); } } ExAcquireFastMutex (&CompBatt->ListMutex); CompbattReleaseDeleteLock(&batt->DeleteLock); } ExReleaseFastMutex (&CompBatt->ListMutex); BattPrint (BATT_NOTE, ("CompBattGetEstimatedTime: using atRate - %x\n", atRate)); // // Did we find a battery? // if (atRate == 0) { // Code could be added to here to handle batteries that return // estimated runtime, but not rate information. return STATUS_SUCCESS; } // // Run through the list of batteries getting their estimated time // ExAcquireFastMutex (&CompBatt->ListMutex); for (entry = CompBatt->Batteries.Flink; entry != &CompBatt->Batteries; entry = entry->Flink) { batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries); if (!NT_SUCCESS (CompbattAcquireDeleteLock(&batt->DeleteLock))) { continue; } ExReleaseFastMutex (&CompBatt->ListMutex); bInfo.BatteryTag = batt->Info.Tag; bInfo.InformationLevel = BatteryEstimatedTime; bInfo.AtRate = atRate; if (batt->Info.Valid & VALID_TAG) { // // issue IOCTL to device // status = BatteryIoctl (IOCTL_BATTERY_QUERY_INFORMATION, batt->DeviceObject, &bInfo, sizeof (bInfo), &localBuffer, sizeof (localBuffer), FALSE); BattPrint (BATT_NOTE, ("CompBattGetEstimatedTime: Status: %08x, EstTime: %08x\n", status, localBuffer)); if (!NT_SUCCESS(status)) { // // This could be an invalid device request for this battery. // Continue with thte next battery. // if (status == STATUS_DEVICE_REMOVED) { // // If one device is removed, that invalidates the tag. // status = STATUS_NO_SUCH_DEVICE; } ExAcquireFastMutex (&CompBatt->ListMutex); CompbattReleaseDeleteLock(&batt->DeleteLock); continue; } // // Add the estimated time. // if (localBuffer != BATTERY_UNKNOWN_TIME) { if (*TimeBuffer == BATTERY_UNKNOWN_TIME) { *TimeBuffer = localBuffer; } else { *TimeBuffer += localBuffer; } } BattPrint (BATT_DATA, ("CompBattGetEstimatedTime: cumulative time: %08x\n", *TimeBuffer)); } // if (batt->Tag != BATTERY_TAG_INVALID) ExAcquireFastMutex (&CompBatt->ListMutex); CompbattReleaseDeleteLock(&batt->DeleteLock); } // for (entry = gBatteries.Flink; entry != &gBatteries; entry = entry->Flink) ExReleaseFastMutex (&CompBatt->ListMutex); BattPrint (BATT_TRACE, ("CompBatt: EXITING GetEstimatedTime\n")); return STATUS_SUCCESS; } NTSTATUS CompBattMonitorIrpComplete ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) /*++ Routine Description: Constantly keeps an irp at the battery either querying for the tag or the status. This routine fills in the irp, sets itself up as the completion routine, and then resends the irp. Arguments: DeviceObject - Device object for the battery sent the irp. Note: In this case DeviceObject is always NULL, so don't use it. Irp - Current irp to work with Context - Currently unused Return Value: TRUE if there are no changes, FALSE otherwise. --*/ { PIO_STACK_LOCATION IrpSp; PCOMPOSITE_ENTRY Batt; BattPrint (BATT_TRACE, ("CompBatt: ENTERING MonitorIrpComplete\n")); IrpSp = IoGetCurrentIrpStackLocation(Irp); Batt = IrpSp->Parameters.Others.Argument2; // // We always want to queue a work item to recycle the IRP. There were too many // problems that could happen trying to recycle in the completion routine. // // If this driver ever gets reworked, it could be done that way, but it would take // more time to get right than I have now. Queueing a work item is the safe thing // to do. // ExQueueWorkItem (&Batt->WorkItem, DelayedWorkQueue); return STATUS_MORE_PROCESSING_REQUIRED; } VOID CompBattMonitorIrpCompleteWorker ( IN PVOID Context ) /*++ Routine Description: This is either queued, or called by the completion routine. Constantly keeps an irp at the battery either querying for the tag or the status. This routine fills in the irp, sets itself up as the completion routine, and then resends the irp. Arguments: Context - Composite battery entry. Return Value: TRUE if there are no changes, FALSE otherwise. --*/ { PCOMPOSITE_ENTRY Batt = (PCOMPOSITE_ENTRY) Context; PDEVICE_OBJECT DeviceObject = Batt->DeviceObject; PIRP Irp = Batt->StatusIrp; PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); PCOMPOSITE_BATTERY compBatt = IrpSp->Parameters.Others.Argument1; BATTERY_STATUS battStatus; ULONG oldPowerState; NTSTATUS status; BattPrint (BATT_TRACE, ("CompBatt: ENTERING MonitorIrpCompleteWorker\n")); IrpSp = IoGetNextIrpStackLocation(Irp); // // Reissue irp to battery to wait for a status change // if (NT_SUCCESS(Irp->IoStatus.Status) || Irp->IoStatus.Status == STATUS_CANCELLED) { switch (Batt->State) { case CB_ST_GET_TAG: // // A battery was just inserted, so the IOCTL_BATTERY_Query_TAG completed. // BattPrint (BATT_NOTE, ("CompBattMonitorIrpCompleteWorker: got tag for %x\n", Batt->DeviceObject)); // // Update the tag, and wait on status // Batt->Wait.BatteryTag = Batt->IrpBuffer.Tag; Batt->Info.Tag = Batt->IrpBuffer.Tag; Batt->Info.Valid = VALID_TAG; // Invalidate all cached info. compBatt->Info.Valid = 0; BattPrint (BATT_NOTE, ("CompBattMonitorIrpCompleteWorker: calling StatusNotify\n")); BatteryClassStatusNotify (compBatt->Class); break; case CB_ST_GET_STATUS: // // IOCTL_BATTERY_QUERY_STATUS just completed. This could mean that the // battery state changed, or the charge has left the acceptable range. // If the battery was removed, it would not get here. // BattPrint (BATT_NOTE, ("CompBattMonitorIrpCompleteWorker: got status for %x\n", Batt->DeviceObject)); if (!(Irp->IoStatus.Status == STATUS_CANCELLED)) { BattPrint (BATT_NOTE, ("Battery's state is\n" "-------- PowerState = %x\n" "-------- Capacity = %x\n" "-------- Voltage = %x\n" "-------- Rate = %x\n", Batt->IrpBuffer.Status.PowerState, Batt->IrpBuffer.Status.Capacity, Batt->IrpBuffer.Status.Voltage, Batt->IrpBuffer.Status.Rate) ); // // Battery status completed sucessfully. // Update our wait, and wait some more // Batt->Wait.PowerState = Batt->IrpBuffer.Status.PowerState; if (Batt->IrpBuffer.Status.Capacity != BATTERY_UNKNOWN_CAPACITY) { if (Batt->Wait.HighCapacity < Batt->IrpBuffer.Status.Capacity) { Batt->Wait.HighCapacity = Batt->IrpBuffer.Status.Capacity; } if (Batt->Wait.LowCapacity > Batt->IrpBuffer.Status.Capacity) { Batt->Wait.LowCapacity = Batt->IrpBuffer.Status.Capacity; } } else { BattPrint (BATT_DEBUG, ("CompBattMonitorIrpCompleteWorker: Unknown Capacity encountered.\n")); Batt->Wait.LowCapacity = BATTERY_UNKNOWN_CAPACITY; Batt->Wait.HighCapacity = BATTERY_UNKNOWN_CAPACITY; } RtlCopyMemory (&Batt->Info.Status, &Batt->IrpBuffer.Status, sizeof(BATTERY_STATUS)); // // Set timestamp to Now. // Batt->Info.StatusTimeStamp = KeQueryInterruptTime (); // // Recalculate the charge/discharge policy and change if needed // // Don't change default BIOS policy for discharge. // CompBattChargeDischarge (compBatt); // // Save the composite's old PowerState and recalculate the composites // overall status. // oldPowerState = compBatt->Info.Status.PowerState; compBatt->Info.StatusTimeStamp = 0; // -CACHE_STATUS_TIMEOUT; // Invalidate cache CompBattQueryStatus (compBatt, compBatt->Info.Tag, &battStatus); // // Check to see if we need to send a notification on the composite // battery. This will be done in a couple of different cases: // // - There is a VALID_NOTIFY and there was a change in the composite's // PowerState, or it went below the Notify.LowCapacity, or it went // above the Notify.HighCapacity. // // - There is no VALID_NOTIFY (SetStatusNotify) and there was a change // in the composite's PowerState. // if (compBatt->Info.Valid & VALID_NOTIFY) { if ((compBatt->Info.Status.PowerState != compBatt->Wait.PowerState) || (compBatt->Info.Status.Capacity < compBatt->Wait.LowCapacity) || (compBatt->Info.Status.Capacity > compBatt->Wait.HighCapacity)) { BattPrint (BATT_NOTE, ("CompBattMonitorIrpCompleteWorker: calling StatusNotify\n")); BatteryClassStatusNotify (compBatt->Class); } } else { if (compBatt->Info.Status.PowerState != oldPowerState) { BattPrint (BATT_NOTE, ("CompBattMonitorIrpCompleteWorker: calling StatusNotify\n")); BatteryClassStatusNotify (compBatt->Class); } } } else { BattPrint (BATT_NOTE, ("CompBattMonitorIrpCompleteWorker: recycling cancelled status irp\n")); } break; default: BattPrint (BATT_ERROR, ("CompBatt: internal error - bad state\n")); break; } // // Set irp to issue query // #if DEBUG if ((Batt->Wait.LowCapacity > 0xf0000000) && (Batt->Wait.LowCapacity != BATTERY_UNKNOWN_CAPACITY)) { BattPrint (BATT_ERROR, ("CompBattMonitorIrpCompleteWorker: LowCapacity < 0, LowCapacity =%x\n", Batt->Wait.LowCapacity)); ASSERT(FALSE); } #endif Batt->State = CB_ST_GET_STATUS; Batt->Wait.Timeout = (ULONG) -1; RtlCopyMemory (&Batt->IrpBuffer.Wait, &Batt->Wait, sizeof (Batt->Wait)); IrpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_BATTERY_QUERY_STATUS; IrpSp->Parameters.DeviceIoControl.InputBufferLength = sizeof(Batt->IrpBuffer.Wait); IrpSp->Parameters.DeviceIoControl.OutputBufferLength = sizeof(Batt->IrpBuffer.Status); BattPrint (BATT_NOTE, ("CompBattMonitorIrpCompleteWorker: waiting for status, Irp - %x\n", Irp)); BattPrint (BATT_NOTE, ("-------- PowerState = %x\n" "-------- LowCapacity = %x\n" "-------- HighCapacity = %x\n", Batt->Wait.PowerState, Batt->Wait.LowCapacity, Batt->Wait.HighCapacity) ); } else if (Irp->IoStatus.Status == STATUS_DEVICE_REMOVED) { // // If the Battery class driver returned STATUS_DEVICE_REMOVED, then the // device has been removed, so we need to quit sending IRPs. // BattPrint (BATT_NOTE, ("Compbatt: MonitorIrpCompleteWorker detected device removal.\n")); CompBattRemoveBattery (&Batt->BattName, compBatt); IoFreeIrp (Irp); return; } else { BattPrint (BATT_NOTE, ("CompBattMonitorIrpCompleteWorker: battery disappeared (status:%08x)\n", Irp->IoStatus.Status)); // // Invalidate the battery's tag, and the individual battery's cache, and // recalculate the composite's tag // Batt->Info.Tag = BATTERY_TAG_INVALID; Batt->Info.Valid = 0; compBatt->Info.Valid = 0; compBatt->Info.StatusTimeStamp = 0; // Invalidate cache BattPrint (BATT_NOTE, ("CompBattMonitorIrpCompleteWorker: calling StatusNotify\n")); BatteryClassStatusNotify (compBatt->Class); Batt->State = CB_ST_GET_TAG; IrpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_BATTERY_QUERY_TAG; IrpSp->Parameters.DeviceIoControl.InputBufferLength = sizeof(ULONG); IrpSp->Parameters.DeviceIoControl.OutputBufferLength = sizeof(ULONG); Batt->IrpBuffer.Tag = (ULONG) -1; BattPrint (BATT_NOTE, ("CompBattMonitorIrpCompleteWorker: getting tag (last error %x)\n", Irp->IoStatus.Status)); } IrpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL; Irp->AssociatedIrp.SystemBuffer = &Batt->IrpBuffer; Irp->PendingReturned = FALSE; Irp->Cancel = FALSE; IoSetCompletionRoutine (Irp, CompBattMonitorIrpComplete, NULL, TRUE, TRUE, TRUE); status = IoCallDriver (Batt->DeviceObject, Irp); BattPrint (BATT_NOTE, ("Compbatt: MonitorIrpCompleteWorker: CallDriver returned 0x%lx.\n", status)); BattPrint (BATT_TRACE, ("CompBatt: EXITING MonitorIrpCompleteWorker\n")); return; } VOID CompBattRecalculateTag ( IN PCOMPOSITE_BATTERY CompBatt ) /*++ Routine Description: The routine checks to see if there is still a valid battery in the composite's list. If so, the composite tag is bumped. This also invalidates all but the composite's tag. Arguments: CompBatt - Composite device extension Return Value: none --*/ { PCOMPOSITE_ENTRY batt; PLIST_ENTRY entry; BattPrint (BATT_TRACE, ("CompBatt: ENTERING CompBattRecalculateTag\n")); // // Run through the list of batteries looking for one that is still good // ExAcquireFastMutex (&CompBatt->ListMutex); for (entry = CompBatt->Batteries.Flink; entry != &CompBatt->Batteries; entry = entry->Flink) { batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries); if (batt->Info.Valid & VALID_TAG) { CompBatt->Info.Valid |= VALID_TAG; CompBatt->Info.Tag = CompBatt->NextTag++; break; } CompBatt->Info.Tag = BATTERY_TAG_INVALID; } ExReleaseFastMutex (&CompBatt->ListMutex); BattPrint (BATT_TRACE, ("CompBatt: EXITING CompBattRecalculateTag\n")); } VOID CompBattChargeDischarge ( IN PCOMPOSITE_BATTERY CompBatt ) /*++ Routine Description: The routine calculates which battery should be charging/discharging and attempts to make it so. Policy is summarized below: CHARGING POLICY: The most charged battery that is also less than 90% of maximum capacity is charged first. DISCHARGING POLICY: The most discharged battery that is also more than 2% of empty is discharged first, until it is empty. Arguments: CompBatt - Composite device extension Return Value: NONE. Nobody really cares if this works or not, since it won't work on all batteries. --*/ { PCOMPOSITE_ENTRY batt; PLIST_ENTRY entry; ULONG capacity; ULONG percentCapacity; ULONG targetCapacity; PCOMPOSITE_ENTRY targetBattery; BATTERY_SET_INFORMATION battSetInfo; NTSTATUS status; BattPrint (BATT_TRACE, ("CompBatt: ENTERING CompBattChargeDischarge\n")); targetBattery = NULL; // // Check if AC is present in the system. // if (CompBatt->Info.Status.PowerState & BATTERY_POWER_ON_LINE) { // // AC is present. Examine all batteries, looking for the most // charged one that is less than 90% full. // targetCapacity = 0; battSetInfo.InformationLevel = BatteryCharge; ExAcquireFastMutex (&CompBatt->ListMutex); for (entry = CompBatt->Batteries.Flink; entry != &CompBatt->Batteries; entry = entry->Flink) { batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries); if (!NT_SUCCESS (CompbattAcquireDeleteLock(&batt->DeleteLock))) { continue; } if (batt->Info.Valid & VALID_TAG) { // // Get the battery max capacity and current % of capacity // capacity = batt->Info.Info.FullChargedCapacity; if (capacity == 0) { CompbattReleaseDeleteLock(&batt->DeleteLock); break; } percentCapacity = (batt->Info.Status.Capacity * 100) / capacity; // // Is this the most charged battery AND < 90% full? // if ((capacity > targetCapacity) && (percentCapacity < BATTERY_MAX_CHARGE_CAPACITY)) { // // Yes, this one is in the running for the one to charge // targetCapacity = capacity; targetBattery = batt; } } CompbattReleaseDeleteLock(&batt->DeleteLock); } ExReleaseFastMutex (&CompBatt->ListMutex); BattPrint (BATT_NOTE, ("CompBattChargeDischarge: Setting battery %x to CHARGE (AC present)\n", targetBattery)); } else { // // We are running on battery power. Examine all batteries, looking // for the one with the least capacity that is greater than some small // safety margin (say 2%). // targetCapacity = -1; battSetInfo.InformationLevel = BatteryDischarge; ExAcquireFastMutex (&CompBatt->ListMutex); for (entry = CompBatt->Batteries.Flink; entry != &CompBatt->Batteries; entry = entry->Flink) { batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries); if (!NT_SUCCESS (CompbattAcquireDeleteLock(&batt->DeleteLock))) { continue; } if (batt->Info.Valid & VALID_TAG) { // // Get the battery max capacity and current % of capacity // capacity = batt->Info.Info.FullChargedCapacity; if (capacity == 0) { CompbattReleaseDeleteLock(&batt->DeleteLock); break; } percentCapacity = (batt->Info.Status.Capacity * 100) / capacity; // // Is this the least charged battery AND has a safety margin? // if ((capacity < targetCapacity) && (percentCapacity > BATTERY_MIN_SAFE_CAPACITY)) { // // Yes, this one is in the running for the one to discharge // targetCapacity = capacity; targetBattery = batt; } } CompbattReleaseDeleteLock(&batt->DeleteLock); } ExReleaseFastMutex (&CompBatt->ListMutex); BattPrint (BATT_NOTE, ("CompBattChargeDischarge: Setting battery %x to DISCHARGE (no AC)\n", targetBattery)); } // // If we have found a suitable battery, complete the setup and send off the Ioctl // if (targetBattery != NULL) { battSetInfo.BatteryTag = targetBattery->Info.Tag; // // Make the Ioctl to the battery. This won't always be successful, since some // batteries don't support it. For example, no control-method batteries support // software charging decisions. Some smart batteries do, however. // status = BatteryIoctl (IOCTL_BATTERY_SET_INFORMATION, batt->DeviceObject, &battSetInfo, sizeof (BATTERY_SET_INFORMATION), NULL, 0, FALSE); } BattPrint (BATT_TRACE, ("CompBatt: EXITING CompBattChargeDischarge\n")); }