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.

2010 lines
63 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. // Make Sure InputLen is at least the minimum size. It may be
  523. // depending on the InformationLevel.
  524. //
  525. // This is checked in Mp.QueryInformation
  526. break;
  527. }
  528. SetInformation = (PBATTERY_SET_INFORMATION) IOBuffer;
  529. if (BattInfo->Mp.SetInformation != NULL) {
  530. Status = BattInfo->Mp.SetInformation (
  531. BattInfo->Mp.Context,
  532. SetInformation->BatteryTag,
  533. SetInformation->InformationLevel,
  534. SetInformation->Buffer
  535. );
  536. BattPrint ((BATT_MP_DATA), ("BattC (%d): Mp.SetInformation status = %08x, Level = %d\n",
  537. BattInfo->BattNPInfo->DeviceNum, Status, SetInformation->InformationLevel));
  538. } else {
  539. Status = STATUS_NOT_SUPPORTED;
  540. }
  541. break;
  542. default:
  543. Status = STATUS_NOT_IMPLEMENTED;
  544. break;
  545. }
  546. BattCMiniportStatus (BattInfo, Status);
  547. Irp->IoStatus.Status = Status;
  548. Irp->IoStatus.Information = OutputLen;
  549. IoCompleteRequest (Irp, IO_NO_INCREMENT);
  550. }
  551. VOID
  552. BattCCheckStatusQueue (
  553. IN PBATT_NP_INFO BattNPInfo,
  554. IN PBATT_INFO BattInfo
  555. )
  556. /*++
  557. Routine Description:
  558. Gets the batteries current status, and checks the pending
  559. status queue for possible IRP completion. Resets the miniport
  560. notification settings if needed.
  561. N.B. Must be invoked from the non-rentrant worker thread.
  562. BattNPInfo->CheckStatus must be non-zero.
  563. Arguments:
  564. BattNPInfo - Battery
  565. BattInfo - Battery
  566. Return Value:
  567. None
  568. --*/
  569. {
  570. PLIST_ENTRY Entry;
  571. PBATTERY_WAIT_STATUS BatteryWaitStatus;
  572. PIRP Irp;
  573. PIO_STACK_LOCATION IrpSp, IrpNextSp;
  574. BATTERY_NOTIFY Notify;
  575. LARGE_INTEGER NextTime;
  576. LARGE_INTEGER CurrentTime;
  577. LARGE_INTEGER li;
  578. ULONG TimeIncrement;
  579. BOOLEAN ReturnCurrentStatus;
  580. NTSTATUS Status;
  581. BOOLEAN StatusNotified = FALSE;
  582. BattPrint ((BATT_TRACE), ("BattC (%d): BattCCheckStatusQueue called\n", BattInfo->BattNPInfo->DeviceNum));
  583. PAGED_CODE();
  584. TimeIncrement = KeQueryTimeIncrement();
  585. //
  586. // Loop while status needs checked, check pending status IRPs
  587. //
  588. while (InterlockedExchange(&BattNPInfo->CheckStatus, 0)) {
  589. Notify.PowerState = BattInfo->Status.PowerState;
  590. Notify.LowCapacity = 0;
  591. Notify.HighCapacity = (ULONG) -1;
  592. //
  593. // Set to recheck no later than MIN_STATUS_POLL_RATE (3 min) from now.
  594. //
  595. NextTime.QuadPart = MIN_STATUS_POLL_RATE;
  596. //
  597. // If the StatusQueue is empty, the status doesn't need to be read
  598. // at this time. BattNPInfo->StatusNotified is not modified
  599. // so the next time an IRP comes through, we'll re-read the status.
  600. // The local value of StatusNotified needs to be set correctly to
  601. // disable notifications if necessary.
  602. //
  603. if (IsListEmpty (&BattInfo->StatusQueue)) {
  604. StatusNotified = (BOOLEAN)BattNPInfo->StatusNotified;
  605. break;
  606. }
  607. StatusNotified = FALSE;
  608. //
  609. // Pickup status notified flag
  610. //
  611. if (BattNPInfo->StatusNotified) {
  612. InterlockedExchange (&BattNPInfo->StatusNotified, 0);
  613. StatusNotified = TRUE;
  614. // Reset the invalid data retry count when we get a notification.
  615. #if DEBUG
  616. if (BattInfo->InvalidRetryCount != 0) {
  617. BattPrint (BATT_DEBUG, ("BattC (%d) Reset InvalidRetryCount\n", BattNPInfo->DeviceNum));
  618. }
  619. #endif
  620. BattInfo->InvalidRetryCount = 0;
  621. }
  622. KeQueryTickCount (&CurrentTime);
  623. CurrentTime.QuadPart = CurrentTime.QuadPart * TimeIncrement;
  624. if (StatusNotified ||
  625. CurrentTime.QuadPart - BattInfo->StatusTime > STATUS_VALID_TIME) {
  626. //
  627. // Get the batteries current status
  628. //
  629. Status = BattInfo->Mp.QueryStatus (
  630. BattInfo->Mp.Context,
  631. BattInfo->Tag,
  632. &BattInfo->Status
  633. );
  634. if (!NT_SUCCESS(Status)) {
  635. //
  636. // Battery status is not valid, complete all pending status irps
  637. //
  638. BattPrint ((BATT_MP_ERROR), ("BattC (%d) CheckStatus: Status read err = %x\n", BattNPInfo->DeviceNum, Status));
  639. BattCCompleteIrpQueue (&(BattInfo->StatusQueue), Status);
  640. break;
  641. }
  642. BattPrint ((BATT_MP_DATA), ("BattC (%d) MP.QueryStatus: st[%08X] Cap[%08X] V[%08x] R[%08x]\n",
  643. BattNPInfo->DeviceNum,
  644. BattInfo->Status.PowerState,
  645. BattInfo->Status.Capacity,
  646. BattInfo->Status.Voltage,
  647. BattInfo->Status.Rate
  648. ));
  649. Notify.PowerState = BattInfo->Status.PowerState;
  650. //
  651. // Get the current time to compute timeouts on status query requests
  652. //
  653. KeQueryTickCount (&CurrentTime);
  654. CurrentTime.QuadPart = CurrentTime.QuadPart * TimeIncrement;
  655. BattInfo->StatusTime = CurrentTime.QuadPart;
  656. }
  657. //
  658. // Check each pending Status IRP
  659. //
  660. BattPrint ((BATT_IOCTL_QUEUE), ("BattC (%d) Processing StatusQueue\n", BattNPInfo->DeviceNum));
  661. Entry = BattInfo->StatusQueue.Flink;
  662. while (Entry != &BattInfo->StatusQueue) {
  663. //
  664. // Get IRP to check
  665. //
  666. Irp = CONTAINING_RECORD (
  667. Entry,
  668. IRP,
  669. Tail.Overlay.ListEntry
  670. );
  671. IrpSp = IoGetCurrentIrpStackLocation(Irp);
  672. IrpNextSp = IoGetNextIrpStackLocation(Irp);
  673. BatteryWaitStatus = (PBATTERY_WAIT_STATUS) Irp->AssociatedIrp.SystemBuffer;
  674. #if DEBUG
  675. if (BattInfo->FullChargedCap == 0) {
  676. BattInfo->FullChargedCap = 1000;
  677. }
  678. #endif
  679. BattPrint ((BATT_IOCTL_QUEUE), ("BattC (%d) StatusQueue: 0x%08x=%d -- 0x%08x=%d time=%08x, st=%08x\n",
  680. BattNPInfo->DeviceNum,
  681. BatteryWaitStatus->HighCapacity, (ULONG) (((LONGLONG) BatteryWaitStatus->HighCapacity * 1000) / BattInfo->FullChargedCap),
  682. BatteryWaitStatus->LowCapacity, (ULONG) (((LONGLONG) BatteryWaitStatus->LowCapacity * 1000) / BattInfo->FullChargedCap),
  683. BatteryWaitStatus->Timeout,
  684. BatteryWaitStatus->PowerState));
  685. //
  686. // Get next request
  687. //
  688. Entry = Entry->Flink;
  689. //
  690. // If status is in error, or tag no longer matches abort the
  691. // request accordingly
  692. //
  693. if (BattInfo->Tag != BatteryWaitStatus->BatteryTag) {
  694. Irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
  695. }
  696. //
  697. // If IRP is flagged as cancelled, complete it
  698. //
  699. if (Irp->Cancel) {
  700. Irp->IoStatus.Status = STATUS_CANCELLED;
  701. }
  702. //
  703. // If request is still pending, check it
  704. //
  705. if (Irp->IoStatus.Status == STATUS_PENDING) {
  706. ReturnCurrentStatus = FALSE;
  707. if (BattInfo->Status.PowerState != BatteryWaitStatus->PowerState ||
  708. BattInfo->Status.Capacity < BatteryWaitStatus->LowCapacity ||
  709. BattInfo->Status.Capacity > BatteryWaitStatus->HighCapacity) {
  710. BattPrint((BATT_IOCTL_DATA), ("BattC (%d) CheckStatusQueue, Returning Current Status, Asked For:\n"
  711. "----------- Irp.PowerState = %x\n"
  712. "----------- Irp.LowCapacity = %x\n"
  713. "----------- Irp.HighCapacity = %x\n"
  714. "----------- BattInfo.PowerState = %x\n"
  715. "----------- BattInfo.Capacity = %x\n",
  716. BattNPInfo->DeviceNum,
  717. BatteryWaitStatus->PowerState,
  718. BatteryWaitStatus->LowCapacity,
  719. BatteryWaitStatus->HighCapacity,
  720. BattInfo->Status.PowerState,
  721. BattInfo->Status.Capacity)
  722. );
  723. //
  724. // Complete this IRP with the current status
  725. //
  726. ReturnCurrentStatus = TRUE;
  727. } else {
  728. //
  729. // Compute time when the request expires
  730. //
  731. BattPrint ((BATT_IOCTL_DATA), ("BattC (%d) CheckStatusQueue: Status Request %x Waiting For:\n"
  732. "----------- Timeout = %x\n"
  733. "----------- Irp.PowerState = %x\n"
  734. "----------- Irp.LowCapacity = %x\n"
  735. "----------- Irp.HighCapacity = %x\n",
  736. BattNPInfo->DeviceNum,
  737. Irp,
  738. BatteryWaitStatus->Timeout,
  739. BatteryWaitStatus->PowerState,
  740. BatteryWaitStatus->LowCapacity,
  741. BatteryWaitStatus->HighCapacity)
  742. );
  743. if (BatteryWaitStatus->Timeout &&
  744. IrpNextSp->Parameters.Others.Argument1 == NULL &&
  745. IrpNextSp->Parameters.Others.Argument2 == NULL) {
  746. // initialize it
  747. li.QuadPart = CurrentTime.QuadPart +
  748. ((ULONGLONG) BatteryWaitStatus->Timeout * NTMS);
  749. IrpNextSp->Parameters.Others.Argument1 = (PVOID)((ULONG_PTR)li.LowPart);
  750. IrpNextSp->Parameters.Others.Argument2 = (PVOID)((ULONG_PTR)li.HighPart);
  751. }
  752. li.LowPart = (ULONG)((ULONG_PTR)IrpNextSp->Parameters.Others.Argument1);
  753. li.HighPart = (ULONG)((ULONG_PTR)IrpNextSp->Parameters.Others.Argument2);
  754. li.QuadPart -= CurrentTime.QuadPart;
  755. if (li.QuadPart <= 0) {
  756. //
  757. // Time's up, complete it
  758. //
  759. ReturnCurrentStatus = TRUE;
  760. } else {
  761. //
  762. // If waiting forever, no need to set a timer
  763. //
  764. if (BatteryWaitStatus->Timeout != 0xFFFFFFFF) {
  765. //
  766. // Check if this will be the next timeout time -- we will use
  767. // the minimum timeout of the pending requests.
  768. //
  769. if (li.QuadPart < NextTime.QuadPart) {
  770. NextTime.QuadPart = li.QuadPart;
  771. }
  772. }
  773. }
  774. }
  775. if (!ReturnCurrentStatus) {
  776. //
  777. // IRP is still pending, calculate LCD of all waiting IRPs
  778. //
  779. if (BatteryWaitStatus->LowCapacity > Notify.LowCapacity) {
  780. Notify.LowCapacity = BatteryWaitStatus->LowCapacity;
  781. }
  782. if (BatteryWaitStatus->HighCapacity < Notify.HighCapacity) {
  783. Notify.HighCapacity = BatteryWaitStatus->HighCapacity;
  784. }
  785. } else {
  786. //
  787. // Return current battery status
  788. //
  789. Irp->IoStatus.Status = STATUS_SUCCESS;
  790. Irp->IoStatus.Information = sizeof(BattInfo->Status);
  791. RtlCopyMemory (
  792. Irp->AssociatedIrp.SystemBuffer,
  793. &BattInfo->Status,
  794. sizeof(BattInfo->Status)
  795. );
  796. }
  797. }
  798. //
  799. // If this request is no longer pending, complete it
  800. //
  801. if (Irp->IoStatus.Status != STATUS_PENDING) {
  802. BattPrint (BATT_IOCTL,
  803. ("BattC (%d): completing QueryStatus irp - %x, status - %x\n",
  804. BattNPInfo->DeviceNum,
  805. Irp,
  806. Irp->IoStatus.Status));
  807. RemoveEntryList (&Irp->Tail.Overlay.ListEntry);
  808. IoSetCancelRoutine (Irp, NULL);
  809. IoCompleteRequest (Irp, IO_NO_INCREMENT);
  810. }
  811. }
  812. }
  813. //
  814. // Status check complete
  815. //
  816. if (IsListEmpty (&BattInfo->StatusQueue)) {
  817. //
  818. // Nothing pending, if being notified disable the notifications
  819. //
  820. if (StatusNotified) {
  821. BattInfo->Mp.DisableStatusNotify (BattInfo->Mp.Context);
  822. BattInfo->StatusTime = 0;
  823. BattPrint ((BATT_MP_DATA), ("BattC (%d) CheckStatus: called Mp.DisableStatusNotify\n", BattNPInfo->DeviceNum));
  824. }
  825. } else {
  826. //
  827. // Set notification setting
  828. //
  829. Status = BattInfo->Mp.SetStatusNotify (
  830. BattInfo->Mp.Context,
  831. BattInfo->Tag,
  832. &Notify
  833. );
  834. if (NT_SUCCESS(Status)) {
  835. //
  836. // New notification set, remember it
  837. //
  838. BattPrint (BATT_MP_DATA, ("BattC (%d) Mp.SetStatusNotify: Notify set for: State=%x, Low=%x, High=%x\n",
  839. BattNPInfo->DeviceNum,
  840. Notify.PowerState,
  841. Notify.LowCapacity,
  842. Notify.HighCapacity
  843. ));
  844. } else {
  845. //
  846. // Could not set notification, handle error
  847. //
  848. BattPrint (BATT_MP_ERROR, ("BattC (%d) Mp.SetStatusNotify: failed (%x), will poll\n", BattNPInfo->DeviceNum, Status));
  849. BattCMiniportStatus (BattInfo, Status);
  850. //
  851. // Compute poll time
  852. //
  853. li.QuadPart = MIN_STATUS_POLL_RATE;
  854. if (BattInfo->Status.Capacity == BATTERY_UNKNOWN_CAPACITY) {
  855. // Retry 10 times at a polling rate of 1 second.
  856. // Then revert to the slow polling rate.
  857. if (BattInfo->InvalidRetryCount < INVALID_DATA_MAX_RETRY) {
  858. BattInfo->InvalidRetryCount++;
  859. li.QuadPart = INVALID_DATA_POLL_RATE;
  860. BattPrint (BATT_DEBUG, ("BattC (%d) InvalidRetryCount = %d\n",
  861. BattNPInfo->DeviceNum, BattInfo->InvalidRetryCount));
  862. } else {
  863. BattPrint (BATT_DEBUG, ("BattC (%d) InvalidRetryCount = %d. Using slow polling rate.\n",
  864. BattNPInfo->DeviceNum, BattInfo->InvalidRetryCount));
  865. li.QuadPart = MIN_STATUS_POLL_RATE;
  866. }
  867. } else if ((BattInfo->Status.Rate != 0) && (BattInfo->Status.Rate != BATTERY_UNKNOWN_RATE)) {
  868. if (BattInfo->Status.Rate > 0) {
  869. li.QuadPart = Notify.HighCapacity - BattInfo->Status.Capacity;
  870. } else if (BattInfo->Status.Rate < 0) {
  871. li.QuadPart = Notify.LowCapacity - BattInfo->Status.Capacity;
  872. }
  873. // convert to 3/4 its target time
  874. li.QuadPart = li.QuadPart * ((ULONGLONG) NTMIN * 45);
  875. li.QuadPart = li.QuadPart / (LONGLONG)(BattInfo->Status.Rate);
  876. //
  877. // Bound it
  878. //
  879. if (li.QuadPart > MIN_STATUS_POLL_RATE) {
  880. // poll at least this fast
  881. li.QuadPart = MIN_STATUS_POLL_RATE;
  882. } else if (li.QuadPart < MAX_STATUS_POLL_RATE) {
  883. // but not faster then this
  884. li.QuadPart = MAX_STATUS_POLL_RATE;
  885. }
  886. }
  887. //
  888. // If sooner then NextTime, adjust NextTime
  889. //
  890. if (li.QuadPart < NextTime.QuadPart) {
  891. NextTime.QuadPart = li.QuadPart;
  892. }
  893. }
  894. //
  895. // If there's a NextTime, queue the timer to recheck
  896. //
  897. if (NextTime.QuadPart) {
  898. NextTime.QuadPart = -NextTime.QuadPart;
  899. //
  900. // Acquire a remove lock.
  901. //
  902. InterlockedIncrement (&BattNPInfo->InUseCount);
  903. BattPrint ((BATT_LOCK), ("BattCCheckStatusQueue: Aqcuired remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount));
  904. if (BattNPInfo->WantToRemove == TRUE) {
  905. //
  906. // If BatteryClassUnload is waiting to remove the device:
  907. // Don't set the timer.
  908. // Release the remove lock just acquired.
  909. // No need to notify BatteryclassUnload because
  910. // at this point there is at least one other lock held.
  911. //
  912. InterlockedDecrement(&BattNPInfo->InUseCount);
  913. BattPrint (BATT_NOTE,
  914. ("BattC (%d) CheckStatus: Poll cancel because of device removal.\n",
  915. BattNPInfo->DeviceNum));
  916. BattPrint ((BATT_LOCK), ("BattCCheckStatusQueue: Released remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount));
  917. } else {
  918. if (KeSetTimer (&BattNPInfo->WorkerTimer, NextTime, &BattNPInfo->WorkerDpc)) {
  919. //
  920. // If the timer was already set, we need to release a remove lock since
  921. // there was already one aquired the last time this timer was set.
  922. //
  923. InterlockedDecrement(&BattNPInfo->InUseCount);
  924. BattPrint ((BATT_LOCK), ("BattCCheckStatusQueue: Released extra remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount));
  925. }
  926. #if DEBUG
  927. NextTime.QuadPart = (-NextTime.QuadPart) / (ULONGLONG) NTSEC;
  928. BattPrint (BATT_NOTE, ("BattC (%d) CheckStatus: Poll in %d seconds (%d minutes)\n",
  929. BattNPInfo->DeviceNum, NextTime.LowPart, NextTime.LowPart/60));
  930. #endif
  931. }
  932. } else {
  933. //
  934. // There should always be a NextTime.
  935. //
  936. ASSERT(FALSE);
  937. }
  938. } // if (IsListEmpty (&BattInfo->StatusQueue)) {...} else
  939. }
  940. VOID
  941. BattCCheckTagQueue (
  942. IN PBATT_NP_INFO BattNPInfo,
  943. IN PBATT_INFO BattInfo
  944. )
  945. /*++
  946. Routine Description:
  947. Gets the batteries current tag, and checks the pending
  948. tag queue for possible IRP completion. Resets the miniport
  949. notification settings if needed.
  950. N.B. must be invoked from the non-reentrant worker thread
  951. Arguments:
  952. BattNPInfo - Battery
  953. BattInfo - Battery
  954. Return Value:
  955. None
  956. --*/
  957. {
  958. PLIST_ENTRY Entry;
  959. PIRP Irp;
  960. PIO_STACK_LOCATION IrpSp, IrpNextSp;
  961. LARGE_INTEGER NextTime;
  962. LARGE_INTEGER CurrentTime;
  963. LARGE_INTEGER li;
  964. ULONG TimeIncrement;
  965. BOOLEAN ReturnCurrentStatus;
  966. NTSTATUS Status;
  967. ULONG batteryTimeout;
  968. BOOLEAN TagNotified;
  969. ULONG tmpTag = BATTERY_TAG_INVALID;
  970. BattPrint ((BATT_TRACE), ("BattC (%d): BattCCheckTagQueue called\n", BattInfo->BattNPInfo->DeviceNum));
  971. PAGED_CODE();
  972. TimeIncrement = KeQueryTimeIncrement();
  973. //
  974. // Loop while tag needs checked, check pending tag IRPs
  975. //
  976. while (InterlockedExchange(&BattNPInfo->CheckTag, 0)) {
  977. NextTime.QuadPart = 0;
  978. //
  979. // If the Tag Queue is empty, done
  980. // but we need to make sure that we leave TagNotified set to TRUE
  981. // so the next time an IRP comes through,we'll re-read the tag.
  982. //
  983. if (IsListEmpty (&BattInfo->TagQueue)) {
  984. break;
  985. }
  986. TagNotified = FALSE;
  987. //
  988. // Pickup tag notified flag
  989. //
  990. if (BattNPInfo->TagNotified) {
  991. InterlockedExchange (&BattNPInfo->TagNotified, 0);
  992. TagNotified = TRUE;
  993. }
  994. KeQueryTickCount (&CurrentTime);
  995. CurrentTime.QuadPart = CurrentTime.QuadPart * TimeIncrement;
  996. if (TagNotified ||
  997. CurrentTime.QuadPart - BattInfo->TagTime > STATUS_VALID_TIME) {
  998. //
  999. // Get the battery's current tag
  1000. //
  1001. tmpTag = 0;
  1002. Status = BattInfo->Mp.QueryTag (
  1003. BattInfo->Mp.Context,
  1004. &tmpTag
  1005. );
  1006. if (!NT_SUCCESS(Status) && (Status != STATUS_NO_SUCH_DEVICE)) {
  1007. //
  1008. // Something went wrong, complete all pending tag irps
  1009. //
  1010. BattPrint (BATT_MP_ERROR, ("BattC (%d) CheckTag: Tag read err = %x\n", BattNPInfo->DeviceNum, Status));
  1011. BattCMiniportStatus (BattInfo, Status);
  1012. break;
  1013. }
  1014. BattPrint (BATT_MP_DATA, ("BattC (%d) MP.QueryTag: Status = %08x, Tag = %08x\n",
  1015. BattNPInfo->DeviceNum, Status, tmpTag));
  1016. if (Status == STATUS_NO_SUCH_DEVICE) {
  1017. //
  1018. // Get the current time to compute timeouts on tag query requests
  1019. //
  1020. KeQueryTickCount (&CurrentTime);
  1021. CurrentTime.QuadPart = CurrentTime.QuadPart * TimeIncrement;
  1022. BattInfo->TagTime = CurrentTime.QuadPart;
  1023. }
  1024. }
  1025. //
  1026. // Check each pending Tag IRP
  1027. //
  1028. Entry = BattInfo->TagQueue.Flink;
  1029. while (Entry != &BattInfo->TagQueue) {
  1030. //
  1031. // Get IRP to check
  1032. //
  1033. Irp = CONTAINING_RECORD (
  1034. Entry,
  1035. IRP,
  1036. Tail.Overlay.ListEntry
  1037. );
  1038. IrpSp = IoGetCurrentIrpStackLocation(Irp);
  1039. IrpNextSp = IoGetNextIrpStackLocation(Irp);
  1040. if (IrpSp->Parameters.DeviceIoControl.InputBufferLength == 0) {
  1041. //
  1042. // If no input was given, then use timeout of 0.
  1043. //
  1044. batteryTimeout = 0;
  1045. } else {
  1046. batteryTimeout = *((PULONG) Irp->AssociatedIrp.SystemBuffer);
  1047. }
  1048. //
  1049. // Get next request
  1050. //
  1051. Entry = Entry->Flink;
  1052. //
  1053. // If IRP is flagged as cancelled, complete it
  1054. //
  1055. if (Irp->Cancel) {
  1056. BattPrint (BATT_IOCTL, ("BattC (%d): QueryTag irp cancelled - %x\n", BattNPInfo->DeviceNum, Irp));
  1057. Irp->IoStatus.Status = STATUS_CANCELLED;
  1058. }
  1059. //
  1060. // If request is still pending, check it
  1061. //
  1062. if (Irp->IoStatus.Status == STATUS_PENDING) {
  1063. ReturnCurrentStatus = FALSE;
  1064. if (tmpTag != BATTERY_TAG_INVALID) {
  1065. //
  1066. // Complete this IRP with the current tag
  1067. //
  1068. ReturnCurrentStatus = TRUE;
  1069. Irp->IoStatus.Status = STATUS_SUCCESS;
  1070. } else {
  1071. //
  1072. // Compute time when the request expires, the battery tag
  1073. // is an input parameter that holds the timeout.
  1074. //
  1075. if (batteryTimeout &&
  1076. IrpNextSp->Parameters.Others.Argument1 == NULL &&
  1077. IrpNextSp->Parameters.Others.Argument2 == NULL) {
  1078. // initialize it
  1079. li.QuadPart = CurrentTime.QuadPart + ((ULONGLONG) batteryTimeout * NTMS);
  1080. IrpNextSp->Parameters.Others.Argument1 = (PVOID)((ULONG_PTR)li.LowPart);
  1081. IrpNextSp->Parameters.Others.Argument2 = (PVOID)((ULONG_PTR)li.HighPart);
  1082. }
  1083. li.LowPart = (ULONG)((ULONG_PTR)IrpNextSp->Parameters.Others.Argument1);
  1084. li.HighPart = (ULONG)((ULONG_PTR)IrpNextSp->Parameters.Others.Argument2);
  1085. li.QuadPart -= CurrentTime.QuadPart;
  1086. if (li.QuadPart <= 0) {
  1087. //
  1088. // Time's up, complete it
  1089. //
  1090. BattPrint ((BATT_NOTE | BATT_IOCTL), ("BattC (%d): QueryTag irp timeout - %x\n", BattNPInfo->DeviceNum, Irp));
  1091. ReturnCurrentStatus = TRUE;
  1092. Irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
  1093. } else {
  1094. //
  1095. // If waiting forever, no need to set a timer
  1096. //
  1097. if (batteryTimeout != 0xFFFFFFFF) {
  1098. //
  1099. // Check if this is the next timeout time
  1100. //
  1101. if (NextTime.QuadPart == 0 || li.QuadPart < NextTime.QuadPart) {
  1102. NextTime.QuadPart = li.QuadPart;
  1103. }
  1104. }
  1105. }
  1106. }
  1107. if (ReturnCurrentStatus) {
  1108. //
  1109. // Return current battery status
  1110. //
  1111. *((PULONG) Irp->AssociatedIrp.SystemBuffer) = tmpTag;
  1112. Irp->IoStatus.Information = sizeof(ULONG);
  1113. if (BattInfo->Tag != tmpTag) {
  1114. //
  1115. // This is a new battery tag, capture tag
  1116. //
  1117. BattInfo->Tag = tmpTag;
  1118. }
  1119. }
  1120. }
  1121. //
  1122. // If this request is no longer pending, complete it
  1123. //
  1124. if (Irp->IoStatus.Status != STATUS_PENDING) {
  1125. RemoveEntryList (&Irp->Tail.Overlay.ListEntry);
  1126. IoSetCancelRoutine (Irp, NULL);
  1127. BattPrint (
  1128. (BATT_IOCTL),
  1129. ("BattC (%d): CheckTag completing request, IRP = %x, status = %x\n",
  1130. BattNPInfo->DeviceNum,
  1131. Irp,
  1132. Irp->IoStatus.Status)
  1133. );
  1134. IoCompleteRequest (Irp, IO_NO_INCREMENT);
  1135. }
  1136. }
  1137. }
  1138. //
  1139. // If there's a NextTime, queue the timer to recheck.
  1140. // This means there is a tag request with a timout other than 0 or -1.
  1141. //
  1142. if (NextTime.QuadPart) {
  1143. NextTime.QuadPart = -NextTime.QuadPart;
  1144. //
  1145. // Acquire a remove lock.
  1146. //
  1147. InterlockedIncrement (&BattNPInfo->InUseCount);
  1148. BattPrint ((BATT_LOCK), ("BattCCheckTagQueue: Aqcuired remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount));
  1149. if (BattNPInfo->WantToRemove == TRUE) {
  1150. //
  1151. // If BatteryClassUnload is waiting to remove the device:
  1152. // Don't set the timer.
  1153. // Release the remove lock just acquired.
  1154. // No need to notify BatteryclassUnload because
  1155. // at this point there is at least one other lock held.
  1156. //
  1157. InterlockedDecrement(&BattNPInfo->InUseCount);
  1158. BattPrint (BATT_NOTE,
  1159. ("BattC (%d) CheckTag: Poll cancel because of device removal.\n",
  1160. BattNPInfo->DeviceNum));
  1161. BattPrint ((BATT_LOCK), ("BattCCheckTagQueue: Released remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount));
  1162. } else {
  1163. if (KeSetTimer (&BattNPInfo->TagTimer, NextTime, &BattNPInfo->TagDpc)){
  1164. //
  1165. // If the timer was already set, we need to release a remove lock since
  1166. // there was already one aquired the last time this timer was set.
  1167. //
  1168. InterlockedDecrement(&BattNPInfo->InUseCount);
  1169. BattPrint ((BATT_LOCK), ("BattCCheckTagQueue: Released extra remove lock %d (count = %d)\n", BattNPInfo->DeviceNum, BattNPInfo->InUseCount));
  1170. }
  1171. #if DEBUG
  1172. NextTime.QuadPart = NextTime.QuadPart / -NTSEC;
  1173. BattPrint (BATT_NOTE, ("BattC (%d) CheckTag: Poll in %x seconds\n", BattNPInfo->DeviceNum, NextTime.LowPart));
  1174. #endif
  1175. }
  1176. }
  1177. }
  1178. VOID
  1179. BattCWmi (
  1180. IN PBATT_NP_INFO BattNPInfo,
  1181. IN PBATT_INFO BattInfo,
  1182. IN PBATT_WMI_REQUEST WmiRequest
  1183. )
  1184. /*++
  1185. Routine Description:
  1186. Processes a single WMI request.
  1187. N.B. must be invoked from the non-reentrant worker thread
  1188. Arguments:
  1189. BattNPInfo - Battery
  1190. BattInfo - Battery
  1191. WmiRequest - Wmi Request to process
  1192. Return Value:
  1193. None
  1194. --*/
  1195. {
  1196. NTSTATUS status = STATUS_SUCCESS;
  1197. ULONG size = 0;
  1198. ULONG OutputLen;
  1199. BATTERY_INFORMATION batteryInformation;
  1200. PWCHAR tempString;
  1201. BattPrint((BATT_WMI), ("BattCWmi (%d): GuidIndex = 0x%x\n",
  1202. BattNPInfo->DeviceNum, WmiRequest->GuidIndex));
  1203. switch (WmiRequest->GuidIndex) {
  1204. case BattWmiStatusId:
  1205. size = sizeof (BATTERY_WMI_STATUS);
  1206. ((PBATTERY_WMI_STATUS) WmiRequest->Buffer)->Tag = BattInfo->Tag;
  1207. ((PBATTERY_WMI_STATUS) WmiRequest->Buffer)->RemainingCapacity = BattInfo->Status.Capacity;
  1208. if (BattInfo->Status.Rate < 0) {
  1209. ((PBATTERY_WMI_STATUS) WmiRequest->Buffer)->ChargeRate = 0;
  1210. ((PBATTERY_WMI_STATUS) WmiRequest->Buffer)->DischargeRate = -BattInfo->Status.Rate;
  1211. } else {
  1212. ((PBATTERY_WMI_STATUS) WmiRequest->Buffer)->ChargeRate = BattInfo->Status.Rate;
  1213. ((PBATTERY_WMI_STATUS) WmiRequest->Buffer)->DischargeRate = 0;
  1214. }
  1215. ((PBATTERY_WMI_STATUS) WmiRequest->Buffer)->Voltage = BattInfo->Status.Voltage;
  1216. ((PBATTERY_WMI_STATUS) WmiRequest->Buffer)->PowerOnline =
  1217. (BattInfo->Status.PowerState & BATTERY_POWER_ON_LINE) ? TRUE : FALSE;
  1218. ((PBATTERY_WMI_STATUS) WmiRequest->Buffer)->Charging =
  1219. (BattInfo->Status.PowerState & BATTERY_CHARGING) ? TRUE : FALSE;
  1220. ((PBATTERY_WMI_STATUS) WmiRequest->Buffer)->Discharging =
  1221. (BattInfo->Status.PowerState & BATTERY_DISCHARGING) ? TRUE : FALSE;
  1222. ((PBATTERY_WMI_STATUS) WmiRequest->Buffer)->Critical =
  1223. (BattInfo->Status.PowerState & BATTERY_CRITICAL) ? TRUE : FALSE;
  1224. BattPrint((BATT_WMI), ("BattCWmi (%d): BatteryStatus\n",
  1225. BattNPInfo->DeviceNum));
  1226. break;
  1227. case BattWmiRuntimeId:
  1228. size = sizeof (BATTERY_WMI_RUNTIME);
  1229. ((PBATTERY_WMI_RUNTIME) WmiRequest->Buffer)->Tag = BattInfo->Tag;
  1230. status = BattInfo->Mp.QueryInformation (
  1231. BattInfo->Mp.Context,
  1232. BattInfo->Tag,
  1233. BatteryEstimatedTime,
  1234. 0,
  1235. &((PBATTERY_WMI_RUNTIME) WmiRequest->Buffer)->EstimatedRuntime,
  1236. sizeof(ULONG),
  1237. &OutputLen
  1238. );
  1239. BattPrint((BATT_WMI), ("BattCWmi (%d): EstimateRuntime = %08x, Status = 0x%08x\n",
  1240. BattNPInfo->DeviceNum, &((PBATTERY_WMI_RUNTIME) WmiRequest->Buffer)->EstimatedRuntime, status));
  1241. break;
  1242. case BattWmiTemperatureId:
  1243. size = sizeof (BATTERY_WMI_TEMPERATURE);
  1244. ((PBATTERY_WMI_TEMPERATURE) WmiRequest->Buffer)->Tag = BattInfo->Tag;
  1245. status = BattInfo->Mp.QueryInformation (
  1246. BattInfo->Mp.Context,
  1247. BattInfo->Tag,
  1248. BatteryTemperature,
  1249. 0,
  1250. &((PBATTERY_WMI_TEMPERATURE) WmiRequest->Buffer)->Temperature,
  1251. sizeof(ULONG),
  1252. &OutputLen
  1253. );
  1254. BattPrint((BATT_WMI), ("BattCWmi (%d): Temperature = %08x, Status = 0x%08x\n",
  1255. BattNPInfo->DeviceNum, &((PBATTERY_WMI_TEMPERATURE) WmiRequest->Buffer)->Temperature, status));
  1256. break;
  1257. case BattWmiFullChargedCapacityId:
  1258. size = sizeof (BATTERY_WMI_FULL_CHARGED_CAPACITY);
  1259. ((PBATTERY_WMI_FULL_CHARGED_CAPACITY) WmiRequest->Buffer)->Tag = BattInfo->Tag;
  1260. status = BattInfo->Mp.QueryInformation (
  1261. BattInfo->Mp.Context,
  1262. BattInfo->Tag,
  1263. BatteryInformation,
  1264. 0,
  1265. &batteryInformation,
  1266. sizeof(BATTERY_INFORMATION),
  1267. &OutputLen
  1268. );
  1269. ((PBATTERY_WMI_FULL_CHARGED_CAPACITY) WmiRequest->Buffer)->FullChargedCapacity =
  1270. batteryInformation.FullChargedCapacity;
  1271. BattPrint((BATT_WMI), ("BattCWmi (%d): FullChargedCapacity = %08x, Status = 0x%08x\n",
  1272. BattNPInfo->DeviceNum, ((PBATTERY_WMI_FULL_CHARGED_CAPACITY) WmiRequest->Buffer)->FullChargedCapacity, status));
  1273. break;
  1274. case BattWmiCycleCountId:
  1275. size = sizeof (BATTERY_WMI_CYCLE_COUNT);
  1276. ((PBATTERY_WMI_CYCLE_COUNT) WmiRequest->Buffer)->Tag = BattInfo->Tag;
  1277. status = BattInfo->Mp.QueryInformation (
  1278. BattInfo->Mp.Context,
  1279. BattInfo->Tag,
  1280. BatteryInformation,
  1281. 0,
  1282. &batteryInformation,
  1283. sizeof(BATTERY_INFORMATION),
  1284. &OutputLen
  1285. );
  1286. ((PBATTERY_WMI_CYCLE_COUNT) WmiRequest->Buffer)->CycleCount =
  1287. batteryInformation.CycleCount;
  1288. BattPrint((BATT_WMI), ("BattCWmi (%d): CycleCount = %08x, Status = 0x%08x\n",
  1289. BattNPInfo->DeviceNum, ((PBATTERY_WMI_CYCLE_COUNT) WmiRequest->Buffer)->CycleCount, status));
  1290. break;
  1291. case BattWmiStaticDataId:
  1292. size = sizeof(BATTERY_WMI_STATIC_DATA)+4*MAX_BATTERY_STRING_SIZE*sizeof(WCHAR);
  1293. ((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->Tag = BattInfo->Tag;
  1294. // ((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->ManufacturerDate[0] =
  1295. // ((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->Granularity =
  1296. status = BattInfo->Mp.QueryInformation (
  1297. BattInfo->Mp.Context,
  1298. BattInfo->Tag,
  1299. BatteryInformation,
  1300. 0,
  1301. &batteryInformation,
  1302. sizeof(BATTERY_INFORMATION),
  1303. &OutputLen
  1304. );
  1305. if (NT_SUCCESS(status)) {
  1306. ((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->Capabilities =
  1307. batteryInformation.Capabilities;
  1308. ((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->Technology =
  1309. batteryInformation.Technology;
  1310. ((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->Chemistry =
  1311. *(PULONG)batteryInformation.Chemistry;
  1312. ((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->DesignedCapacity =
  1313. batteryInformation.DesignedCapacity;
  1314. ((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->DefaultAlert1 =
  1315. batteryInformation.DefaultAlert1;
  1316. ((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->DefaultAlert2 =
  1317. batteryInformation.DefaultAlert2;
  1318. ((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->CriticalBias =
  1319. batteryInformation.CriticalBias;
  1320. tempString = ((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->Strings;
  1321. status = BattInfo->Mp.QueryInformation (
  1322. BattInfo->Mp.Context,
  1323. BattInfo->Tag,
  1324. BatteryDeviceName,
  1325. 0,
  1326. &tempString[1],
  1327. MAX_BATTERY_STRING_SIZE,
  1328. &OutputLen
  1329. );
  1330. if (!NT_SUCCESS(status)) {
  1331. // Some batteries may not support some types of Information Queries
  1332. // Don't fail request, simply leave this one blank.
  1333. OutputLen = 0;
  1334. }
  1335. tempString[0] = (USHORT) OutputLen;
  1336. tempString = (PWCHAR) ((PCHAR) &tempString[1] + tempString[0]);
  1337. status = BattInfo->Mp.QueryInformation (
  1338. BattInfo->Mp.Context,
  1339. BattInfo->Tag,
  1340. BatteryManufactureName,
  1341. 0,
  1342. &tempString[1],
  1343. MAX_BATTERY_STRING_SIZE,
  1344. &OutputLen
  1345. );
  1346. if (!NT_SUCCESS(status)) {
  1347. // Some batteries may not support some types of Information Queries
  1348. // Don't fail request, simply leave this one blank.
  1349. OutputLen = 0;
  1350. }
  1351. tempString[0] = (USHORT) OutputLen;
  1352. tempString = (PWCHAR) ((PCHAR) &tempString[1] + tempString[0]);
  1353. status = BattInfo->Mp.QueryInformation (
  1354. BattInfo->Mp.Context,
  1355. BattInfo->Tag,
  1356. BatterySerialNumber,
  1357. 0,
  1358. &tempString[1],
  1359. MAX_BATTERY_STRING_SIZE,
  1360. &OutputLen
  1361. );
  1362. if (!NT_SUCCESS(status)) {
  1363. // Some batteries may not support some types of Information Queries
  1364. // Don't fail request, simply leave this one blank.
  1365. OutputLen = 0;
  1366. }
  1367. tempString[0] = (USHORT) OutputLen;
  1368. tempString = (PWCHAR) ((PCHAR) &tempString[1] + tempString[0]);
  1369. status = BattInfo->Mp.QueryInformation (
  1370. BattInfo->Mp.Context,
  1371. BattInfo->Tag,
  1372. BatteryUniqueID,
  1373. 0,
  1374. &tempString[1],
  1375. MAX_BATTERY_STRING_SIZE,
  1376. &OutputLen
  1377. );
  1378. if (!NT_SUCCESS(status)) {
  1379. // Some batteries may not support some types of Information Queries
  1380. // Don't fail request, simply leave this one blank.
  1381. OutputLen = 0;
  1382. status = STATUS_SUCCESS;
  1383. }
  1384. tempString[0] = (USHORT) OutputLen;
  1385. tempString = (PWCHAR) ((PCHAR) &tempString[1] + tempString[0]);
  1386. size = (ULONG)(sizeof(BATTERY_WMI_STATIC_DATA)+(tempString - ((PBATTERY_WMI_STATIC_DATA) WmiRequest->Buffer)->Strings));
  1387. }
  1388. break;
  1389. default:
  1390. status = STATUS_WMI_GUID_NOT_FOUND;
  1391. }
  1392. *WmiRequest->InstanceLengthArray = size;
  1393. status = WmiCompleteRequest(WmiRequest->DeviceObject,
  1394. WmiRequest->Irp,
  1395. status,
  1396. size,
  1397. IO_NO_INCREMENT);
  1398. }
  1399. VOID
  1400. BattCMiniportStatus (
  1401. IN PBATT_INFO BattInfo,
  1402. IN NTSTATUS Status
  1403. )
  1404. /*++
  1405. Routine Description:
  1406. Function to return status from miniport. If the battery tag has gone
  1407. invalid the pending statuses are aborted.
  1408. N.B. must be invoked from the non-rentrant worker thread
  1409. Arguments:
  1410. BattInfo - Battery
  1411. Status - Status from miniport.
  1412. Return Value:
  1413. None
  1414. --*/
  1415. {
  1416. if (NT_SUCCESS(Status)) {
  1417. return ;
  1418. }
  1419. switch (Status) {
  1420. #if DEBUG
  1421. case STATUS_SUCCESS:
  1422. case STATUS_NOT_IMPLEMENTED:
  1423. case STATUS_BUFFER_TOO_SMALL:
  1424. case STATUS_INVALID_BUFFER_SIZE:
  1425. case STATUS_NOT_SUPPORTED:
  1426. case STATUS_INVALID_PARAMETER:
  1427. case STATUS_OBJECT_NAME_NOT_FOUND:
  1428. case STATUS_INVALID_DEVICE_REQUEST:
  1429. // no action
  1430. break;
  1431. default:
  1432. BattPrint (BATT_ERROR, ("BattCMiniportStatus: unknown status from miniport: %x BattInfo %x\n",
  1433. Status, BattInfo));
  1434. break;
  1435. #endif
  1436. case STATUS_NO_SUCH_DEVICE:
  1437. //
  1438. // Our battery tag is wrong. Cancel any queued status irps
  1439. //
  1440. BattCCompleteIrpQueue (&(BattInfo->StatusQueue), Status);
  1441. break;
  1442. }
  1443. }
  1444. VOID
  1445. BattCCompleteIrpQueue (
  1446. IN PLIST_ENTRY Queue,
  1447. IN NTSTATUS Status
  1448. )
  1449. /*++
  1450. Routine Description:
  1451. Complete all pending Irps in the IoQueue, TagQueue, or StatusQueue.
  1452. N.B. must be invoked from the non-rentrant worker thread
  1453. Arguments:
  1454. BattInfo - Battery
  1455. Status - Error status to complete pending status request with
  1456. Return Value:
  1457. None
  1458. --*/
  1459. {
  1460. PLIST_ENTRY Entry;
  1461. PIRP Irp;
  1462. ASSERT (!NT_SUCCESS(Status));
  1463. BattPrint (BATT_TRACE, ("BattC: ENTERING BattCCompleteIrpQueue\n"));
  1464. while (!IsListEmpty(Queue)) {
  1465. Entry = RemoveHeadList (Queue);
  1466. Irp = CONTAINING_RECORD (
  1467. Entry,
  1468. IRP,
  1469. Tail.Overlay.ListEntry
  1470. );
  1471. //
  1472. // Use Cancel Spinlock to make sure that Completion routine isn't being called
  1473. //
  1474. IoAcquireCancelSpinLock (&Irp->CancelIrql);
  1475. IoSetCancelRoutine (Irp, NULL);
  1476. IoReleaseCancelSpinLock(Irp->CancelIrql);
  1477. BattPrint (BATT_NOTE, ("BattC: Completing IRP 0x%0lx at IRQL %d.\n", Irp, KeGetCurrentIrql()));
  1478. Irp->IoStatus.Status = Status;
  1479. IoCompleteRequest (Irp, IO_NO_INCREMENT);
  1480. }
  1481. BattPrint (BATT_TRACE, ("BattC: EXITING BattCCompleteIrpQueue\n"));
  1482. }
  1483. VOID
  1484. BattCCompleteWmiQueue (
  1485. IN PLIST_ENTRY Queue,
  1486. IN NTSTATUS Status
  1487. )
  1488. /*++
  1489. Routine Description:
  1490. Complete all pending Irps in the IoQueue, TagQueue, or StatusQueue.
  1491. N.B. must be invoked from the non-rentrant worker thread
  1492. Arguments:
  1493. BattInfo - Battery
  1494. Status - Error status to complete pending status request with
  1495. Return Value:
  1496. None
  1497. --*/
  1498. {
  1499. PLIST_ENTRY Entry;
  1500. PBATT_WMI_REQUEST WmiRequest;
  1501. ASSERT (!NT_SUCCESS(Status));
  1502. BattPrint (BATT_TRACE, ("BattC: ENTERING BattCCompleteWmiQueue\n"));
  1503. while (!IsListEmpty(Queue)) {
  1504. Entry = RemoveHeadList (Queue);
  1505. WmiRequest = CONTAINING_RECORD (
  1506. Entry,
  1507. BATT_WMI_REQUEST,
  1508. ListEntry
  1509. );
  1510. BattPrint (BATT_NOTE, ("BattC: Completing Wmi Request 0x%0lx at IRQL %d.\n", WmiRequest, KeGetCurrentIrql()));
  1511. *WmiRequest->InstanceLengthArray = 0;
  1512. WmiCompleteRequest(WmiRequest->DeviceObject,
  1513. WmiRequest->Irp,
  1514. Status,
  1515. 0,
  1516. IO_NO_INCREMENT);
  1517. }
  1518. BattPrint (BATT_TRACE, ("BattC: EXITING BattCCompleteWmiQueue\n"));
  1519. }