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.

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