Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2006 lines
61 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. bsrv.c
  5. Abstract:
  6. Service battery class device
  7. Author:
  8. Ken Reneris
  9. Environment:
  10. Notes:
  11. Revision History:
  12. --*/
  13. #include "battcp.h"
  14. VOID
  15. BattCIoctl (
  16. IN PBATT_INFO BattInfo,
  17. IN PIRP Irp,
  18. IN PIO_STACK_LOCATION IrpSp
  19. );
  20. VOID
  21. BattCCheckTagQueue (
  22. IN PBATT_NP_INFO BattNPInfo,
  23. IN PBATT_INFO BattInfo
  24. );
  25. VOID
  26. BattCCheckStatusQueue (
  27. IN PBATT_NP_INFO BattNPInfo,
  28. IN PBATT_INFO BattInfo
  29. );
  30. VOID
  31. BattCWmi (
  32. IN PBATT_NP_INFO BattNPInfo,
  33. IN PBATT_INFO BattInfo,
  34. IN PBATT_WMI_REQUEST WmiRequest
  35. );
  36. VOID
  37. BattCMiniportStatus (
  38. IN PBATT_INFO BattInfo,
  39. IN NTSTATUS Status
  40. );
  41. VOID
  42. BattCCompleteIrpQueue (
  43. IN PLIST_ENTRY Queue,
  44. IN NTSTATUS Status
  45. );
  46. VOID
  47. BattCCompleteWmiQueue (
  48. IN PLIST_ENTRY Queue,
  49. IN NTSTATUS Status
  50. );
  51. #ifdef ALLOC_PRAGMA
  52. #pragma alloc_text(PAGE,BattCCheckStatusQueue)
  53. #pragma alloc_text(PAGE,BattCCheckTagQueue)
  54. #pragma alloc_text(PAGE,BattCWorkerThread)
  55. #pragma alloc_text(PAGE,BattCIoctl)
  56. #endif
  57. VOID
  58. BattCWorkerDpc (
  59. IN struct _KDPC *Dpc,
  60. IN PVOID DeferredContext,
  61. IN PVOID SystemArgument1,
  62. IN PVOID SystemArgument2
  63. )
  64. /*++
  65. Routine Description:
  66. DPC used to get worker thread when status needs to be checked.
  67. Arguments:
  68. Dpc - the worker dpc
  69. Return Value:
  70. None.
  71. --*/
  72. {
  73. PBATT_NP_INFO BattNPInfo;
  74. BattNPInfo = (PBATT_NP_INFO) DeferredContext;
  75. BattCQueueWorker (BattNPInfo, TRUE);
  76. // Release Removal Lock
  77. if (0 == InterlockedDecrement(&BattNPInfo->InUseCount)) {
  78. KeSetEvent (&BattNPInfo->ReadyToRemove, IO_NO_INCREMENT, FALSE);
  79. }
  80. BattPrint ((BATT_LOCK), ("BattCWorkerDpc: Released remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount));
  81. }
  82. VOID
  83. BattCTagDpc (
  84. IN struct _KDPC *Dpc,
  85. IN PVOID DeferredContext,
  86. IN PVOID SystemArgument1,
  87. IN PVOID SystemArgument2
  88. )
  89. /*++
  90. Routine Description:
  91. DPC used to get worker thread when status needs to be checked.
  92. Arguments:
  93. Dpc - the worker dpc
  94. Return Value:
  95. None.
  96. --*/
  97. {
  98. PBATT_NP_INFO BattNPInfo;
  99. BattNPInfo = (PBATT_NP_INFO) DeferredContext;
  100. InterlockedExchange(&BattNPInfo->CheckTag, 1);
  101. BattCQueueWorker (BattNPInfo, FALSE);
  102. // Release Removal Lock
  103. if (0 == InterlockedDecrement(&BattNPInfo->InUseCount)) {
  104. KeSetEvent (&BattNPInfo->ReadyToRemove, IO_NO_INCREMENT, FALSE);
  105. }
  106. BattPrint ((BATT_LOCK), ("BattCTagDpc: Released remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount));
  107. }
  108. VOID
  109. BattCCancelStatus (
  110. IN PDEVICE_OBJECT DeviceObject,
  111. IN PIRP Irp
  112. )
  113. /*++
  114. Routine Description:
  115. Queued status IRP is being canceled
  116. Arguments:
  117. DeviceObject - Device object of the miniport. Not useful to the
  118. class driver - ignored.
  119. Irp - Irp being cancelled
  120. Return Value:
  121. None.
  122. --*/
  123. {
  124. PIO_STACK_LOCATION IrpNextSp;
  125. PBATT_NP_INFO BattNPInfo;
  126. //
  127. // IRP is flagged as needing cancled, cause a check status which will
  128. // complete any pending cancled irps
  129. //
  130. IrpNextSp = IoGetNextIrpStackLocation(Irp);
  131. BattNPInfo = (PBATT_NP_INFO) IrpNextSp->Parameters.Others.Argument4;
  132. BattPrint ((BATT_TRACE), ("BattC (%d): BatteryCCancelStatus. Irp - %08x\n", BattNPInfo->DeviceNum, Irp));
  133. BattCQueueWorker (BattNPInfo, TRUE);
  134. //
  135. // The cancel Spinlock must be released after attempting to queue the
  136. // worker thread so that there is no timeing problems on remove.
  137. //
  138. IoReleaseCancelSpinLock(Irp->CancelIrql);
  139. }
  140. VOID
  141. BattCCancelTag (
  142. IN PDEVICE_OBJECT DeviceObject,
  143. IN PIRP Irp
  144. )
  145. /*++
  146. Routine Description:
  147. Queued tag IRP is being canceled
  148. Arguments:
  149. DeviceObject - Device object of the miniport. Not useful to the
  150. class driver - ignored.
  151. Irp - Irp being cancelled
  152. Return Value:
  153. None.
  154. --*/
  155. {
  156. PIO_STACK_LOCATION IrpNextSp;
  157. PBATT_NP_INFO BattNPInfo;
  158. //
  159. // IRP is flagged as needing canceled. Cause a check tag which will
  160. // complete any pending cancled irps
  161. //
  162. IrpNextSp = IoGetNextIrpStackLocation(Irp);
  163. BattNPInfo = (PBATT_NP_INFO) IrpNextSp->Parameters.Others.Argument4;
  164. BattPrint ((BATT_TRACE), ("BattC (%d): BatteryCCancelTag. Irp - %08x\n", BattNPInfo->DeviceNum, Irp));
  165. InterlockedExchange(&BattNPInfo->CheckTag, 1);
  166. BattCQueueWorker (BattNPInfo, FALSE);
  167. //
  168. // The cancel Spinlock must be released after attempting to queue the
  169. // worker thread so that there is no timeing problems on remove.
  170. //
  171. IoReleaseCancelSpinLock(Irp->CancelIrql);
  172. }
  173. VOID
  174. BattCQueueWorker (
  175. IN PBATT_NP_INFO BattNPInfo,
  176. IN BOOLEAN CheckStatus
  177. )
  178. /*++
  179. Routine Description:
  180. Get worker thread to check the battery state (IoQueue). The
  181. battery IOs are serialized here as only one worker thread is
  182. used to process the battery IOs. If the worker thread is already
  183. running, it is flagged to loop are re-check the state. If the
  184. worker thread is not running, one is queued.
  185. If CheckStatus is set, the worker thread is informed that the
  186. batteries current status is read and the pending status queue
  187. is checked.
  188. Arguments:
  189. BattNPInfo - Battery to check
  190. CheckStatus - Whether or not the status also needs checked
  191. Return Value:
  192. None.
  193. --*/
  194. {
  195. PBATT_INFO BattInfo = BattNPInfo->BattInfo;
  196. //
  197. // Add 1 to the WorkerActive value, if this is the first count
  198. // queue a worker thread
  199. //
  200. BattPrint ((BATT_TRACE), ("BattC (%d): BatteryCQueueWorker.\n", BattNPInfo->DeviceNum));
  201. if (CheckStatus) {
  202. InterlockedExchange(&BattNPInfo->CheckStatus, 1);
  203. InterlockedExchange (&BattNPInfo->CheckTag, 1);
  204. }
  205. //
  206. // Increment WorkerActive count. If the worker thread is already running,
  207. // there is no need to requeue it.
  208. //
  209. if (InterlockedIncrement(&BattNPInfo->WorkerActive) == 1) {
  210. // Removal lock.
  211. if ((BattNPInfo->WantToRemove == TRUE) && (KeGetCurrentIrql() == PASSIVE_LEVEL)) {
  212. // Check Irql to make sure this wasn't called by an ISR. If so,
  213. // queue the worker rather than complete the requests in this thread.
  214. //
  215. // Empty IRP queues.
  216. //
  217. BattCCompleteIrpQueue(&(BattInfo->IoQueue), STATUS_DEVICE_REMOVED);
  218. BattCCompleteIrpQueue(&(BattInfo->TagQueue), STATUS_DEVICE_REMOVED);
  219. BattCCompleteIrpQueue(&(BattInfo->StatusQueue), STATUS_DEVICE_REMOVED);
  220. BattCCompleteIrpQueue(&(BattInfo->WmiQueue), STATUS_DEVICE_REMOVED);
  221. //
  222. // Remove lock and trigger Remove function if necessary.
  223. //
  224. if (0 == InterlockedDecrement(&BattNPInfo->InUseCount)) {
  225. KeSetEvent (&BattNPInfo->ReadyToRemove, IO_NO_INCREMENT, FALSE);
  226. }
  227. BattPrint ((BATT_LOCK), ("BattCQueueWorker: Released remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount));
  228. } else {
  229. ExQueueWorkItem (&BattNPInfo->WorkerThread, DelayedWorkQueue);
  230. }
  231. }
  232. }
  233. VOID
  234. BattCWorkerThread (
  235. IN PVOID Context
  236. )
  237. /*++
  238. Routine Description:
  239. Battery IO worker thread entry point.
  240. N.B. There is only one worker thread handling the battery at any one time
  241. Arguments:
  242. Context - BattInfo. Battery to check
  243. Return Value:
  244. None.
  245. --*/
  246. {
  247. PBATT_INFO BattInfo;
  248. PBATT_NP_INFO BattNPInfo;
  249. PLIST_ENTRY Entry;
  250. PIRP Irp;
  251. PIO_STACK_LOCATION IrpSp;
  252. ULONG i;
  253. PAGED_CODE();
  254. BattNPInfo = (PBATT_NP_INFO) Context;
  255. BattInfo = BattNPInfo->BattInfo;
  256. BattPrint ((BATT_TRACE), ("BattC (%d): BatteryCWorkerThread entered.\n", BattNPInfo->DeviceNum));
  257. //
  258. // Loop while there is work to check
  259. //
  260. for (; ;) {
  261. // Removal code. This makes sure that the structures aren't freed in the middle of
  262. // processing. All Irp Queues will be emptied by BatteryClassUnload.
  263. if (BattNPInfo->WantToRemove == TRUE) {
  264. //
  265. // Empty IRP queues.
  266. //
  267. BattCCompleteIrpQueue(&(BattInfo->IoQueue), STATUS_DEVICE_REMOVED);
  268. BattCCompleteIrpQueue(&(BattInfo->TagQueue), STATUS_DEVICE_REMOVED);
  269. BattCCompleteIrpQueue(&(BattInfo->StatusQueue), STATUS_DEVICE_REMOVED);
  270. BattCCompleteIrpQueue(&(BattInfo->WmiQueue), STATUS_DEVICE_REMOVED);
  271. //
  272. // Signal BatteryClassUnload that it is safe to return.
  273. //
  274. if (0 == InterlockedDecrement(&BattNPInfo->InUseCount)) {
  275. KeSetEvent (&BattNPInfo->ReadyToRemove, IO_NO_INCREMENT, FALSE);
  276. }
  277. BattPrint ((BATT_LOCK), ("BattCWorkerThread: Released remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount));
  278. return;
  279. }
  280. //
  281. // Acquire queue locks
  282. //
  283. ExAcquireFastMutex (&BattNPInfo->Mutex);
  284. //
  285. // While there are IRPs in the IoQueue handle them
  286. //
  287. while (!IsListEmpty(&BattInfo->IoQueue)) {
  288. //
  289. // Remove entry from IoQueue and drop device lock
  290. //
  291. Entry = RemoveHeadList(&BattInfo->IoQueue);
  292. ExReleaseFastMutex (&BattNPInfo->Mutex);
  293. //
  294. // Handle this entry
  295. //
  296. Irp = CONTAINING_RECORD (
  297. Entry,
  298. IRP,
  299. Tail.Overlay.ListEntry
  300. );
  301. BattPrint (BATT_IOCTL, ("BattC (%d): WorkerThread, Got Irp - %x\n", BattNPInfo->DeviceNum, Irp));
  302. IrpSp = IoGetCurrentIrpStackLocation(Irp);
  303. if (IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_BATTERY_QUERY_STATUS &&
  304. IrpSp->Parameters.DeviceIoControl.InputBufferLength == sizeof (BATTERY_WAIT_STATUS) &&
  305. IrpSp->Parameters.DeviceIoControl.OutputBufferLength == sizeof (BATTERY_STATUS)) {
  306. BattPrint (BATT_IOCTL,
  307. ("BattC (%d): Received QueryStatus Irp - %x, timeout - %x\n",
  308. BattNPInfo->DeviceNum,
  309. Irp,
  310. ((PBATTERY_WAIT_STATUS)Irp->AssociatedIrp.SystemBuffer)->Timeout));
  311. //
  312. // Valid query status irp, put it on the StatusQueue and handle later
  313. //
  314. InterlockedExchange (&BattNPInfo->CheckStatus, 1);
  315. IrpSp = IoGetNextIrpStackLocation(Irp);
  316. IrpSp->Parameters.Others.Argument1 = (PVOID) 0;
  317. IrpSp->Parameters.Others.Argument2 = (PVOID) 0;
  318. IrpSp->Parameters.Others.Argument3 = NULL;
  319. IrpSp->Parameters.Others.Argument4 = BattNPInfo;
  320. //
  321. // Set IRPs cancel routine
  322. //
  323. IoSetCancelRoutine (Irp, BattCCancelStatus);
  324. //
  325. // Queue it
  326. //
  327. InsertTailList (
  328. &BattInfo->StatusQueue,
  329. &Irp->Tail.Overlay.ListEntry
  330. );
  331. } else if (IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_BATTERY_QUERY_TAG &&
  332. (IrpSp->Parameters.DeviceIoControl.InputBufferLength == sizeof (ULONG) ||
  333. IrpSp->Parameters.DeviceIoControl.InputBufferLength == 0) &&
  334. IrpSp->Parameters.DeviceIoControl.OutputBufferLength == sizeof (ULONG)) {
  335. BattPrint (BATT_IOCTL,
  336. ("BattC (%d): Received QueryTag with timeout %x\n",
  337. BattNPInfo->DeviceNum,
  338. *((PULONG) Irp->AssociatedIrp.SystemBuffer))
  339. );
  340. //
  341. // Valid query tag irp, put it on the TagQueue and handle later
  342. //
  343. InterlockedExchange (&BattNPInfo->CheckTag, 1);
  344. IrpSp = IoGetNextIrpStackLocation(Irp);
  345. IrpSp->Parameters.Others.Argument1 = (PVOID) 0;
  346. IrpSp->Parameters.Others.Argument2 = (PVOID) 0;
  347. IrpSp->Parameters.Others.Argument3 = NULL;
  348. IrpSp->Parameters.Others.Argument4 = BattNPInfo;
  349. //
  350. // Set IRPs cancel routine
  351. //
  352. IoSetCancelRoutine (Irp, BattCCancelTag);
  353. InsertTailList (
  354. &BattInfo->TagQueue,
  355. &Irp->Tail.Overlay.ListEntry
  356. );
  357. } else {
  358. //
  359. // Handle IRP now
  360. //
  361. BattPrint (BATT_IOCTL, ("BattC (%d): Calling BattCIoctl with irp %x\n", BattNPInfo->DeviceNum, Irp));
  362. BattCIoctl (BattInfo, Irp, IrpSp);
  363. }
  364. //
  365. // Acquire IoQueue lock and check for anything else in the IoQueueu
  366. //
  367. ExAcquireFastMutex (&BattNPInfo->Mutex);
  368. }
  369. //
  370. // Done with the IoQueue
  371. //
  372. ExReleaseFastMutex (&BattNPInfo->Mutex);
  373. //
  374. // Check pending status queue
  375. //
  376. if (BattNPInfo->CheckStatus) {
  377. BattCCheckStatusQueue (BattNPInfo, BattInfo);
  378. }
  379. //
  380. // Check pending tag queue
  381. //
  382. if (BattNPInfo->CheckTag) {
  383. BattCCheckTagQueue (BattNPInfo, BattInfo);
  384. }
  385. //
  386. // Acquire queue locks
  387. //
  388. ExAcquireFastMutex (&BattNPInfo->Mutex);
  389. //
  390. // While there are outstanding WMI requests handle them
  391. //
  392. while (!IsListEmpty(&BattInfo->WmiQueue)) {
  393. PBATT_WMI_REQUEST WmiRequest;
  394. //
  395. // Remove entry from WmiQueue and drop device lock
  396. //
  397. Entry = RemoveHeadList(&BattInfo->WmiQueue);
  398. ExReleaseFastMutex (&BattNPInfo->Mutex);
  399. //
  400. // Handle this entry
  401. //
  402. WmiRequest = CONTAINING_RECORD (
  403. Entry,
  404. BATT_WMI_REQUEST,
  405. ListEntry
  406. );
  407. BattPrint (BATT_WMI, ("BattC (%d): WorkerThread, Got WMI Rewest - %x\n", BattNPInfo->DeviceNum, WmiRequest));
  408. //
  409. // Process the request here.
  410. //
  411. BattCWmi (BattNPInfo, BattInfo, WmiRequest);
  412. //
  413. // Acquire IoQueue lock and check for anything else in the IoQueueu
  414. //
  415. ExAcquireFastMutex (&BattNPInfo->Mutex);
  416. }
  417. //
  418. // Done with the IoQueue
  419. //
  420. ExReleaseFastMutex (&BattNPInfo->Mutex);
  421. //
  422. // See if we need to recheck
  423. //
  424. i = InterlockedDecrement(&BattNPInfo->WorkerActive);
  425. BattPrint (BATT_TRACE, ("BattC (%d): WorkerActive count=%x\n", BattNPInfo->DeviceNum, i));
  426. if (i == 0) {
  427. // done
  428. BattPrint (BATT_TRACE, ("BattC (%d): WorkerActive count is zero!\n", BattNPInfo->DeviceNum));
  429. break;
  430. }
  431. //
  432. // No need to loop multiple times, if count is not one lower it
  433. //
  434. if (i != 1) {
  435. BattPrint (BATT_TRACE, ("BattC (%d): WorkerActive set to 1\n", BattNPInfo->DeviceNum));
  436. InterlockedExchange(&BattNPInfo->WorkerActive, 1);
  437. }
  438. }
  439. BattPrint ((BATT_TRACE), ("BattC (%d): BatteryCWorkerThread exiting.\n", BattNPInfo->DeviceNum));
  440. }
  441. VOID
  442. BattCIoctl (
  443. IN PBATT_INFO BattInfo,
  444. IN PIRP Irp,
  445. IN PIO_STACK_LOCATION IrpSp
  446. )
  447. /*++
  448. Routine Description:
  449. Completes the battery IOCTL request.
  450. N.B. must be invoked from the non-rentrant worker thread
  451. Arguments:
  452. BattInfo - Battery
  453. Irp - IOCTL request
  454. IrpSp - Current stack location
  455. Return Value:
  456. IRP has been completed
  457. --*/
  458. {
  459. ULONG InputLen, OutputLen;
  460. PVOID IOBuffer;
  461. NTSTATUS Status;
  462. PBATTERY_QUERY_INFORMATION QueryInfo;
  463. PBATTERY_SET_INFORMATION SetInformation;
  464. #if DEBUG
  465. BATTERY_QUERY_INFORMATION_LEVEL inflevel;
  466. #endif
  467. PAGED_CODE();
  468. BattPrint ((BATT_TRACE), ("BattC (%d): BattCIoctl called\n", BattInfo->BattNPInfo->DeviceNum));
  469. IOBuffer = Irp->AssociatedIrp.SystemBuffer;
  470. InputLen = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
  471. OutputLen = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
  472. //
  473. // Dispatch IOCtl request to proper miniport function
  474. //
  475. Status = STATUS_INVALID_BUFFER_SIZE;
  476. switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
  477. case IOCTL_BATTERY_QUERY_TAG:
  478. //
  479. // Query tag only gets here if the input or output buffer lengths are
  480. // wrong. Return STATUS_INVALID_BUFFER_SIZE
  481. //
  482. break;
  483. case IOCTL_BATTERY_QUERY_INFORMATION:
  484. if (InputLen != sizeof (BATTERY_QUERY_INFORMATION)) {
  485. //
  486. // Don't check size of the output buffer since it is variable size.
  487. // This is checked in Mp.QueryInformation
  488. //
  489. // Return STATUS_INVALID_BUFFER_SIZE
  490. //
  491. break;
  492. }
  493. QueryInfo = (PBATTERY_QUERY_INFORMATION) IOBuffer;
  494. #if DEBUG
  495. inflevel = QueryInfo->InformationLevel;
  496. #endif
  497. Status = BattInfo->Mp.QueryInformation (
  498. BattInfo->Mp.Context,
  499. QueryInfo->BatteryTag,
  500. QueryInfo->InformationLevel,
  501. QueryInfo->AtRate,
  502. IOBuffer,
  503. OutputLen,
  504. &OutputLen
  505. );
  506. #if DEBUG
  507. if (inflevel == BatteryInformation) {
  508. BattInfo->FullChargedCap = ((PBATTERY_INFORMATION)IOBuffer)->FullChargedCapacity;
  509. }
  510. #endif
  511. BattPrint ((BATT_MP_DATA), ("BattC (%d): Mp.QueryInformation status = %08x, Level = %d\n",
  512. BattInfo->BattNPInfo->DeviceNum, Status, QueryInfo->InformationLevel));
  513. break;
  514. case IOCTL_BATTERY_QUERY_STATUS:
  515. //
  516. // Query status only gets here if the input or output buffer lengths are
  517. // wrong. Return STATUS_INVALID_BUFFER_SIZE
  518. //
  519. break;
  520. case IOCTL_BATTERY_SET_INFORMATION:
  521. if ((InputLen != sizeof(BATTERY_SET_INFORMATION)) || (OutputLen != 0)) {
  522. break;
  523. }
  524. SetInformation = (PBATTERY_SET_INFORMATION) IOBuffer;
  525. if (BattInfo->Mp.SetInformation != NULL) {
  526. Status = BattInfo->Mp.SetInformation (
  527. BattInfo->Mp.Context,
  528. SetInformation->BatteryTag,
  529. SetInformation->InformationLevel,
  530. SetInformation->Buffer
  531. );
  532. BattPrint ((BATT_MP_DATA), ("BattC (%d): Mp.SetInformation status = %08x, Level = %d\n",
  533. BattInfo->BattNPInfo->DeviceNum, Status, SetInformation->InformationLevel));
  534. } else {
  535. Status = STATUS_NOT_SUPPORTED;
  536. }
  537. break;
  538. default:
  539. Status = STATUS_NOT_IMPLEMENTED;
  540. break;
  541. }
  542. BattCMiniportStatus (BattInfo, Status);
  543. Irp->IoStatus.Status = Status;
  544. Irp->IoStatus.Information = OutputLen;
  545. IoCompleteRequest (Irp, IO_NO_INCREMENT);
  546. }
  547. VOID
  548. BattCCheckStatusQueue (
  549. IN PBATT_NP_INFO BattNPInfo,
  550. IN PBATT_INFO BattInfo
  551. )
  552. /*++
  553. Routine Description:
  554. Gets the batteries current status, and checks the pending
  555. status queue for possible IRP completion. Resets the miniport
  556. notification settings if needed.
  557. N.B. Must be invoked from the non-rentrant worker thread.
  558. BattNPInfo->CheckStatus must be non-zero.
  559. Arguments:
  560. BattNPInfo - Battery
  561. BattInfo - Battery
  562. Return Value:
  563. None
  564. --*/
  565. {
  566. PLIST_ENTRY Entry;
  567. PBATTERY_WAIT_STATUS BatteryWaitStatus;
  568. PIRP Irp;
  569. PIO_STACK_LOCATION IrpSp, IrpNextSp;
  570. BATTERY_NOTIFY Notify;
  571. LARGE_INTEGER NextTime;
  572. LARGE_INTEGER CurrentTime;
  573. LARGE_INTEGER li;
  574. ULONG TimeIncrement;
  575. BOOLEAN ReturnCurrentStatus;
  576. NTSTATUS Status;
  577. BOOLEAN StatusNotified;
  578. BattPrint ((BATT_TRACE), ("BattC (%d): BattCCheckStatusQueue called\n", BattInfo->BattNPInfo->DeviceNum));
  579. PAGED_CODE();
  580. TimeIncrement = KeQueryTimeIncrement();
  581. //
  582. // Loop while status needs checked, check pending status IRPs
  583. //
  584. while (InterlockedExchange(&BattNPInfo->CheckStatus, 0)) {
  585. Notify.PowerState = BattInfo->Status.PowerState;
  586. Notify.LowCapacity = 0;
  587. Notify.HighCapacity = (ULONG) -1;
  588. //
  589. // Set to recheck no later than MIN_STATUS_POLL_RATE (3 min) from now.
  590. //
  591. NextTime.QuadPart = MIN_STATUS_POLL_RATE;
  592. //
  593. // If the StatusQueue is empty, the status doesn't need to be read
  594. // at this time. BattNPInfo->StatusNotified is not modified
  595. // so the next time an IRP comes through, we'll re-read the status.
  596. // The local value of StatusNotified needs to be set correctly to
  597. // disable notifications if necessary.
  598. //
  599. if (IsListEmpty (&BattInfo->StatusQueue)) {
  600. StatusNotified = (BOOLEAN)BattNPInfo->StatusNotified;
  601. break;
  602. }
  603. StatusNotified = FALSE;
  604. //
  605. // Pickup status notified flag
  606. //
  607. if (BattNPInfo->StatusNotified) {
  608. InterlockedExchange (&BattNPInfo->StatusNotified, 0);
  609. StatusNotified = TRUE;
  610. // Reset the invalid data retry count when we get a notification.
  611. #if DEBUG
  612. if (BattInfo->InvalidRetryCount != 0) {
  613. BattPrint (BATT_DEBUG, ("BattC (%d) Reset InvalidRetryCount\n", BattNPInfo->DeviceNum));
  614. }
  615. #endif
  616. BattInfo->InvalidRetryCount = 0;
  617. }
  618. KeQueryTickCount (&CurrentTime);
  619. CurrentTime.QuadPart = CurrentTime.QuadPart * TimeIncrement;
  620. if (StatusNotified ||
  621. CurrentTime.QuadPart - BattInfo->StatusTime > STATUS_VALID_TIME) {
  622. //
  623. // Get the batteries current status
  624. //
  625. Status = BattInfo->Mp.QueryStatus (
  626. BattInfo->Mp.Context,
  627. BattInfo->Tag,
  628. &BattInfo->Status
  629. );
  630. if (!NT_SUCCESS(Status)) {
  631. //
  632. // Battery status is not valid, complete all pending status irps
  633. //
  634. BattPrint ((BATT_MP_ERROR), ("BattC (%d) CheckStatus: Status read err = %x\n", BattNPInfo->DeviceNum, Status));
  635. BattCCompleteIrpQueue (&(BattInfo->StatusQueue), Status);
  636. break;
  637. }
  638. BattPrint ((BATT_MP_DATA), ("BattC (%d) MP.QueryStatus: st[%08X] Cap[%08X] V[%08x] R[%08x]\n",
  639. BattNPInfo->DeviceNum,
  640. BattInfo->Status.PowerState,
  641. BattInfo->Status.Capacity,
  642. BattInfo->Status.Voltage,
  643. BattInfo->Status.Rate
  644. ));
  645. Notify.PowerState = BattInfo->Status.PowerState;
  646. //
  647. // Get the current time to compute timeouts on status query requests
  648. //
  649. KeQueryTickCount (&CurrentTime);
  650. CurrentTime.QuadPart = CurrentTime.QuadPart * TimeIncrement;
  651. BattInfo->StatusTime = CurrentTime.QuadPart;
  652. }
  653. //
  654. // Check each pending Status IRP
  655. //
  656. BattPrint ((BATT_IOCTL_QUEUE), ("BattC (%d) Processing StatusQueue\n", BattNPInfo->DeviceNum));
  657. Entry = BattInfo->StatusQueue.Flink;
  658. while (Entry != &BattInfo->StatusQueue) {
  659. //
  660. // Get IRP to check
  661. //
  662. Irp = CONTAINING_RECORD (
  663. Entry,
  664. IRP,
  665. Tail.Overlay.ListEntry
  666. );
  667. IrpSp = IoGetCurrentIrpStackLocation(Irp);
  668. IrpNextSp = IoGetNextIrpStackLocation(Irp);
  669. BatteryWaitStatus = (PBATTERY_WAIT_STATUS) Irp->AssociatedIrp.SystemBuffer;
  670. #if DEBUG
  671. if (BattInfo->FullChargedCap == 0) {
  672. BattInfo->FullChargedCap = 1000;
  673. }
  674. #endif
  675. BattPrint ((BATT_IOCTL_QUEUE), ("BattC (%d) StatusQueue: 0x%08x=%d -- 0x%08x=%d time=%08x, st=%08x\n",
  676. BattNPInfo->DeviceNum,
  677. BatteryWaitStatus->HighCapacity, (ULONG) (((LONGLONG) BatteryWaitStatus->HighCapacity * 1000) / BattInfo->FullChargedCap),
  678. BatteryWaitStatus->LowCapacity, (ULONG) (((LONGLONG) BatteryWaitStatus->LowCapacity * 1000) / BattInfo->FullChargedCap),
  679. BatteryWaitStatus->Timeout,
  680. BatteryWaitStatus->PowerState));
  681. //
  682. // Get next request
  683. //
  684. Entry = Entry->Flink;
  685. //
  686. // If status is in error, or tag no longer matches abort the
  687. // request accordingly
  688. //
  689. if (BattInfo->Tag != BatteryWaitStatus->BatteryTag) {
  690. Irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
  691. }
  692. //
  693. // If IRP is flagged as cancelled, complete it
  694. //
  695. if (Irp->Cancel) {
  696. Irp->IoStatus.Status = STATUS_CANCELLED;
  697. }
  698. //
  699. // If request is still pending, check it
  700. //
  701. if (Irp->IoStatus.Status == STATUS_PENDING) {
  702. ReturnCurrentStatus = FALSE;
  703. if (BattInfo->Status.PowerState != BatteryWaitStatus->PowerState ||
  704. BattInfo->Status.Capacity < BatteryWaitStatus->LowCapacity ||
  705. BattInfo->Status.Capacity > BatteryWaitStatus->HighCapacity) {
  706. BattPrint((BATT_IOCTL_DATA), ("BattC (%d) CheckStatusQueue, Returning Current Status, Asked For:\n"
  707. "----------- Irp.PowerState = %x\n"
  708. "----------- Irp.LowCapacity = %x\n"
  709. "----------- Irp.HighCapacity = %x\n"
  710. "----------- BattInfo.PowerState = %x\n"
  711. "----------- BattInfo.Capacity = %x\n",
  712. BattNPInfo->DeviceNum,
  713. BatteryWaitStatus->PowerState,
  714. BatteryWaitStatus->LowCapacity,
  715. BatteryWaitStatus->HighCapacity,
  716. BattInfo->Status.PowerState,
  717. BattInfo->Status.Capacity)
  718. );
  719. //
  720. // Complete this IRP with the current status
  721. //
  722. ReturnCurrentStatus = TRUE;
  723. } else {
  724. //
  725. // Compute time when the request expires
  726. //
  727. BattPrint ((BATT_IOCTL_DATA), ("BattC (%d) CheckStatusQueue: Status Request %x Waiting For:\n"
  728. "----------- Timeout = %x\n"
  729. "----------- Irp.PowerState = %x\n"
  730. "----------- Irp.LowCapacity = %x\n"
  731. "----------- Irp.HighCapacity = %x\n",
  732. BattNPInfo->DeviceNum,
  733. Irp,
  734. BatteryWaitStatus->Timeout,
  735. BatteryWaitStatus->PowerState,
  736. BatteryWaitStatus->LowCapacity,
  737. BatteryWaitStatus->HighCapacity)
  738. );
  739. if (BatteryWaitStatus->Timeout &&
  740. IrpNextSp->Parameters.Others.Argument1 == NULL &&
  741. IrpNextSp->Parameters.Others.Argument2 == NULL) {
  742. // initialize it
  743. li.QuadPart = CurrentTime.QuadPart +
  744. ((ULONGLONG) BatteryWaitStatus->Timeout * NTMS);
  745. IrpNextSp->Parameters.Others.Argument1 = (PVOID)((ULONG_PTR)li.LowPart);
  746. IrpNextSp->Parameters.Others.Argument2 = (PVOID)((ULONG_PTR)li.HighPart);
  747. }
  748. li.LowPart = (ULONG)((ULONG_PTR)IrpNextSp->Parameters.Others.Argument1);
  749. li.HighPart = (ULONG)((ULONG_PTR)IrpNextSp->Parameters.Others.Argument2);
  750. li.QuadPart -= CurrentTime.QuadPart;
  751. if (li.QuadPart <= 0) {
  752. //
  753. // Time's up, complete it
  754. //
  755. ReturnCurrentStatus = TRUE;
  756. } else {
  757. //
  758. // If waiting forever, no need to set a timer
  759. //
  760. if (BatteryWaitStatus->Timeout != 0xFFFFFFFF) {
  761. //
  762. // Check if this will be the next timeout time -- we will use
  763. // the minimum timeout of the pending requests.
  764. //
  765. if (li.QuadPart < NextTime.QuadPart) {
  766. NextTime.QuadPart = li.QuadPart;
  767. }
  768. }
  769. }
  770. }
  771. if (!ReturnCurrentStatus) {
  772. //
  773. // IRP is still pending, calculate LCD of all waiting IRPs
  774. //
  775. if (BatteryWaitStatus->LowCapacity > Notify.LowCapacity) {
  776. Notify.LowCapacity = BatteryWaitStatus->LowCapacity;
  777. }
  778. if (BatteryWaitStatus->HighCapacity < Notify.HighCapacity) {
  779. Notify.HighCapacity = BatteryWaitStatus->HighCapacity;
  780. }
  781. } else {
  782. //
  783. // Return current battery status
  784. //
  785. Irp->IoStatus.Status = STATUS_SUCCESS;
  786. Irp->IoStatus.Information = sizeof(BattInfo->Status);
  787. RtlCopyMemory (
  788. Irp->AssociatedIrp.SystemBuffer,
  789. &BattInfo->Status,
  790. sizeof(BattInfo->Status)
  791. );
  792. }
  793. }
  794. //
  795. // If this request is no longer pending, complete it
  796. //
  797. if (Irp->IoStatus.Status != STATUS_PENDING) {
  798. BattPrint (BATT_IOCTL,
  799. ("BattC (%d): completing QueryStatus irp - %x, status - %x\n",
  800. BattNPInfo->DeviceNum,
  801. Irp,
  802. Irp->IoStatus.Status));
  803. RemoveEntryList (&Irp->Tail.Overlay.ListEntry);
  804. IoSetCancelRoutine (Irp, NULL);
  805. IoCompleteRequest (Irp, IO_NO_INCREMENT);
  806. }
  807. }
  808. }
  809. //
  810. // Status check complete
  811. //
  812. if (IsListEmpty (&BattInfo->StatusQueue)) {
  813. //
  814. // Nothing pending, if being notified disable the notifications
  815. //
  816. if (StatusNotified) {
  817. BattInfo->Mp.DisableStatusNotify (BattInfo->Mp.Context);
  818. BattInfo->StatusTime = 0;
  819. BattPrint ((BATT_MP_DATA), ("BattC (%d) CheckStatus: called Mp.DisableStatusNotify\n", BattNPInfo->DeviceNum));
  820. }
  821. } else {
  822. //
  823. // Set notification setting
  824. //
  825. Status = BattInfo->Mp.SetStatusNotify (
  826. BattInfo->Mp.Context,
  827. BattInfo->Tag,
  828. &Notify
  829. );
  830. if (NT_SUCCESS(Status)) {
  831. //
  832. // New notification set, remember it
  833. //
  834. BattPrint (BATT_MP_DATA, ("BattC (%d) Mp.SetStatusNotify: Notify set for: State=%x, Low=%x, High=%x\n",
  835. BattNPInfo->DeviceNum,
  836. Notify.PowerState,
  837. Notify.LowCapacity,
  838. Notify.HighCapacity
  839. ));
  840. } else {
  841. //
  842. // Could not set notification, handle error
  843. //
  844. BattPrint (BATT_MP_ERROR, ("BattC (%d) Mp.SetStatusNotify: failed (%x), will poll\n", BattNPInfo->DeviceNum, Status));
  845. BattCMiniportStatus (BattInfo, Status);
  846. //
  847. // Compute poll time
  848. //
  849. li.QuadPart = MIN_STATUS_POLL_RATE;
  850. if (BattInfo->Status.Capacity == BATTERY_UNKNOWN_CAPACITY) {
  851. // Retry 10 times at a polling rate of 1 second.
  852. // Then revert to the slow polling rate.
  853. if (BattInfo->InvalidRetryCount < INVALID_DATA_MAX_RETRY) {
  854. BattInfo->InvalidRetryCount++;
  855. li.QuadPart = INVALID_DATA_POLL_RATE;
  856. BattPrint (BATT_DEBUG, ("BattC (%d) InvalidRetryCount = %d\n",
  857. BattNPInfo->DeviceNum, BattInfo->InvalidRetryCount));
  858. } else {
  859. BattPrint (BATT_DEBUG, ("BattC (%d) InvalidRetryCount = %d. Using slow polling rate.\n",
  860. BattNPInfo->DeviceNum, BattInfo->InvalidRetryCount));
  861. li.QuadPart = MIN_STATUS_POLL_RATE;
  862. }
  863. } else if ((BattInfo->Status.Rate != 0) && (BattInfo->Status.Rate != BATTERY_UNKNOWN_RATE)) {
  864. if (BattInfo->Status.Rate > 0) {
  865. li.QuadPart = Notify.HighCapacity - BattInfo->Status.Capacity;
  866. } else if (BattInfo->Status.Rate < 0) {
  867. li.QuadPart = Notify.LowCapacity - BattInfo->Status.Capacity;
  868. }
  869. // convert to 3/4 its target time
  870. li.QuadPart = li.QuadPart * ((ULONGLONG) NTMIN * 45);
  871. li.QuadPart = li.QuadPart / (LONGLONG)(BattInfo->Status.Rate);
  872. //
  873. // Bound it
  874. //
  875. if (li.QuadPart > MIN_STATUS_POLL_RATE) {
  876. // poll at least this fast
  877. li.QuadPart = MIN_STATUS_POLL_RATE;
  878. } else if (li.QuadPart < MAX_STATUS_POLL_RATE) {
  879. // but not faster then this
  880. li.QuadPart = MAX_STATUS_POLL_RATE;
  881. }
  882. }
  883. //
  884. // If sooner then NextTime, adjust NextTime
  885. //
  886. if (li.QuadPart < NextTime.QuadPart) {
  887. NextTime.QuadPart = li.QuadPart;
  888. }
  889. }
  890. //
  891. // If there's a NextTime, queue the timer to recheck
  892. //
  893. if (NextTime.QuadPart) {
  894. NextTime.QuadPart = -NextTime.QuadPart;
  895. //
  896. // Acquire a remove lock.
  897. //
  898. InterlockedIncrement (&BattNPInfo->InUseCount);
  899. BattPrint ((BATT_LOCK), ("BattCCheckStatusQueue: Aqcuired remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount));
  900. if (BattNPInfo->WantToRemove == TRUE) {
  901. //
  902. // If BatteryClassUnload is waiting to remove the device:
  903. // Don't set the timer.
  904. // Release the remove lock just acquired.
  905. // No need to notify BatteryclassUnload because
  906. // at this point there is at least one other lock held.
  907. //
  908. InterlockedDecrement(&BattNPInfo->InUseCount);
  909. BattPrint (BATT_NOTE,
  910. ("BattC (%d) CheckStatus: Poll cancel because of device removal.\n",
  911. BattNPInfo->DeviceNum));
  912. BattPrint ((BATT_LOCK), ("BattCCheckStatusQueue: Released remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount));
  913. } else {
  914. if (KeSetTimer (&BattNPInfo->WorkerTimer, NextTime, &BattNPInfo->WorkerDpc)) {
  915. //
  916. // If the timer was already set, we need to release a remove lock since
  917. // there was already one aquired the last time this timer was set.
  918. //
  919. InterlockedDecrement(&BattNPInfo->InUseCount);
  920. BattPrint ((BATT_LOCK), ("BattCCheckStatusQueue: Released extra remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount));
  921. }
  922. #if DEBUG
  923. NextTime.QuadPart = (-NextTime.QuadPart) / (ULONGLONG) NTSEC;
  924. BattPrint (BATT_NOTE, ("BattC (%d) CheckStatus: Poll in %d seconds (%d minutes)\n",
  925. BattNPInfo->DeviceNum, NextTime.LowPart, NextTime.LowPart/60));
  926. #endif
  927. }
  928. } else {
  929. //
  930. // There should always be a NextTime.
  931. //
  932. ASSERT(FALSE);
  933. }
  934. } // if (IsListEmpty (&BattInfo->StatusQueue)) {...} else
  935. }
  936. VOID
  937. BattCCheckTagQueue (
  938. IN PBATT_NP_INFO BattNPInfo,
  939. IN PBATT_INFO BattInfo
  940. )
  941. /*++
  942. Routine Description:
  943. Gets the batteries current tag, and checks the pending
  944. tag queue for possible IRP completion. Resets the miniport
  945. notification settings if needed.
  946. N.B. must be invoked from the non-reentrant worker thread
  947. Arguments:
  948. BattNPInfo - Battery
  949. BattInfo - Battery
  950. Return Value:
  951. None
  952. --*/
  953. {
  954. PLIST_ENTRY Entry;
  955. PIRP Irp;
  956. PIO_STACK_LOCATION IrpSp, IrpNextSp;
  957. LARGE_INTEGER NextTime;
  958. LARGE_INTEGER CurrentTime;
  959. LARGE_INTEGER li;
  960. ULONG TimeIncrement;
  961. BOOLEAN ReturnCurrentStatus;
  962. NTSTATUS Status;
  963. ULONG batteryTimeout;
  964. BOOLEAN TagNotified;
  965. ULONG tmpTag = BATTERY_TAG_INVALID;
  966. BattPrint ((BATT_TRACE), ("BattC (%d): BattCCheckTagQueue called\n", BattInfo->BattNPInfo->DeviceNum));
  967. PAGED_CODE();
  968. TimeIncrement = KeQueryTimeIncrement();
  969. //
  970. // Loop while tag needs checked, check pending tag IRPs
  971. //
  972. while (InterlockedExchange(&BattNPInfo->CheckTag, 0)) {
  973. NextTime.QuadPart = 0;
  974. //
  975. // If the Tag Queue is empty, done
  976. // but we need to make sure that we leave TagNotified set to TRUE
  977. // so the next time an IRP comes through,we'll re-read the tag.
  978. //
  979. if (IsListEmpty (&BattInfo->TagQueue)) {
  980. break;
  981. }
  982. TagNotified = FALSE;
  983. //
  984. // Pickup tag notified flag
  985. //
  986. if (BattNPInfo->TagNotified) {
  987. InterlockedExchange (&BattNPInfo->TagNotified, 0);
  988. TagNotified = TRUE;
  989. }
  990. KeQueryTickCount (&CurrentTime);
  991. CurrentTime.QuadPart = CurrentTime.QuadPart * TimeIncrement;
  992. if (TagNotified ||
  993. CurrentTime.QuadPart - BattInfo->TagTime > STATUS_VALID_TIME) {
  994. //
  995. // Get the battery's current tag
  996. //
  997. tmpTag = 0;
  998. Status = BattInfo->Mp.QueryTag (
  999. BattInfo->Mp.Context,
  1000. &tmpTag
  1001. );
  1002. if (!NT_SUCCESS(Status) && (Status != STATUS_NO_SUCH_DEVICE)) {
  1003. //
  1004. // Something went wrong, complete all pending tag irps
  1005. //
  1006. BattPrint (BATT_MP_ERROR, ("BattC (%d) CheckTag: Tag read err = %x\n", BattNPInfo->DeviceNum, Status));
  1007. BattCMiniportStatus (BattInfo, Status);
  1008. break;
  1009. }
  1010. BattPrint (BATT_MP_DATA, ("BattC (%d) MP.QueryTag: Status = %08x, Tag = %08x\n",
  1011. BattNPInfo->DeviceNum, Status, tmpTag));
  1012. if (Status == STATUS_NO_SUCH_DEVICE) {
  1013. //
  1014. // Get the current time to compute timeouts on tag query requests
  1015. //
  1016. KeQueryTickCount (&CurrentTime);
  1017. CurrentTime.QuadPart = CurrentTime.QuadPart * TimeIncrement;
  1018. BattInfo->TagTime = CurrentTime.QuadPart;
  1019. }
  1020. }
  1021. //
  1022. // Check each pending Tag IRP
  1023. //
  1024. Entry = BattInfo->TagQueue.Flink;
  1025. while (Entry != &BattInfo->TagQueue) {
  1026. //
  1027. // Get IRP to check
  1028. //
  1029. Irp = CONTAINING_RECORD (
  1030. Entry,
  1031. IRP,
  1032. Tail.Overlay.ListEntry
  1033. );
  1034. IrpSp = IoGetCurrentIrpStackLocation(Irp);
  1035. IrpNextSp = IoGetNextIrpStackLocation(Irp);
  1036. if (IrpSp->Parameters.DeviceIoControl.InputBufferLength == 0) {
  1037. //
  1038. // If no input was given, then use timeout of 0.
  1039. //
  1040. batteryTimeout = 0;
  1041. } else {
  1042. batteryTimeout = *((PULONG) Irp->AssociatedIrp.SystemBuffer);
  1043. }
  1044. //
  1045. // Get next request
  1046. //
  1047. Entry = Entry->Flink;
  1048. //
  1049. // If IRP is flagged as cancelled, complete it
  1050. //
  1051. if (Irp->Cancel) {
  1052. BattPrint (BATT_IOCTL, ("BattC (%d): QueryTag irp cancelled - %x\n", BattNPInfo->DeviceNum, Irp));
  1053. Irp->IoStatus.Status = STATUS_CANCELLED;
  1054. }
  1055. //
  1056. // If request is still pending, check it
  1057. //
  1058. if (Irp->IoStatus.Status == STATUS_PENDING) {
  1059. ReturnCurrentStatus = FALSE;
  1060. if (tmpTag != BATTERY_TAG_INVALID) {
  1061. //
  1062. // Complete this IRP with the current tag
  1063. //
  1064. ReturnCurrentStatus = TRUE;
  1065. Irp->IoStatus.Status = STATUS_SUCCESS;
  1066. } else {
  1067. //
  1068. // Compute time when the request expires, the battery tag
  1069. // is an input parameter that holds the timeout.
  1070. //
  1071. if (batteryTimeout &&
  1072. IrpNextSp->Parameters.Others.Argument1 == NULL &&
  1073. IrpNextSp->Parameters.Others.Argument2 == NULL) {
  1074. // initialize it
  1075. li.QuadPart = CurrentTime.QuadPart + ((ULONGLONG) batteryTimeout * NTMS);
  1076. IrpNextSp->Parameters.Others.Argument1 = (PVOID)((ULONG_PTR)li.LowPart);
  1077. IrpNextSp->Parameters.Others.Argument2 = (PVOID)((ULONG_PTR)li.HighPart);
  1078. }
  1079. li.LowPart = (ULONG)((ULONG_PTR)IrpNextSp->Parameters.Others.Argument1);
  1080. li.HighPart = (ULONG)((ULONG_PTR)IrpNextSp->Parameters.Others.Argument2);
  1081. li.QuadPart -= CurrentTime.QuadPart;
  1082. if (li.QuadPart <= 0) {
  1083. //
  1084. // Time's up, complete it
  1085. //
  1086. BattPrint ((BATT_NOTE | BATT_IOCTL), ("BattC (%d): QueryTag irp timeout - %x\n", BattNPInfo->DeviceNum, Irp));
  1087. ReturnCurrentStatus = TRUE;
  1088. Irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
  1089. } else {
  1090. //
  1091. // If waiting forever, no need to set a timer
  1092. //
  1093. if (batteryTimeout != 0xFFFFFFFF) {
  1094. //
  1095. // Check if this is the next timeout time
  1096. //
  1097. if (NextTime.QuadPart == 0 || li.QuadPart < NextTime.QuadPart) {
  1098. NextTime.QuadPart = li.QuadPart;
  1099. }
  1100. }
  1101. }
  1102. }
  1103. if (ReturnCurrentStatus) {
  1104. //
  1105. // Return current battery status
  1106. //
  1107. *((PULONG) Irp->AssociatedIrp.SystemBuffer) = tmpTag;
  1108. Irp->IoStatus.Information = sizeof(ULONG);
  1109. if (BattInfo->Tag != tmpTag) {
  1110. //
  1111. // This is a new battery tag, capture tag
  1112. //
  1113. BattInfo->Tag = tmpTag;
  1114. }
  1115. }
  1116. }
  1117. //
  1118. // If this request is no longer pending, complete it
  1119. //
  1120. if (Irp->IoStatus.Status != STATUS_PENDING) {
  1121. RemoveEntryList (&Irp->Tail.Overlay.ListEntry);
  1122. IoSetCancelRoutine (Irp, NULL);
  1123. BattPrint (
  1124. (BATT_IOCTL),
  1125. ("BattC (%d): CheckTag completing request, IRP = %x, status = %x\n",
  1126. BattNPInfo->DeviceNum,
  1127. Irp,
  1128. Irp->IoStatus.Status)
  1129. );
  1130. IoCompleteRequest (Irp, IO_NO_INCREMENT);
  1131. }
  1132. }
  1133. }
  1134. //
  1135. // If there's a NextTime, queue the timer to recheck.
  1136. // This means there is a tag request with a timout other than 0 or -1.
  1137. //
  1138. if (NextTime.QuadPart) {
  1139. NextTime.QuadPart = -NextTime.QuadPart;
  1140. //
  1141. // Acquire a remove lock.
  1142. //
  1143. InterlockedIncrement (&BattNPInfo->InUseCount);
  1144. BattPrint ((BATT_LOCK), ("BattCCheckTagQueue: Aqcuired remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount));
  1145. if (BattNPInfo->WantToRemove == TRUE) {
  1146. //
  1147. // If BatteryClassUnload is waiting to remove the device:
  1148. // Don't set the timer.
  1149. // Release the remove lock just acquired.
  1150. // No need to notify BatteryclassUnload because
  1151. // at this point there is at least one other lock held.
  1152. //
  1153. InterlockedDecrement(&BattNPInfo->InUseCount);
  1154. BattPrint (BATT_NOTE,
  1155. ("BattC (%d) CheckTag: Poll cancel because of device removal.\n",
  1156. BattNPInfo->DeviceNum));
  1157. BattPrint ((BATT_LOCK), ("BattCCheckTagQueue: Released remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount));
  1158. } else {
  1159. if (KeSetTimer (&BattNPInfo->TagTimer, NextTime, &BattNPInfo->TagDpc)){
  1160. //
  1161. // If the timer was already set, we need to release a remove lock since
  1162. // there was already one aquired the last time this timer was set.
  1163. //
  1164. InterlockedDecrement(&BattNPInfo->InUseCount);
  1165. BattPrint ((BATT_LOCK), ("BattCCheckTagQueue: Released extra remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount));
  1166. }
  1167. #if DEBUG
  1168. NextTime.QuadPart = NextTime.QuadPart / -NTSEC;
  1169. BattPrint (BATT_NOTE, ("BattC (%d) CheckTag: Poll in %x seconds\n", BattNPInfo->DeviceNum, NextTime.LowPart));
  1170. #endif
  1171. }
  1172. }
  1173. }
  1174. VOID
  1175. BattCWmi (
  1176. IN PBATT_NP_INFO BattNPInfo,
  1177. IN PBATT_INFO BattInfo,
  1178. IN PBATT_WMI_REQUEST WmiRequest
  1179. )
  1180. /*++
  1181. Routine Description:
  1182. Processes a single WMI request.
  1183. N.B. must be invoked from the non-reentrant worker thread
  1184. Arguments:
  1185. BattNPInfo - Battery
  1186. BattInfo - Battery
  1187. WmiRequest - Wmi Request to process
  1188. Return Value:
  1189. None
  1190. --*/
  1191. {
  1192. NTSTATUS status = STATUS_SUCCESS;
  1193. ULONG size = 0;
  1194. ULONG OutputLen;
  1195. BATTERY_INFORMATION batteryInformation;
  1196. PWCHAR tempString;
  1197. BattPrint((BATT_WMI), ("BattCWmi (%d): GuidIndex = 0x%x\n",
  1198. BattNPInfo->DeviceNum, WmiRequest->GuidIndex));
  1199. switch (WmiRequest->GuidIndex) {
  1200. case BattWmiStatusId:
  1201. size = sizeof (BATTERY_WMI_STATUS);
  1202. ((PBATTERY_WMI_STATUS) WmiRequest->Buffer)->Tag = BattInfo->Tag;
  1203. ((PBATTERY_WMI_STATUS) WmiRequest->Buffer)->RemainingCapacity = BattInfo->Status.Capacity;
  1204. if (BattInfo->Status.Rate < 0) {
  1205. ((PBATTERY_WMI_STATUS) WmiRequest->Buffer)->ChargeRate = 0;
  1206. ((PBATTERY_WMI_STATUS) WmiRequest->Buffer)->DischargeRate = -BattInfo->Status.Rate;
  1207. } else {
  1208. ((PBATTERY_WMI_STATUS) WmiRequest->Buffer)->ChargeRate = BattInfo->Status.Rate;
  1209. ((PBATTERY_WMI_STATUS) WmiRequest->Buffer)->DischargeRate = 0;
  1210. }
  1211. ((PBATTERY_WMI_STATUS) WmiRequest->Buffer)->Voltage = BattInfo->Status.Voltage;
  1212. ((PBATTERY_WMI_STATUS) WmiRequest->Buffer)->PowerOnline =
  1213. (BattInfo->Status.PowerState & BATTERY_POWER_ON_LINE) ? TRUE : FALSE;
  1214. ((PBATTERY_WMI_STATUS) WmiRequest->Buffer)->Charging =
  1215. (BattInfo->Status.PowerState & BATTERY_CHARGING) ? TRUE : FALSE;
  1216. ((PBATTERY_WMI_STATUS) WmiRequest->Buffer)->Discharging =
  1217. (BattInfo->Status.PowerState & BATTERY_DISCHARGING) ? TRUE : FALSE;
  1218. ((PBATTERY_WMI_STATUS) WmiRequest->Buffer)->Critical =
  1219. (BattInfo->Status.PowerState & BATTERY_CRITICAL) ? TRUE : FALSE;
  1220. BattPrint((BATT_WMI), ("BattCWmi (%d): BatteryStatus\n",
  1221. BattNPInfo->DeviceNum));
  1222. break;
  1223. case BattWmiRuntimeId:
  1224. size = sizeof (BATTERY_WMI_RUNTIME);
  1225. ((PBATTERY_WMI_RUNTIME) WmiRequest->Buffer)->Tag = BattInfo->Tag;
  1226. status = BattInfo->Mp.QueryInformation (
  1227. BattInfo->Mp.Context,
  1228. BattInfo->Tag,
  1229. BatteryEstimatedTime,
  1230. 0,
  1231. &((PBATTERY_WMI_RUNTIME) WmiRequest->Buffer)->EstimatedRuntime,
  1232. sizeof(ULONG),
  1233. &OutputLen
  1234. );
  1235. BattPrint((BATT_WMI), ("BattCWmi (%d): EstimateRuntime = %08x, Status = 0x%08x\n",
  1236. BattNPInfo->DeviceNum, &((PBATTERY_WMI_RUNTIME) WmiRequest->Buffer)->EstimatedRuntime, status));
  1237. break;
  1238. case BattWmiTemperatureId:
  1239. size = sizeof (BATTERY_WMI_TEMPERATURE);
  1240. ((PBATTERY_WMI_TEMPERATURE) WmiRequest->Buffer)->Tag = BattInfo->Tag;
  1241. status = BattInfo->Mp.QueryInformation (
  1242. BattInfo->Mp.Context,
  1243. BattInfo->Tag,
  1244. BatteryTemperature,
  1245. 0,
  1246. &((PBATTERY_WMI_TEMPERATURE) WmiRequest->Buffer)->Temperature,
  1247. sizeof(ULONG),
  1248. &OutputLen
  1249. );
  1250. BattPrint((BATT_WMI), ("BattCWmi (%d): Temperature = %08x, Status = 0x%08x\n",
  1251. BattNPInfo->DeviceNum, &((PBATTERY_WMI_TEMPERATURE) WmiRequest->Buffer)->Temperature, status));
  1252. break;
  1253. case BattWmiFullChargedCapacityId:
  1254. size = sizeof (BATTERY_WMI_FULL_CHARGED_CAPACITY);
  1255. ((PBATTERY_WMI_FULL_CHARGED_CAPACITY) WmiRequest->Buffer)->Tag = BattInfo->Tag;
  1256. status = BattInfo->Mp.QueryInformation (
  1257. BattInfo->Mp.Context,
  1258. BattInfo->Tag,
  1259. BatteryInformation,
  1260. 0,
  1261. &batteryInformation,
  1262. sizeof(BATTERY_INFORMATION),
  1263. &OutputLen
  1264. );
  1265. ((PBATTERY_WMI_FULL_CHARGED_CAPACITY) WmiRequest->Buffer)->FullChargedCapacity =
  1266. batteryInformation.FullChargedCapacity;
  1267. BattPrint((BATT_WMI), ("BattCWmi (%d): FullChargedCapacity = %08x, Status = 0x%08x\n",
  1268. BattNPInfo->DeviceNum, ((PBATTERY_WMI_FULL_CHARGED_CAPACITY) WmiRequest->Buffer)->FullChargedCapacity, status));
  1269. break;
  1270. case BattWmiCycleCountId:
  1271. size = sizeof (BATTERY_WMI_CYCLE_COUNT);
  1272. ((PBATTERY_WMI_CYCLE_COUNT) WmiRequest->Buffer)->Tag = BattInfo->Tag;
  1273. status = BattInfo->Mp.QueryInformation (
  1274. BattInfo->Mp.Context,
  1275. BattInfo->Tag,
  1276. BatteryInformation,
  1277. 0,
  1278. &batteryInformation,
  1279. sizeof(BATTERY_INFORMATION),
  1280. &OutputLen
  1281. );
  1282. ((PBATTERY_WMI_CYCLE_COUNT) WmiRequest->Buffer)->CycleCount =
  1283. batteryInformation.CycleCount;
  1284. BattPrint((BATT_WMI), ("BattCWmi (%d): CycleCount = %08x, Status = 0x%08x\n",
  1285. BattNPInfo->DeviceNum, ((PBATTERY_WMI_CYCLE_COUNT) WmiRequest->Buffer)->CycleCount, status));
  1286. break;
  1287. case BattWmiStaticDataId:
  1288. size = sizeof(BATTERY_WMI_STATIC_DATA)+4*MAX_BATTERY_STRING_SIZE*sizeof(WCHAR);
  1289. ((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->Tag = BattInfo->Tag;
  1290. // ((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->ManufacturerDate[0] =
  1291. // ((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->Granularity =
  1292. status = BattInfo->Mp.QueryInformation (
  1293. BattInfo->Mp.Context,
  1294. BattInfo->Tag,
  1295. BatteryInformation,
  1296. 0,
  1297. &batteryInformation,
  1298. sizeof(BATTERY_INFORMATION),
  1299. &OutputLen
  1300. );
  1301. if (NT_SUCCESS(status)) {
  1302. ((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->Capabilities =
  1303. batteryInformation.Capabilities;
  1304. ((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->Technology =
  1305. batteryInformation.Technology;
  1306. ((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->Chemistry =
  1307. *(PULONG)batteryInformation.Chemistry;
  1308. ((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->DesignedCapacity =
  1309. batteryInformation.DesignedCapacity;
  1310. ((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->DefaultAlert1 =
  1311. batteryInformation.DefaultAlert1;
  1312. ((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->DefaultAlert2 =
  1313. batteryInformation.DefaultAlert2;
  1314. ((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->CriticalBias =
  1315. batteryInformation.CriticalBias;
  1316. tempString = ((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->Strings;
  1317. status = BattInfo->Mp.QueryInformation (
  1318. BattInfo->Mp.Context,
  1319. BattInfo->Tag,
  1320. BatteryDeviceName,
  1321. 0,
  1322. &tempString[1],
  1323. MAX_BATTERY_STRING_SIZE,
  1324. &OutputLen
  1325. );
  1326. if (!NT_SUCCESS(status)) {
  1327. // Some batteries may not support some types of Information Queries
  1328. // Don't fail request, simply leave this one blank.
  1329. OutputLen = 0;
  1330. }
  1331. tempString[0] = (USHORT) OutputLen;
  1332. tempString = (PWCHAR) ((PCHAR) &tempString[1] + tempString[0]);
  1333. status = BattInfo->Mp.QueryInformation (
  1334. BattInfo->Mp.Context,
  1335. BattInfo->Tag,
  1336. BatteryManufactureName,
  1337. 0,
  1338. &tempString[1],
  1339. MAX_BATTERY_STRING_SIZE,
  1340. &OutputLen
  1341. );
  1342. if (!NT_SUCCESS(status)) {
  1343. // Some batteries may not support some types of Information Queries
  1344. // Don't fail request, simply leave this one blank.
  1345. OutputLen = 0;
  1346. }
  1347. tempString[0] = (USHORT) OutputLen;
  1348. tempString = (PWCHAR) ((PCHAR) &tempString[1] + tempString[0]);
  1349. status = BattInfo->Mp.QueryInformation (
  1350. BattInfo->Mp.Context,
  1351. BattInfo->Tag,
  1352. BatterySerialNumber,
  1353. 0,
  1354. &tempString[1],
  1355. MAX_BATTERY_STRING_SIZE,
  1356. &OutputLen
  1357. );
  1358. if (!NT_SUCCESS(status)) {
  1359. // Some batteries may not support some types of Information Queries
  1360. // Don't fail request, simply leave this one blank.
  1361. OutputLen = 0;
  1362. }
  1363. tempString[0] = (USHORT) OutputLen;
  1364. tempString = (PWCHAR) ((PCHAR) &tempString[1] + tempString[0]);
  1365. status = BattInfo->Mp.QueryInformation (
  1366. BattInfo->Mp.Context,
  1367. BattInfo->Tag,
  1368. BatteryUniqueID,
  1369. 0,
  1370. &tempString[1],
  1371. MAX_BATTERY_STRING_SIZE,
  1372. &OutputLen
  1373. );
  1374. if (!NT_SUCCESS(status)) {
  1375. // Some batteries may not support some types of Information Queries
  1376. // Don't fail request, simply leave this one blank.
  1377. OutputLen = 0;
  1378. status = STATUS_SUCCESS;
  1379. }
  1380. tempString[0] = (USHORT) OutputLen;
  1381. tempString = (PWCHAR) ((PCHAR) &tempString[1] + tempString[0]);
  1382. size = (ULONG)(sizeof(BATTERY_WMI_STATIC_DATA)+(tempString - ((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->Strings));
  1383. }
  1384. break;
  1385. default:
  1386. status = STATUS_WMI_GUID_NOT_FOUND;
  1387. }
  1388. *WmiRequest->InstanceLengthArray = size;
  1389. status = WmiCompleteRequest(WmiRequest->DeviceObject,
  1390. WmiRequest->Irp,
  1391. status,
  1392. size,
  1393. IO_NO_INCREMENT);
  1394. }
  1395. VOID
  1396. BattCMiniportStatus (
  1397. IN PBATT_INFO BattInfo,
  1398. IN NTSTATUS Status
  1399. )
  1400. /*++
  1401. Routine Description:
  1402. Function to return status from miniport. If the battery tag has gone
  1403. invalid the pending statuses are aborted.
  1404. N.B. must be invoked from the non-rentrant worker thread
  1405. Arguments:
  1406. BattInfo - Battery
  1407. Status - Status from miniport.
  1408. Return Value:
  1409. None
  1410. --*/
  1411. {
  1412. if (NT_SUCCESS(Status)) {
  1413. return ;
  1414. }
  1415. switch (Status) {
  1416. #if DEBUG
  1417. case STATUS_SUCCESS:
  1418. case STATUS_NOT_IMPLEMENTED:
  1419. case STATUS_BUFFER_TOO_SMALL:
  1420. case STATUS_INVALID_BUFFER_SIZE:
  1421. case STATUS_NOT_SUPPORTED:
  1422. case STATUS_INVALID_PARAMETER:
  1423. case STATUS_OBJECT_NAME_NOT_FOUND:
  1424. case STATUS_INVALID_DEVICE_REQUEST:
  1425. // no action
  1426. break;
  1427. default:
  1428. BattPrint (BATT_ERROR, ("BattCMiniportStatus: unknown status from miniport: %x BattInfo %x\n",
  1429. Status, BattInfo));
  1430. break;
  1431. #endif
  1432. case STATUS_NO_SUCH_DEVICE:
  1433. //
  1434. // Our battery tag is wrong. Cancel any queued status irps
  1435. //
  1436. BattCCompleteIrpQueue (&(BattInfo->StatusQueue), Status);
  1437. break;
  1438. }
  1439. }
  1440. VOID
  1441. BattCCompleteIrpQueue (
  1442. IN PLIST_ENTRY Queue,
  1443. IN NTSTATUS Status
  1444. )
  1445. /*++
  1446. Routine Description:
  1447. Complete all pending Irps in the IoQueue, TagQueue, or StatusQueue.
  1448. N.B. must be invoked from the non-rentrant worker thread
  1449. Arguments:
  1450. BattInfo - Battery
  1451. Status - Error status to complete pending status request with
  1452. Return Value:
  1453. None
  1454. --*/
  1455. {
  1456. PLIST_ENTRY Entry;
  1457. PIRP Irp;
  1458. ASSERT (!NT_SUCCESS(Status));
  1459. BattPrint (BATT_TRACE, ("BattC: ENTERING BattCCompleteIrpQueue\n"));
  1460. while (!IsListEmpty(Queue)) {
  1461. Entry = RemoveHeadList (Queue);
  1462. Irp = CONTAINING_RECORD (
  1463. Entry,
  1464. IRP,
  1465. Tail.Overlay.ListEntry
  1466. );
  1467. //
  1468. // Use Cancel Spinlock to make sure that Completion routine isn't being called
  1469. //
  1470. IoAcquireCancelSpinLock (&Irp->CancelIrql);
  1471. IoSetCancelRoutine (Irp, NULL);
  1472. IoReleaseCancelSpinLock(Irp->CancelIrql);
  1473. BattPrint (BATT_NOTE, ("BattC: Completing IRP 0x%0lx at IRQL %d.\n", Irp, KeGetCurrentIrql()));
  1474. Irp->IoStatus.Status = Status;
  1475. IoCompleteRequest (Irp, IO_NO_INCREMENT);
  1476. }
  1477. BattPrint (BATT_TRACE, ("BattC: EXITING BattCCompleteIrpQueue\n"));
  1478. }
  1479. VOID
  1480. BattCCompleteWmiQueue (
  1481. IN PLIST_ENTRY Queue,
  1482. IN NTSTATUS Status
  1483. )
  1484. /*++
  1485. Routine Description:
  1486. Complete all pending Irps in the IoQueue, TagQueue, or StatusQueue.
  1487. N.B. must be invoked from the non-rentrant worker thread
  1488. Arguments:
  1489. BattInfo - Battery
  1490. Status - Error status to complete pending status request with
  1491. Return Value:
  1492. None
  1493. --*/
  1494. {
  1495. PLIST_ENTRY Entry;
  1496. PBATT_WMI_REQUEST WmiRequest;
  1497. ASSERT (!NT_SUCCESS(Status));
  1498. BattPrint (BATT_TRACE, ("BattC: ENTERING BattCCompleteWmiQueue\n"));
  1499. while (!IsListEmpty(Queue)) {
  1500. Entry = RemoveHeadList (Queue);
  1501. WmiRequest = CONTAINING_RECORD (
  1502. Entry,
  1503. BATT_WMI_REQUEST,
  1504. ListEntry
  1505. );
  1506. BattPrint (BATT_NOTE, ("BattC: Completing Wmi Request 0x%0lx at IRQL %d.\n", WmiRequest, KeGetCurrentIrql()));
  1507. *WmiRequest->InstanceLengthArray = 0;
  1508. WmiCompleteRequest(WmiRequest->DeviceObject,
  1509. WmiRequest->Irp,
  1510. Status,
  1511. 0,
  1512. IO_NO_INCREMENT);
  1513. }
  1514. BattPrint (BATT_TRACE, ("BattC: EXITING BattCCompleteWmiQueue\n"));
  1515. }