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.

1856 lines
63 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. CmBatt.c
  5. Abstract:
  6. Control Method Battery Miniport Driver
  7. Author:
  8. Ron Mosgrove (Intel)
  9. Environment:
  10. Kernel mode
  11. Revision History:
  12. --*/
  13. #include "CmBattp.h"
  14. #if DEBUG
  15. #if DBG
  16. ULONG CmBattDebug = CMBATT_ERROR;
  17. #else
  18. // Turn off all debug info by default for free builds.
  19. ULONG CmBattDebug = 0;
  20. #endif //DBG
  21. #endif //DEBUG
  22. #ifndef _WIN32_WINNT
  23. ULONG CmBattPrevPowerSource = 1;
  24. #endif //_WIN32_WINNT
  25. UNICODE_STRING GlobalRegistryPath;
  26. PVOID CmBattPowerCallBackRegistration;
  27. PCALLBACK_OBJECT CmBattPowerCallBackObject;
  28. KDPC CmBattWakeDpcObject;
  29. KTIMER CmBattWakeDpcTimerObject;
  30. LARGE_INTEGER CmBattWakeDpcDelay = WAKE_DPC_DELAY;
  31. //
  32. // Prototypes
  33. //
  34. NTSTATUS
  35. DriverEntry(
  36. IN PDRIVER_OBJECT DriverObject,
  37. IN PUNICODE_STRING RegistryPath
  38. );
  39. NTSTATUS
  40. CmBattOpenClose(
  41. IN PDEVICE_OBJECT DeviceObject,
  42. IN PIRP Irp
  43. );
  44. NTSTATUS
  45. CmBattIoctl(
  46. IN PDEVICE_OBJECT DeviceObject,
  47. IN PIRP Irp
  48. );
  49. VOID
  50. CmBattUnload(
  51. IN PDRIVER_OBJECT DriverObject
  52. );
  53. NTSTATUS
  54. CmBattGetBatteryStatus(
  55. PCM_BATT CmBatt,
  56. IN ULONG BatteryTag
  57. );
  58. NTSTATUS
  59. CmBattGetSetAlarm(
  60. IN PCM_BATT CmBatt,
  61. IN OUT PULONG AlarmPtr,
  62. IN UCHAR OpType
  63. );
  64. #ifdef ALLOC_PRAGMA
  65. #pragma alloc_text(INIT,DriverEntry)
  66. #pragma alloc_text(PAGE,CmBattQueryTag)
  67. #pragma alloc_text(PAGE,CmBattQueryInformation)
  68. #pragma alloc_text(PAGE,CmBattQueryStatus)
  69. #pragma alloc_text(PAGE,CmBattSetStatusNotify)
  70. #pragma alloc_text(PAGE,CmBattDisableStatusNotify)
  71. #pragma alloc_text(PAGE,CmBattUnload)
  72. #pragma alloc_text(PAGE,CmBattOpenClose)
  73. #pragma alloc_text(PAGE,CmBattIoctl)
  74. #pragma alloc_text(PAGE,CmBattGetBatteryStatus)
  75. #endif
  76. NTSTATUS
  77. DriverEntry(
  78. IN PDRIVER_OBJECT DriverObject,
  79. IN PUNICODE_STRING RegistryPath
  80. )
  81. /*++
  82. Routine Description:
  83. This routine initializes the ACPI Embedded Controller Driver
  84. Arguments:
  85. DriverObject - Pointer to driver object created by system.
  86. RegistryPath - Pointer to the Unicode name of the registry path
  87. for this driver.
  88. Return Value:
  89. The function value is the final status from the initialization operation.
  90. --*/
  91. {
  92. NTSTATUS status;
  93. OBJECT_ATTRIBUTES objAttributes;
  94. UNICODE_STRING callBackName;
  95. //
  96. // Save the RegistryPath.
  97. //
  98. GlobalRegistryPath.MaximumLength = RegistryPath->Length +
  99. sizeof(UNICODE_NULL);
  100. GlobalRegistryPath.Length = RegistryPath->Length;
  101. GlobalRegistryPath.Buffer = ExAllocatePoolWithTag (
  102. PagedPool,
  103. GlobalRegistryPath.MaximumLength,
  104. 'MtaB');
  105. if (!GlobalRegistryPath.Buffer) {
  106. CmBattPrint ((CMBATT_ERROR),("CmBatt: Couldn't allocate pool for registry path."));
  107. return STATUS_INSUFFICIENT_RESOURCES;
  108. }
  109. RtlCopyUnicodeString(&GlobalRegistryPath, RegistryPath);
  110. CmBattPrint (CMBATT_TRACE, ("CmBatt DriverEntry - Obj (%08x) Path \"%ws\"\n",
  111. DriverObject, RegistryPath->Buffer));
  112. //
  113. // Set up the device driver entry points.
  114. //
  115. DriverObject->DriverUnload = CmBattUnload;
  116. DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = CmBattIoctl;
  117. DriverObject->MajorFunction[IRP_MJ_CREATE] = CmBattOpenClose;
  118. DriverObject->MajorFunction[IRP_MJ_CLOSE] = CmBattOpenClose;
  119. DriverObject->MajorFunction[IRP_MJ_POWER] = CmBattPowerDispatch;
  120. DriverObject->MajorFunction[IRP_MJ_PNP] = CmBattPnpDispatch;
  121. DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = CmBattSystemControl;
  122. DriverObject->DriverExtension->AddDevice = CmBattAddDevice;
  123. //
  124. // Register a callback that tells us when the system is in the
  125. // process of sleeping or waking.
  126. //
  127. RtlInitUnicodeString( &callBackName, L"\\Callback\\PowerState" );
  128. InitializeObjectAttributes(
  129. &objAttributes,
  130. &callBackName,
  131. OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
  132. NULL,
  133. NULL
  134. );
  135. status = ExCreateCallback(
  136. &CmBattPowerCallBackObject,
  137. &objAttributes,
  138. FALSE,
  139. TRUE
  140. );
  141. if (NT_SUCCESS(status)) {
  142. CmBattPowerCallBackRegistration = ExRegisterCallback(
  143. CmBattPowerCallBackObject,
  144. (PCALLBACK_FUNCTION) CmBattPowerCallBack,
  145. DriverObject
  146. );
  147. if (CmBattPowerCallBackRegistration) {
  148. KeInitializeDpc (&CmBattWakeDpcObject,
  149. (PKDEFERRED_ROUTINE) CmBattWakeDpc,
  150. DriverObject);
  151. KeInitializeTimer (&CmBattWakeDpcTimerObject);
  152. } else {
  153. ObDereferenceObject (CmBattPowerCallBackObject);
  154. CmBattPrint (CMBATT_ERROR, ("CmBattRegisterPowerCallBack: ExRegisterCallback failed.\n"));
  155. }
  156. } else {
  157. CmBattPowerCallBackObject = NULL;
  158. CmBattPrint (CMBATT_ERROR, ("CmBattRegisterPowerCallBack: failed status=0x%08x\n", status));
  159. }
  160. return STATUS_SUCCESS;
  161. }
  162. VOID
  163. CmBattUnload(
  164. IN PDRIVER_OBJECT DriverObject
  165. )
  166. /*++
  167. Routine Description:
  168. Cleanup all devices and unload the driver
  169. Arguments:
  170. DriverObject - Driver object for unload
  171. Return Value:
  172. Status
  173. --*/
  174. {
  175. CmBattPrint (CMBATT_TRACE, ("CmBattUnload: \n"));
  176. if (CmBattPowerCallBackObject) {
  177. ExUnregisterCallback (CmBattPowerCallBackRegistration);
  178. ObDereferenceObject (CmBattPowerCallBackObject);
  179. }
  180. if (GlobalRegistryPath.Buffer) {
  181. ExFreePool (GlobalRegistryPath.Buffer);
  182. }
  183. if (DriverObject->DeviceObject != NULL) {
  184. CmBattPrint (CMBATT_ERROR, ("Unload called before all devices removed.\n"));
  185. }
  186. }
  187. NTSTATUS
  188. CmBattOpenClose(
  189. IN PDEVICE_OBJECT DeviceObject,
  190. IN PIRP Irp
  191. )
  192. /*++
  193. Routine Description:
  194. This is the routine called as a result of a Open or Close on the device
  195. Arguments:
  196. DeviceObject - Battery for request
  197. Irp - IO request
  198. Return Value:
  199. STATUS_SUCCESS - no way to fail this puppy
  200. If Device has received a query remove, this will fail.
  201. STATUS_NO_SUCH_DEVICE
  202. --*/
  203. {
  204. PCM_BATT CmBatt;
  205. NTSTATUS status;
  206. PIO_STACK_LOCATION irpStack;
  207. PAGED_CODE();
  208. CmBattPrint (CMBATT_TRACE, ("CmBattOpenClose\n"));
  209. CmBatt = (PCM_BATT) DeviceObject->DeviceExtension;
  210. //
  211. // A remove lock is not needed in this dispatch function because
  212. // all data accessed is in the device extension. If any other functionality was
  213. // added to this routine, a remove lock might be neccesary.
  214. //
  215. status = STATUS_SUCCESS; // Success by default.
  216. ExAcquireFastMutex (&CmBatt->OpenCloseMutex);
  217. if (CmBatt->OpenCount == (ULONG) -1) { // A query remove has come requested
  218. status = STATUS_NO_SUCH_DEVICE;
  219. CmBattPrint (CMBATT_PNP, ("CmBattOpenClose: Failed (UID = %x)(device being removed).\n", CmBatt->Info.Tag));
  220. } else {
  221. irpStack = IoGetCurrentIrpStackLocation(Irp);
  222. if (irpStack->MajorFunction == IRP_MJ_CREATE) {
  223. CmBatt->OpenCount++;
  224. CmBattPrint (CMBATT_PNP, ("CmBattOpenClose: Open (DeviceNumber = %x)(count = %x).\n",
  225. CmBatt->DeviceNumber, CmBatt->OpenCount));
  226. } else if (irpStack->MajorFunction == IRP_MJ_CLOSE) {
  227. CmBatt->OpenCount--;
  228. CmBattPrint (CMBATT_PNP, ("CmBattOpenClose: Close (DeviceNumber = %x)(count = %x).\n",
  229. CmBatt->DeviceNumber, CmBatt->OpenCount));
  230. }
  231. }
  232. ExReleaseFastMutex (&CmBatt->OpenCloseMutex);
  233. //
  234. // Complete Irp.
  235. //
  236. Irp->IoStatus.Status = status;
  237. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  238. return status;
  239. }
  240. NTSTATUS
  241. CmBattIoctl(
  242. IN PDEVICE_OBJECT DeviceObject,
  243. IN PIRP Irp
  244. )
  245. /*++
  246. Routine Description:
  247. IOCTL handler. As this is an exclusive battery device, send the
  248. Irp to the battery class driver to handle battery IOCTLs.
  249. Arguments:
  250. DeviceObject - Battery for request
  251. Irp - IO request
  252. Return Value:
  253. Status of request
  254. --*/
  255. {
  256. NTSTATUS Status = STATUS_NOT_SUPPORTED;
  257. PCM_BATT CmBatt;
  258. #if DIRECT_ACCESS
  259. PIO_STACK_LOCATION IrpSp;
  260. #endif //DIRECT_ACCESS
  261. PAGED_CODE();
  262. CmBattPrint (CMBATT_TRACE, ("CmBattIoctl\n"));
  263. CmBatt = (PCM_BATT) DeviceObject->DeviceExtension;
  264. //
  265. // Aquire remove lock
  266. //
  267. InterlockedIncrement (&CmBatt->InUseCount);
  268. if (CmBatt->WantToRemove == TRUE) {
  269. if (0 == InterlockedDecrement(&CmBatt->InUseCount)) {
  270. KeSetEvent (&CmBatt->ReadyToRemove, IO_NO_INCREMENT, FALSE);
  271. }
  272. Status = STATUS_DEVICE_REMOVED;
  273. Irp->IoStatus.Status = Status;
  274. IoCompleteRequest (Irp, IO_NO_INCREMENT);
  275. return Status;
  276. }
  277. if (CmBatt->Type == CM_BATTERY_TYPE) {
  278. Status = BatteryClassIoctl (CmBatt->Class, Irp);
  279. #if DIRECT_ACCESS
  280. if (Status == STATUS_NOT_SUPPORTED) {
  281. //
  282. // Is it a Direct Access IOCTL?
  283. //
  284. IrpSp = IoGetCurrentIrpStackLocation(Irp);
  285. CmBattPrint((CMBATT_BIOS),
  286. ("CmBattIoctl: Received Direct Access IOCTL %x\n",
  287. IrpSp->Parameters.DeviceIoControl.IoControlCode));
  288. switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
  289. case IOCTL_CMBATT_UID:
  290. if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength == sizeof (ULONG)) {
  291. Status = CmBattGetUniqueId (CmBatt->Pdo, Irp->AssociatedIrp.SystemBuffer);
  292. if (NT_SUCCESS(Status)) {
  293. Irp->IoStatus.Information = sizeof (ULONG);
  294. } else {
  295. Irp->IoStatus.Information = 0;
  296. }
  297. } else {
  298. Status = STATUS_INVALID_BUFFER_SIZE;
  299. };
  300. break;
  301. case IOCTL_CMBATT_STA:
  302. if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength == sizeof (ULONG)) {
  303. Status = CmBattGetStaData (CmBatt->Pdo, Irp->AssociatedIrp.SystemBuffer);
  304. if (NT_SUCCESS(Status)) {
  305. Irp->IoStatus.Information = sizeof (ULONG);
  306. } else {
  307. Irp->IoStatus.Information = 0;
  308. }
  309. } else {
  310. Status = STATUS_INVALID_BUFFER_SIZE;
  311. };
  312. break;
  313. case IOCTL_CMBATT_PSR:
  314. if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength == sizeof (ULONG)) {
  315. if (AcAdapterPdo != NULL) {
  316. Status = CmBattGetPsrData (AcAdapterPdo, Irp->AssociatedIrp.SystemBuffer);
  317. } else {
  318. Status = STATUS_NO_SUCH_DEVICE;
  319. }
  320. if (NT_SUCCESS(Status)) {
  321. Irp->IoStatus.Information = sizeof (ULONG);
  322. } else {
  323. Irp->IoStatus.Information = 0;
  324. }
  325. } else {
  326. Status = STATUS_INVALID_BUFFER_SIZE;
  327. };
  328. break;
  329. case IOCTL_CMBATT_BTP:
  330. if (IrpSp->Parameters.DeviceIoControl.InputBufferLength == sizeof (ULONG)) {
  331. Status = CmBattSetTripPpoint (CmBatt, *((PULONG) (Irp->AssociatedIrp.SystemBuffer)));
  332. Irp->IoStatus.Information = 0;
  333. } else {
  334. Status = STATUS_INVALID_BUFFER_SIZE;
  335. };
  336. break;
  337. case IOCTL_CMBATT_BIF:
  338. if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength == sizeof (CM_BIF_BAT_INFO)) {
  339. Status = CmBattGetBifData (CmBatt, Irp->AssociatedIrp.SystemBuffer);
  340. if (NT_SUCCESS(Status)) {
  341. Irp->IoStatus.Information = sizeof (CM_BIF_BAT_INFO);
  342. } else {
  343. Irp->IoStatus.Information = 0;
  344. }
  345. } else {
  346. Status = STATUS_INVALID_BUFFER_SIZE;
  347. };
  348. break;
  349. case IOCTL_CMBATT_BST:
  350. if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength == sizeof (CM_BST_BAT_INFO)) {
  351. Status = CmBattGetBstData (CmBatt, Irp->AssociatedIrp.SystemBuffer);
  352. if (NT_SUCCESS(Status)) {
  353. Irp->IoStatus.Information = sizeof (CM_BST_BAT_INFO);
  354. } else {
  355. Irp->IoStatus.Information = 0;
  356. }
  357. } else {
  358. Status = STATUS_INVALID_BUFFER_SIZE;
  359. };
  360. break;
  361. default:
  362. CmBattPrint((CMBATT_ERROR),
  363. ("CmBattIoctl: Unknown IOCTL %x\n",
  364. IrpSp->Parameters.DeviceIoControl.IoControlCode));
  365. }
  366. if (Status != STATUS_NOT_SUPPORTED) {
  367. //
  368. // We just handled this IOCTL. Complete it.
  369. //
  370. Irp->IoStatus.Status = Status;
  371. IoCompleteRequest (Irp, IO_NO_INCREMENT);
  372. }
  373. }
  374. #endif //DIRECT_ACCESS
  375. }
  376. if (Status == STATUS_NOT_SUPPORTED) {
  377. //
  378. // Not for the battery. Pass it down the stack.
  379. //
  380. IoSkipCurrentIrpStackLocation (Irp);
  381. Status = IoCallDriver (CmBatt->LowerDeviceObject, Irp);
  382. }
  383. //
  384. // Release Removal Lock
  385. //
  386. if (0 == InterlockedDecrement(&CmBatt->InUseCount)) {
  387. KeSetEvent (&CmBatt->ReadyToRemove, IO_NO_INCREMENT, FALSE);
  388. }
  389. return Status;
  390. }
  391. NTSTATUS
  392. CmBattQueryTag (
  393. IN PVOID Context,
  394. OUT PULONG TagPtr
  395. )
  396. /*++
  397. Routine Description:
  398. Called by the class driver to retrieve the batteries current tag value
  399. The battery class driver will serialize all requests it issues to
  400. the miniport for a given battery.
  401. Arguments:
  402. Context - Miniport context value for battery
  403. TagPtr - Pointer to return current tag
  404. Return Value:
  405. Success if there is a battery currently installed, else no such device.
  406. --*/
  407. {
  408. NTSTATUS Status;
  409. PCM_BATT CmBatt = (PCM_BATT) Context;
  410. ULONG BatteryStatus;
  411. PAGED_CODE();
  412. CmBattPrint ((CMBATT_TRACE | CMBATT_MINI),
  413. ("CmBattQueryTag - Tag (%d), Battery %x, Device %d\n",
  414. *TagPtr, CmBatt, CmBatt->DeviceNumber));
  415. //
  416. // Check if battery is still there
  417. //
  418. CmBatt->ReCheckSta = FALSE;
  419. Status = CmBattGetStaData (CmBatt->Pdo, &BatteryStatus);
  420. if (NT_SUCCESS (Status)) {
  421. if (BatteryStatus & STA_DEVICE_PRESENT) {
  422. //
  423. // If the tag isn't assigned, assign a new one
  424. //
  425. if (CmBatt->Info.Tag == BATTERY_TAG_INVALID) {
  426. //
  427. // See if there is a battery out there.
  428. //
  429. CmBatt->TagCount += 1;
  430. if (CmBatt->TagCount == BATTERY_TAG_INVALID) {
  431. CmBatt->TagCount += 1;
  432. }
  433. CmBatt->Info.Tag = CmBatt->TagCount;
  434. RtlZeroMemory (&CmBatt->Alarm, sizeof(BAT_ALARM_INFO));
  435. CmBatt->Alarm.Setting = CM_ALARM_INVALID;
  436. CmBattPrint (CMBATT_TRACE, ("CmBattQueryTag - New Tag: (%d)\n", CmBatt->Info.Tag));
  437. InterlockedExchange (&CmBatt->CacheState, 0);
  438. CmBatt->DischargeTime = KeQueryInterruptTime();
  439. }
  440. } else {
  441. CmBatt->Info.Tag = BATTERY_TAG_INVALID;
  442. Status = STATUS_NO_SUCH_DEVICE;
  443. }
  444. }
  445. //
  446. // Done
  447. //
  448. CmBattPrint ((CMBATT_MINI),
  449. ("CmBattQueryTag: Returning Tag: 0x%x, status 0x%x\n",
  450. CmBatt->Info.Tag, Status));
  451. *TagPtr = CmBatt->Info.Tag;
  452. return Status;
  453. }
  454. NTSTATUS
  455. CmBattQueryInformation (
  456. IN PVOID Context,
  457. IN ULONG BatteryTag,
  458. IN BATTERY_QUERY_INFORMATION_LEVEL Level,
  459. IN LONG AtRate OPTIONAL,
  460. OUT PVOID Buffer,
  461. IN ULONG BufferLength,
  462. OUT PULONG ReturnedLength
  463. )
  464. /*++
  465. Routine Description:
  466. Called by the class driver to retrieve battery information
  467. The battery class driver will serialize all requests it issues to
  468. the miniport for a given battery.
  469. We return invalid parameter when we can't handle a request for a
  470. specific level of information. This is defined in the battery class spec.
  471. Arguments:
  472. Context - Miniport context value for battery
  473. BatteryTag - Tag of current battery
  474. Level - type of information required
  475. AtRate - Used only when Level==BatteryEstimatedTime
  476. Buffer - Location for the information
  477. BufferLength - Length in bytes of the buffer
  478. ReturnedLength - Length in bytes of the returned data
  479. Return Value:
  480. Success if there is a battery currently installed, else no such device.
  481. --*/
  482. {
  483. PCM_BATT CmBatt = (PCM_BATT) Context;
  484. ULONG ResultData;
  485. NTSTATUS Status;
  486. PVOID ReturnBuffer;
  487. ULONG ReturnBufferLength;
  488. WCHAR scratchBuffer[CM_MAX_STRING_LENGTH];
  489. WCHAR buffer2[CM_MAX_STRING_LENGTH];
  490. UNICODE_STRING tmpUnicodeString;
  491. UNICODE_STRING unicodeString;
  492. ANSI_STRING ansiString;
  493. BATTERY_REMAINING_SCALE ScalePtr[2];
  494. PAGED_CODE();
  495. CmBattPrint ((CMBATT_TRACE | CMBATT_MINI),
  496. ("CmBattQueryInformation - Tag (%d) Device %d, Informationlevel %d\n",
  497. BatteryTag, CmBatt->DeviceNumber, Level));
  498. //
  499. // Be sure there's a battery out there
  500. // This also checks BatteryTag
  501. //
  502. Status = CmBattVerifyStaticInfo (CmBatt, BatteryTag);
  503. if (!NT_SUCCESS(Status)) {
  504. return Status;
  505. }
  506. ResultData = 0;
  507. ReturnBuffer = NULL;
  508. ReturnBufferLength = 0;
  509. Status = STATUS_SUCCESS;
  510. //
  511. // Get the info requested
  512. //
  513. switch (Level) {
  514. case BatteryInformation:
  515. //
  516. // This data structure is populated by CmBattVerifyStaticInfo
  517. //
  518. ReturnBuffer = (PVOID) &CmBatt->Info.ApiInfo;
  519. ReturnBufferLength = sizeof (CmBatt->Info.ApiInfo);
  520. break;
  521. case BatteryGranularityInformation:
  522. //
  523. // Get the granularity from the static info structure
  524. // This data structure is populated by CmBattVerifyStaticInfo
  525. //
  526. {
  527. ScalePtr[0].Granularity = CmBatt->Info.ApiGranularity_1;
  528. ScalePtr[0].Capacity = CmBatt->Info.ApiInfo.DefaultAlert1;
  529. ScalePtr[1].Granularity = CmBatt->Info.ApiGranularity_2;
  530. ScalePtr[1].Capacity = CmBatt->Info.ApiInfo.DesignedCapacity;
  531. ReturnBuffer = ScalePtr;
  532. ReturnBufferLength = 2 * sizeof (BATTERY_REMAINING_SCALE);
  533. }
  534. break;
  535. case BatteryTemperature:
  536. Status = STATUS_INVALID_DEVICE_REQUEST;
  537. break;
  538. case BatteryEstimatedTime:
  539. //
  540. // Return unknown time if battery has been discharging less than 15 seconds
  541. //
  542. if (KeQueryInterruptTime() > (CmBatt->DischargeTime + CM_ESTIMATED_TIME_DELAY)) {
  543. //
  544. // The BatteryEstimatedTime for the control method batteries is defined
  545. // by the following formula:
  546. //
  547. // EstimatedTime [min] = RemainingCapacity [mAh|mWh] * 60 [min/hr] * 60 [sec/min]
  548. // ----------------------------------
  549. // PresentRate [mA|mW]
  550. //
  551. //
  552. // Rerun _BST since we don't have a timeout on this data.
  553. // Also Calculate API status values from CM values
  554. //
  555. CmBattGetBatteryStatus (CmBatt, CmBatt->Info.Tag);
  556. //
  557. // If AtRate is zero, we need to use the present rate
  558. //
  559. if (AtRate == 0) {
  560. AtRate = CmBatt->Info.ApiStatus.Rate;
  561. }
  562. if (AtRate >= 0) {
  563. AtRate = BATTERY_UNKNOWN_RATE;
  564. }
  565. if ((AtRate != BATTERY_UNKNOWN_RATE) &&
  566. (CmBatt->Info.ApiStatus.Capacity != BATTERY_UNKNOWN_CAPACITY)) {
  567. // Calculate estimated time.
  568. #if DEBUG
  569. // Make sure we don't overflow...
  570. if (CmBatt->Info.ApiStatus.Capacity > (0xffffffff/3600)) {
  571. CmBattPrint (CMBATT_ERROR_ONLY, ("CmBattQueryInformation: Data Overflow in calculating Remaining Capacity.\n"));
  572. }
  573. #endif //DEBUG
  574. ResultData = (ULONG) (CmBatt->Info.ApiStatus.Capacity * 3600) / (-AtRate);
  575. } else {
  576. //
  577. // We don't know have enough information to calculate the value.
  578. // Return BATTERY_UNKNONW_TIME.
  579. //
  580. // If this battery is incapable of returning estimated time, return with
  581. // STATUS_INVALID_DEVICE_REQUEST
  582. //
  583. #if DEBUG
  584. if (CmBatt->Info.Status.BatteryState & CM_BST_STATE_DISCHARGING) {
  585. CmBattPrint (CMBATT_WARN,
  586. ("CmBattQueryInformation: Can't calculate EstimatedTime.\n"));
  587. }
  588. #endif //DEBUG
  589. if (CmBatt->Info.ApiStatus.Rate == BATTERY_UNKNOWN_RATE &&
  590. (CmBatt->Info.Status.BatteryState & CM_BST_STATE_DISCHARGING)) {
  591. Status = STATUS_INVALID_DEVICE_REQUEST;
  592. CmBattPrint (CMBATT_WARN,
  593. ("---------------------- PresentRate = BATTERY_UNKNOWN_RATE\n"));
  594. }
  595. if (CmBatt->Info.ApiStatus.Capacity == BATTERY_UNKNOWN_CAPACITY) {
  596. Status = STATUS_INVALID_DEVICE_REQUEST;
  597. CmBattPrint (CMBATT_WARN,
  598. ("---------------------- RemainingCapacity = BATTERY_UNKNOWN_CAPACITY\n"));
  599. }
  600. ResultData = BATTERY_UNKNOWN_TIME;
  601. }
  602. } else { // if (KeQueryInterruptTime() > CmBatt->DischargeTime + CM_ESTIMATED_TIME_DELAY)
  603. //
  604. // Return unknown time if battery has been discharging less than 15 seconds
  605. //
  606. ResultData = BATTERY_UNKNOWN_TIME;
  607. }
  608. ReturnBuffer = &ResultData;
  609. ReturnBufferLength = sizeof(ResultData);
  610. break;
  611. case BatteryDeviceName:
  612. //
  613. // Model Number must be returned as a wide string
  614. //
  615. unicodeString.Buffer = scratchBuffer;
  616. unicodeString.MaximumLength = CM_MAX_STRING_LENGTH;
  617. RtlInitAnsiString (&ansiString, CmBatt->Info.ModelNum);
  618. Status = RtlAnsiStringToUnicodeString (&unicodeString, &ansiString, FALSE);
  619. ReturnBuffer = unicodeString.Buffer;
  620. ReturnBufferLength = unicodeString.Length;
  621. break;
  622. case BatteryManufactureDate:
  623. Status = STATUS_INVALID_DEVICE_REQUEST;
  624. break;
  625. case BatteryManufactureName:
  626. //
  627. // Oem Info must be returned as wide string
  628. //
  629. unicodeString.Buffer = scratchBuffer;
  630. unicodeString.MaximumLength = CM_MAX_STRING_LENGTH;
  631. RtlInitAnsiString (&ansiString, CmBatt->Info.OEMInfo);
  632. Status = RtlAnsiStringToUnicodeString (&unicodeString, &ansiString, FALSE);
  633. ReturnBuffer = unicodeString.Buffer;
  634. ReturnBufferLength = unicodeString.Length;
  635. break;
  636. case BatteryUniqueID:
  637. //
  638. // Concatenate the serial #, OEM info, and Model #
  639. //
  640. unicodeString.Buffer = scratchBuffer;
  641. unicodeString.MaximumLength = CM_MAX_STRING_LENGTH;
  642. tmpUnicodeString.Buffer = buffer2;
  643. tmpUnicodeString.MaximumLength = CM_MAX_STRING_LENGTH;
  644. RtlInitAnsiString (&ansiString, CmBatt->Info.SerialNum);
  645. RtlAnsiStringToUnicodeString (&unicodeString, &ansiString, FALSE);
  646. if (CmBatt->Info.OEMInfo[0]) {
  647. RtlInitAnsiString (&ansiString, CmBatt->Info.OEMInfo);
  648. RtlAnsiStringToUnicodeString (&tmpUnicodeString, &ansiString, FALSE);
  649. RtlAppendUnicodeStringToString (&unicodeString, &tmpUnicodeString);
  650. }
  651. RtlInitAnsiString (&ansiString, CmBatt->Info.ModelNum);
  652. RtlAnsiStringToUnicodeString (&tmpUnicodeString, &ansiString, FALSE);
  653. RtlAppendUnicodeStringToString (&unicodeString, &tmpUnicodeString);
  654. ReturnBuffer = unicodeString.Buffer;
  655. ReturnBufferLength = unicodeString.Length;
  656. break;
  657. default:
  658. Status = STATUS_INVALID_PARAMETER;
  659. break;
  660. }
  661. //
  662. // Done, return buffer if needed
  663. //
  664. *ReturnedLength = ReturnBufferLength;
  665. if (BufferLength < ReturnBufferLength) {
  666. Status = STATUS_BUFFER_TOO_SMALL;
  667. }
  668. if (NT_SUCCESS(Status) && ReturnBuffer) {
  669. RtlCopyMemory (Buffer, ReturnBuffer, ReturnBufferLength); // Copy what's needed
  670. }
  671. return Status;
  672. }
  673. NTSTATUS
  674. CmBattQueryStatus (
  675. IN PVOID Context,
  676. IN ULONG BatteryTag,
  677. OUT PBATTERY_STATUS BatteryStatus
  678. )
  679. /*++
  680. Routine Description:
  681. Called by the class driver to retrieve the batteries current status
  682. The battery class driver will serialize all requests it issues to
  683. the miniport for a given battery.
  684. Arguments:
  685. Context - Miniport context value for battery
  686. BatteryTag - Tag of current battery
  687. BatteryStatus - Pointer to structure to return the current battery status
  688. Return Value:
  689. Success if there is a battery currently installed, else no such device.
  690. --*/
  691. {
  692. PCM_BATT CmBatt = (PCM_BATT) Context;
  693. NTSTATUS Status;
  694. PAGED_CODE();
  695. CmBattPrint ((CMBATT_TRACE | CMBATT_MINI), ("CmBattQueryStatus - Tag (%d) Device %x\n",
  696. BatteryTag, CmBatt->DeviceNumber));
  697. Status = CmBattGetBatteryStatus (CmBatt, BatteryTag);
  698. if (NT_SUCCESS(Status)) {
  699. RtlCopyMemory (BatteryStatus, &CmBatt->Info.ApiStatus, sizeof(BATTERY_STATUS));
  700. }
  701. CmBattPrint ((CMBATT_MINI), ("CmBattQueryStatus: Returning [%#08lx][%#08lx][%#08lx][%#08lx]\n",
  702. BatteryStatus->PowerState, BatteryStatus->Capacity, BatteryStatus->Voltage, BatteryStatus->Rate));
  703. return Status;
  704. }
  705. NTSTATUS
  706. CmBattSetStatusNotify (
  707. IN PVOID Context,
  708. IN ULONG BatteryTag,
  709. IN PBATTERY_NOTIFY Notify
  710. )
  711. /*++
  712. Routine Description:
  713. Called by the class driver to set the batteries current notification
  714. setting. When the battery trips the notification, one call to
  715. BatteryClassStatusNotify is issued. If an error is returned, the
  716. class driver will poll the battery status - primarily for capacity
  717. changes. Which is to say the miniport should still issue BatteryClass-
  718. StatusNotify whenever the power state changes.
  719. The class driver will always set the notification level it needs
  720. after each call to BatteryClassStatusNotify.
  721. The battery class driver will serialize all requests it issues to
  722. the miniport for a given battery.
  723. Arguments:
  724. Context - Miniport context value for battery
  725. BatteryTag - Tag of current battery
  726. BatteryNotify - The notification setting
  727. Return Value:
  728. Status
  729. --*/
  730. {
  731. PCM_BATT CmBatt;
  732. NTSTATUS Status;
  733. ULONG Target;
  734. LONG ActualAlarm; // Value after adjusting for limit conditions.
  735. CM_BST_BAT_INFO bstData;
  736. PAGED_CODE();
  737. CmBattPrint ((CMBATT_TRACE | CMBATT_MINI), ("CmBattSetStatusNotify: Tag (%d) Target(0x%x)\n",
  738. BatteryTag, Notify->LowCapacity));
  739. Status = STATUS_SUCCESS;
  740. CmBatt = (PCM_BATT) Context;
  741. Status = CmBattVerifyStaticInfo (CmBatt, BatteryTag);
  742. if (!NT_SUCCESS(Status)) {
  743. return Status;
  744. }
  745. //
  746. // If _BTP doesn't exist, don't call it again.
  747. //
  748. if (!CmBatt->Info.BtpExists) {
  749. return STATUS_OBJECT_NAME_NOT_FOUND;
  750. }
  751. if ((Notify->HighCapacity == BATTERY_UNKNOWN_CAPACITY) ||
  752. (Notify->LowCapacity == BATTERY_UNKNOWN_CAPACITY)) {
  753. CmBattPrint (CMBATT_WARN, ("CmBattSetStatusNotify: Failing request because of BATTERY_UNKNOWN_CAPACITY.\n"));
  754. return STATUS_NOT_SUPPORTED;
  755. }
  756. if (CmBatt->Info.Status.BatteryState & CM_BST_STATE_CHARGING) {
  757. Target = Notify->HighCapacity;
  758. } else if (CmBatt->Info.Status.BatteryState & CM_BST_STATE_DISCHARGING) {
  759. Target = Notify->LowCapacity;
  760. } else {
  761. // No trip point needs to be set, the battery will trip as soon as it starts
  762. // charging or discharging.
  763. //return STATUS_SUCCESS;
  764. // but it doesn't hurt to set the trip point just in case the battery
  765. // system screws up and doesn't send the notification when the status changed.
  766. Target = Notify->LowCapacity;
  767. }
  768. ActualAlarm = Target;
  769. //
  770. // If the battery operates on mA we need to convert the trip point from mW
  771. // to mA. The formula for doing this is:
  772. //
  773. // mA = mW / V or mA = (mW / mV) * 1000
  774. //
  775. if (CmBatt->Info.StaticData.PowerUnit & CM_BIF_UNITS_AMPS) {
  776. if ((CmBatt->Info.StaticData.DesignVoltage == CM_UNKNOWN_VALUE) ||
  777. (CmBatt->Info.StaticData.DesignVoltage == 0)) {
  778. CmBattPrint (CMBATT_ERROR_ONLY,
  779. ("CmBattSetStatusNotify: Can't calculate BTP, DesignVoltage = 0x%08x\n",
  780. CmBatt->Info.StaticData.DesignVoltage));
  781. return STATUS_NOT_SUPPORTED;
  782. }
  783. //
  784. // Calculate optimized Ah target
  785. //
  786. if (CmBatt->Info.Status.BatteryState & CM_BST_STATE_CHARGING) {
  787. //
  788. // (ActualAlarm * 1000 + 500) / DesignVoltage + 1 will generate
  789. // the correct battery trip point, except in cases when
  790. // (ActualAlarm * 1000)+ 500) is evenly divisible by the
  791. // DesignVoltage. In that case, it will be 1 mAh higher than
  792. // it should be.
  793. //
  794. // This is in the form of a single expression rather than an
  795. // "if" statement to encourage the compiler to use the remainder
  796. // from the original div operation rather than performing div
  797. // twice
  798. //
  799. ActualAlarm = (ActualAlarm * 1000 + 500) / CmBatt->Info.StaticData.DesignVoltage +
  800. ( ((ActualAlarm * 1000 + 500) % CmBatt->Info.StaticData.DesignVoltage == 0)? 0 : 1 );
  801. } else {
  802. //
  803. // (ActualAlarm * 1000 - 500) / DesignVoltage will generate
  804. // the correct battery trip point, except in cases when
  805. // (ActualAlarm * 1000)+ 500) is evenly divisible by the
  806. // DesignVoltage. In that case, it will be 1 mAh higher than
  807. // it should be
  808. //
  809. ActualAlarm = (ActualAlarm * 1000 - 500) / CmBatt->Info.StaticData.DesignVoltage -
  810. ( ((ActualAlarm * 1000 - 500) % CmBatt->Info.StaticData.DesignVoltage == 0)? 1 : 0);
  811. }
  812. } else {
  813. // Increment or decrement the alarm value by 1 since the input to this
  814. // function is < or >, but _BTP is <= or >=
  815. if (CmBatt->Info.Status.BatteryState & CM_BST_STATE_CHARGING) {
  816. ActualAlarm++;
  817. } else {
  818. if (ActualAlarm > 0) {
  819. ActualAlarm--;
  820. }
  821. }
  822. }
  823. if (ActualAlarm == CmBatt->Alarm.Setting) {
  824. //
  825. // Don't need to reset the alarm to the same value.
  826. //
  827. CmBattPrint(CMBATT_LOW,
  828. ("CmBattSetStatusNotify: Keeping original setting: %X\n",
  829. CmBatt->Alarm.Setting
  830. ));
  831. return STATUS_SUCCESS;
  832. }
  833. //
  834. // Save current setting, so we won't waste time setting it twice.
  835. //
  836. CmBatt->Alarm.Setting = ActualAlarm;
  837. //
  838. // Set the alarm
  839. //
  840. Status = CmBattSetTripPpoint (CmBatt, ActualAlarm);
  841. if ((ActualAlarm == 0) && (Target != 0)) {
  842. // If the driver really wanted to be notified when the capacity
  843. // reached 0, return STATUS_NOT_SUPPORTED because seting _BTP to zero
  844. // disables notification. The battery class will perform polling since
  845. // STATUS_NOT_SUPPORTED was returned.
  846. Status = STATUS_NOT_SUPPORTED;
  847. }
  848. if (!NT_SUCCESS (Status)) {
  849. //
  850. // Something failed in the Trip point call, get out
  851. //
  852. CmBattPrint (CMBATT_ERROR, ("CmBattSetStatusNotify: SetTripPoint failed - %x\n",
  853. Status));
  854. CmBatt->Alarm.Setting = CM_ALARM_INVALID;
  855. return Status;
  856. }
  857. // Make sure that the trip point hasn't been passed already.
  858. Status = CmBattGetBstData (CmBatt, &bstData);
  859. if (!NT_SUCCESS (Status)) {
  860. //
  861. // Something failed in the Trip point call, get out
  862. //
  863. CmBattPrint (CMBATT_ERROR, ("CmBattSetStatusNotify: GetBstData - %x\n",
  864. Status));
  865. } else {
  866. if (CmBatt->Info.Status.BatteryState & CM_BST_STATE_CHARGING) {
  867. if (bstData.RemainingCapacity >= (ULONG)ActualAlarm) {
  868. CmBattPrint (CMBATT_WARN, ("CmBattSetStatusNotify: Trip point already crossed (1): TP = %08x, remaining capacity = %08x\n",
  869. ActualAlarm, bstData.RemainingCapacity));
  870. CmBattNotifyHandler (CmBatt, BATTERY_STATUS_CHANGE);
  871. }
  872. } else {
  873. if ((bstData.RemainingCapacity <= (ULONG)ActualAlarm) && (Target != 0)) {
  874. CmBattPrint (CMBATT_WARN, ("CmBattSetStatusNotify: Trip point already crossed (1): TP = %08x, remaining capacity = %08x\n",
  875. ActualAlarm, bstData.RemainingCapacity));
  876. CmBattNotifyHandler (CmBatt, BATTERY_STATUS_CHANGE);
  877. }
  878. }
  879. }
  880. CmBattPrint(CMBATT_LOW,
  881. ("CmBattSetStatusNotify: Want %X CurrentCap %X\n",
  882. Target,
  883. CmBatt->Info.ApiStatus.Capacity
  884. ));
  885. CmBattPrint ((CMBATT_MINI),
  886. ("CmBattSetStatusNotify: Set to: [%#08lx][%#08lx][%#08lx] Status %x\n",
  887. Notify->PowerState, Notify->LowCapacity, Notify->HighCapacity));
  888. return Status;
  889. }
  890. NTSTATUS
  891. CmBattDisableStatusNotify (
  892. IN PVOID Context
  893. )
  894. /*++
  895. Routine Description:
  896. Called by the class driver to disable the notification setting
  897. for the battery supplied by Context. Note, to disable a setting
  898. does not require the battery tag. Any notification is to be
  899. masked off until a subsequent call to CmBattSetStatusNotify.
  900. The battery class driver will serialize all requests it issues to
  901. the miniport for a given battery.
  902. Arguments:
  903. Context - Miniport context value for battery
  904. Return Value:
  905. Status
  906. --*/
  907. {
  908. PCM_BATT CmBatt;
  909. NTSTATUS Status;
  910. PAGED_CODE();
  911. CmBattPrint ((CMBATT_TRACE | CMBATT_MINI), ("CmBattDisableStatusNotify\n"));
  912. CmBatt = (PCM_BATT) Context;
  913. //
  914. // If _BTP doesn't exist, don't call it again.
  915. //
  916. if (!CmBatt->Info.BtpExists) {
  917. return STATUS_OBJECT_NAME_NOT_FOUND;
  918. }
  919. if (CmBatt->Alarm.Setting != CM_BATT_CLEAR_TRIP_POINT) {
  920. CmBatt->Alarm.Setting = CM_BATT_CLEAR_TRIP_POINT;
  921. //
  922. // Clear the trip point.
  923. //
  924. Status = CmBattSetTripPpoint (CmBatt, CM_BATT_CLEAR_TRIP_POINT);
  925. if (!NT_SUCCESS (Status)) {
  926. CmBattPrint ((CMBATT_MINI),
  927. ("CmBattDisableStatusNotify: SetTripPoint failed - %x\n",
  928. Status));
  929. CmBatt->Alarm.Setting = CM_ALARM_INVALID;
  930. }
  931. } else {
  932. //
  933. // Don't need to disable alarm is it's already been disabled.
  934. //
  935. Status = STATUS_SUCCESS;
  936. }
  937. return Status;
  938. }
  939. NTSTATUS
  940. CmBattGetBatteryStatus (
  941. PCM_BATT CmBatt,
  942. IN ULONG BatteryTag
  943. )
  944. /*++
  945. Routine Description:
  946. Called to setup the status data required by the IOCTL API defined for
  947. the battery class. This is the data defined in the BATTERY_STATUS
  948. structure.
  949. Arguments:
  950. CmBatt - The extension for this device.
  951. Return Value:
  952. Status
  953. --*/
  954. {
  955. NTSTATUS Status = STATUS_SUCCESS;
  956. PBATTERY_STATUS ApiStatus;
  957. PCM_BST_BAT_INFO CmBattStatus;
  958. ULONG AcStatus = 0;
  959. ULONG LastPowerState;
  960. PAGED_CODE();
  961. CmBattPrint (CMBATT_TRACE, ("CmBattGetBatteryStatus - CmBatt (%08x) Tag (%d)\n",
  962. CmBatt, BatteryTag));
  963. Status = CmBattVerifyStaticInfo (CmBatt, BatteryTag);
  964. if (!NT_SUCCESS(Status)) {
  965. return Status;
  966. }
  967. if (CmBatt->Sleeping) {
  968. //
  969. // Return cached data, and ensure that this gets requeried when we are fully awake.
  970. //
  971. CmBattNotifyHandler (CmBatt, BATTERY_STATUS_CHANGE);
  972. return Status;
  973. }
  974. CmBattStatus = &CmBatt->Info.Status;
  975. Status = CmBattGetBstData(CmBatt, CmBattStatus);
  976. if (!NT_SUCCESS(Status)) {
  977. InterlockedExchange (&CmBatt->CacheState, 0);
  978. return Status;
  979. }
  980. ApiStatus = &CmBatt->Info.ApiStatus;
  981. LastPowerState = ApiStatus->PowerState;
  982. RtlZeroMemory (ApiStatus, sizeof(BATTERY_STATUS));
  983. //
  984. // Decode the state bits
  985. //
  986. #if DEBUG
  987. if (((CmBattStatus->BatteryState & CM_BST_STATE_DISCHARGING) &&
  988. (CmBattStatus->BatteryState & CM_BST_STATE_CHARGING) )) {
  989. CmBattPrint ((CMBATT_ERROR),
  990. ("************************ ACPI BIOS BUG ********************\n"
  991. "* CmBattGetBatteryStatus: Invalid state: _BST method returned 0x%08x for Battery State.\n"
  992. "* One battery cannot be charging and discharging at the same time.\n",
  993. CmBattStatus->BatteryState));
  994. }
  995. // ASSERT(!((CmBattStatus->BatteryState & CM_BST_STATE_DISCHARGING) &&
  996. // (CmBattStatus->BatteryState & CM_BST_STATE_CHARGING) ));
  997. #endif
  998. if (CmBattStatus->BatteryState & CM_BST_STATE_DISCHARGING) {
  999. ApiStatus->PowerState |= BATTERY_DISCHARGING;
  1000. if (!(LastPowerState & BATTERY_DISCHARGING)) {
  1001. //
  1002. // Keep track of when battery started discharging.
  1003. //
  1004. CmBatt->DischargeTime = KeQueryInterruptTime();
  1005. }
  1006. } else if (CmBattStatus->BatteryState & CM_BST_STATE_CHARGING) {
  1007. ApiStatus->PowerState |= (BATTERY_CHARGING | BATTERY_POWER_ON_LINE);
  1008. }
  1009. if (CmBattStatus->BatteryState & CM_BST_STATE_CRITICAL)
  1010. ApiStatus->PowerState |= BATTERY_CRITICAL;
  1011. ApiStatus->Voltage = CmBattStatus->PresentVoltage;
  1012. //
  1013. // Run the _PSR method on the AC adapter to get the current power status.
  1014. // Otherwise, we don't know if it is connected, unless the battery reports charging.
  1015. // This isn't enough information for the upper software to work properly, so
  1016. // just find out for sure.
  1017. //
  1018. if (AcAdapterPdo != NULL) {
  1019. CmBattGetPsrData (AcAdapterPdo, &AcStatus);
  1020. } else {
  1021. // If the AcAdapterPdo is NULL, then we need to assume the AC status from
  1022. // the battery charging status.
  1023. if (CmBattStatus->BatteryState & CM_BST_STATE_CHARGING) {
  1024. AcStatus = 1;
  1025. } else {
  1026. AcStatus = 0;
  1027. }
  1028. }
  1029. if (AcStatus == 0x01) {
  1030. ApiStatus->PowerState |= BATTERY_POWER_ON_LINE;
  1031. CmBattPrint ((CMBATT_TRACE | CMBATT_DATA),
  1032. ("CmBattGetBatteryStatus: AC adapter is connected\n"));
  1033. } else {
  1034. CmBattPrint ((CMBATT_TRACE | CMBATT_DATA),
  1035. ("CmBattGetBatteryStatus: AC adapter is NOT connected\n"));
  1036. }
  1037. // The following is an awful hack put into the win98 version that really
  1038. // shouldn't be there. The purpose of this is reduce the delay in notification
  1039. // when AC status changes, but this doesn't help the problem of delays when
  1040. // other events such as battery insertion or removal happen. In addition it
  1041. // violates the priciple of WDM drivers being binary compatible, and this fix
  1042. // does nothing for any other battery driver that may later be added by a third
  1043. // party. This should be handled by the OS maintianing an outstanding long term
  1044. // status or tag request to the composite battery at all times. That would
  1045. // involve starting the Irps then recycleing it in the completion routine doing
  1046. // what this hack does if there was a change to report.
  1047. #ifndef _WIN32_WINNT
  1048. // JASONCL: check for a power source change and notify vpowerd if there has been one.
  1049. if ( ((AcStatus & 0x01) && (CmBattPrevPowerSource == 0)) ||
  1050. (!(AcStatus & 0x01) && (CmBattPrevPowerSource == 1)) ) {
  1051. CmBattPrint ((CMBATT_TRACE | CMBATT_DATA),
  1052. ("CmBattGetBatteryStatus: Detected Power Source Change\n"));
  1053. CmBattPrevPowerSource = AcStatus & 0x01;
  1054. CmBattNotifyVPOWERDOfPowerChange (1);
  1055. }
  1056. #endif
  1057. //
  1058. // Decode the power/current
  1059. //
  1060. if (CmBatt->Info.StaticData.PowerUnit == CM_BIF_UNITS_AMPS) {
  1061. //
  1062. // This battery expresses power in terms of amps. The system expects
  1063. // it to be Watts, so we have to do a conversion. The Conversion is:
  1064. //
  1065. // mW = mA * Volts or mW = mA * mV / 1000
  1066. //
  1067. // Using DesignVoltage for conversions since presentvoltage
  1068. // may vary over time, giving inconsistent results.
  1069. if ((CmBatt->Info.StaticData.DesignVoltage != CM_UNKNOWN_VALUE) &&
  1070. (CmBatt->Info.StaticData.DesignVoltage != 0)) {
  1071. if (CmBattStatus->RemainingCapacity != CM_UNKNOWN_VALUE) {
  1072. ApiStatus->Capacity = (CmBattStatus->RemainingCapacity *
  1073. CmBatt->Info.StaticData.DesignVoltage +
  1074. 500) / 1000;
  1075. } else {
  1076. CmBattPrint (CMBATT_ERROR_ONLY,
  1077. ("CmBattGetBatteryStatus - Can't calculate RemainingCapacity \n"));
  1078. CmBattPrint (CMBATT_ERROR_ONLY,
  1079. ("---------------------- RemainingCapacity = CM_UNKNOWN_VALUE\n"));
  1080. ApiStatus->Capacity = BATTERY_UNKNOWN_CAPACITY;
  1081. }
  1082. if (CmBattStatus->PresentRate != CM_UNKNOWN_VALUE) {
  1083. if (CmBattStatus->PresentRate > ((MAXULONG - 500)/ CmBatt->Info.StaticData.DesignVoltage)) { CmBattPrint (CMBATT_ERROR_ONLY,
  1084. ("CmBattGetBatteryStatus - Can't calculate Rate \n"));
  1085. CmBattPrint (CMBATT_ERROR_ONLY,
  1086. ("---------------------- Overflow: PresentRate = 0x%08x\n", CmBattStatus->PresentRate));
  1087. ApiStatus->Rate = BATTERY_UNKNOWN_RATE;
  1088. }
  1089. ApiStatus->Rate = (CmBattStatus->PresentRate *
  1090. CmBatt->Info.StaticData.DesignVoltage +
  1091. 500) / 1000;
  1092. } else {
  1093. CmBattPrint (CMBATT_ERROR_ONLY,
  1094. ("CmBattGetBatteryStatus - Can't calculate Rate \n"));
  1095. CmBattPrint (CMBATT_ERROR_ONLY,
  1096. ("---------------------- Present Rate = CM_UNKNOWN_VALUE\n"));
  1097. ApiStatus->Rate = BATTERY_UNKNOWN_RATE;
  1098. }
  1099. } else {
  1100. CmBattPrint (CMBATT_ERROR_ONLY,
  1101. ("CmBattGetBatteryStatus - Can't calculate RemainingCapacity and Rate \n"));
  1102. CmBattPrint (CMBATT_ERROR_ONLY,
  1103. ("---------------------- DesignVoltage = 0x%08x\n",
  1104. CmBatt->Info.StaticData.DesignVoltage));
  1105. ApiStatus->Capacity = BATTERY_UNKNOWN_CAPACITY;
  1106. ApiStatus->Rate = BATTERY_UNKNOWN_RATE;
  1107. }
  1108. } else {
  1109. //
  1110. // This battery expresses power in terms of Watts
  1111. //
  1112. ApiStatus->Capacity = CmBattStatus->RemainingCapacity;
  1113. ApiStatus->Rate = CmBattStatus->PresentRate;
  1114. if (CmBattStatus->PresentRate > CM_MAX_VALUE) {
  1115. ApiStatus->Rate = BATTERY_UNKNOWN_RATE;
  1116. if (CmBattStatus->PresentRate != CM_UNKNOWN_VALUE) {
  1117. CmBattPrint (CMBATT_ERROR_ONLY,
  1118. ("CmBattGetBatteryStatus - Rate is greater than CM_MAX_VALUE\n"));
  1119. CmBattPrint (CMBATT_ERROR_ONLY,
  1120. ("---------------------- PresentRate = 0x%08x\n", CmBattStatus->PresentRate));
  1121. }
  1122. }
  1123. }
  1124. //
  1125. // If the rate is "unkown" set it to zero
  1126. //
  1127. if (ApiStatus->Rate == BATTERY_UNKNOWN_RATE) {
  1128. //
  1129. // This is only allowed when -c-h-a-r-g-i-n-g- not discharging.
  1130. // Batteries are allowed to return UNKNOWN_RATE when AC is online
  1131. // but they aren't being charged.
  1132. //
  1133. if (CmBattStatus->BatteryState & CM_BST_STATE_DISCHARGING) {
  1134. CmBattPrint(
  1135. CMBATT_ERROR,
  1136. ("CmBattGetBatteryStatus: battery rate is unkown when battery "
  1137. "is not charging!\n")
  1138. );
  1139. }
  1140. } else {
  1141. //
  1142. // The OS expects the PresentRate to be a signed value, with positive values
  1143. // indicating a charge and negative values indicating a discharge. Since the
  1144. // control methods only return unsigned values we need to do the conversion here.
  1145. //
  1146. if (ApiStatus->PowerState & BATTERY_DISCHARGING) {
  1147. ApiStatus->Rate = 0 - ApiStatus->Rate;
  1148. } else if (!(ApiStatus->PowerState & BATTERY_CHARGING) && (ApiStatus->Rate != 0)) {
  1149. CmBattPrint ((CMBATT_BIOS), ("CmBattGetBatteryStatus: battery is not charging or discharging, but rate = %x\n", ApiStatus->Rate));
  1150. ApiStatus->Rate = 0;
  1151. } else {
  1152. // Rate already equals 0. Battery is not Charging or discharging.
  1153. }
  1154. }
  1155. return STATUS_SUCCESS;
  1156. }
  1157. NTSTATUS
  1158. CmBattVerifyStaticInfo (
  1159. IN PCM_BATT CmBatt,
  1160. IN ULONG BatteryTag
  1161. )
  1162. /*++
  1163. Routine Description:
  1164. In order to detect battery changes, we'll check to see if any part of the data
  1165. returned by the cm is different from what we had read in the past.
  1166. Arguments:
  1167. CmBatt - Battery to read
  1168. BatteryTag - Tag of battery as expected by the caller
  1169. Return Value:
  1170. Returns a boolean to indicate to the caller that IO was performed.
  1171. This allows the caller to iterate on changes it may be making until
  1172. the battery state is correct.
  1173. --*/
  1174. {
  1175. NTSTATUS Status;
  1176. CM_BIF_BAT_INFO NewInfo;
  1177. ULONG StaResult;
  1178. PBATTERY_INFORMATION ApiData = &CmBatt->Info.ApiInfo;
  1179. PCM_BIF_BAT_INFO BIFData = &CmBatt->Info.StaticData;
  1180. PAGED_CODE();
  1181. CmBattPrint (CMBATT_TRACE, ("CmBattVerifyStaticInfo - CmBatt (%08x) Tag (%d) Device %d\n",
  1182. CmBatt, BatteryTag, CmBatt->DeviceNumber));
  1183. Status = STATUS_SUCCESS;
  1184. if ((CmBatt->Info.Tag == BATTERY_TAG_INVALID) || (BatteryTag != CmBatt->Info.Tag)) {
  1185. return STATUS_NO_SUCH_DEVICE;
  1186. }
  1187. if ((CmBatt->CacheState == 2) && (!CmBatt->ReCheckSta)) {
  1188. return Status;
  1189. }
  1190. if (CmBatt->Sleeping) {
  1191. //
  1192. // Return cached data, and ensure that this gets requeried when we are fully awake.
  1193. //
  1194. CmBattNotifyHandler (CmBatt, BATTERY_STATUS_CHANGE);
  1195. return Status;
  1196. }
  1197. // Check to make sure that the battery does exist
  1198. // before continuing
  1199. if (CmBatt->ReCheckSta) {
  1200. CmBatt->ReCheckSta = FALSE;
  1201. Status = CmBattGetStaData (CmBatt->Pdo, &StaResult);
  1202. if (NT_SUCCESS (Status)) {
  1203. if (!(StaResult & STA_DEVICE_PRESENT)) {
  1204. CmBatt->Info.Tag = BATTERY_TAG_INVALID;
  1205. Status = STATUS_NO_SUCH_DEVICE;
  1206. return Status;
  1207. }
  1208. }
  1209. }
  1210. //
  1211. // The first time through the loop, CacheState will be 1
  1212. // If a notification occurs, this will be reset to 0, and the loop will run again.
  1213. // If no notification occurs, it will increment to 2, the "Valid" value.
  1214. //
  1215. while (NT_SUCCESS(Status) && (InterlockedIncrement (&CmBatt->CacheState) == 1)) {
  1216. //
  1217. // Go get fresh data
  1218. // Issue the Control method
  1219. //
  1220. if (CmBatt->ReCheckSta) {
  1221. CmBatt->ReCheckSta = FALSE;
  1222. Status = CmBattGetStaData (CmBatt->Pdo, &StaResult);
  1223. if (NT_SUCCESS (Status)) {
  1224. if (!(StaResult & STA_DEVICE_PRESENT)) {
  1225. CmBatt->Info.Tag = BATTERY_TAG_INVALID;
  1226. Status = STATUS_NO_SUCH_DEVICE;
  1227. }
  1228. }
  1229. }
  1230. if (NT_SUCCESS (Status)) {
  1231. Status = CmBattGetBifData(CmBatt, &NewInfo);
  1232. }
  1233. if (NT_SUCCESS (Status)) {
  1234. CmBattPrint ((CMBATT_TRACE | CMBATT_DATA | CMBATT_BIOS),
  1235. ("CmBattGetStaticInfo: _BIF Returned: PowerUnit=%x DesignCapacity=%x LastFull=%x\n",
  1236. NewInfo.PowerUnit, NewInfo.DesignCapacity, NewInfo.LastFullChargeCapacity ));
  1237. CmBattPrint ((CMBATT_TRACE | CMBATT_DATA | CMBATT_BIOS),
  1238. (" ---------------- Technology=%x Voltage=%x DesignWarning=%x\n",
  1239. NewInfo.BatteryTechnology, NewInfo.DesignVoltage,
  1240. NewInfo.DesignCapacityOfWarning ));
  1241. CmBattPrint ((CMBATT_TRACE | CMBATT_DATA | CMBATT_BIOS),
  1242. (" ---------------- DesignLow=%x Gran1=%x Gran2=%x\n",
  1243. NewInfo.DesignCapacityOfLow, NewInfo.BatteryCapacityGran_1,
  1244. NewInfo.BatteryCapacityGran_2 ));
  1245. CmBattPrint ((CMBATT_TRACE | CMBATT_DATA | CMBATT_BIOS),
  1246. (" ---------------- ModelNumber=%s \n",
  1247. NewInfo.ModelNumber));
  1248. CmBattPrint ((CMBATT_TRACE | CMBATT_DATA | CMBATT_BIOS),
  1249. (" ---------------- SerialNumber=%s \n",
  1250. NewInfo.SerialNumber));
  1251. CmBattPrint ((CMBATT_TRACE | CMBATT_DATA | CMBATT_BIOS),
  1252. (" ---------------- BatteryType=%s \n",
  1253. NewInfo.BatteryType));
  1254. CmBattPrint ((CMBATT_TRACE | CMBATT_DATA | CMBATT_BIOS),
  1255. (" ---------------- OEMInformation=%s \n",
  1256. NewInfo.OEMInformation));
  1257. //
  1258. // Update static area with the new data
  1259. //
  1260. if ((CmBatt->Info.Tag == CmBatt->Info.StaticDataTag) &&
  1261. (CmBatt->Info.StaticDataTag != BATTERY_TAG_INVALID)) {
  1262. if (RtlCompareMemory (&NewInfo, BIFData, sizeof(NewInfo)) == sizeof(NewInfo)) {
  1263. //
  1264. // Nothing has changed. Don't need to update anything.
  1265. //
  1266. continue;
  1267. } else {
  1268. //
  1269. // Something has changed. The tag should have been invalidated.
  1270. //
  1271. CmBattPrint ((CMBATT_BIOS | CMBATT_ERROR),
  1272. ("CmBattVerifyStaticInfo: Static data changed without recieving notify 0x81.\n"));
  1273. CmBatt->Info.Tag = BATTERY_TAG_INVALID;
  1274. Status = STATUS_NO_SUCH_DEVICE;
  1275. CmBatt->Info.StaticDataTag = BATTERY_TAG_INVALID;
  1276. }
  1277. }
  1278. CmBatt->Info.StaticDataTag = CmBatt->Info.Tag;
  1279. RtlCopyMemory (BIFData, &NewInfo, sizeof(CM_BIF_BAT_INFO));
  1280. RtlZeroMemory (ApiData, sizeof(BATTERY_INFORMATION));
  1281. ApiData->Capabilities = BATTERY_SYSTEM_BATTERY;
  1282. ApiData->Technology = (UCHAR) BIFData->BatteryTechnology;
  1283. //
  1284. // Use first four chars of BatteryType as Chemistry string
  1285. //
  1286. ApiData->Chemistry[0] = BIFData->BatteryType[0];
  1287. ApiData->Chemistry[1] = BIFData->BatteryType[1];
  1288. ApiData->Chemistry[2] = BIFData->BatteryType[2];
  1289. ApiData->Chemistry[3] = BIFData->BatteryType[3];
  1290. ApiData->CriticalBias = 0;
  1291. ApiData->CycleCount = 0;
  1292. if (BIFData->PowerUnit & CM_BIF_UNITS_AMPS) {
  1293. //
  1294. // This battery reports in mA we need to convert all the capacities to
  1295. // mW because this is what the OS expects. The algorithm for doing this
  1296. // is:
  1297. //
  1298. // mW = mA * Volts or mW = mA * mV / 1000
  1299. //
  1300. if (BIFData->DesignVoltage != CM_UNKNOWN_VALUE) {
  1301. //
  1302. // Convert the DesignCapacity
  1303. //
  1304. if (BIFData->DesignCapacity != CM_UNKNOWN_VALUE) {
  1305. ApiData->DesignedCapacity = (BIFData->DesignCapacity *
  1306. BIFData->DesignVoltage +
  1307. 500) /
  1308. 1000;
  1309. } else {
  1310. ApiData->DesignedCapacity = BATTERY_UNKNOWN_CAPACITY;
  1311. CmBattPrint (CMBATT_ERROR_ONLY,
  1312. ("CmBattGetStaticInfo - Can't calculate DesignCapacity \n"));
  1313. CmBattPrint (CMBATT_ERROR_ONLY,
  1314. ("-------------------- DesignCapacity = CM_UNKNOWN_VALUE\n"));
  1315. }
  1316. //
  1317. // Convert the LastFullChargeCapacity
  1318. //
  1319. if (BIFData->LastFullChargeCapacity != CM_UNKNOWN_VALUE) {
  1320. ApiData->FullChargedCapacity = (BIFData->LastFullChargeCapacity *
  1321. BIFData->DesignVoltage +
  1322. 500) /
  1323. 1000;
  1324. } else {
  1325. ApiData->FullChargedCapacity = BATTERY_UNKNOWN_CAPACITY;
  1326. CmBattPrint (CMBATT_ERROR_ONLY,
  1327. ("CmBattGetStaticInfo - Can't calculate LastFullChargeCapacity \n"));
  1328. CmBattPrint (CMBATT_ERROR_ONLY,
  1329. ("-------------------- LastFullChargeCapacity = CM_UNKNOWN_VALUE\n"));
  1330. }
  1331. //
  1332. // Convert the DesignCapacityOfWarning
  1333. //
  1334. if (BIFData->DesignCapacityOfWarning != CM_UNKNOWN_VALUE) {
  1335. ApiData->DefaultAlert2 = (BIFData->DesignCapacityOfWarning *
  1336. BIFData->DesignVoltage +
  1337. 500) /
  1338. 1000;
  1339. } else {
  1340. ApiData->DefaultAlert2 = BATTERY_UNKNOWN_CAPACITY;
  1341. CmBattPrint (CMBATT_ERROR_ONLY,
  1342. ("CmBattGetStaticInfo - Can't calculate DesignCapacityOfWarning \n"));
  1343. CmBattPrint (CMBATT_ERROR_ONLY,
  1344. ("-------------------- DesignCapacityOfWarning = CM_UNKNOWN_VALUE\n"));
  1345. }
  1346. //
  1347. // Convert the DesignCapacityOfLow
  1348. //
  1349. if (BIFData->DesignCapacityOfLow != CM_UNKNOWN_VALUE) {
  1350. ApiData->DefaultAlert1 = (BIFData->DesignCapacityOfLow *
  1351. BIFData->DesignVoltage +
  1352. 500) /
  1353. 1000;
  1354. } else {
  1355. ApiData->DefaultAlert1 = BATTERY_UNKNOWN_CAPACITY;
  1356. CmBattPrint (CMBATT_ERROR_ONLY,
  1357. ("CmBattGetStaticInfo - Can't calculate DesignCapacityOfLow \n"));
  1358. CmBattPrint (CMBATT_ERROR_ONLY,
  1359. ("-------------------- DesignCapacityOfLow = CM_UNKNOWN_VALUE\n"));
  1360. }
  1361. //
  1362. // Convert the BatteryCapacityGran_1
  1363. //
  1364. if (BIFData->BatteryCapacityGran_1 != CM_UNKNOWN_VALUE) {
  1365. CmBatt->Info.ApiGranularity_1 = (BIFData->BatteryCapacityGran_1 *
  1366. BIFData->DesignVoltage +
  1367. 500) /
  1368. 1000;
  1369. } else {
  1370. CmBatt->Info.ApiGranularity_1 = BATTERY_UNKNOWN_CAPACITY;
  1371. CmBattPrint (CMBATT_ERROR_ONLY,
  1372. ("CmBattGetStaticInfo - Can't calculate BatteryCapacityGran_1 \n"));
  1373. CmBattPrint (CMBATT_ERROR_ONLY,
  1374. ("-------------------- BatteryCapacityGran_1 = CM_UNKNOWN_VALUE\n"));
  1375. }
  1376. //
  1377. // Convert the BatteryCapacityGran_2
  1378. //
  1379. if (BIFData->BatteryCapacityGran_2 != CM_UNKNOWN_VALUE) {
  1380. CmBatt->Info.ApiGranularity_2 = (BIFData->BatteryCapacityGran_2 *
  1381. BIFData->DesignVoltage +
  1382. 500) /
  1383. 1000;
  1384. } else {
  1385. CmBatt->Info.ApiGranularity_2 = BATTERY_UNKNOWN_CAPACITY;
  1386. CmBattPrint (CMBATT_ERROR_ONLY,
  1387. ("CmBattGetStaticInfo - Can't calculate BatteryCapacityGran_2 \n"));
  1388. CmBattPrint (CMBATT_ERROR_ONLY,
  1389. ("-------------------- BatteryCapacityGran_2 = CM_UNKNOWN_VALUE\n"));
  1390. }
  1391. } else {
  1392. CmBattPrint (CMBATT_ERROR_ONLY,
  1393. ("CmBattGetStaticInfo - Can't calculate Capacities \n"));
  1394. CmBattPrint (CMBATT_ERROR_ONLY,
  1395. ("-------------------- DesignVoltage = CM_UNKNOWN_VALUE\n"));
  1396. ApiData->DesignedCapacity = BATTERY_UNKNOWN_CAPACITY;
  1397. ApiData->FullChargedCapacity = BATTERY_UNKNOWN_CAPACITY;
  1398. ApiData->DefaultAlert1 = BATTERY_UNKNOWN_CAPACITY;
  1399. ApiData->DefaultAlert2 = BATTERY_UNKNOWN_CAPACITY;
  1400. CmBatt->Info.ApiGranularity_1 = BATTERY_UNKNOWN_CAPACITY;
  1401. CmBatt->Info.ApiGranularity_2 = BATTERY_UNKNOWN_CAPACITY;
  1402. }
  1403. } else {
  1404. ApiData->DesignedCapacity = BIFData->DesignCapacity;
  1405. ApiData->FullChargedCapacity = BIFData->LastFullChargeCapacity;
  1406. ApiData->DefaultAlert1 = BIFData->DesignCapacityOfLow;
  1407. ApiData->DefaultAlert2 = BIFData->DesignCapacityOfWarning;
  1408. CmBatt->Info.ApiGranularity_1 = BIFData->BatteryCapacityGran_1;
  1409. CmBatt->Info.ApiGranularity_2 = BIFData->BatteryCapacityGran_2;
  1410. }
  1411. CmBatt->Info.ModelNum = (PUCHAR) &BIFData->ModelNumber;
  1412. CmBatt->Info.ModelNumLen = (ULONG) strlen (CmBatt->Info.ModelNum);
  1413. CmBatt->Info.SerialNum = (PUCHAR) &BIFData->SerialNumber;
  1414. CmBatt->Info.SerialNumLen = (ULONG) strlen (CmBatt->Info.SerialNum);
  1415. CmBatt->Info.OEMInfo = (PUCHAR) &BIFData->OEMInformation;
  1416. CmBatt->Info.OEMInfoLen = (ULONG) strlen (CmBatt->Info.OEMInfo);
  1417. }
  1418. }
  1419. if ((CmBatt->Info.Tag) == BATTERY_TAG_INVALID || (BatteryTag != CmBatt->Info.Tag)) {
  1420. // If the tag has been invalidated since we started, fail the request.
  1421. Status = STATUS_NO_SUCH_DEVICE;
  1422. }
  1423. if (!NT_SUCCESS (Status)) {
  1424. // If somthing failed, make sure the cache is marked as invalid.
  1425. InterlockedExchange (&CmBatt->CacheState, 0);
  1426. }
  1427. CmBattPrint (CMBATT_TRACE ,("CmBattGetStaticInfo: Exit\n"));
  1428. return Status;
  1429. }