Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

517 lines
18 KiB

  1. /*
  2. * title: cbattery.cpp
  3. *
  4. * purpose: wdm kernel implementation for battery object classes
  5. *
  6. * initial checkin for the hid to battery class driver. This should be
  7. * the same for both Win 98 and NT 5. Alpha level source. Requires
  8. * modified composite battery driver and modified battery class driver for
  9. * windows 98 support
  10. *
  11. */
  12. #include "hidbatt.h"
  13. static USHORT gBatteryTag = 0;
  14. USAGE_ENTRY UsageArray[MAX_USAGE_INDEXS] = {
  15. { POWER_PAGE, PRESENT_STATUS_ID},
  16. { POWER_PAGE, UPS_ID },
  17. { POWER_PAGE, POWER_SUMMARY_ID },
  18. { POWER_PAGE, VOLTAGE_ID },
  19. { POWER_PAGE, CURRENT_ID },
  20. { POWER_PAGE, CONFIG_VOLTAGE_ID },
  21. { POWER_PAGE, CONFIG_CURRENT_ID },
  22. { POWER_PAGE, DELAY_BEFORE_SHUTDOWN_ID },
  23. { POWER_PAGE, SHUTDOWN_IMMINENT_ID },
  24. { POWER_PAGE, MANUFACTURER_ID },
  25. { POWER_PAGE, PRODUCT_ID },
  26. { POWER_PAGE, SERIAL_NUMBER_ID },
  27. { BATTERY_PAGE, REMAINING_CAPACITY_LIMIT_ID },
  28. { BATTERY_PAGE, CAPACITY_MODE_ID},
  29. { BATTERY_PAGE, BELOW_REMAINING_CAPACITY_ID },
  30. { BATTERY_PAGE, CHARGING_ID },
  31. { BATTERY_PAGE, DISCHARGING_ID },
  32. { BATTERY_PAGE, REMAINING_CAPACITY_ID },
  33. { BATTERY_PAGE, FULL_CHARGED_CAPACITY_ID },
  34. { BATTERY_PAGE, RUNTIME_TO_EMPTY_ID},
  35. { BATTERY_PAGE, DESIGN_CAPACITY_ID },
  36. { BATTERY_PAGE, MANUFACTURE_DATE_ID },
  37. { BATTERY_PAGE, ICHEMISTRY_ID },
  38. { BATTERY_PAGE, WARNING_CAPACITY_LIMIT_ID },
  39. { BATTERY_PAGE, GRANULARITY1_ID },
  40. { BATTERY_PAGE, GRANULARITY2_ID },
  41. { BATTERY_PAGE, OEM_INFO_ID },
  42. { BATTERY_PAGE, AC_PRESENT_ID }
  43. };
  44. CBattery::CBattery(CHidDevice *)
  45. {
  46. RtlZeroMemory(&m_BatteryStatus, sizeof(BATTERY_STATUS));
  47. RtlZeroMemory(&m_BatteryInfo,sizeof(BATTERY_INFORMATION));
  48. m_pBatteryClass = NULL;
  49. m_Tag = ++gBatteryTag;
  50. m_RefreshTime = 0;
  51. m_bRelative = FALSE;
  52. }
  53. CBattery::~CBattery()
  54. {
  55. // delete hid device if present
  56. if(m_pCHidDevice) {
  57. delete m_pCHidDevice;
  58. m_pCHidDevice = NULL;
  59. }
  60. if(m_pSerialNumber) {
  61. delete m_pSerialNumber;
  62. m_pSerialNumber = NULL;
  63. }
  64. if(m_pOEMInformation) {
  65. delete m_pOEMInformation;
  66. m_pOEMInformation = NULL;
  67. }
  68. if(m_pProduct) {
  69. delete m_pProduct;
  70. m_pProduct = NULL;
  71. }
  72. if(m_pManufacturer) {
  73. delete m_pManufacturer;
  74. m_pManufacturer = NULL;
  75. }
  76. }
  77. bool CBattery::InitValues()
  78. {
  79. bool bResult;
  80. ULONG ulReturnValue = 0;
  81. ULONG ulValue;
  82. CUString * pChemString;
  83. NTSTATUS ntStatus;
  84. SHORT sExponent;
  85. // initialize the static data structures
  86. HIDDebugBreak(HIDBATT_BREAK_ALWAYS);
  87. // Init Values
  88. // start with the info structure
  89. m_BatteryInfo.Capabilities = BATTERY_SYSTEM_BATTERY |
  90. BATTERY_IS_SHORT_TERM;
  91. // get CapacityMode, find out what style of reporting is used
  92. bResult = GetSetValue(CAPACITY_MODE_INDEX,&ulReturnValue,FALSE);
  93. if (ulReturnValue == 2) {
  94. m_BatteryInfo.Capabilities |= BATTERY_CAPACITY_RELATIVE;
  95. m_bRelative = TRUE;
  96. }
  97. // now get voltage for use in amperage to watt calculations
  98. // get voltage
  99. bResult = GetSetValue(VOLTAGE_INDEX, &ulValue,FALSE);
  100. if(!bResult)
  101. {
  102. bResult = GetSetValue(CONFIG_VOLTAGE_INDEX,&ulValue,FALSE);
  103. sExponent = GetExponent(CONFIG_VOLTAGE_INDEX);
  104. if(!bResult) {
  105. ulValue = 24;
  106. sExponent = 0;
  107. }
  108. } else
  109. {
  110. sExponent = GetExponent(VOLTAGE_INDEX);
  111. }
  112. ULONG ulNewValue = CorrectExponent(ulValue,sExponent, 4); // HID exponent for millivolts is 4
  113. m_BatteryStatus.Voltage = ulNewValue;
  114. // HID unit is typically Volt
  115. // designed capacity
  116. bResult = GetSetValue(DESIGN_CAPACITY_INDEX, &ulReturnValue,FALSE);
  117. ulValue = bResult ? ulReturnValue : BATTERY_UNKNOWN_VOLTAGE;
  118. if (m_bRelative) {
  119. m_BatteryInfo.DesignedCapacity = ulValue; // in percent
  120. } else {
  121. // must convert to millwatts from centiAmp
  122. sExponent = GetExponent(DESIGN_CAPACITY_INDEX);
  123. ulNewValue = CorrectExponent(ulValue,sExponent,-2);
  124. m_BatteryInfo.DesignedCapacity = CentiAmpSecsToMilliWattHours(ulNewValue,m_BatteryStatus.Voltage);
  125. }
  126. // Technology
  127. m_BatteryInfo.Technology = 1; // secondary, rechargeable battery
  128. // init static strings from device
  129. // Chemistry
  130. pChemString = GetCUString(CHEMISTRY_INDEX);
  131. if (pChemString) {
  132. // make into ascii
  133. char * pCString;
  134. ntStatus = pChemString->ToCString(&pCString);
  135. if (NT_ERROR(ntStatus)) {
  136. RtlZeroMemory(&m_BatteryInfo.Chemistry,sizeof(m_BatteryInfo.Chemistry));
  137. } else {
  138. RtlCopyMemory(&m_BatteryInfo.Chemistry, pCString,sizeof(m_BatteryInfo.Chemistry));
  139. ExFreePool(pCString);
  140. }
  141. } else {
  142. RtlZeroMemory(&m_BatteryInfo.Chemistry,sizeof(m_BatteryInfo.Chemistry));
  143. }
  144. delete pChemString;
  145. // serial number string
  146. m_pSerialNumber = GetCUString(SERIAL_NUMBER_INDEX);
  147. HidBattPrint (HIDBATT_TRACE, ("GetCUString (Serial Number) returned - Serial = %08x\n", m_pSerialNumber));
  148. if (m_pSerialNumber) {
  149. HidBattPrint (HIDBATT_TRACE, (" Serial # = %s\n", m_pSerialNumber));
  150. }
  151. // OEMInformation
  152. m_pOEMInformation = GetCUString(OEM_INFO_INDEX);
  153. m_pProduct = GetCUString(PRODUCT_INDEX);
  154. m_pManufacturer = GetCUString(MANUFACTURER_INDEX);
  155. bResult = GetSetValue(MANUFACTURE_DATE_INDEX, &ulReturnValue,FALSE);
  156. if (bResult) {
  157. // make conformant date
  158. m_ManufactureDate.Day = (UCHAR) ulReturnValue & 0x1f; // low nibble is day
  159. m_ManufactureDate.Month = (UCHAR) ((ulReturnValue & 0x1e0) >> 5); // high nibble is month
  160. m_ManufactureDate.Year = (USHORT) ((ulReturnValue & 0xfffe00) >> 9) + 1980; // high byte is year
  161. } else {
  162. // set mfr date to zeros
  163. m_ManufactureDate.Day = m_ManufactureDate.Month = 0;
  164. m_ManufactureDate.Year = 0;
  165. }
  166. // FullChargedCapacity
  167. bResult = GetSetValue(FULL_CHARGED_CAPACITY_INDEX,&ulReturnValue,FALSE);
  168. ulValue = bResult ? ulReturnValue : m_BatteryInfo.DesignedCapacity;
  169. // if absolute must convert from ampsecs to millwatts
  170. if (!m_bRelative) {
  171. sExponent = GetExponent(FULL_CHARGED_CAPACITY_INDEX);
  172. ulNewValue = CorrectExponent(ulValue,sExponent,-2);
  173. ulValue = CentiAmpSecsToMilliWattHours(ulNewValue,m_BatteryStatus.Voltage);
  174. }
  175. m_BatteryInfo.FullChargedCapacity = ulValue;
  176. BOOLEAN warningCapacityValid;
  177. BOOLEAN remainingCapacityValid;
  178. // DefaultAlert2
  179. bResult = GetSetValue(WARNING_CAPACITY_LIMIT_INDEX, &ulReturnValue,FALSE);
  180. ulValue = bResult ? ulReturnValue : 0;
  181. warningCapacityValid = bResult;
  182. if (!m_bRelative) {
  183. sExponent = GetExponent(WARNING_CAPACITY_LIMIT_INDEX);
  184. ulNewValue = CorrectExponent(ulValue,sExponent,-2);
  185. ulValue = CentiAmpSecsToMilliWattHours(ulNewValue,m_BatteryStatus.Voltage);
  186. }
  187. m_BatteryInfo.DefaultAlert2 = ulValue; // also in ampsecs (millwatts?)
  188. // DefaultAlert1
  189. bResult = GetSetValue(REMAINING_CAPACITY_LIMIT_INDEX,&ulReturnValue,FALSE);
  190. ulValue = bResult ? ulReturnValue : 0; // also in ampsecs (millwatts?)
  191. remainingCapacityValid = bResult;
  192. //
  193. // Hack to allow STOP_DEVICE
  194. // Since Default Alert 1 is only valid initially, after the device is
  195. // stopped and restarted this data from the device is invalid, so we
  196. // must use cached data.
  197. //
  198. if (((CBatteryDevExt *) m_pCHidDevice->m_pDeviceObject->DeviceExtension)->m_ulDefaultAlert1 == (ULONG)-1) {
  199. ((CBatteryDevExt *) m_pCHidDevice->m_pDeviceObject->DeviceExtension)->m_ulDefaultAlert1 = ulValue;
  200. } else {
  201. ulValue = ((CBatteryDevExt *) m_pCHidDevice->m_pDeviceObject->DeviceExtension)->m_ulDefaultAlert1;
  202. }
  203. if (!m_bRelative) {
  204. sExponent = GetExponent(REMAINING_CAPACITY_LIMIT_INDEX);
  205. ulNewValue = CorrectExponent(ulValue,sExponent,-2);
  206. ulValue = CentiAmpSecsToMilliWattHours(ulNewValue,m_BatteryStatus.Voltage);
  207. }
  208. m_BatteryInfo.DefaultAlert1 = ulValue;
  209. if (warningCapacityValid && !remainingCapacityValid) {
  210. m_BatteryInfo.DefaultAlert1 = m_BatteryInfo.DefaultAlert2;
  211. } else if (!warningCapacityValid && remainingCapacityValid) {
  212. m_BatteryInfo.DefaultAlert2 = m_BatteryInfo.DefaultAlert1;
  213. }
  214. // pro forma initialization for unsupported members
  215. m_BatteryInfo.CriticalBias = 0;
  216. m_BatteryInfo.CycleCount = 0;
  217. return TRUE;
  218. }
  219. #define REFRESH_INTERVAL 80000000 // 10 million ticks per sec with 100 nanosec tics * 5 secs
  220. // 8 seconds is my best guess for a reasonable interval - djk
  221. NTSTATUS CBattery::RefreshStatus()
  222. {
  223. ULONG ulValue;
  224. ULONG ulPowerState;
  225. bool bResult;
  226. ULONGLONG CurrTime;
  227. SHORT sExponent;
  228. ULONG ulScaledValue,ulNewValue;
  229. LONG ulMillWatts;
  230. ULONG ulUnit;
  231. // insure that the values in the Battery Status are fresh for delivery
  232. // first get power state
  233. // build battery state mask
  234. // or online, discharging,charging,and critical
  235. CurrTime = KeQueryInterruptTime();
  236. if(((CurrTime - m_RefreshTime) < REFRESH_INTERVAL) && m_bIsCacheValid)
  237. {
  238. return STATUS_SUCCESS;
  239. }
  240. m_bIsCacheValid = TRUE;
  241. m_RefreshTime = CurrTime;
  242. bResult = GetSetValue(AC_PRESENT_INDEX, &ulValue,FALSE);
  243. if(!bResult) {
  244. ulValue = 0;
  245. HidBattPrint (HIDBATT_DATA, ("HidBattRefreshStatus: error reading AC_PRESENT\n" ));
  246. }
  247. ulPowerState = ulValue ? BATTERY_POWER_ON_LINE : 0;
  248. bResult = GetSetValue(CURRENT_INDEX, &ulValue,FALSE);
  249. if (!bResult) {
  250. ulMillWatts = BATTERY_UNKNOWN_RATE;
  251. } else {
  252. // convert from amps to watts
  253. // must convert to millwatts from centiAmp
  254. sExponent = GetExponent(CURRENT_INDEX);
  255. ulNewValue = CorrectExponent(ulValue,sExponent,0);
  256. ulMillWatts = ulNewValue * m_BatteryStatus.Voltage;
  257. // now have millwatts
  258. }
  259. bResult = GetSetValue(DISCHARGING_INDEX, &ulValue,FALSE);
  260. if(!bResult) {
  261. ulValue = 0;
  262. HidBattPrint (HIDBATT_DATA, ("HidBattRefreshStatus: error reading DISCHARGING\n" ));
  263. }
  264. if(ulValue) // discharging
  265. {
  266. ulPowerState |= BATTERY_DISCHARGING;
  267. //This assumes that CURRENT is always positive and that
  268. //it's the right value to begin with. Need to double check.
  269. if (ulMillWatts != BATTERY_UNKNOWN_RATE) {
  270. ulMillWatts = -ulMillWatts;
  271. }
  272. m_BatteryStatus.Rate = ulMillWatts;
  273. //m_BatteryStatus.Rate = BATTERY_UNKNOWN_RATE;
  274. } else
  275. {
  276. m_BatteryStatus.Rate = ulMillWatts;
  277. //m_BatteryStatus.Rate = BATTERY_UNKNOWN_RATE; // not discharging
  278. }
  279. bResult = GetSetValue(CHARGING_INDEX, &ulValue,FALSE);
  280. if(!bResult) {
  281. ulValue = 0;
  282. HidBattPrint (HIDBATT_DATA, ("HidBattRefreshStatus: error reading CHARGING\n" ));
  283. }
  284. ulPowerState |= ulValue ? BATTERY_CHARGING : 0;
  285. bResult = GetSetValue(SHUTDOWN_IMMINENT_INDEX, &ulValue,FALSE);
  286. if(!bResult) {
  287. ulValue = 0;
  288. HidBattPrint (HIDBATT_DATA, ("HidBattRefreshStatus: error reading SHUTDOWN_IMMINENT\n" ));
  289. }
  290. ulPowerState |= ulValue ? BATTERY_CRITICAL : 0;
  291. m_BatteryStatus.PowerState = ulPowerState;
  292. // next capacity
  293. bResult = GetSetValue(REMAINING_CAPACITY_INDEX,&ulValue,FALSE);
  294. // check if relative or absolute
  295. if(!m_bRelative && bResult && m_BatteryStatus.Voltage)
  296. {
  297. sExponent = GetExponent(REMAINING_CAPACITY_INDEX);
  298. ulValue = CorrectExponent(ulValue,sExponent,-2);
  299. ulValue = CentiAmpSecsToMilliWattHours(ulValue,m_BatteryStatus.Voltage);
  300. }
  301. m_BatteryStatus.Capacity = bResult ? ulValue : BATTERY_UNKNOWN_CAPACITY;
  302. return STATUS_SUCCESS;
  303. }
  304. CUString * CBattery::GetCUString(USAGE_INDEX eUsageIndex)
  305. {
  306. NTSTATUS ntStatus;
  307. ULONG ulBytesReturned;
  308. USHORT usBuffLen = 100; // arbitary size to pick up battery strings
  309. // build path to to power summary usage
  310. CUsagePath * pThisPath = new (NonPagedPool, HidBattTag) CUsagePath(
  311. UsageArray[UPS_INDEX].Page,
  312. UsageArray[UPS_INDEX].UsageID);
  313. if(!pThisPath) return NULL;
  314. pThisPath->m_pNextEntry = new (NonPagedPool, HidBattTag) CUsagePath(
  315. UsageArray[POWER_SUMMARY_INDEX].Page,
  316. UsageArray[POWER_SUMMARY_INDEX].UsageID);
  317. if(!pThisPath->m_pNextEntry) return NULL;
  318. // is this one of the values in presentstatus ?
  319. pThisPath->m_pNextEntry->m_pNextEntry = new (NonPagedPool, HidBattTag) CUsagePath(
  320. UsageArray[eUsageIndex].Page,
  321. UsageArray[eUsageIndex].UsageID);
  322. if(!pThisPath->m_pNextEntry->m_pNextEntry) return NULL;
  323. CUsage * pThisUsage = m_pCHidDevice->FindUsage(pThisPath, READABLE);
  324. delete pThisPath; // clean up
  325. if(!pThisUsage) return NULL;
  326. PVOID pBuffer = ExAllocatePoolWithTag(NonPagedPool, usBuffLen, HidBattTag); // allocate a scratch buffer rather than consume stack
  327. if(!pBuffer) return NULL;
  328. ntStatus = pThisUsage->GetString((char *) pBuffer, usBuffLen, &ulBytesReturned);
  329. if(!NT_SUCCESS(ntStatus)) {
  330. ExFreePool(pBuffer);
  331. return NULL;
  332. }
  333. // create a custring to return
  334. CUString * pTheString = new (NonPagedPool, HidBattTag) CUString((PWSTR) pBuffer);
  335. if(!pTheString) return NULL;
  336. // free our temp buffer
  337. ExFreePool(pBuffer);
  338. return pTheString;
  339. }
  340. SHORT CBattery::GetExponent(USAGE_INDEX eUsageIndex)
  341. {
  342. SHORT exponent;
  343. CUsage * pThisUsage = GetUsage(eUsageIndex);
  344. if(!pThisUsage) return 0;
  345. exponent = pThisUsage->GetExponent();
  346. HidBattPrint (HIDBATT_DATA, ("HidBattGetExponent: Exponent for USAGE_INDEX_0x%x = 0x%08x\n", eUsageIndex, exponent));
  347. return exponent;
  348. }
  349. CUsage * CBattery::GetUsage(USAGE_INDEX eUsageIndex)
  350. {
  351. CUsagePath * pCurrEntry;
  352. bool bResult;
  353. // build path to to power summary usage
  354. CUsagePath * pThisPath = new (NonPagedPool, HidBattTag) CUsagePath(
  355. UsageArray[UPS_INDEX].Page,
  356. UsageArray[UPS_INDEX].UsageID);
  357. if (!pThisPath) return NULL;
  358. pThisPath->m_pNextEntry = new (NonPagedPool, HidBattTag) CUsagePath(
  359. UsageArray[POWER_SUMMARY_INDEX].Page,
  360. UsageArray[POWER_SUMMARY_INDEX].UsageID);
  361. if (!pThisPath->m_pNextEntry) return NULL;
  362. pCurrEntry = pThisPath->m_pNextEntry;
  363. // check if need to tack on presentstatus collection to path
  364. if(eUsageIndex == AC_PRESENT_INDEX ||
  365. eUsageIndex == DISCHARGING_INDEX ||
  366. eUsageIndex == CHARGING_INDEX ||
  367. eUsageIndex == BELOW_REMAINING_CAPACITY_INDEX ||
  368. eUsageIndex == CURRENT_INDEX)
  369. {
  370. pCurrEntry->m_pNextEntry = new (NonPagedPool, HidBattTag)
  371. CUsagePath(UsageArray[PRESENT_STATUS_INDEX].Page,
  372. UsageArray[PRESENT_STATUS_INDEX].UsageID);
  373. if (!pCurrEntry->m_pNextEntry) return NULL;
  374. pCurrEntry = pCurrEntry->m_pNextEntry;
  375. }
  376. pCurrEntry->m_pNextEntry = new (NonPagedPool, HidBattTag) CUsagePath(
  377. UsageArray[eUsageIndex].Page,
  378. UsageArray[eUsageIndex].UsageID);
  379. if (!pCurrEntry->m_pNextEntry) return NULL;
  380. CUsage * pThisUsage = m_pCHidDevice->FindUsage(pThisPath, READABLE);
  381. delete pThisPath; // clean up
  382. return pThisUsage;
  383. }
  384. ULONG CBattery::GetUnit(USAGE_INDEX eUsageIndex)
  385. {
  386. CUsage * pThisUsage = GetUsage(eUsageIndex);
  387. if(!pThisUsage) return 0;
  388. return pThisUsage->GetUnit();
  389. }
  390. bool CBattery::GetSetValue(USAGE_INDEX eUsageIndex, PULONG ulResult, bool bWriteFlag)
  391. {
  392. bool bResult;
  393. CUsagePath * pCurrEntry;
  394. // build path to to power summary usage
  395. CUsagePath * pThisPath = new (NonPagedPool, HidBattTag) CUsagePath(
  396. UsageArray[UPS_INDEX].Page,
  397. UsageArray[UPS_INDEX].UsageID);
  398. if (!pThisPath) return FALSE;
  399. pThisPath->m_pNextEntry = new (NonPagedPool, HidBattTag) CUsagePath(
  400. UsageArray[POWER_SUMMARY_INDEX].Page,
  401. UsageArray[POWER_SUMMARY_INDEX].UsageID);
  402. if (!pThisPath->m_pNextEntry) return FALSE;
  403. pCurrEntry = pThisPath->m_pNextEntry;
  404. // check if need to tack on presentstatus collection to path
  405. if(eUsageIndex == AC_PRESENT_INDEX ||
  406. eUsageIndex == DISCHARGING_INDEX ||
  407. eUsageIndex == CHARGING_INDEX ||
  408. eUsageIndex == BELOW_REMAINING_CAPACITY_INDEX ||
  409. eUsageIndex == CURRENT_INDEX ||
  410. eUsageIndex == SHUTDOWN_IMMINENT_INDEX)
  411. {
  412. pCurrEntry->m_pNextEntry = new (NonPagedPool, HidBattTag)
  413. CUsagePath(UsageArray[PRESENT_STATUS_INDEX].Page,
  414. UsageArray[PRESENT_STATUS_INDEX].UsageID);
  415. if (!pCurrEntry->m_pNextEntry) return FALSE;
  416. pCurrEntry = pCurrEntry->m_pNextEntry;
  417. }
  418. pCurrEntry->m_pNextEntry = new (NonPagedPool, HidBattTag) CUsagePath(
  419. UsageArray[eUsageIndex].Page,
  420. UsageArray[eUsageIndex].UsageID);
  421. if (!pCurrEntry->m_pNextEntry) return FALSE;
  422. CUsage * pThisUsage = m_pCHidDevice->FindUsage(pThisPath, READABLE);
  423. delete pThisPath; // clean up
  424. if(!pThisUsage) return FALSE;
  425. if(bWriteFlag) // this is a write
  426. {
  427. bResult = pThisUsage->SetValue(*ulResult);
  428. if(!bResult) return bResult;
  429. } else
  430. {
  431. // this is a read
  432. bResult = pThisUsage->GetValue();
  433. if(!bResult) return bResult;
  434. *ulResult = pThisUsage->m_Value;
  435. HidBattPrint (HIDBATT_DATA, ("HidBattGetSetValue: Got USAGE_INDEX_0x%x = 0x%08x\n", eUsageIndex, *ulResult ));
  436. }
  437. return TRUE;
  438. }