|
|
/*
* title: cbattery.cpp * * purpose: wdm kernel implementation for battery object classes * * initial checkin for the hid to battery class driver. This should be * the same for both Win 98 and NT 5. Alpha level source. Requires * modified composite battery driver and modified battery class driver for * windows 98 support * */ #include "hidbatt.h"
static USHORT gBatteryTag = 0;
USAGE_ENTRY UsageArray[MAX_USAGE_INDEXS] = { { POWER_PAGE, PRESENT_STATUS_ID}, { POWER_PAGE, UPS_ID }, { POWER_PAGE, POWER_SUMMARY_ID }, { POWER_PAGE, VOLTAGE_ID }, { POWER_PAGE, CURRENT_ID }, { POWER_PAGE, CONFIG_VOLTAGE_ID }, { POWER_PAGE, CONFIG_CURRENT_ID }, { POWER_PAGE, DELAY_BEFORE_SHUTDOWN_ID }, { POWER_PAGE, SHUTDOWN_IMMINENT_ID }, { POWER_PAGE, MANUFACTURER_ID }, { POWER_PAGE, PRODUCT_ID }, { POWER_PAGE, SERIAL_NUMBER_ID }, { BATTERY_PAGE, REMAINING_CAPACITY_LIMIT_ID }, { BATTERY_PAGE, CAPACITY_MODE_ID}, { BATTERY_PAGE, BELOW_REMAINING_CAPACITY_ID }, { BATTERY_PAGE, CHARGING_ID }, { BATTERY_PAGE, DISCHARGING_ID }, { BATTERY_PAGE, REMAINING_CAPACITY_ID }, { BATTERY_PAGE, FULL_CHARGED_CAPACITY_ID }, { BATTERY_PAGE, RUNTIME_TO_EMPTY_ID}, { BATTERY_PAGE, DESIGN_CAPACITY_ID }, { BATTERY_PAGE, MANUFACTURE_DATE_ID }, { BATTERY_PAGE, ICHEMISTRY_ID }, { BATTERY_PAGE, WARNING_CAPACITY_LIMIT_ID }, { BATTERY_PAGE, GRANULARITY1_ID }, { BATTERY_PAGE, GRANULARITY2_ID }, { BATTERY_PAGE, OEM_INFO_ID }, { BATTERY_PAGE, AC_PRESENT_ID } };
CBattery::CBattery(CHidDevice *) { RtlZeroMemory(&m_BatteryStatus, sizeof(BATTERY_STATUS)); RtlZeroMemory(&m_BatteryInfo,sizeof(BATTERY_INFORMATION)); m_pBatteryClass = NULL; m_Tag = ++gBatteryTag; m_RefreshTime = 0; m_bRelative = FALSE; }
CBattery::~CBattery() { // delete hid device if present
if(m_pCHidDevice) { delete m_pCHidDevice; m_pCHidDevice = NULL; } if(m_pSerialNumber) { delete m_pSerialNumber; m_pSerialNumber = NULL; } if(m_pOEMInformation) { delete m_pOEMInformation; m_pOEMInformation = NULL; } if(m_pProduct) { delete m_pProduct; m_pProduct = NULL; } if(m_pManufacturer) { delete m_pManufacturer; m_pManufacturer = NULL; } }
bool CBattery::InitValues() { bool bResult; ULONG ulReturnValue = 0; ULONG ulValue; CUString * pChemString; NTSTATUS ntStatus; SHORT sExponent;
// initialize the static data structures
HIDDebugBreak(HIDBATT_BREAK_ALWAYS); // Init Values
// start with the info structure
m_BatteryInfo.Capabilities = BATTERY_SYSTEM_BATTERY | BATTERY_IS_SHORT_TERM; // get CapacityMode, find out what style of reporting is used
bResult = GetSetValue(CAPACITY_MODE_INDEX,&ulReturnValue,FALSE); if (ulReturnValue == 2) { m_BatteryInfo.Capabilities |= BATTERY_CAPACITY_RELATIVE; m_bRelative = TRUE; }
// now get voltage for use in amperage to watt calculations
// get voltage
bResult = GetSetValue(VOLTAGE_INDEX, &ulValue,FALSE); if(!bResult) { bResult = GetSetValue(CONFIG_VOLTAGE_INDEX,&ulValue,FALSE); sExponent = GetExponent(CONFIG_VOLTAGE_INDEX); if(!bResult) { ulValue = 24; sExponent = 0; } } else { sExponent = GetExponent(VOLTAGE_INDEX); }
ULONG ulNewValue = CorrectExponent(ulValue,sExponent, 4); // HID exponent for millivolts is 4
m_BatteryStatus.Voltage = ulNewValue;
// HID unit is typically Volt
// designed capacity
bResult = GetSetValue(DESIGN_CAPACITY_INDEX, &ulReturnValue,FALSE); ulValue = bResult ? ulReturnValue : BATTERY_UNKNOWN_VOLTAGE; if (m_bRelative) { m_BatteryInfo.DesignedCapacity = ulValue; // in percent
} else { // must convert to millwatts from centiAmp
sExponent = GetExponent(DESIGN_CAPACITY_INDEX); ulNewValue = CorrectExponent(ulValue,sExponent,-2); m_BatteryInfo.DesignedCapacity = CentiAmpSecsToMilliWattHours(ulNewValue,m_BatteryStatus.Voltage); }
// Technology
m_BatteryInfo.Technology = 1; // secondary, rechargeable battery
// init static strings from device
// Chemistry
pChemString = GetCUString(CHEMISTRY_INDEX); if (pChemString) { // make into ascii
char * pCString; ntStatus = pChemString->ToCString(&pCString); if (NT_ERROR(ntStatus)) { RtlZeroMemory(&m_BatteryInfo.Chemistry,sizeof(m_BatteryInfo.Chemistry)); } else { RtlCopyMemory(&m_BatteryInfo.Chemistry, pCString,sizeof(m_BatteryInfo.Chemistry)); ExFreePool(pCString); } } else { RtlZeroMemory(&m_BatteryInfo.Chemistry,sizeof(m_BatteryInfo.Chemistry)); } delete pChemString;
// serial number string
m_pSerialNumber = GetCUString(SERIAL_NUMBER_INDEX); HidBattPrint (HIDBATT_TRACE, ("GetCUString (Serial Number) returned - Serial = %08x\n", m_pSerialNumber)); if (m_pSerialNumber) { HidBattPrint (HIDBATT_TRACE, (" Serial # = %s\n", m_pSerialNumber)); }
// OEMInformation
m_pOEMInformation = GetCUString(OEM_INFO_INDEX);
m_pProduct = GetCUString(PRODUCT_INDEX);
m_pManufacturer = GetCUString(MANUFACTURER_INDEX);
bResult = GetSetValue(MANUFACTURE_DATE_INDEX, &ulReturnValue,FALSE); if (bResult) { // make conformant date
m_ManufactureDate.Day = (UCHAR) ulReturnValue & 0x1f; // low nibble is day
m_ManufactureDate.Month = (UCHAR) ((ulReturnValue & 0x1e0) >> 5); // high nibble is month
m_ManufactureDate.Year = (USHORT) ((ulReturnValue & 0xfffe00) >> 9) + 1980; // high byte is year
} else { // set mfr date to zeros
m_ManufactureDate.Day = m_ManufactureDate.Month = 0; m_ManufactureDate.Year = 0; } // FullChargedCapacity
bResult = GetSetValue(FULL_CHARGED_CAPACITY_INDEX,&ulReturnValue,FALSE); ulValue = bResult ? ulReturnValue : m_BatteryInfo.DesignedCapacity;
// if absolute must convert from ampsecs to millwatts
if (!m_bRelative) { sExponent = GetExponent(FULL_CHARGED_CAPACITY_INDEX); ulNewValue = CorrectExponent(ulValue,sExponent,-2); ulValue = CentiAmpSecsToMilliWattHours(ulNewValue,m_BatteryStatus.Voltage); }
m_BatteryInfo.FullChargedCapacity = ulValue;
BOOLEAN warningCapacityValid; BOOLEAN remainingCapacityValid;
// DefaultAlert2
bResult = GetSetValue(WARNING_CAPACITY_LIMIT_INDEX, &ulReturnValue,FALSE); ulValue = bResult ? ulReturnValue : 0; warningCapacityValid = bResult; if (!m_bRelative) { sExponent = GetExponent(WARNING_CAPACITY_LIMIT_INDEX); ulNewValue = CorrectExponent(ulValue,sExponent,-2); ulValue = CentiAmpSecsToMilliWattHours(ulNewValue,m_BatteryStatus.Voltage); }
m_BatteryInfo.DefaultAlert2 = ulValue; // also in ampsecs (millwatts?)
// DefaultAlert1
bResult = GetSetValue(REMAINING_CAPACITY_LIMIT_INDEX,&ulReturnValue,FALSE); ulValue = bResult ? ulReturnValue : 0; // also in ampsecs (millwatts?)
remainingCapacityValid = bResult;
//
// Hack to allow STOP_DEVICE
// Since Default Alert 1 is only valid initially, after the device is
// stopped and restarted this data from the device is invalid, so we
// must use cached data.
//
if (((CBatteryDevExt *) m_pCHidDevice->m_pDeviceObject->DeviceExtension)->m_ulDefaultAlert1 == (ULONG)-1) { ((CBatteryDevExt *) m_pCHidDevice->m_pDeviceObject->DeviceExtension)->m_ulDefaultAlert1 = ulValue; } else { ulValue = ((CBatteryDevExt *) m_pCHidDevice->m_pDeviceObject->DeviceExtension)->m_ulDefaultAlert1; }
if (!m_bRelative) { sExponent = GetExponent(REMAINING_CAPACITY_LIMIT_INDEX); ulNewValue = CorrectExponent(ulValue,sExponent,-2); ulValue = CentiAmpSecsToMilliWattHours(ulNewValue,m_BatteryStatus.Voltage); }
m_BatteryInfo.DefaultAlert1 = ulValue;
if (warningCapacityValid && !remainingCapacityValid) { m_BatteryInfo.DefaultAlert1 = m_BatteryInfo.DefaultAlert2; } else if (!warningCapacityValid && remainingCapacityValid) { m_BatteryInfo.DefaultAlert2 = m_BatteryInfo.DefaultAlert1; }
// pro forma initialization for unsupported members
m_BatteryInfo.CriticalBias = 0; m_BatteryInfo.CycleCount = 0; return TRUE;
}
#define REFRESH_INTERVAL 80000000 // 10 million ticks per sec with 100 nanosec tics * 5 secs
// 8 seconds is my best guess for a reasonable interval - djk
NTSTATUS CBattery::RefreshStatus() { ULONG ulValue; ULONG ulPowerState; bool bResult; ULONGLONG CurrTime; SHORT sExponent; ULONG ulScaledValue,ulNewValue; LONG ulMillWatts; ULONG ulUnit; // insure that the values in the Battery Status are fresh for delivery
// first get power state
// build battery state mask
// or online, discharging,charging,and critical
CurrTime = KeQueryInterruptTime(); if(((CurrTime - m_RefreshTime) < REFRESH_INTERVAL) && m_bIsCacheValid) { return STATUS_SUCCESS; }
m_bIsCacheValid = TRUE; m_RefreshTime = CurrTime;
bResult = GetSetValue(AC_PRESENT_INDEX, &ulValue,FALSE); if(!bResult) { ulValue = 0; HidBattPrint (HIDBATT_DATA, ("HidBattRefreshStatus: error reading AC_PRESENT\n" )); } ulPowerState = ulValue ? BATTERY_POWER_ON_LINE : 0;
bResult = GetSetValue(CURRENT_INDEX, &ulValue,FALSE); if (!bResult) { ulMillWatts = BATTERY_UNKNOWN_RATE; } else { // convert from amps to watts
// must convert to millwatts from centiAmp
sExponent = GetExponent(CURRENT_INDEX); ulNewValue = CorrectExponent(ulValue,sExponent,0); ulMillWatts = ulNewValue * m_BatteryStatus.Voltage; // now have millwatts
}
bResult = GetSetValue(DISCHARGING_INDEX, &ulValue,FALSE); if(!bResult) { ulValue = 0; HidBattPrint (HIDBATT_DATA, ("HidBattRefreshStatus: error reading DISCHARGING\n" )); } if(ulValue) // discharging
{ ulPowerState |= BATTERY_DISCHARGING; //This assumes that CURRENT is always positive and that
//it's the right value to begin with. Need to double check.
if (ulMillWatts != BATTERY_UNKNOWN_RATE) { ulMillWatts = -ulMillWatts; } m_BatteryStatus.Rate = ulMillWatts; //m_BatteryStatus.Rate = BATTERY_UNKNOWN_RATE;
} else { m_BatteryStatus.Rate = ulMillWatts; //m_BatteryStatus.Rate = BATTERY_UNKNOWN_RATE; // not discharging
}
bResult = GetSetValue(CHARGING_INDEX, &ulValue,FALSE); if(!bResult) { ulValue = 0; HidBattPrint (HIDBATT_DATA, ("HidBattRefreshStatus: error reading CHARGING\n" )); } ulPowerState |= ulValue ? BATTERY_CHARGING : 0;
bResult = GetSetValue(SHUTDOWN_IMMINENT_INDEX, &ulValue,FALSE); if(!bResult) { ulValue = 0; HidBattPrint (HIDBATT_DATA, ("HidBattRefreshStatus: error reading SHUTDOWN_IMMINENT\n" )); } ulPowerState |= ulValue ? BATTERY_CRITICAL : 0;
m_BatteryStatus.PowerState = ulPowerState;
// next capacity
bResult = GetSetValue(REMAINING_CAPACITY_INDEX,&ulValue,FALSE); // check if relative or absolute
if(!m_bRelative && bResult && m_BatteryStatus.Voltage) { sExponent = GetExponent(REMAINING_CAPACITY_INDEX); ulValue = CorrectExponent(ulValue,sExponent,-2); ulValue = CentiAmpSecsToMilliWattHours(ulValue,m_BatteryStatus.Voltage);
}
m_BatteryStatus.Capacity = bResult ? ulValue : BATTERY_UNKNOWN_CAPACITY;
return STATUS_SUCCESS; }
CUString * CBattery::GetCUString(USAGE_INDEX eUsageIndex) { NTSTATUS ntStatus; ULONG ulBytesReturned; USHORT usBuffLen = 100; // arbitary size to pick up battery strings
// build path to to power summary usage
CUsagePath * pThisPath = new (NonPagedPool, HidBattTag) CUsagePath( UsageArray[UPS_INDEX].Page, UsageArray[UPS_INDEX].UsageID); if(!pThisPath) return NULL;
pThisPath->m_pNextEntry = new (NonPagedPool, HidBattTag) CUsagePath( UsageArray[POWER_SUMMARY_INDEX].Page, UsageArray[POWER_SUMMARY_INDEX].UsageID); if(!pThisPath->m_pNextEntry) return NULL;
// is this one of the values in presentstatus ?
pThisPath->m_pNextEntry->m_pNextEntry = new (NonPagedPool, HidBattTag) CUsagePath( UsageArray[eUsageIndex].Page, UsageArray[eUsageIndex].UsageID); if(!pThisPath->m_pNextEntry->m_pNextEntry) return NULL;
CUsage * pThisUsage = m_pCHidDevice->FindUsage(pThisPath, READABLE); delete pThisPath; // clean up
if(!pThisUsage) return NULL; PVOID pBuffer = ExAllocatePoolWithTag(NonPagedPool, usBuffLen, HidBattTag); // allocate a scratch buffer rather than consume stack
if(!pBuffer) return NULL; ntStatus = pThisUsage->GetString((char *) pBuffer, usBuffLen, &ulBytesReturned); if(!NT_SUCCESS(ntStatus)) { ExFreePool(pBuffer); return NULL; } // create a custring to return
CUString * pTheString = new (NonPagedPool, HidBattTag) CUString((PWSTR) pBuffer); if(!pTheString) return NULL;
// free our temp buffer
ExFreePool(pBuffer); return pTheString; }
SHORT CBattery::GetExponent(USAGE_INDEX eUsageIndex) { SHORT exponent;
CUsage * pThisUsage = GetUsage(eUsageIndex); if(!pThisUsage) return 0;
exponent = pThisUsage->GetExponent(); HidBattPrint (HIDBATT_DATA, ("HidBattGetExponent: Exponent for USAGE_INDEX_0x%x = 0x%08x\n", eUsageIndex, exponent));
return exponent; }
CUsage * CBattery::GetUsage(USAGE_INDEX eUsageIndex) { CUsagePath * pCurrEntry; bool bResult; // build path to to power summary usage
CUsagePath * pThisPath = new (NonPagedPool, HidBattTag) CUsagePath( UsageArray[UPS_INDEX].Page, UsageArray[UPS_INDEX].UsageID); if (!pThisPath) return NULL;
pThisPath->m_pNextEntry = new (NonPagedPool, HidBattTag) CUsagePath( UsageArray[POWER_SUMMARY_INDEX].Page, UsageArray[POWER_SUMMARY_INDEX].UsageID); if (!pThisPath->m_pNextEntry) return NULL;
pCurrEntry = pThisPath->m_pNextEntry; // check if need to tack on presentstatus collection to path
if(eUsageIndex == AC_PRESENT_INDEX || eUsageIndex == DISCHARGING_INDEX || eUsageIndex == CHARGING_INDEX || eUsageIndex == BELOW_REMAINING_CAPACITY_INDEX || eUsageIndex == CURRENT_INDEX) { pCurrEntry->m_pNextEntry = new (NonPagedPool, HidBattTag) CUsagePath(UsageArray[PRESENT_STATUS_INDEX].Page, UsageArray[PRESENT_STATUS_INDEX].UsageID); if (!pCurrEntry->m_pNextEntry) return NULL;
pCurrEntry = pCurrEntry->m_pNextEntry; }
pCurrEntry->m_pNextEntry = new (NonPagedPool, HidBattTag) CUsagePath( UsageArray[eUsageIndex].Page, UsageArray[eUsageIndex].UsageID); if (!pCurrEntry->m_pNextEntry) return NULL;
CUsage * pThisUsage = m_pCHidDevice->FindUsage(pThisPath, READABLE); delete pThisPath; // clean up
return pThisUsage; }
ULONG CBattery::GetUnit(USAGE_INDEX eUsageIndex) { CUsage * pThisUsage = GetUsage(eUsageIndex); if(!pThisUsage) return 0; return pThisUsage->GetUnit(); }
bool CBattery::GetSetValue(USAGE_INDEX eUsageIndex, PULONG ulResult, bool bWriteFlag) { bool bResult; CUsagePath * pCurrEntry;
// build path to to power summary usage
CUsagePath * pThisPath = new (NonPagedPool, HidBattTag) CUsagePath( UsageArray[UPS_INDEX].Page, UsageArray[UPS_INDEX].UsageID); if (!pThisPath) return FALSE;
pThisPath->m_pNextEntry = new (NonPagedPool, HidBattTag) CUsagePath( UsageArray[POWER_SUMMARY_INDEX].Page, UsageArray[POWER_SUMMARY_INDEX].UsageID); if (!pThisPath->m_pNextEntry) return FALSE;
pCurrEntry = pThisPath->m_pNextEntry; // check if need to tack on presentstatus collection to path
if(eUsageIndex == AC_PRESENT_INDEX || eUsageIndex == DISCHARGING_INDEX || eUsageIndex == CHARGING_INDEX || eUsageIndex == BELOW_REMAINING_CAPACITY_INDEX || eUsageIndex == CURRENT_INDEX || eUsageIndex == SHUTDOWN_IMMINENT_INDEX) { pCurrEntry->m_pNextEntry = new (NonPagedPool, HidBattTag) CUsagePath(UsageArray[PRESENT_STATUS_INDEX].Page, UsageArray[PRESENT_STATUS_INDEX].UsageID); if (!pCurrEntry->m_pNextEntry) return FALSE;
pCurrEntry = pCurrEntry->m_pNextEntry; }
pCurrEntry->m_pNextEntry = new (NonPagedPool, HidBattTag) CUsagePath( UsageArray[eUsageIndex].Page, UsageArray[eUsageIndex].UsageID); if (!pCurrEntry->m_pNextEntry) return FALSE;
CUsage * pThisUsage = m_pCHidDevice->FindUsage(pThisPath, READABLE); delete pThisPath; // clean up
if(!pThisUsage) return FALSE; if(bWriteFlag) // this is a write
{ bResult = pThisUsage->SetValue(*ulResult); if(!bResult) return bResult; } else { // this is a read
bResult = pThisUsage->GetValue(); if(!bResult) return bResult; *ulResult = pThisUsage->m_Value;
HidBattPrint (HIDBATT_DATA, ("HidBattGetSetValue: Got USAGE_INDEX_0x%x = 0x%08x\n", eUsageIndex, *ulResult ));
}
return TRUE; }
|