Source code of Windows XP (NT5)
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.

2336 lines
70 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. CompBatt.c
  5. Abstract:
  6. Composite Battery device functions
  7. The purpose of the composite battery device is to open all batteries
  8. in the system which supply system power and provide a logical sumation
  9. of the information under one battery device.
  10. Author:
  11. Ken Reneris
  12. Environment:
  13. Notes:
  14. Revision History:
  15. 07/02/97: Local cache timestamps/timeouts
  16. --*/
  17. #include "compbatt.h"
  18. #if DEBUG
  19. #if DBG
  20. ULONG CompBattDebug = BATT_ERRORS;
  21. #else
  22. ULONG CompBattDebug = 0;
  23. #endif
  24. #endif
  25. #ifdef ALLOC_PRAGMA
  26. #pragma alloc_text(INIT, DriverEntry)
  27. #pragma alloc_text(PAGE, CompBattUnload)
  28. #pragma alloc_text(PAGE, CompBattIoctl)
  29. #pragma alloc_text(PAGE, CompBattQueryTag)
  30. #pragma alloc_text(PAGE, CompBattQueryInformation)
  31. #pragma alloc_text(PAGE, CompBattQueryStatus)
  32. #pragma alloc_text(PAGE, CompBattSetStatusNotify)
  33. #pragma alloc_text(PAGE, CompBattDisableStatusNotify)
  34. #pragma alloc_text(PAGE, CompBattGetBatteryInformation)
  35. #pragma alloc_text(PAGE, CompBattGetBatteryGranularity)
  36. #pragma alloc_text(PAGE, CompBattGetEstimatedTime)
  37. #endif
  38. NTSTATUS
  39. DriverEntry (
  40. IN PDRIVER_OBJECT DriverObject,
  41. IN PUNICODE_STRING RegistryPath
  42. )
  43. /*++
  44. Routine Description:
  45. The first time the battery class driver is loaded it will check to
  46. see if the composite battery has been created. If not, it will create
  47. a driver object with this routine as the DriverEntry. This routine
  48. then does the necessary things to initialize the composite battery.
  49. Arguments:
  50. DriverObject - Driver object for newly created driver
  51. RegistryPath - Not used
  52. Return Value:
  53. Status
  54. --*/
  55. {
  56. // DbgBreakPoint ();
  57. //
  58. // Initialize the driver entry points
  59. //
  60. //DriverObject->DriverUnload = CompBattUnload;
  61. DriverObject->DriverExtension->AddDevice = CompBattAddDevice;
  62. DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = CompBattIoctl;
  63. DriverObject->MajorFunction[IRP_MJ_CREATE] = CompBattOpenClose;
  64. DriverObject->MajorFunction[IRP_MJ_CLOSE] = CompBattOpenClose;
  65. DriverObject->MajorFunction[IRP_MJ_PNP] = CompBattPnpDispatch;
  66. DriverObject->MajorFunction[IRP_MJ_POWER] = CompBattPowerDispatch;
  67. DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = CompBattSystemControl;
  68. return STATUS_SUCCESS;
  69. }
  70. NTSTATUS
  71. CompBattAddDevice (
  72. IN PDRIVER_OBJECT DriverObject,
  73. IN PDEVICE_OBJECT PDO
  74. )
  75. /*++
  76. Routine Description:
  77. Arguments:
  78. DriverObject - Pointer to driver object created by system.
  79. PDO - PDO for the new device(s)
  80. Return Value:
  81. Status
  82. --*/
  83. {
  84. PDEVICE_OBJECT fdo;
  85. BATTERY_MINIPORT_INFO BattInit;
  86. UNICODE_STRING UnicodeString;
  87. NTSTATUS Status;
  88. UNICODE_STRING DosLinkName;
  89. PCOMPOSITE_BATTERY compBatt;
  90. BattPrint (BATT_NOTE, ("CompBatt: Got an AddDevice - %x\n", PDO));
  91. //
  92. // Build the composite battery device and register it to the
  93. // battery class driver (i.e., ourselves)
  94. //
  95. RtlInitUnicodeString(&UnicodeString, L"\\Device\\CompositeBattery");
  96. Status = IoCreateDevice(
  97. DriverObject,
  98. sizeof (COMPOSITE_BATTERY),
  99. &UnicodeString,
  100. FILE_DEVICE_BATTERY, // DeviceType
  101. 0,
  102. FALSE,
  103. &fdo
  104. );
  105. if (!NT_SUCCESS(Status)) {
  106. return Status;
  107. }
  108. RtlInitUnicodeString(&DosLinkName, L"\\DosDevices\\CompositeBattery");
  109. IoCreateSymbolicLink(&DosLinkName, &UnicodeString);
  110. //
  111. // Layer our FDO on top of the PDO.
  112. //
  113. compBatt = (PCOMPOSITE_BATTERY) fdo->DeviceExtension;
  114. RtlZeroMemory (compBatt, sizeof(COMPOSITE_BATTERY));
  115. compBatt->LowerDevice = IoAttachDeviceToDeviceStack (fdo,PDO);
  116. compBatt->DeviceObject = fdo;
  117. //
  118. // No status. Do the best we can.
  119. //
  120. if (!compBatt->LowerDevice) {
  121. BattPrint (BATT_ERROR, ("CompBattAddDevice: Could not attach to LowerDevice.\n"));
  122. return STATUS_UNSUCCESSFUL;
  123. }
  124. //
  125. // Initialize composite battery info
  126. //
  127. fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;
  128. fdo->Flags &= ~DO_DEVICE_INITIALIZING;
  129. InitializeListHead (&compBatt->Batteries);
  130. ExInitializeFastMutex (&compBatt->ListMutex);
  131. compBatt->NextTag = 1; // first valid battery tag for composite
  132. compBatt->Info.Valid = 0;
  133. RtlZeroMemory (&BattInit, sizeof(BattInit));
  134. BattInit.MajorVersion = BATTERY_CLASS_MAJOR_VERSION;
  135. BattInit.MinorVersion = BATTERY_CLASS_MINOR_VERSION;
  136. BattInit.Context = compBatt;
  137. BattInit.QueryTag = CompBattQueryTag;
  138. BattInit.QueryInformation = CompBattQueryInformation;
  139. BattInit.SetInformation = NULL;
  140. BattInit.QueryStatus = CompBattQueryStatus;
  141. BattInit.SetStatusNotify = CompBattSetStatusNotify;
  142. BattInit.DisableStatusNotify = CompBattDisableStatusNotify;
  143. BattInit.Pdo = NULL;
  144. BattInit.DeviceName = &UnicodeString;
  145. //
  146. // Register myself with the battery class driver
  147. //
  148. Status = BatteryClassInitializeDevice (&BattInit, &compBatt->Class);
  149. if (!NT_SUCCESS(Status)) {
  150. IoDeleteDevice(fdo);
  151. }
  152. return Status;
  153. }
  154. VOID
  155. CompBattUnload(
  156. IN PDRIVER_OBJECT DriverObject
  157. )
  158. /*++
  159. Routine Description:
  160. Cleanup all devices and unload the driver
  161. Arguments:
  162. DriverObject - Driver object for unload
  163. Return Value:
  164. Status
  165. --*/
  166. {
  167. DbgBreakPoint();
  168. //
  169. // Unloading the composite battery is not supported.
  170. // If it were implemented, we would
  171. // need to call the class driver's unload and then
  172. // delete all nodes in the battery list, clean up
  173. // then delete our FDO.
  174. //
  175. }
  176. NTSTATUS
  177. CompBattOpenClose(
  178. IN PDEVICE_OBJECT DeviceObject,
  179. IN PIRP Irp
  180. )
  181. {
  182. PAGED_CODE();
  183. BattPrint (BATT_TRACE, ("CompBatt: ENTERING OpenClose\n"));
  184. //
  185. // Complete the request and return status.
  186. //
  187. Irp->IoStatus.Status = STATUS_SUCCESS;
  188. Irp->IoStatus.Information = 0;
  189. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  190. BattPrint (BATT_TRACE, ("CompBatt: Exiting OpenClose\n"));
  191. return(STATUS_SUCCESS);
  192. }
  193. NTSTATUS
  194. CompBattIoctl(
  195. IN PDEVICE_OBJECT DeviceObject,
  196. IN PIRP Irp
  197. )
  198. /*++
  199. Routine Description:
  200. IOCTL handler. As this is an exclusive battery device, send the
  201. Irp to the battery class driver to handle battery IOCTLs.
  202. Arguments:
  203. DeviceObject - Battery for request
  204. Irp - IO request
  205. Return Value:
  206. Status of request
  207. --*/
  208. {
  209. PCOMPOSITE_BATTERY compBatt;
  210. NTSTATUS status;
  211. PAGED_CODE();
  212. BattPrint (BATT_TRACE, ("CompBatt: ENTERING Ioctl\n"));
  213. compBatt = (PCOMPOSITE_BATTERY) DeviceObject->DeviceExtension;
  214. status = BatteryClassIoctl (compBatt->Class, Irp);
  215. if (status == STATUS_NOT_SUPPORTED) {
  216. //
  217. // Not for the battery, pass it down the stack.
  218. //
  219. Irp->IoStatus.Status = status;
  220. IoSkipCurrentIrpStackLocation(Irp);
  221. status = IoCallDriver(compBatt->LowerDevice, Irp);
  222. }
  223. BattPrint (BATT_TRACE, ("CompBatt: EXITING Ioctl\n"));
  224. return status;
  225. }
  226. NTSTATUS
  227. CompBattSystemControl(
  228. IN PDEVICE_OBJECT DeviceObject,
  229. IN PIRP Irp
  230. )
  231. /*++
  232. Routine Description:
  233. This routine forwards System Control requests down the stack
  234. Arguments:
  235. DeviceObject - the device object in question
  236. Irp - the request to forward
  237. Return Value:
  238. NTSTATUS
  239. --*/
  240. {
  241. PCOMPOSITE_BATTERY compBatt;
  242. NTSTATUS status;
  243. PAGED_CODE();
  244. BattPrint (BATT_TRACE, ("CompBatt: ENTERING System Control\n"));
  245. compBatt = (PCOMPOSITE_BATTERY) DeviceObject->DeviceExtension;
  246. if (compBatt->LowerDevice != NULL) {
  247. IoSkipCurrentIrpStackLocation( Irp );
  248. status = IoCallDriver( compBatt->LowerDevice, Irp );
  249. } else {
  250. Irp->IoStatus.Status = status = STATUS_NOT_SUPPORTED;
  251. IoCompleteRequest( Irp, IO_NO_INCREMENT );
  252. }
  253. return status;
  254. }
  255. NTSTATUS
  256. CompBattQueryTag (
  257. IN PVOID Context,
  258. OUT PULONG BatteryTag
  259. )
  260. /*++
  261. Routine Description:
  262. Called by the class driver to retrieve the batteries current tag value
  263. Arguments:
  264. Context - Miniport context value for battery
  265. BatteryTag - Pointer to return current tag
  266. Return Value:
  267. Success if there is a battery currently installed, else no such device.
  268. --*/
  269. {
  270. PCOMPOSITE_BATTERY compBatt;
  271. NTSTATUS status = STATUS_SUCCESS;
  272. PAGED_CODE();
  273. BattPrint (BATT_TRACE, ("CompBatt: ENTERING QueryTag\n"));
  274. compBatt = (PCOMPOSITE_BATTERY) Context;
  275. if (!(compBatt->Info.Valid & VALID_TAG)) {
  276. //
  277. // Recalculate the composite's tag.
  278. //
  279. CompBattRecalculateTag(compBatt);
  280. }
  281. if ((compBatt->Info.Valid & VALID_TAG) && (compBatt->Info.Tag != BATTERY_TAG_INVALID)) {
  282. *BatteryTag = compBatt->Info.Tag;
  283. status = STATUS_SUCCESS;
  284. } else {
  285. *BatteryTag = BATTERY_TAG_INVALID;
  286. status = STATUS_NO_SUCH_DEVICE;
  287. }
  288. BattPrint (BATT_TRACE, ("CompBatt: EXITING QueryTag\n"));
  289. return status;
  290. }
  291. NTSTATUS
  292. CompBattQueryInformation (
  293. IN PVOID Context,
  294. IN ULONG BatteryTag,
  295. IN BATTERY_QUERY_INFORMATION_LEVEL Level,
  296. IN LONG AtRate,
  297. OUT PVOID Buffer,
  298. IN ULONG BufferLength,
  299. OUT PULONG ReturnedLength
  300. )
  301. {
  302. ULONG resultData;
  303. NTSTATUS status;
  304. PVOID returnBuffer;
  305. ULONG returnBufferLength;
  306. PCOMPOSITE_BATTERY compBatt;
  307. BATTERY_INFORMATION totalBattInfo;
  308. BATTERY_REPORTING_SCALE granularity[4];
  309. BATTERY_MANUFACTURE_DATE date;
  310. WCHAR compositeName[] = L"Composite Battery";
  311. PAGED_CODE();
  312. BattPrint (BATT_TRACE, ("CompBatt: ENTERING QueryInformation\n"));
  313. compBatt = (PCOMPOSITE_BATTERY) Context;
  314. if ((BatteryTag != compBatt->Info.Tag) || !(compBatt->Info.Valid & VALID_TAG)) {
  315. return STATUS_NO_SUCH_DEVICE;
  316. }
  317. returnBuffer = NULL;
  318. returnBufferLength = 0;
  319. status = STATUS_SUCCESS;
  320. //
  321. // Get the info requested
  322. //
  323. switch (Level) {
  324. case BatteryInformation:
  325. RtlZeroMemory (&totalBattInfo, sizeof(totalBattInfo));
  326. status = CompBattGetBatteryInformation (&totalBattInfo, compBatt);
  327. if (NT_SUCCESS(status)) {
  328. returnBuffer = &totalBattInfo;
  329. returnBufferLength = sizeof(totalBattInfo);
  330. }
  331. break;
  332. case BatteryGranularityInformation:
  333. RtlZeroMemory (&granularity[0], sizeof(granularity));
  334. status = CompBattGetBatteryGranularity (&granularity[0], compBatt);
  335. if (NT_SUCCESS(status)) {
  336. returnBuffer = &granularity[0];
  337. returnBufferLength = sizeof(granularity);
  338. }
  339. break;
  340. case BatteryTemperature:
  341. resultData = 0;
  342. returnBuffer = &resultData;
  343. returnBufferLength = sizeof (resultData);
  344. break;
  345. case BatteryEstimatedTime:
  346. RtlZeroMemory (&resultData, sizeof(resultData));
  347. status = CompBattGetEstimatedTime (&resultData, compBatt);
  348. if (NT_SUCCESS(status)) {
  349. returnBuffer = &resultData;
  350. returnBufferLength = sizeof(resultData);
  351. }
  352. break;
  353. case BatteryDeviceName:
  354. returnBuffer = compositeName;
  355. returnBufferLength = sizeof (compositeName);
  356. break;
  357. case BatteryManufactureDate:
  358. date.Day = 26;
  359. date.Month = 6;
  360. date.Year = 1997;
  361. returnBuffer = &date;
  362. returnBufferLength = sizeof (date);
  363. break;
  364. case BatteryManufactureName:
  365. returnBuffer = compositeName;
  366. returnBufferLength = sizeof (compositeName);
  367. break;
  368. case BatteryUniqueID:
  369. resultData = 0;
  370. returnBuffer = &resultData;
  371. returnBufferLength = sizeof (resultData);
  372. break;
  373. default:
  374. status = STATUS_INVALID_PARAMETER;
  375. break;
  376. }
  377. //
  378. // Make sure nothing changed while reading batteries.
  379. //
  380. if ((BatteryTag != compBatt->Info.Tag) || !(compBatt->Info.Valid & VALID_TAG)) {
  381. return STATUS_NO_SUCH_DEVICE;
  382. }
  383. //
  384. // Done, return buffer if needed
  385. //
  386. *ReturnedLength = returnBufferLength;
  387. if (BufferLength < returnBufferLength) {
  388. status = STATUS_BUFFER_TOO_SMALL;
  389. }
  390. if (NT_SUCCESS(status) && returnBuffer) {
  391. memcpy (Buffer, returnBuffer, returnBufferLength);
  392. }
  393. BattPrint (BATT_TRACE, ("CompBatt: EXITING QueryInformation\n"));
  394. return status;
  395. }
  396. NTSTATUS
  397. CompBattQueryStatus (
  398. IN PVOID Context,
  399. IN ULONG BatteryTag,
  400. OUT PBATTERY_STATUS BatteryStatus
  401. )
  402. /*++
  403. Routine Description:
  404. Called by the class driver to retrieve the batteries current status. This
  405. routine loops through all of the batteries in the system and reports a
  406. composite battery.
  407. Arguments:
  408. Context - Miniport context value for battery
  409. BatteryTag - Tag of current battery
  410. BatteryStatus - Pointer to structure to return the current battery status
  411. Return Value:
  412. Success if there is a battery currently installed, else no such device.
  413. --*/
  414. {
  415. NTSTATUS status = STATUS_SUCCESS;
  416. PCOMPOSITE_ENTRY batt;
  417. PLIST_ENTRY entry;
  418. PBATTERY_STATUS localBatteryStatus;
  419. PCOMPOSITE_BATTERY compBatt;
  420. BATTERY_WAIT_STATUS batteryWaitStatus;
  421. ULONGLONG wallClockTime;
  422. BattPrint (BATT_TRACE, ("CompBatt: ENTERING QueryStatus\n"));
  423. compBatt = (PCOMPOSITE_BATTERY) Context;
  424. if ((BatteryTag != compBatt->Info.Tag) || !(compBatt->Info.Valid & VALID_TAG)) {
  425. return STATUS_NO_SUCH_DEVICE;
  426. }
  427. //
  428. // Initialize Composite data structure.
  429. //
  430. BatteryStatus->Rate = BATTERY_UNKNOWN_RATE;
  431. BatteryStatus->Voltage = BATTERY_UNKNOWN_VOLTAGE;
  432. BatteryStatus->Capacity = BATTERY_UNKNOWN_CAPACITY;
  433. // Composite battery will only report POWER_ON_LINE if all batteries report
  434. // this flag.
  435. BatteryStatus->PowerState = BATTERY_POWER_ON_LINE;
  436. //
  437. // Set up the local battery status structure for calls to the batteries
  438. //
  439. RtlZeroMemory (&batteryWaitStatus, sizeof (BATTERY_WAIT_STATUS));
  440. //
  441. // Get current time for timestamps
  442. //
  443. wallClockTime = KeQueryInterruptTime ();
  444. //
  445. // If cache is fresh, no need to do anything
  446. //
  447. if ((wallClockTime - compBatt->Info.StatusTimeStamp) <= CACHE_STATUS_TIMEOUT) {
  448. BattPrint (BATT_NOTE, ("CompBattQueryStatus: Composite battery status cache is [valid]\n"));
  449. //
  450. // Copy status info to caller's buffer
  451. //
  452. RtlCopyMemory (BatteryStatus, &compBatt->Info.Status, sizeof (BATTERY_STATUS));
  453. return STATUS_SUCCESS;
  454. }
  455. BattPrint (BATT_NOTE, ("CompBattQueryStatus: Composite battery status cache is [stale] - refreshing\n"));
  456. //
  457. // Walk the list of batteries, getting status of each
  458. //
  459. ExAcquireFastMutex (&compBatt->ListMutex);
  460. for (entry = compBatt->Batteries.Flink; entry != &compBatt->Batteries; entry = entry->Flink) {
  461. batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries);
  462. if (!NT_SUCCESS (CompbattAcquireDeleteLock(&batt->DeleteLock))) {
  463. continue;
  464. }
  465. ExReleaseFastMutex (&compBatt->ListMutex);
  466. batteryWaitStatus.BatteryTag = batt->Info.Tag;
  467. localBatteryStatus = &batt->Info.Status;
  468. if (batt->Info.Valid & VALID_TAG) {
  469. //
  470. // If cached status for this battery is stale, refresh it
  471. //
  472. if ((wallClockTime - batt->Info.StatusTimeStamp) > CACHE_STATUS_TIMEOUT) {
  473. BattPrint (BATT_NOTE, ("CompBattQueryStatus: Battery status cache is [stale] - refreshing\n"));
  474. //
  475. // issue IOCTL to device
  476. //
  477. RtlZeroMemory (localBatteryStatus, sizeof(BATTERY_STATUS));
  478. status = BatteryIoctl (IOCTL_BATTERY_QUERY_STATUS,
  479. batt->DeviceObject,
  480. &batteryWaitStatus,
  481. sizeof (BATTERY_WAIT_STATUS),
  482. localBatteryStatus,
  483. sizeof (BATTERY_STATUS),
  484. FALSE);
  485. if (!NT_SUCCESS(status)) {
  486. //
  487. // In case of failure, this function should simply return the
  488. // status code. Invalidating of data is now performed only
  489. // in MonitorIrpComplete.
  490. //
  491. // This raises the slight possibility that the sender of this
  492. // request could retry before the data is properly invalidated,
  493. // but worst case, they would again get this same error condition
  494. // until the data is properly invalidated by MonitorIrpComplete.
  495. //
  496. if (status == STATUS_DEVICE_REMOVED) {
  497. //
  498. // This battery is being removed.
  499. // The composite battery tag is or will soon be
  500. // invalidated by MonitorIrpComplete.
  501. //
  502. status = STATUS_NO_SUCH_DEVICE;
  503. }
  504. //
  505. // Return failure code.
  506. //
  507. ExAcquireFastMutex (&compBatt->ListMutex);
  508. CompbattReleaseDeleteLock(&batt->DeleteLock);
  509. break;
  510. }
  511. // Set new timestamp
  512. batt->Info.StatusTimeStamp = wallClockTime;
  513. } else {
  514. BattPrint (BATT_NOTE, ("CompBattQueryStatus: Battery status cache is [valid]\n"));
  515. }
  516. //
  517. // Accumulate data.
  518. //
  519. //
  520. // Combine the power states.
  521. //
  522. // Logical OR CHARGING and DISCHARGING
  523. BatteryStatus->PowerState |= (localBatteryStatus->PowerState &
  524. (BATTERY_CHARGING |
  525. BATTERY_DISCHARGING));
  526. // Logical AND POWER_ON_LINE
  527. BatteryStatus->PowerState &= (localBatteryStatus->PowerState |
  528. ~BATTERY_POWER_ON_LINE);
  529. // Compbatt is critical if one battery is critical and discharging
  530. if ((localBatteryStatus->PowerState & BATTERY_CRITICAL) &&
  531. (localBatteryStatus->PowerState & BATTERY_DISCHARGING)) {
  532. BatteryStatus->PowerState |= BATTERY_CRITICAL;
  533. }
  534. //
  535. // The Capacity could possibly be "Unknown" for CMBatt, and if so
  536. // we should not add it to the total capacity.
  537. //
  538. if (BatteryStatus->Capacity == BATTERY_UNKNOWN_CAPACITY) {
  539. BatteryStatus->Capacity = localBatteryStatus->Capacity;
  540. } else if (localBatteryStatus->Capacity != BATTERY_UNKNOWN_CAPACITY) {
  541. BatteryStatus->Capacity += localBatteryStatus->Capacity;
  542. }
  543. //
  544. // The Voltage should just be the greatest one encountered.
  545. //
  546. if (BatteryStatus->Voltage == BATTERY_UNKNOWN_VOLTAGE) {
  547. BatteryStatus->Voltage = localBatteryStatus->Voltage;
  548. } else if ((localBatteryStatus->Voltage > BatteryStatus->Voltage) &&
  549. (localBatteryStatus->Voltage != BATTERY_UNKNOWN_VOLTAGE)) {
  550. BatteryStatus->Voltage = localBatteryStatus->Voltage;
  551. }
  552. //
  553. // The Current should just be total of all currents encountered. This could
  554. // also possibly be "Unknown" for CMBatt, and if so we should not use it
  555. // in the calculation.
  556. //
  557. if (BatteryStatus->Rate == BATTERY_UNKNOWN_RATE) {
  558. BatteryStatus->Rate = localBatteryStatus->Rate;
  559. } else if (localBatteryStatus->Rate != BATTERY_UNKNOWN_RATE) {
  560. BatteryStatus->Rate += localBatteryStatus->Rate;
  561. }
  562. } // if (batt->Tag != BATTERY_TAG_INVALID)
  563. ExAcquireFastMutex (&compBatt->ListMutex);
  564. CompbattReleaseDeleteLock(&batt->DeleteLock);
  565. } // for (entry = gBatteries.Flink; entry != &gBatteries; entry = entry->Flink)
  566. ExReleaseFastMutex (&compBatt->ListMutex);
  567. //
  568. // If one battery was discharging while another was charging
  569. // Assume that it is discharging. (This could happen with a UPS attached)
  570. //
  571. if ((BatteryStatus->PowerState & BATTERY_CHARGING) &&
  572. (BatteryStatus->PowerState & BATTERY_DISCHARGING)) {
  573. BatteryStatus->PowerState &= ~BATTERY_CHARGING;
  574. }
  575. //
  576. // Make sure nothing changed while reading batteries.
  577. //
  578. if ((BatteryTag != compBatt->Info.Tag) || !(compBatt->Info.Valid & VALID_TAG)) {
  579. return STATUS_NO_SUCH_DEVICE;
  580. }
  581. //
  582. // Save the status in the composites cache
  583. //
  584. if (NT_SUCCESS(status)) {
  585. RtlCopyMemory (&compBatt->Info.Status, BatteryStatus, sizeof (BATTERY_STATUS));
  586. compBatt->Info.StatusTimeStamp = wallClockTime;
  587. BattPrint (BATT_DATA, ("CompBatt: Composite's new Status\n"
  588. "-------- PowerState = %x\n"
  589. "-------- Capacity = %x\n"
  590. "-------- Voltage = %x\n"
  591. "-------- Rate = %x\n",
  592. compBatt->Info.Status.PowerState,
  593. compBatt->Info.Status.Capacity,
  594. compBatt->Info.Status.Voltage,
  595. compBatt->Info.Status.Rate)
  596. );
  597. }
  598. BattPrint (BATT_TRACE, ("CompBatt: EXITING QueryStatus\n"));
  599. return status;
  600. }
  601. NTSTATUS
  602. CompBattSetStatusNotify (
  603. IN PVOID Context,
  604. IN ULONG BatteryTag,
  605. IN PBATTERY_NOTIFY BatteryNotify
  606. )
  607. /*++
  608. Routine Description:
  609. Called by the class driver to set the batteries current notification
  610. setting. When the battery trips the notification, one call to
  611. BatteryClassStatusNotify is issued. If an error is returned, the
  612. class driver will poll the battery status - primarily for capacity
  613. changes. Which is to say the miniport should still issue BatteryClass-
  614. StatusNotify whenever the power state changes.
  615. Arguments:
  616. Context - Miniport context value for battery
  617. BatteryTag - Tag of current battery
  618. BatteryNotify - The notification setting
  619. Return Value:
  620. Status
  621. --*/
  622. {
  623. PCOMPOSITE_ENTRY batt;
  624. PLIST_ENTRY entry;
  625. PCOMPOSITE_BATTERY compBatt;
  626. BATTERY_STATUS batteryStatus;
  627. LONG totalRate = 0;
  628. ULONG delta;
  629. ULONG highCapacityDelta;
  630. ULONG lowCapacityDelta;
  631. NTSTATUS status;
  632. BOOLEAN inconsistent = FALSE;
  633. ULONG battCount = 0;
  634. BattPrint (BATT_TRACE, ("CompBatt: ENTERING SetStatusNotify\n"));
  635. compBatt = (PCOMPOSITE_BATTERY) Context;
  636. //
  637. // Check to see if this is the right battery
  638. //
  639. if ((BatteryTag != compBatt->Info.Tag) || !(compBatt->Info.Valid & VALID_TAG)) {
  640. return STATUS_NO_SUCH_DEVICE;
  641. }
  642. //
  643. // Refresh the composite battery status cache if necessary.
  644. //
  645. status = CompBattQueryStatus (compBatt, BatteryTag, &batteryStatus);
  646. if (!NT_SUCCESS(status)) {
  647. return status;
  648. }
  649. //
  650. // Save away the composite notification parameters for future reference
  651. //
  652. compBatt->Wait.PowerState = BatteryNotify->PowerState;
  653. compBatt->Wait.LowCapacity = BatteryNotify->LowCapacity;
  654. compBatt->Wait.HighCapacity = BatteryNotify->HighCapacity;
  655. compBatt->Info.Valid |= VALID_NOTIFY;
  656. BattPrint (BATT_DATA, ("CompBatt: Got SetStatusNotify\n"
  657. "-------- PowerState = %x\n"
  658. "-------- LowCapacity = %x\n"
  659. "-------- HighCapacity = %x\n",
  660. compBatt->Wait.PowerState,
  661. compBatt->Wait.LowCapacity,
  662. compBatt->Wait.HighCapacity)
  663. );
  664. //
  665. // Compute capacity deltas based on the total system capacity
  666. //
  667. lowCapacityDelta = compBatt->Info.Status.Capacity - BatteryNotify->LowCapacity;
  668. highCapacityDelta = BatteryNotify->HighCapacity - compBatt->Info.Status.Capacity;
  669. //
  670. // Run through the list of batteries and add up the total rate
  671. //
  672. //
  673. // Hold Mutex for this entire loop, since this loop doesn't call any drivers, etc
  674. //
  675. ExAcquireFastMutex (&compBatt->ListMutex);
  676. for (entry = compBatt->Batteries.Flink; entry != &compBatt->Batteries; entry = entry->Flink) {
  677. batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries);
  678. if (!NT_SUCCESS (CompbattAcquireDeleteLock(&batt->DeleteLock))) {
  679. continue;
  680. }
  681. if (!(batt->Info.Valid & VALID_TAG) || (batt->Info.Status.Rate == BATTERY_UNKNOWN_RATE)) {
  682. CompbattReleaseDeleteLock(&batt->DeleteLock);
  683. continue;
  684. }
  685. battCount++;
  686. if (((batt->Info.Status.PowerState & BATTERY_DISCHARGING) && (batt->Info.Status.Rate >= 0)) ||
  687. ((batt->Info.Status.PowerState & BATTERY_CHARGING) && (batt->Info.Status.Rate <= 0)) ||
  688. (((batt->Info.Status.PowerState & (BATTERY_CHARGING | BATTERY_DISCHARGING)) == 0) && (batt->Info.Status.Rate != 0))) {
  689. inconsistent = TRUE;
  690. BattPrint (BATT_ERROR, ("CompBatt: PowerState 0x%08x does not match Rate 0x%08x\n",
  691. batt->Info.Status.PowerState,
  692. batt->Info.Status.Rate));
  693. }
  694. if (((batt->Info.Status.Rate < 0) ^ (totalRate < 0)) && (batt->Info.Status.Rate != 0) && (totalRate != 0)) {
  695. inconsistent = TRUE;
  696. BattPrint (BATT_ERROR, ("CompBatt: It appears that one battery is charging while another is discharging.\n"
  697. " This situation is not handled correctly.\n"));
  698. }
  699. totalRate += batt->Info.Status.Rate;
  700. CompbattReleaseDeleteLock(&batt->DeleteLock);
  701. }
  702. ExReleaseFastMutex (&compBatt->ListMutex);
  703. //
  704. // Run through the list of batteries and update the new wait status params
  705. //
  706. ExAcquireFastMutex (&compBatt->ListMutex);
  707. for (entry = compBatt->Batteries.Flink; entry != &compBatt->Batteries; entry = entry->Flink) {
  708. batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries);
  709. if (!NT_SUCCESS (CompbattAcquireDeleteLock(&batt->DeleteLock))) {
  710. continue;
  711. }
  712. ExReleaseFastMutex (&compBatt->ListMutex);
  713. if (!(batt->Info.Valid & VALID_TAG) ||
  714. (batt->Info.Status.Capacity == BATTERY_UNKNOWN_CAPACITY)) {
  715. batt->Wait.LowCapacity = BATTERY_UNKNOWN_CAPACITY;
  716. batt->Wait.HighCapacity = BATTERY_UNKNOWN_CAPACITY;
  717. #if DEBUG
  718. if (batt->Info.Status.Capacity == BATTERY_UNKNOWN_CAPACITY) {
  719. BattPrint (BATT_DEBUG, ("CompBattSetStatusNotify: Unknown Capacity encountered.\n"));
  720. }
  721. #endif
  722. ExAcquireFastMutex (&compBatt->ListMutex);
  723. CompbattReleaseDeleteLock(&batt->DeleteLock);
  724. continue;
  725. }
  726. //
  727. // Adjust the LowCapacity alarm
  728. //
  729. //
  730. // Calculate the portion of the composite battery delta that belongs to
  731. // this battery.
  732. //
  733. if (inconsistent) {
  734. //
  735. // If data returned from batteries was inconsistent, don't do anything intelligent.
  736. // Just divide the notifications evenly between the batteries as if they were
  737. // draining at the same rate. This will most likely result in early notification,
  738. // but by that time the data on the batteries ought to have settled down.
  739. //
  740. delta = lowCapacityDelta/battCount;
  741. } else if (totalRate != 0) {
  742. delta = (ULONG) (((LONGLONG) lowCapacityDelta * batt->Info.Status.Rate) / totalRate);
  743. } else {
  744. //
  745. // If total rate is zero, we would expect no change in battery
  746. // capacity, so we should get notified of any.
  747. //
  748. delta = 0;
  749. }
  750. //
  751. // Check for underflow on low capacity
  752. //
  753. if (batt->Info.Status.Capacity > delta) {
  754. batt->Wait.LowCapacity = batt->Info.Status.Capacity - delta;
  755. } else {
  756. //
  757. // If there is still some charge in the battery set the LowCapacity
  758. // alarm to 1, else to 0.
  759. //
  760. // No need to do that. If this battery runs out, it doesn't
  761. // need to notify. One of the other batteries will be notifying
  762. // right away. If there isn't another battery, this shouldn't
  763. // happen.
  764. BattPrint (BATT_NOTE, ("CompBatt: Unexpectedly huge delta encountered. \n"
  765. " Capacity = %08x\n"
  766. " LowCapcityDelta = %08x\n"
  767. " Rate = %08x\n"
  768. " TotalRate = %08x\n",
  769. batt->Info.Status.Capacity,
  770. lowCapacityDelta,
  771. batt->Info.Status.Rate,
  772. totalRate));
  773. batt->Wait.LowCapacity = 0;
  774. }
  775. //
  776. // Adjust the HighCapacity alarm for charging batteries only
  777. //
  778. //
  779. // Calculate the portion of the composite battery delta that belongs to
  780. // this battery.
  781. //
  782. if (inconsistent) {
  783. delta = highCapacityDelta/battCount;
  784. } else if (totalRate != 0) {
  785. delta = (ULONG) (((LONGLONG) highCapacityDelta * batt->Info.Status.Rate) / totalRate);
  786. } else {
  787. //
  788. // If total rate is zero, we would expect no change in battery
  789. // capacity, so we should get notified of any.
  790. //
  791. delta = 0;
  792. }
  793. //
  794. // Check for overflow on high capacity.
  795. // Allow setting the percentage above full charged capacity
  796. // since some batteries do that when new.
  797. //
  798. if ((MAX_HIGH_CAPACITY - delta) < batt->Wait.HighCapacity) {
  799. batt->Wait.HighCapacity = MAX_HIGH_CAPACITY;
  800. } else {
  801. batt->Wait.HighCapacity = batt->Info.Status.Capacity + delta;
  802. }
  803. //
  804. // If we're currently waiting, and the parameters are in
  805. // conflict, get the irp back to reset it
  806. //
  807. if (batt->State == CB_ST_GET_STATUS &&
  808. (batt->Wait.PowerState != batt->IrpBuffer.Wait.PowerState ||
  809. batt->Wait.LowCapacity != batt->IrpBuffer.Wait.LowCapacity ||
  810. batt->Wait.HighCapacity != batt->IrpBuffer.Wait.HighCapacity)) {
  811. IoCancelIrp (batt->StatusIrp);
  812. }
  813. ExAcquireFastMutex (&compBatt->ListMutex);
  814. CompbattReleaseDeleteLock(&batt->DeleteLock);
  815. }
  816. ExReleaseFastMutex (&compBatt->ListMutex);
  817. //
  818. // Make sure nothing changed while reading batteries.
  819. //
  820. if ((BatteryTag != compBatt->Info.Tag) || !(compBatt->Info.Valid & VALID_TAG)) {
  821. return STATUS_NO_SUCH_DEVICE;
  822. }
  823. BattPrint (BATT_TRACE, ("CompBatt: EXITING SetStatusNotify\n"));
  824. return STATUS_SUCCESS;
  825. }
  826. NTSTATUS
  827. CompBattDisableStatusNotify (
  828. IN PVOID Context
  829. )
  830. /*++
  831. Routine Description:
  832. Called by the class driver to disable the notification setting
  833. for the battery supplied by Context. Note, to disable a setting
  834. does not require the battery tag. Any notification is to be
  835. masked off until a subsequent call to SmbBattSetStatusNotify.
  836. Arguments:
  837. Context - Miniport context value for battery
  838. Return Value:
  839. Status
  840. --*/
  841. {
  842. PCOMPOSITE_ENTRY batt;
  843. PLIST_ENTRY entry;
  844. PCOMPOSITE_BATTERY compBatt;
  845. BattPrint (BATT_TRACE, ("CompBatt: ENTERING DisableStatusNotify\n"));
  846. compBatt = (PCOMPOSITE_BATTERY) Context;
  847. //
  848. // Run through the list of batteries and disable the wait status params
  849. // Hold mutex for entire loop, since loop doesn't make any calls.
  850. ExAcquireFastMutex (&compBatt->ListMutex);
  851. for (entry = compBatt->Batteries.Flink; entry != &compBatt->Batteries; entry = entry->Flink) {
  852. batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries);
  853. batt->Wait.LowCapacity = MIN_LOW_CAPACITY;
  854. batt->Wait.HighCapacity = MAX_HIGH_CAPACITY;
  855. }
  856. ExReleaseFastMutex (&compBatt->ListMutex);
  857. BattPrint (BATT_TRACE, ("CompBatt: EXITING DisableStatusNotify\n"));
  858. return STATUS_SUCCESS;
  859. }
  860. NTSTATUS
  861. CompBattGetBatteryInformation (
  862. IN PBATTERY_INFORMATION TotalBattInfo,
  863. IN PCOMPOSITE_BATTERY CompBatt
  864. )
  865. /*++
  866. Routine Description:
  867. The routine loops through the batteries in the system and queries them
  868. for information. It then forms a composite representation of this
  869. information to send back to the caller.
  870. Arguments:
  871. TotalBattInfo - Buffer to place the composite battery information in
  872. Return Value:
  873. STATUS_SUCCESS or the status returned by the Ioctl to the battery.
  874. --*/
  875. {
  876. NTSTATUS status;
  877. PBATTERY_INFORMATION battInfo;
  878. PCOMPOSITE_ENTRY batt;
  879. PLIST_ENTRY entry;
  880. BATTERY_QUERY_INFORMATION bInfo;
  881. BattPrint (BATT_TRACE, ("CompBatt: ENTERING GetBatteryInformation\n"));
  882. TotalBattInfo->DefaultAlert1 = 0;
  883. TotalBattInfo->DefaultAlert2 = 0;
  884. TotalBattInfo->CriticalBias = 0;
  885. status = STATUS_SUCCESS;
  886. //
  887. // Run through the list of batteries getting the information
  888. //
  889. ExAcquireFastMutex (&CompBatt->ListMutex);
  890. for (entry = CompBatt->Batteries.Flink; entry != &CompBatt->Batteries; entry = entry->Flink) {
  891. batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries);
  892. if (!NT_SUCCESS (CompbattAcquireDeleteLock(&batt->DeleteLock))) {
  893. continue;
  894. }
  895. ExReleaseFastMutex (&CompBatt->ListMutex);
  896. bInfo.BatteryTag = batt->Info.Tag;
  897. bInfo.InformationLevel = BatteryInformation;
  898. bInfo.AtRate = 0;
  899. battInfo = &batt->Info.Info;
  900. if (batt->Info.Tag != BATTERY_TAG_INVALID) {
  901. if (!(batt->Info.Valid & VALID_INFO)) {
  902. //
  903. // issue IOCTL to device
  904. //
  905. RtlZeroMemory (battInfo, sizeof(BATTERY_INFORMATION));
  906. status = BatteryIoctl (IOCTL_BATTERY_QUERY_INFORMATION,
  907. batt->DeviceObject,
  908. &bInfo,
  909. sizeof (bInfo),
  910. battInfo,
  911. sizeof (BATTERY_INFORMATION),
  912. FALSE);
  913. if (!NT_SUCCESS(status)) {
  914. if (status == STATUS_DEVICE_REMOVED) {
  915. //
  916. // If one device is removed, that invalidates the tag.
  917. //
  918. status = STATUS_NO_SUCH_DEVICE;
  919. }
  920. ExAcquireFastMutex (&CompBatt->ListMutex);
  921. CompbattReleaseDeleteLock(&batt->DeleteLock);
  922. break;
  923. }
  924. BattPrint (BATT_DATA, ("CompBattGetBatteryInformation: Read individual BATTERY_INFORMATION\n"
  925. "-------- Capabilities = %x\n"
  926. "-------- Technology = %x\n"
  927. "-------- Chemistry[4] = %x\n"
  928. "-------- DesignedCapacity = %x\n"
  929. "-------- FullChargedCapacity = %x\n"
  930. "-------- DefaultAlert1 = %x\n"
  931. "-------- DefaultAlert2 = %x\n"
  932. "-------- CriticalBias = %x\n"
  933. "-------- CycleCount = %x\n",
  934. battInfo->Capabilities,
  935. battInfo->Technology,
  936. battInfo->Chemistry[4],
  937. battInfo->DesignedCapacity,
  938. battInfo->FullChargedCapacity,
  939. battInfo->DefaultAlert1,
  940. battInfo->DefaultAlert2,
  941. battInfo->CriticalBias,
  942. battInfo->CycleCount)
  943. );
  944. batt->Info.Valid |= VALID_INFO;
  945. } // if (!(batt->Info.Valid & VALID_INFO))
  946. //
  947. // Logically OR the capabilities
  948. //
  949. TotalBattInfo->Capabilities |= battInfo->Capabilities;
  950. //
  951. // Add the designed capacities. If this is UNKNOWN (possible
  952. // with the control method batteries, don't add them in.
  953. //
  954. if (battInfo->DesignedCapacity != BATTERY_UNKNOWN_CAPACITY) {
  955. TotalBattInfo->DesignedCapacity += battInfo->DesignedCapacity;
  956. }
  957. if (battInfo->FullChargedCapacity != BATTERY_UNKNOWN_CAPACITY) {
  958. TotalBattInfo->FullChargedCapacity += battInfo->FullChargedCapacity;
  959. }
  960. if (TotalBattInfo->DefaultAlert1 < battInfo->DefaultAlert1) {
  961. TotalBattInfo->DefaultAlert1 = battInfo->DefaultAlert1;
  962. }
  963. if (TotalBattInfo->DefaultAlert2 < battInfo->DefaultAlert2) {
  964. TotalBattInfo->DefaultAlert2 = battInfo->DefaultAlert2;
  965. }
  966. if (TotalBattInfo->CriticalBias < battInfo->CriticalBias) {
  967. TotalBattInfo->CriticalBias = battInfo->CriticalBias;
  968. }
  969. } // if (batt->Tag != BATTERY_TAG_INVALID)
  970. ExAcquireFastMutex (&CompBatt->ListMutex);
  971. CompbattReleaseDeleteLock(&batt->DeleteLock);
  972. } // for (entry = gBatteries.Flink; entry != &gBatteries; entry = entry->Flink)
  973. ExReleaseFastMutex (&CompBatt->ListMutex);
  974. //
  975. // Save the battery information in the composite battery cache
  976. //
  977. if (NT_SUCCESS(status)) {
  978. //
  979. // Check to see if we have an UNKNOWN full charge capacity. If so, set this
  980. // to the design capacity.
  981. //
  982. if (TotalBattInfo->FullChargedCapacity == 0) {
  983. TotalBattInfo->FullChargedCapacity = TotalBattInfo->DesignedCapacity;
  984. }
  985. BattPrint (BATT_DATA, ("CompBattGetBatteryInformation: Returning BATTERY_INFORMATION\n"
  986. "-------- Capabilities = %x\n"
  987. "-------- Technology = %x\n"
  988. "-------- Chemistry[4] = %x\n"
  989. "-------- DesignedCapacity = %x\n"
  990. "-------- FullChargedCapacity = %x\n"
  991. "-------- DefaultAlert1 = %x\n"
  992. "-------- DefaultAlert2 = %x\n"
  993. "-------- CriticalBias = %x\n"
  994. "-------- CycleCount = %x\n",
  995. TotalBattInfo->Capabilities,
  996. TotalBattInfo->Technology,
  997. TotalBattInfo->Chemistry[4],
  998. TotalBattInfo->DesignedCapacity,
  999. TotalBattInfo->FullChargedCapacity,
  1000. TotalBattInfo->DefaultAlert1,
  1001. TotalBattInfo->DefaultAlert2,
  1002. TotalBattInfo->CriticalBias,
  1003. TotalBattInfo->CycleCount)
  1004. );
  1005. RtlCopyMemory (&CompBatt->Info.Info, TotalBattInfo, sizeof(BATTERY_INFORMATION));
  1006. CompBatt->Info.Valid |= VALID_INFO;
  1007. }
  1008. BattPrint (BATT_TRACE, ("CompBatt: EXITING GetBatteryInformation\n"));
  1009. return status;
  1010. }
  1011. NTSTATUS
  1012. CompBattGetBatteryGranularity (
  1013. IN PBATTERY_REPORTING_SCALE GranularityBuffer,
  1014. IN PCOMPOSITE_BATTERY CompBatt
  1015. )
  1016. /*++
  1017. Routine Description:
  1018. The routine queries all the batteries in the system to get their granularity
  1019. settings. It then returns the setting that has the finest granularity in each range.
  1020. Arguments:
  1021. GranularityBuffer - Buffer for containing the results of the query
  1022. Return Value:
  1023. STATUS_SUCCESS or the status returned by the Ioctl to the battery.
  1024. --*/
  1025. {
  1026. NTSTATUS status;
  1027. BATTERY_REPORTING_SCALE localGranularity[4];
  1028. PCOMPOSITE_ENTRY batt;
  1029. PLIST_ENTRY entry;
  1030. ULONG i;
  1031. BATTERY_QUERY_INFORMATION bInfo;
  1032. BattPrint (BATT_TRACE, ("CompBatt: ENTERING GetBatteryGranularity\n"));
  1033. GranularityBuffer[0].Granularity = 0xFFFFFFFF;
  1034. GranularityBuffer[1].Granularity = 0xFFFFFFFF;
  1035. GranularityBuffer[2].Granularity = 0xFFFFFFFF;
  1036. GranularityBuffer[3].Granularity = 0xFFFFFFFF;
  1037. //
  1038. // Run through the list of batteries getting the granularity
  1039. //
  1040. ExAcquireFastMutex (&CompBatt->ListMutex);
  1041. for (entry = CompBatt->Batteries.Flink; entry != &CompBatt->Batteries; entry = entry->Flink) {
  1042. batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries);
  1043. if (!NT_SUCCESS (CompbattAcquireDeleteLock(&batt->DeleteLock))) {
  1044. continue;
  1045. }
  1046. ExReleaseFastMutex (&CompBatt->ListMutex);
  1047. bInfo.BatteryTag = batt->Info.Tag;
  1048. bInfo.InformationLevel = BatteryGranularityInformation;
  1049. if (batt->Info.Tag != BATTERY_TAG_INVALID) {
  1050. //
  1051. // issue IOCTL to device
  1052. //
  1053. RtlZeroMemory (&localGranularity[0], sizeof(localGranularity));
  1054. status = BatteryIoctl (IOCTL_BATTERY_QUERY_INFORMATION,
  1055. batt->DeviceObject,
  1056. &bInfo,
  1057. sizeof (bInfo),
  1058. &localGranularity,
  1059. sizeof (localGranularity),
  1060. FALSE);
  1061. if (!NT_SUCCESS(status)) {
  1062. if (status == STATUS_DEVICE_REMOVED) {
  1063. //
  1064. // If one device is removed, that invalidates the tag.
  1065. //
  1066. status = STATUS_NO_SUCH_DEVICE;
  1067. }
  1068. ExAcquireFastMutex (&CompBatt->ListMutex);
  1069. CompbattReleaseDeleteLock(&batt->DeleteLock);
  1070. break;
  1071. }
  1072. //
  1073. // Check for the best granularity in each range.
  1074. //
  1075. for (i = 0; i < 4; i++) {
  1076. if (localGranularity[i].Granularity) {
  1077. if (localGranularity[i].Granularity < GranularityBuffer[i].Granularity) {
  1078. GranularityBuffer[i].Granularity = localGranularity[i].Granularity;
  1079. }
  1080. GranularityBuffer[i].Capacity = localGranularity[i].Capacity;
  1081. }
  1082. }
  1083. } // if (batt->Tag != BATTERY_TAG_INVALID)
  1084. ExAcquireFastMutex (&CompBatt->ListMutex);
  1085. CompbattReleaseDeleteLock(&batt->DeleteLock);
  1086. } // for (entry = gBatteries.Flink; entry != &gBatteries; entry = entry->Flink)
  1087. ExReleaseFastMutex (&CompBatt->ListMutex);
  1088. BattPrint (BATT_TRACE, ("CompBatt: EXITING GetBatteryGranularity\n"));
  1089. return STATUS_SUCCESS;
  1090. }
  1091. NTSTATUS
  1092. CompBattGetEstimatedTime (
  1093. IN PULONG TimeBuffer,
  1094. IN PCOMPOSITE_BATTERY CompBatt
  1095. )
  1096. /*++
  1097. Routine Description:
  1098. The routine queries all the batteries in the system to get their estimated time left.
  1099. If one of the batteries in the system does not support this function then an error
  1100. is returned.
  1101. Arguments:
  1102. TimeBuffer - Buffer for containing cumulative time left
  1103. Return Value:
  1104. STATUS_SUCCESS or the status returned by the Ioctl to the battery.
  1105. --*/
  1106. {
  1107. NTSTATUS status;
  1108. LONG localBuffer = 0;
  1109. PCOMPOSITE_ENTRY batt;
  1110. PLIST_ENTRY entry;
  1111. BATTERY_QUERY_INFORMATION bInfo;
  1112. BATTERY_STATUS batteryStatus;
  1113. LONG atRate = 0;
  1114. BattPrint (BATT_TRACE, ("CompBatt: ENTERING GetEstimatedTime\n"));
  1115. *TimeBuffer = BATTERY_UNKNOWN_TIME;
  1116. //
  1117. // Refresh the composite battery status cache if necessary.
  1118. //
  1119. status = CompBattQueryStatus (CompBatt, CompBatt->Info.Tag, &batteryStatus);
  1120. if (!NT_SUCCESS(status)) {
  1121. return status;
  1122. }
  1123. //
  1124. // If we're on AC then our estimated run time is invalid.
  1125. //
  1126. if (CompBatt->Info.Status.PowerState & BATTERY_POWER_ON_LINE) {
  1127. return STATUS_SUCCESS;
  1128. }
  1129. //
  1130. // We are on battery power and may have more than one battery in the system.
  1131. //
  1132. // We need to find the total rate of power being drawn from all batteries
  1133. // then we need to ask how long each battery would last at that rate (as
  1134. // if they were being discharged one at a time). This should give us a fairly
  1135. // good measure of how long it will last.
  1136. //
  1137. // To find the power being drawn we read the devide the remaining capacity by
  1138. // the estimated time rather than simply reading the rate. This is because the
  1139. // rate is theoretically the instantanious current wereas the estimated time
  1140. // should be based on average usage. This isn't the case for control method
  1141. // batteries, but it is for smart batteries, and could be for others as well.
  1142. //
  1143. ExAcquireFastMutex (&CompBatt->ListMutex);
  1144. for (entry = CompBatt->Batteries.Flink; entry != &CompBatt->Batteries; entry = entry->Flink) {
  1145. batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries);
  1146. if (!NT_SUCCESS (CompbattAcquireDeleteLock(&batt->DeleteLock))) {
  1147. continue;
  1148. }
  1149. ExReleaseFastMutex (&CompBatt->ListMutex);
  1150. if (batt->Info.Valid & VALID_TAG) {
  1151. bInfo.BatteryTag = batt->Info.Tag;
  1152. bInfo.InformationLevel = BatteryEstimatedTime;
  1153. bInfo.AtRate = 0;
  1154. //
  1155. // issue IOCTL to device
  1156. //
  1157. status = BatteryIoctl (IOCTL_BATTERY_QUERY_INFORMATION,
  1158. batt->DeviceObject,
  1159. &bInfo,
  1160. sizeof (bInfo),
  1161. &localBuffer,
  1162. sizeof (localBuffer),
  1163. FALSE);
  1164. if ((localBuffer != BATTERY_UNKNOWN_TIME) &&
  1165. (localBuffer != 0) &&
  1166. (NT_SUCCESS(status))) {
  1167. atRate -= ((long)batt->Info.Status.Capacity)*3600 / localBuffer;
  1168. BattPrint (BATT_NOTE, ("CompBattGetEstimatedTime: EstTime: %08x, Capacity: %08x, cumulative AtRate: %08x\n", localBuffer, batt->Info.Status.Capacity, atRate));
  1169. } else {
  1170. BattPrint (BATT_NOTE, ("CompBattGetEstimatedTime: Bad Estimated time. Status: %08x, localBuffer: %08x, Capacity: %08x, cumulative AtRate: %08x\n",
  1171. status, localBuffer, batt->Info.Status.Capacity, atRate));
  1172. }
  1173. }
  1174. ExAcquireFastMutex (&CompBatt->ListMutex);
  1175. CompbattReleaseDeleteLock(&batt->DeleteLock);
  1176. }
  1177. ExReleaseFastMutex (&CompBatt->ListMutex);
  1178. BattPrint (BATT_NOTE, ("CompBattGetEstimatedTime: using atRate - %x\n", atRate));
  1179. //
  1180. // Did we find a battery?
  1181. //
  1182. if (atRate == 0) {
  1183. // Code could be added to here to handle batteries that return
  1184. // estimated runtime, but not rate information.
  1185. return STATUS_SUCCESS;
  1186. }
  1187. //
  1188. // Run through the list of batteries getting their estimated time
  1189. //
  1190. ExAcquireFastMutex (&CompBatt->ListMutex);
  1191. for (entry = CompBatt->Batteries.Flink; entry != &CompBatt->Batteries; entry = entry->Flink) {
  1192. batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries);
  1193. if (!NT_SUCCESS (CompbattAcquireDeleteLock(&batt->DeleteLock))) {
  1194. continue;
  1195. }
  1196. ExReleaseFastMutex (&CompBatt->ListMutex);
  1197. bInfo.BatteryTag = batt->Info.Tag;
  1198. bInfo.InformationLevel = BatteryEstimatedTime;
  1199. bInfo.AtRate = atRate;
  1200. if (batt->Info.Valid & VALID_TAG) {
  1201. //
  1202. // issue IOCTL to device
  1203. //
  1204. status = BatteryIoctl (IOCTL_BATTERY_QUERY_INFORMATION,
  1205. batt->DeviceObject,
  1206. &bInfo,
  1207. sizeof (bInfo),
  1208. &localBuffer,
  1209. sizeof (localBuffer),
  1210. FALSE);
  1211. BattPrint (BATT_NOTE, ("CompBattGetEstimatedTime: Status: %08x, EstTime: %08x\n", status, localBuffer));
  1212. if (!NT_SUCCESS(status)) {
  1213. //
  1214. // This could be an invalid device request for this battery.
  1215. // Continue with thte next battery.
  1216. //
  1217. if (status == STATUS_DEVICE_REMOVED) {
  1218. //
  1219. // If one device is removed, that invalidates the tag.
  1220. //
  1221. status = STATUS_NO_SUCH_DEVICE;
  1222. }
  1223. ExAcquireFastMutex (&CompBatt->ListMutex);
  1224. CompbattReleaseDeleteLock(&batt->DeleteLock);
  1225. continue;
  1226. }
  1227. //
  1228. // Add the estimated time.
  1229. //
  1230. if (localBuffer != BATTERY_UNKNOWN_TIME) {
  1231. if (*TimeBuffer == BATTERY_UNKNOWN_TIME) {
  1232. *TimeBuffer = localBuffer;
  1233. } else {
  1234. *TimeBuffer += localBuffer;
  1235. }
  1236. }
  1237. BattPrint (BATT_DATA, ("CompBattGetEstimatedTime: cumulative time: %08x\n", *TimeBuffer));
  1238. } // if (batt->Tag != BATTERY_TAG_INVALID)
  1239. ExAcquireFastMutex (&CompBatt->ListMutex);
  1240. CompbattReleaseDeleteLock(&batt->DeleteLock);
  1241. } // for (entry = gBatteries.Flink; entry != &gBatteries; entry = entry->Flink)
  1242. ExReleaseFastMutex (&CompBatt->ListMutex);
  1243. BattPrint (BATT_TRACE, ("CompBatt: EXITING GetEstimatedTime\n"));
  1244. return STATUS_SUCCESS;
  1245. }
  1246. NTSTATUS
  1247. CompBattMonitorIrpComplete (
  1248. IN PDEVICE_OBJECT DeviceObject,
  1249. IN PIRP Irp,
  1250. IN PVOID Context
  1251. )
  1252. /*++
  1253. Routine Description:
  1254. Constantly keeps an irp at the battery either querying for the tag or the
  1255. status. This routine fills in the irp, sets itself up as the completion
  1256. routine, and then resends the irp.
  1257. Arguments:
  1258. DeviceObject - Device object for the battery sent the irp.
  1259. Note: In this case DeviceObject is always NULL, so don't use it.
  1260. Irp - Current irp to work with
  1261. Context - Currently unused
  1262. Return Value:
  1263. TRUE if there are no changes, FALSE otherwise.
  1264. --*/
  1265. {
  1266. PIO_STACK_LOCATION IrpSp;
  1267. PCOMPOSITE_ENTRY Batt;
  1268. BattPrint (BATT_TRACE, ("CompBatt: ENTERING MonitorIrpComplete\n"));
  1269. IrpSp = IoGetCurrentIrpStackLocation(Irp);
  1270. Batt = IrpSp->Parameters.Others.Argument2;
  1271. //
  1272. // We always want to queue a work item to recycle the IRP. There were too many
  1273. // problems that could happen trying to recycle in the completion routine.
  1274. //
  1275. // If this driver ever gets reworked, it could be done that way, but it would take
  1276. // more time to get right than I have now. Queueing a work item is the safe thing
  1277. // to do.
  1278. //
  1279. ExQueueWorkItem (&Batt->WorkItem, DelayedWorkQueue);
  1280. return STATUS_MORE_PROCESSING_REQUIRED;
  1281. }
  1282. VOID CompBattMonitorIrpCompleteWorker (
  1283. IN PVOID Context
  1284. )
  1285. /*++
  1286. Routine Description:
  1287. This is either queued, or called by the completion routine.
  1288. Constantly keeps an irp at the battery either querying for the tag or the
  1289. status. This routine fills in the irp, sets itself up as the completion
  1290. routine, and then resends the irp.
  1291. Arguments:
  1292. Context - Composite battery entry.
  1293. Return Value:
  1294. TRUE if there are no changes, FALSE otherwise.
  1295. --*/
  1296. {
  1297. PCOMPOSITE_ENTRY Batt = (PCOMPOSITE_ENTRY) Context;
  1298. PDEVICE_OBJECT DeviceObject = Batt->DeviceObject;
  1299. PIRP Irp = Batt->StatusIrp;
  1300. PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
  1301. PCOMPOSITE_BATTERY compBatt = IrpSp->Parameters.Others.Argument1;
  1302. BATTERY_STATUS battStatus;
  1303. ULONG oldPowerState;
  1304. NTSTATUS status;
  1305. BattPrint (BATT_TRACE, ("CompBatt: ENTERING MonitorIrpCompleteWorker\n"));
  1306. IrpSp = IoGetNextIrpStackLocation(Irp);
  1307. //
  1308. // Reissue irp to battery to wait for a status change
  1309. //
  1310. if (NT_SUCCESS(Irp->IoStatus.Status) || Irp->IoStatus.Status == STATUS_CANCELLED) {
  1311. switch (Batt->State) {
  1312. case CB_ST_GET_TAG:
  1313. //
  1314. // A battery was just inserted, so the IOCTL_BATTERY_Query_TAG completed.
  1315. //
  1316. BattPrint (BATT_NOTE, ("CompBattMonitorIrpCompleteWorker: got tag for %x\n", Batt->DeviceObject));
  1317. //
  1318. // Update the tag, and wait on status
  1319. //
  1320. Batt->Wait.BatteryTag = Batt->IrpBuffer.Tag;
  1321. Batt->Info.Tag = Batt->IrpBuffer.Tag;
  1322. Batt->Info.Valid = VALID_TAG;
  1323. // Invalidate all cached info.
  1324. compBatt->Info.Valid = 0;
  1325. BattPrint (BATT_NOTE, ("CompBattMonitorIrpCompleteWorker: calling StatusNotify\n"));
  1326. BatteryClassStatusNotify (compBatt->Class);
  1327. break;
  1328. case CB_ST_GET_STATUS:
  1329. //
  1330. // IOCTL_BATTERY_QUERY_STATUS just completed. This could mean that the
  1331. // battery state changed, or the charge has left the acceptable range.
  1332. // If the battery was removed, it would not get here.
  1333. //
  1334. BattPrint (BATT_NOTE, ("CompBattMonitorIrpCompleteWorker: got status for %x\n", Batt->DeviceObject));
  1335. if (!(Irp->IoStatus.Status == STATUS_CANCELLED)) {
  1336. BattPrint (BATT_NOTE, ("Battery's state is\n"
  1337. "-------- PowerState = %x\n"
  1338. "-------- Capacity = %x\n"
  1339. "-------- Voltage = %x\n"
  1340. "-------- Rate = %x\n",
  1341. Batt->IrpBuffer.Status.PowerState,
  1342. Batt->IrpBuffer.Status.Capacity,
  1343. Batt->IrpBuffer.Status.Voltage,
  1344. Batt->IrpBuffer.Status.Rate)
  1345. );
  1346. //
  1347. // Battery status completed sucessfully.
  1348. // Update our wait, and wait some more
  1349. //
  1350. Batt->Wait.PowerState = Batt->IrpBuffer.Status.PowerState;
  1351. if (Batt->IrpBuffer.Status.Capacity != BATTERY_UNKNOWN_CAPACITY) {
  1352. if (Batt->Wait.HighCapacity < Batt->IrpBuffer.Status.Capacity) {
  1353. Batt->Wait.HighCapacity = Batt->IrpBuffer.Status.Capacity;
  1354. }
  1355. if (Batt->Wait.LowCapacity > Batt->IrpBuffer.Status.Capacity) {
  1356. Batt->Wait.LowCapacity = Batt->IrpBuffer.Status.Capacity;
  1357. }
  1358. } else {
  1359. BattPrint (BATT_DEBUG, ("CompBattMonitorIrpCompleteWorker: Unknown Capacity encountered.\n"));
  1360. Batt->Wait.LowCapacity = BATTERY_UNKNOWN_CAPACITY;
  1361. Batt->Wait.HighCapacity = BATTERY_UNKNOWN_CAPACITY;
  1362. }
  1363. RtlCopyMemory (&Batt->Info.Status, &Batt->IrpBuffer.Status, sizeof(BATTERY_STATUS));
  1364. //
  1365. // Set timestamp to Now.
  1366. //
  1367. Batt->Info.StatusTimeStamp = KeQueryInterruptTime ();
  1368. //
  1369. // Recalculate the charge/discharge policy and change if needed
  1370. //
  1371. // Don't change default BIOS policy for discharge.
  1372. // CompBattChargeDischarge (compBatt);
  1373. //
  1374. // Save the composite's old PowerState and recalculate the composites
  1375. // overall status.
  1376. //
  1377. oldPowerState = compBatt->Info.Status.PowerState;
  1378. compBatt->Info.StatusTimeStamp = 0; // -CACHE_STATUS_TIMEOUT; // Invalidate cache
  1379. CompBattQueryStatus (compBatt, compBatt->Info.Tag, &battStatus);
  1380. //
  1381. // Check to see if we need to send a notification on the composite
  1382. // battery. This will be done in a couple of different cases:
  1383. //
  1384. // - There is a VALID_NOTIFY and there was a change in the composite's
  1385. // PowerState, or it went below the Notify.LowCapacity, or it went
  1386. // above the Notify.HighCapacity.
  1387. //
  1388. // - There is no VALID_NOTIFY (SetStatusNotify) and there was a change
  1389. // in the composite's PowerState.
  1390. //
  1391. if (compBatt->Info.Valid & VALID_NOTIFY) {
  1392. if ((compBatt->Info.Status.PowerState != compBatt->Wait.PowerState) ||
  1393. (compBatt->Info.Status.Capacity < compBatt->Wait.LowCapacity) ||
  1394. (compBatt->Info.Status.Capacity > compBatt->Wait.HighCapacity)) {
  1395. BattPrint (BATT_NOTE, ("CompBattMonitorIrpCompleteWorker: calling StatusNotify\n"));
  1396. BatteryClassStatusNotify (compBatt->Class);
  1397. }
  1398. } else {
  1399. if (compBatt->Info.Status.PowerState != oldPowerState) {
  1400. BattPrint (BATT_NOTE, ("CompBattMonitorIrpCompleteWorker: calling StatusNotify\n"));
  1401. BatteryClassStatusNotify (compBatt->Class);
  1402. }
  1403. }
  1404. } else {
  1405. BattPrint (BATT_NOTE, ("CompBattMonitorIrpCompleteWorker: recycling cancelled status irp\n"));
  1406. }
  1407. break;
  1408. default:
  1409. BattPrint (BATT_ERROR, ("CompBatt: internal error - bad state\n"));
  1410. break;
  1411. }
  1412. //
  1413. // Set irp to issue query
  1414. //
  1415. #if DEBUG
  1416. if ((Batt->Wait.LowCapacity > 0xf0000000) && (Batt->Wait.LowCapacity != BATTERY_UNKNOWN_CAPACITY)) {
  1417. BattPrint (BATT_ERROR, ("CompBattMonitorIrpCompleteWorker: LowCapacity < 0, LowCapacity =%x\n",
  1418. Batt->Wait.LowCapacity));
  1419. ASSERT(FALSE);
  1420. }
  1421. #endif
  1422. Batt->State = CB_ST_GET_STATUS;
  1423. Batt->Wait.Timeout = (ULONG) -1;
  1424. RtlCopyMemory (&Batt->IrpBuffer.Wait, &Batt->Wait, sizeof (Batt->Wait));
  1425. IrpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_BATTERY_QUERY_STATUS;
  1426. IrpSp->Parameters.DeviceIoControl.InputBufferLength = sizeof(Batt->IrpBuffer.Wait);
  1427. IrpSp->Parameters.DeviceIoControl.OutputBufferLength = sizeof(Batt->IrpBuffer.Status);
  1428. BattPrint (BATT_NOTE, ("CompBattMonitorIrpCompleteWorker: waiting for status, Irp - %x\n", Irp));
  1429. BattPrint (BATT_NOTE, ("-------- PowerState = %x\n"
  1430. "-------- LowCapacity = %x\n"
  1431. "-------- HighCapacity = %x\n",
  1432. Batt->Wait.PowerState,
  1433. Batt->Wait.LowCapacity,
  1434. Batt->Wait.HighCapacity)
  1435. );
  1436. } else if (Irp->IoStatus.Status == STATUS_DEVICE_REMOVED) {
  1437. //
  1438. // If the Battery class driver returned STATUS_DEVICE_REMOVED, then the
  1439. // device has been removed, so we need to quit sending IRPs.
  1440. //
  1441. BattPrint (BATT_NOTE, ("Compbatt: MonitorIrpCompleteWorker detected device removal.\n"));
  1442. CompBattRemoveBattery (&Batt->BattName, compBatt);
  1443. IoFreeIrp (Irp);
  1444. return;
  1445. } else {
  1446. BattPrint (BATT_NOTE, ("CompBattMonitorIrpCompleteWorker: battery disappeared (status:%08x)\n",
  1447. Irp->IoStatus.Status));
  1448. //
  1449. // Invalidate the battery's tag, and the individual battery's cache, and
  1450. // recalculate the composite's tag
  1451. //
  1452. Batt->Info.Tag = BATTERY_TAG_INVALID;
  1453. Batt->Info.Valid = 0;
  1454. compBatt->Info.Valid = 0;
  1455. compBatt->Info.StatusTimeStamp = 0; // Invalidate cache
  1456. BattPrint (BATT_NOTE, ("CompBattMonitorIrpCompleteWorker: calling StatusNotify\n"));
  1457. BatteryClassStatusNotify (compBatt->Class);
  1458. Batt->State = CB_ST_GET_TAG;
  1459. IrpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_BATTERY_QUERY_TAG;
  1460. IrpSp->Parameters.DeviceIoControl.InputBufferLength = sizeof(ULONG);
  1461. IrpSp->Parameters.DeviceIoControl.OutputBufferLength = sizeof(ULONG);
  1462. Batt->IrpBuffer.Tag = (ULONG) -1;
  1463. BattPrint (BATT_NOTE, ("CompBattMonitorIrpCompleteWorker: getting tag (last error %x)\n",
  1464. Irp->IoStatus.Status));
  1465. }
  1466. IrpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
  1467. Irp->AssociatedIrp.SystemBuffer = &Batt->IrpBuffer;
  1468. Irp->PendingReturned = FALSE;
  1469. Irp->Cancel = FALSE;
  1470. IoSetCompletionRoutine (Irp, CompBattMonitorIrpComplete, NULL, TRUE, TRUE, TRUE);
  1471. status = IoCallDriver (Batt->DeviceObject, Irp);
  1472. BattPrint (BATT_NOTE, ("Compbatt: MonitorIrpCompleteWorker: CallDriver returned 0x%lx.\n", status));
  1473. BattPrint (BATT_TRACE, ("CompBatt: EXITING MonitorIrpCompleteWorker\n"));
  1474. return;
  1475. }
  1476. VOID
  1477. CompBattRecalculateTag (
  1478. IN PCOMPOSITE_BATTERY CompBatt
  1479. )
  1480. /*++
  1481. Routine Description:
  1482. The routine checks to see if there is still a valid battery in the
  1483. composite's list. If so, the composite tag is bumped. This also
  1484. invalidates all but the composite's tag.
  1485. Arguments:
  1486. CompBatt - Composite device extension
  1487. Return Value:
  1488. none
  1489. --*/
  1490. {
  1491. PCOMPOSITE_ENTRY batt;
  1492. PLIST_ENTRY entry;
  1493. BattPrint (BATT_TRACE, ("CompBatt: ENTERING CompBattRecalculateTag\n"));
  1494. //
  1495. // Run through the list of batteries looking for one that is still good
  1496. //
  1497. ExAcquireFastMutex (&CompBatt->ListMutex);
  1498. for (entry = CompBatt->Batteries.Flink; entry != &CompBatt->Batteries; entry = entry->Flink) {
  1499. batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries);
  1500. if (batt->Info.Valid & VALID_TAG) {
  1501. CompBatt->Info.Valid |= VALID_TAG;
  1502. CompBatt->Info.Tag = CompBatt->NextTag++;
  1503. break;
  1504. }
  1505. CompBatt->Info.Tag = BATTERY_TAG_INVALID;
  1506. }
  1507. ExReleaseFastMutex (&CompBatt->ListMutex);
  1508. BattPrint (BATT_TRACE, ("CompBatt: EXITING CompBattRecalculateTag\n"));
  1509. }
  1510. VOID
  1511. CompBattChargeDischarge (
  1512. IN PCOMPOSITE_BATTERY CompBatt
  1513. )
  1514. /*++
  1515. Routine Description:
  1516. The routine calculates which battery should be charging/discharging
  1517. and attempts to make it so. Policy is summarized below:
  1518. CHARGING POLICY:
  1519. The most charged battery that is also less than 90% of maximum capacity
  1520. is charged first.
  1521. DISCHARGING POLICY:
  1522. The most discharged battery that is also more than 2% of empty is discharged
  1523. first, until it is empty.
  1524. Arguments:
  1525. CompBatt - Composite device extension
  1526. Return Value:
  1527. NONE. Nobody really cares if this works or not, since it won't work on all batteries.
  1528. --*/
  1529. {
  1530. PCOMPOSITE_ENTRY batt;
  1531. PLIST_ENTRY entry;
  1532. ULONG capacity;
  1533. ULONG percentCapacity;
  1534. ULONG targetCapacity;
  1535. PCOMPOSITE_ENTRY targetBattery;
  1536. BATTERY_SET_INFORMATION battSetInfo;
  1537. NTSTATUS status;
  1538. BattPrint (BATT_TRACE, ("CompBatt: ENTERING CompBattChargeDischarge\n"));
  1539. targetBattery = NULL;
  1540. //
  1541. // Check if AC is present in the system.
  1542. //
  1543. if (CompBatt->Info.Status.PowerState & BATTERY_POWER_ON_LINE) {
  1544. //
  1545. // AC is present. Examine all batteries, looking for the most
  1546. // charged one that is less than 90% full.
  1547. //
  1548. targetCapacity = 0;
  1549. battSetInfo.InformationLevel = BatteryCharge;
  1550. ExAcquireFastMutex (&CompBatt->ListMutex);
  1551. for (entry = CompBatt->Batteries.Flink; entry != &CompBatt->Batteries; entry = entry->Flink) {
  1552. batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries);
  1553. if (!NT_SUCCESS (CompbattAcquireDeleteLock(&batt->DeleteLock))) {
  1554. continue;
  1555. }
  1556. if (batt->Info.Valid & VALID_TAG) {
  1557. //
  1558. // Get the battery max capacity and current % of capacity
  1559. //
  1560. capacity = batt->Info.Info.FullChargedCapacity;
  1561. if (capacity == 0) {
  1562. CompbattReleaseDeleteLock(&batt->DeleteLock);
  1563. break;
  1564. }
  1565. percentCapacity = (batt->Info.Status.Capacity * 100) / capacity;
  1566. //
  1567. // Is this the most charged battery AND < 90% full?
  1568. //
  1569. if ((capacity > targetCapacity) && (percentCapacity < BATTERY_MAX_CHARGE_CAPACITY)) {
  1570. //
  1571. // Yes, this one is in the running for the one to charge
  1572. //
  1573. targetCapacity = capacity;
  1574. targetBattery = batt;
  1575. }
  1576. }
  1577. CompbattReleaseDeleteLock(&batt->DeleteLock);
  1578. }
  1579. ExReleaseFastMutex (&CompBatt->ListMutex);
  1580. BattPrint (BATT_NOTE, ("CompBattChargeDischarge: Setting battery %x to CHARGE (AC present)\n",
  1581. targetBattery));
  1582. } else {
  1583. //
  1584. // We are running on battery power. Examine all batteries, looking
  1585. // for the one with the least capacity that is greater than some small
  1586. // safety margin (say 2%).
  1587. //
  1588. targetCapacity = -1;
  1589. battSetInfo.InformationLevel = BatteryDischarge;
  1590. ExAcquireFastMutex (&CompBatt->ListMutex);
  1591. for (entry = CompBatt->Batteries.Flink; entry != &CompBatt->Batteries; entry = entry->Flink) {
  1592. batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries);
  1593. if (!NT_SUCCESS (CompbattAcquireDeleteLock(&batt->DeleteLock))) {
  1594. continue;
  1595. }
  1596. if (batt->Info.Valid & VALID_TAG) {
  1597. //
  1598. // Get the battery max capacity and current % of capacity
  1599. //
  1600. capacity = batt->Info.Info.FullChargedCapacity;
  1601. if (capacity == 0) {
  1602. CompbattReleaseDeleteLock(&batt->DeleteLock);
  1603. break;
  1604. }
  1605. percentCapacity = (batt->Info.Status.Capacity * 100) / capacity;
  1606. //
  1607. // Is this the least charged battery AND has a safety margin?
  1608. //
  1609. if ((capacity < targetCapacity) && (percentCapacity > BATTERY_MIN_SAFE_CAPACITY)) {
  1610. //
  1611. // Yes, this one is in the running for the one to discharge
  1612. //
  1613. targetCapacity = capacity;
  1614. targetBattery = batt;
  1615. }
  1616. }
  1617. CompbattReleaseDeleteLock(&batt->DeleteLock);
  1618. }
  1619. ExReleaseFastMutex (&CompBatt->ListMutex);
  1620. BattPrint (BATT_NOTE, ("CompBattChargeDischarge: Setting battery %x to DISCHARGE (no AC)\n",
  1621. targetBattery));
  1622. }
  1623. //
  1624. // If we have found a suitable battery, complete the setup and send off the Ioctl
  1625. //
  1626. if (targetBattery != NULL) {
  1627. battSetInfo.BatteryTag = targetBattery->Info.Tag;
  1628. //
  1629. // Make the Ioctl to the battery. This won't always be successful, since some
  1630. // batteries don't support it. For example, no control-method batteries support
  1631. // software charging decisions. Some smart batteries do, however.
  1632. //
  1633. status = BatteryIoctl (IOCTL_BATTERY_SET_INFORMATION,
  1634. batt->DeviceObject,
  1635. &battSetInfo,
  1636. sizeof (BATTERY_SET_INFORMATION),
  1637. NULL,
  1638. 0,
  1639. FALSE);
  1640. }
  1641. BattPrint (BATT_TRACE, ("CompBatt: EXITING CompBattChargeDischarge\n"));
  1642. }