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.

959 lines
24 KiB

  1. /*++
  2. Module Name:
  3. ntapm.c
  4. Abstract:
  5. OS source for ntapm.sys
  6. Author:
  7. Environment:
  8. Kernel mode
  9. Notes:
  10. Revision History:
  11. --*/
  12. #include "ntddk.h"
  13. #include "ntpoapi.h"
  14. #include "string.h"
  15. #include "ntcrib.h"
  16. #include "ntapmdbg.h"
  17. #include "apm.h"
  18. #include "apmp.h"
  19. #include "ntapmp.h"
  20. #include <ntapm.h>
  21. #include <poclass.h>
  22. #include <ntapmlog.h>
  23. //
  24. // Global debug flag. There are 3 separate groupings, see ntapmdbg.h for
  25. // break out.
  26. //
  27. ULONG NtApmDebugFlag = 0;
  28. ULONG ApmWorks = 0;
  29. WCHAR rgzApmActiveFlag[] =
  30. L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\ApmActive";
  31. WCHAR rgzApmFlag[] =
  32. L"Active";
  33. WCHAR rgzAcpiKey[] =
  34. L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\ACPI";
  35. WCHAR rgzAcpiStart[] =
  36. L"Start";
  37. //
  38. // Define driver entry routine.
  39. //
  40. NTSTATUS DriverEntry(
  41. PDRIVER_OBJECT DriverObject,
  42. PUNICODE_STRING RegistryPath
  43. );
  44. NTSTATUS ApmDispatch(
  45. PDEVICE_OBJECT DeviceObject,
  46. PIRP Irp
  47. );
  48. BOOLEAN
  49. IsAcpiMachine(
  50. VOID
  51. );
  52. ULONG DoApmPoll();
  53. NTSTATUS DoApmInitMachine();
  54. VOID (*BattChangeNotify)() = NULL;
  55. #define POLL_INTERVAL (500) // 500 milliseconds == 1/2 second
  56. #define APM_POLL_MULTIPLY (4) // only call ApmInProgress once every 4 Poll intervals
  57. // which with current values is once every 2 seconds
  58. #define APM_SPIN_LIMIT (6) // 6 spin passes, each with a call to ApmInProgress,
  59. // at APM_POLL_MULTIPLY * POLL_INTERVAL time spacing.
  60. // Current values (500, 4, 6) should yield APM bios
  61. // waiting from 12s to 17s, depending on how large
  62. // or small their value of 5s is.
  63. volatile BOOLEAN OperationDone = FALSE; // used to make some sync between SuspendPollThread
  64. // and ApmSleep and ApmOff work.
  65. //
  66. // Our own driver object. This is rude, but this is a very weird
  67. // and special driver. We will pass this to our APM library to
  68. // allow error logging to work. Note that we don't actually have
  69. // an active IRP around when the error occurs.
  70. //
  71. PDRIVER_OBJECT NtApmDriverObject = NULL;
  72. //
  73. // Define the local routines used by this driver module.
  74. //
  75. VOID SuspendPollThread(PVOID Dummy);
  76. VOID ApmSleep(VOID);
  77. VOID ApmOff(VOID);
  78. KTIMER PollTimer;
  79. NTSTATUS
  80. DriverEntry(
  81. IN PDRIVER_OBJECT DriverObject,
  82. IN PUNICODE_STRING RegistryPath
  83. )
  84. /*++
  85. Routine Description:
  86. This is the initialization routine for the laptop driver.
  87. Arguments:
  88. DriverObject - Pointer to driver object created by the system.
  89. Return Value:
  90. The function value is the final status from the initialization operation.
  91. --*/
  92. {
  93. NTSTATUS status;
  94. ULONG MajorVersion;
  95. ULONG MinorVersion;
  96. //
  97. // refuse to load on machines with more than 1 cpu
  98. //
  99. if (KeNumberProcessors != 1) {
  100. DrDebug(SYS_INFO, ("ntapm: more than 1 cpu, ntapm will exit\n"));
  101. return STATUS_UNSUCCESSFUL;
  102. }
  103. //
  104. // refuse to load if version number is not 5.1 or 5.0
  105. // NOTE WELL: This is a manual version check, do NOT put a system
  106. // constant in here. This driver depends on hacks in
  107. // the kernel that will someday go away...
  108. //
  109. PsGetVersion(&MajorVersion, &MinorVersion, NULL, NULL);
  110. if ( !
  111. (
  112. ((MajorVersion == 5) && (MinorVersion == 0)) ||
  113. ((MajorVersion == 5) && (MinorVersion == 1))
  114. )
  115. )
  116. {
  117. DrDebug(SYS_INFO, ("ntapm: system version number != 5.1, exit\n"));
  118. return STATUS_UNSUCCESSFUL;
  119. }
  120. //
  121. // refuse to load if ACPI.SYS should be running
  122. //
  123. if (IsAcpiMachine()) {
  124. DrDebug(SYS_INFO, ("ntapm: this is an acpi machine apm exiting\n"));
  125. return STATUS_UNSUCCESSFUL;
  126. }
  127. //
  128. // init the driver object
  129. //
  130. DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = ApmDispatch;
  131. DriverObject->MajorFunction[IRP_MJ_CREATE] = ApmDispatch;
  132. DriverObject->MajorFunction[IRP_MJ_CLOSE] = ApmDispatch;
  133. DriverObject->MajorFunction[IRP_MJ_PNP] = NtApm_PnP;
  134. DriverObject->MajorFunction[IRP_MJ_POWER] = NtApm_Power;
  135. DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = ApmDispatch;
  136. DriverObject->DriverExtension->AddDevice = NtApm_AddDevice;
  137. NtApmDriverObject = DriverObject;
  138. return STATUS_SUCCESS;
  139. }
  140. BOOLEAN ApmAddHelperDone = FALSE;
  141. NTSTATUS
  142. ApmAddHelper(
  143. )
  144. /*++
  145. Routine Description:
  146. We do these things in the Add routine so that we cannot fail
  147. and leave the Kernel/Hal/Apm chain in a corrupt state.
  148. This includes linking up with the Hal.
  149. Turns out the caller doesn't know if this work has already
  150. been done, so disallow doing it more than once here.
  151. Arguments:
  152. Return Value:
  153. The function value is the final status from the initialization operation.
  154. --*/
  155. {
  156. UCHAR HalTable[HAL_APM_TABLE_SIZE];
  157. PPM_DISPATCH_TABLE InTable;
  158. HANDLE ThreadHandle;
  159. HANDLE hKey;
  160. NTSTATUS status;
  161. ULONG flagvalue;
  162. OBJECT_ATTRIBUTES objectAttributes;
  163. UNICODE_STRING unicodeString;
  164. ULONG battresult;
  165. if (ApmAddHelperDone) {
  166. return STATUS_SUCCESS;
  167. }
  168. ApmAddHelperDone = TRUE;
  169. //
  170. // call ApmInitMachine so that Bios, etc, can be engaged
  171. // no suspends can happen before this call.
  172. //
  173. if (! NT_SUCCESS(DoApmInitMachine()) ) {
  174. DrDebug(SYS_INFO, ("ntapm: DoApmInitMachine failed\n"));
  175. return STATUS_UNSUCCESSFUL;
  176. }
  177. //
  178. // call the hal
  179. //
  180. InTable = (PPM_DISPATCH_TABLE)HalTable;
  181. InTable->Signature = HAL_APM_SIGNATURE;
  182. InTable->Version = HAL_APM_VERSION;
  183. //
  184. // In theory, APM should be fired up by now.
  185. // So call off into it to see if there is any sign
  186. // of a battery on the box.
  187. //
  188. // If we do not see a battery, then do NOT enable
  189. // S3, but do allow S4. This keeps people's machines
  190. // from puking on failed S3 calls (almost always desktops)
  191. // while allowing auto-shutdown at the end of hibernate to work.
  192. //
  193. battresult = DoApmReportBatteryStatus();
  194. if (battresult & NTAPM_NO_SYS_BATT) {
  195. //
  196. // it appears that the machine does not have
  197. // a battery, or least APM doesn't report one.
  198. //
  199. InTable->Function[HAL_APM_SLEEP_VECTOR] = NULL;
  200. } else {
  201. InTable->Function[HAL_APM_SLEEP_VECTOR] = &ApmSleep;
  202. }
  203. InTable->Function[HAL_APM_OFF_VECTOR] = &ApmOff;
  204. status = HalInitPowerManagement(InTable, NULL);
  205. if (! NT_SUCCESS(status)) {
  206. DrDebug(SYS_INFO, ("ntapm: HalInitPowerManagement failed\n"));
  207. return STATUS_UNSUCCESSFUL;
  208. }
  209. //
  210. // From this point on, INIT MUST succeed, otherwise we'll leave
  211. // the Hal with hanging pointers. So long as ApmSleep and ApmOff
  212. // are present in memory, things will be OK (though suspend may
  213. // not work, the box won't bugcheck.)
  214. //
  215. // init periodic timer, init suspend done event, init suspend dpc,
  216. // create and start poll thread
  217. //
  218. status = PsCreateSystemThread(&ThreadHandle,
  219. (ACCESS_MASK) 0L,
  220. NULL,
  221. NULL,
  222. NULL,
  223. &SuspendPollThread,
  224. NULL
  225. );
  226. //
  227. // the create didn't work, turns out that some apm functions
  228. // will still work, so just keep going.
  229. //
  230. if (! NT_SUCCESS(status)) {
  231. DrDebug(SYS_INFO, ("ntapm: could not create thread, but continunuing\n"));
  232. // return STATUS_INSUFFICIENT_RESOURCES;
  233. }
  234. KeInitializeTimerEx(&PollTimer, SynchronizationTimer);
  235. //
  236. // set a flag in the registry so that code with special hacks
  237. // based on apm being active can tell we're here and at least
  238. // nominally running
  239. //
  240. RtlInitUnicodeString(&unicodeString, rgzApmActiveFlag);
  241. InitializeObjectAttributes(
  242. &objectAttributes,
  243. &unicodeString,
  244. OBJ_CASE_INSENSITIVE,
  245. NULL, // handle
  246. NULL
  247. );
  248. status = ZwCreateKey(
  249. &hKey,
  250. KEY_WRITE,
  251. &objectAttributes,
  252. 0,
  253. NULL,
  254. REG_OPTION_VOLATILE,
  255. NULL
  256. );
  257. RtlInitUnicodeString(&unicodeString, rgzApmFlag);
  258. if (NT_SUCCESS(status)) {
  259. flagvalue = 1;
  260. ZwSetValueKey(
  261. hKey,
  262. &unicodeString,
  263. 0,
  264. REG_DWORD,
  265. &flagvalue,
  266. sizeof(ULONG)
  267. );
  268. ZwClose(hKey);
  269. }
  270. return STATUS_SUCCESS;
  271. }
  272. NTSTATUS
  273. ApmDispatch(
  274. PDEVICE_OBJECT DeviceObject,
  275. PIRP Irp
  276. )
  277. /*++
  278. Routine Description:
  279. When an application calls the Laptop driver, it comes here.
  280. This is NOT the dispatch point for PNP or Power calls.
  281. Arguments:
  282. DeviceObject - Pointer to the device object for this driver.
  283. Irp - Pointer to the request packet representing the I/O request.
  284. Return Value:
  285. The function value is the status of the operation.
  286. --*/
  287. {
  288. NTSTATUS status;
  289. PIO_STACK_LOCATION irpSp;
  290. PVOID outbuffer;
  291. ULONG outlength;
  292. PFILE_OBJECT fileObject;
  293. ULONG percent;
  294. BOOLEAN acon;
  295. PNTAPM_LINK pparms;
  296. PULONG p;
  297. ULONG t;
  298. UNREFERENCED_PARAMETER( DeviceObject );
  299. //
  300. // Get a pointer to the current stack location in the IRP. This is where
  301. // the function codes and parameters are stored.
  302. //
  303. irpSp = IoGetCurrentIrpStackLocation( Irp );
  304. Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
  305. Irp->IoStatus.Information = 0;
  306. switch (irpSp->MajorFunction) {
  307. //
  308. // device control
  309. //
  310. case IRP_MJ_INTERNAL_DEVICE_CONTROL:
  311. //
  312. // Only one valid command, which is to set (or null out) the
  313. // the link call pointers.
  314. //
  315. if (irpSp->MinorFunction == 0) {
  316. pparms = (PNTAPM_LINK) &(irpSp->Parameters.Others);
  317. if ((pparms->Signature == NTAPM_LINK_SIGNATURE) &&
  318. (pparms->Version == NTAPM_LINK_VERSION))
  319. {
  320. t = (ULONG) (&DoApmReportBatteryStatus);
  321. p = (PULONG)(pparms->BattLevelPtr);
  322. *p = t;
  323. BattChangeNotify = (PVOID)(pparms->ChangeNotify);
  324. Irp->IoStatus.Status = STATUS_SUCCESS;
  325. Irp->IoStatus.Information = 0;
  326. }
  327. }
  328. break;
  329. default:
  330. //
  331. // for all other operations, including create/open and close,
  332. // simply report failure, no matter what the operation is
  333. //
  334. break;
  335. }
  336. //
  337. // Copy the final status into the return status, complete the request and
  338. // get out of here.
  339. //
  340. status = Irp->IoStatus.Status;
  341. IoCompleteRequest( Irp, 0 );
  342. return status;
  343. }
  344. VOID
  345. SuspendPollThread(
  346. PVOID Dummy
  347. )
  348. /*++
  349. Routine Description:
  350. This routine is the laptop suspend polling thread.
  351. Arguments:
  352. Dummy Ignored parameter
  353. Return Value:
  354. None
  355. --*/
  356. {
  357. LARGE_INTEGER DueTime;
  358. ULONG LocalSuspendFlag;
  359. ULONG LocalClockFlag;
  360. KIRQL OldIrql;
  361. POWER_ACTION SystemAction;
  362. SYSTEM_POWER_STATE MinSystemState;
  363. ULONG Flags;
  364. ULONG ApmEvent;
  365. ULONG count, count2;
  366. LONG i, j;
  367. ULONG BatteryResult, PriorBatteryResult;
  368. ULONG BattPresentMask, PriorBattPresentMask;
  369. BOOLEAN DoANotify;
  370. PriorBatteryResult = BatteryResult = 0;
  371. //
  372. // Start the poll timer going, we'll wait for 1 second,
  373. // then POLL_INTERVAL milliseconds after that
  374. //
  375. DueTime.HighPart = 0;
  376. DueTime.LowPart = 10*1000*1000; // 10 million * 100nano = 1 second
  377. KeSetTimerEx(&PollTimer, DueTime, POLL_INTERVAL, NULL);
  378. while (1) {
  379. KeWaitForSingleObject(&PollTimer, Executive, KernelMode, TRUE, NULL);
  380. //
  381. // Call APM to poll for us
  382. //
  383. Flags = 0; // clear all flags
  384. switch (DoApmPoll()) {
  385. case APM_DO_CRITICAL_SUSPEND:
  386. //
  387. // Here we force the Flags to have the
  388. // CRITICAL flag set, other than that it's the same thing
  389. // as for normal suspend and standby
  390. //
  391. Flags = POWER_ACTION_CRITICAL;
  392. /* FALL FALL FALL */
  393. case APM_DO_SUSPEND:
  394. case APM_DO_STANDBY:
  395. //
  396. // For either Suspend or Standby, call the
  397. // the system and tell it to suspend us
  398. //
  399. DrDebug(SYS_INFO, ("ntapm: about to call OS to suspend\n"));
  400. SystemAction = PowerActionSleep;
  401. MinSystemState = PowerSystemSleeping3;
  402. OperationDone = FALSE;
  403. ZwInitiatePowerAction(
  404. SystemAction,
  405. MinSystemState,
  406. Flags,
  407. TRUE // async
  408. );
  409. //
  410. // If we just call ZwInitiatePowerAction, most machines
  411. // will work, but a few get impatient and try to suspend
  412. // out from under us before the OS comes back round and
  413. // does the suspend. So, we need to call ApmInProgress
  414. // every so often to make these bioses wait.
  415. //
  416. // BUT, if the system is truly wedged, or the suspend fails,
  417. // we don't want to spin calling ApmInProgress forever, so
  418. // limit the number of times we do that. And once the
  419. // operation is about to happen, stop.
  420. //
  421. // Since we're not polling while we're waiting for something
  422. // to happen, we'll use the poll timer...
  423. //
  424. if (OperationDone) goto Done;
  425. ApmInProgress();
  426. for (count = 0; count < APM_SPIN_LIMIT; count++) {
  427. for (count2 = 0; count2 < APM_POLL_MULTIPLY; count2++) {
  428. KeWaitForSingleObject(&PollTimer, Executive, KernelMode, TRUE, NULL);
  429. }
  430. if (OperationDone) goto Done;
  431. ApmInProgress();
  432. }
  433. DrDebug(SYS_INFO, ("ntapm: back from suspend\n"));
  434. Done:
  435. break;
  436. case APM_DO_NOTIFY:
  437. //
  438. // Call out to battery driver with Notify op here
  439. //
  440. if (BattChangeNotify) {
  441. //DrDebug(SYS_INFO, ("ntapm: about to make notify call\n"));
  442. BattChangeNotify();
  443. //DrDebug(SYS_INFO, ("ntapm: back from notify call\n"));
  444. PriorBatteryResult = DoApmReportBatteryStatus();
  445. }
  446. break;
  447. case APM_DO_FIXCLOCK:
  448. case APM_DO_NOTHING:
  449. default:
  450. //
  451. // fixing the clock is too scary with other power
  452. // code doing it, so we don't do it here.
  453. //
  454. // nothing is nothing
  455. //
  456. // if we don't understand, do nothing
  457. // (remember, bios will force op under us if it's critical)
  458. //
  459. if (BattChangeNotify) {
  460. //
  461. // we hereby redefine "nothing" to be "check on the
  462. // status of the bleeding battery" since not all bioses
  463. // tell us what is going on in a timely fashion
  464. //
  465. DoANotify = FALSE;
  466. BatteryResult = DoApmReportBatteryStatus();
  467. if ((BatteryResult & NTAPM_ACON) !=
  468. (PriorBatteryResult & NTAPM_ACON))
  469. {
  470. DoANotify = TRUE;
  471. }
  472. if ((BatteryResult & NTAPM_BATTERY_STATE) !=
  473. (PriorBatteryResult & NTAPM_BATTERY_STATE))
  474. {
  475. DoANotify = TRUE;
  476. }
  477. i = BatteryResult & NTAPM_POWER_PERCENT;
  478. j = PriorBatteryResult & NTAPM_POWER_PERCENT;
  479. if (( (i - j) > 25 ) ||
  480. ( (j - i) > 25 ))
  481. {
  482. DoANotify = TRUE;
  483. }
  484. PriorBattPresentMask = PriorBatteryResult & (NTAPM_NO_BATT | NTAPM_NO_SYS_BATT);
  485. BattPresentMask = BatteryResult & (NTAPM_NO_BATT | NTAPM_NO_SYS_BATT);
  486. if (BattPresentMask != PriorBattPresentMask) {
  487. //
  488. // battery either went or reappeared
  489. //
  490. DoANotify = TRUE;
  491. }
  492. PriorBatteryResult = BatteryResult;
  493. if (DoANotify) {
  494. ASSERT(BattChangeNotify);
  495. BattChangeNotify();
  496. }
  497. }
  498. break;
  499. } // switch
  500. } // while
  501. }
  502. VOID
  503. ApmSleep(
  504. VOID
  505. )
  506. /*++
  507. Routine Description:
  508. When the OS calls the Hal's S3 vector, the hal calls us here.
  509. We call APM to put the box to sleep
  510. --*/
  511. {
  512. OperationDone = TRUE;
  513. if (ApmWorks) {
  514. DrDebug(SYS_L2,("ntapm: apmsleep: calling apm to sleep\n"));
  515. ApmSuspendSystem();
  516. DrDebug(SYS_L2,("ntapm: apmsleep: back from apm call\n"));
  517. } else { // ApmWorks == FALSE
  518. DrDebug(SYS_INFO, ("ntapm: ApmSleep: no APM attached, Exit\n"));
  519. }
  520. }
  521. VOID
  522. ApmOff(
  523. VOID
  524. )
  525. /*++
  526. Routine Description:
  527. When the OS calls the Hal's S4 or S5 routines, the hal calls us here.
  528. We turn the machine off.
  529. --*/
  530. {
  531. OperationDone = TRUE;
  532. if (ApmWorks) {
  533. DrDebug(SYS_L2,("ntapm: ApmOff: calling APM\n"));
  534. ApmTurnOffSystem();
  535. DrDebug(SYS_INFO,("ntapm: ApmOff: we are back from Off, uh oh!\n"));
  536. }
  537. }
  538. NTSTATUS
  539. DoApmInitMachine(
  540. )
  541. /*++
  542. Routine Description:
  543. This routine makes the BIOS ready to interact with laptop.sys.
  544. This code works with APM.
  545. Return Value:
  546. None
  547. --*/
  548. {
  549. NTSTATUS Status;
  550. ULONG Ebx, Ecx;
  551. DrDebug(SYS_INIT,("ApmInitMachine: enter\n"));
  552. Status = ApmInitializeConnection ();
  553. if (NT_SUCCESS(Status)) {
  554. DrDebug(SYS_INIT,("ApmInitMachine: Connection established!\n"));
  555. //
  556. // Note that ntdetect (2nd version) will have set apm bios
  557. // to min of (machine version) and (1.2)
  558. // (so a 1.1 bios will be set to 1.1, a 1.2 to 1.2, a 1.3 to 1.2
  559. //
  560. ApmWorks = 1;
  561. } else {
  562. DrDebug(SYS_INIT,("ApmInitMachine: No connection made!\n"));
  563. ApmWorks = 0;
  564. }
  565. DrDebug(SYS_INIT,("ApmInitMachine: exit\n"));
  566. return Status;
  567. }
  568. ULONG
  569. DoApmPoll(
  570. )
  571. /*++
  572. Routine Description:
  573. This routine is called in the ntapm.sys polling loop to poll
  574. for APM events. It returns APM_DO_NOTHING unless there is
  575. actually something meaningful for us to do. (That is, things
  576. we don't want and/or don't understand are filtered down to
  577. APM_DO_NOTHING)
  578. Return Value:
  579. APM event code.
  580. --*/
  581. {
  582. DrDebug(SYS_L2,("ApmPoll: enter\n"));
  583. if (ApmWorks) {
  584. return ApmCheckForEvent();
  585. } else { // ApmWorks == FALSE
  586. DrDebug(SYS_L2,("ApmPoll: no APM attachment, exit\n"));
  587. return APM_DO_NOTHING;
  588. }
  589. }
  590. ULONG
  591. DoApmReportBatteryStatus()
  592. /*++
  593. Routine Description:
  594. This routine queries the BIOS/HW for the state of the power connection
  595. and the current battery level.
  596. Arguments:
  597. Return Value:
  598. ULONG, fields defined by NTAPM_POWER_STATE and NTAPM_POWER_PERCENT
  599. --*/
  600. {
  601. ULONG percent = 100;
  602. ULONG ac = 1;
  603. ULONG Status = 0;
  604. ULONG Ebx = 0;
  605. ULONG Ecx = 0;
  606. ULONG flags = 0;
  607. ULONG result = 0;
  608. DrDebug(SYS_L2,("ntapm: DoApmReportBatteryStatus: enter\n"));
  609. if (ApmWorks) {
  610. //
  611. // Call APM BIOS and get power status
  612. //
  613. Ebx = 1;
  614. Ecx = 0;
  615. Status = ApmFunction (APM_GET_POWER_STATUS, &Ebx, &Ecx);
  616. if (!NT_SUCCESS(Status)) {
  617. //
  618. // If we cannot read the power, jam in 50% and power off!
  619. //
  620. DrDebug(SYS_INFO,("ntapm: DoApmReportBatteryStatus: Can't get power!\n"));
  621. percent = 50;
  622. ac = 0;
  623. } else {
  624. //
  625. // Get battery/AC state -- anything but full 'on-line' means on
  626. // battery
  627. //
  628. ac = (Ebx & APM_LINEMASK) >> APM_LINEMASK_SHIFT;
  629. if (ac != APM_GET_LINE_ONLINE) {
  630. ac = 0;
  631. }
  632. percent = Ecx & APM_PERCENT_MASK;
  633. }
  634. } else {
  635. DrDebug(SYS_INFO,("ntapm: DoApmReportBatteryStatus: no APM attachment\n"));
  636. DrDebug(SYS_INFO,("ntapm: Return AC OFF 50% Life\n"));
  637. percent = 50;
  638. ac = FALSE;
  639. }
  640. flags = 0;
  641. result = 0;
  642. if (Ecx & APM_NO_BATT) {
  643. result |= NTAPM_NO_BATT;
  644. }
  645. if (Ecx & APM_NO_SYS_BATT) {
  646. result |= NTAPM_NO_SYS_BATT;
  647. }
  648. if ((percent == 255) || (Ecx & APM_NO_BATT) || (Ecx & APM_NO_SYS_BATT)) {
  649. percent = 0;
  650. } else if (percent > 100) {
  651. percent = 100;
  652. }
  653. if ((Ecx & APM_BATT_CHARGING) && (percent < 100)) {
  654. flags |= BATTERY_CHARGING;
  655. } else {
  656. flags |= BATTERY_DISCHARGING;
  657. }
  658. if (Ecx & APM_BATT_CRITICAL) {
  659. flags |= BATTERY_CRITICAL;
  660. percent = 1;
  661. }
  662. if (ac) {
  663. result |= NTAPM_ACON;
  664. flags |= BATTERY_POWER_ON_LINE;
  665. }
  666. result |= (flags << NTAPM_BATTERY_STATE_SHIFT);
  667. result |= percent;
  668. DrDebug(SYS_L2,("ntapm: BatteryLevel: %08lx Percent: %d flags: %1x ac: %1x\n",
  669. result, percent, flags, ac));
  670. return result;
  671. }
  672. BOOLEAN
  673. IsAcpiMachine(
  674. VOID
  675. )
  676. /*++
  677. Routine Description:
  678. IsAcpiMachine reports whether the OS thinks this is an ACPI
  679. machine or not.
  680. Return Value:
  681. FALSE - this is NOT an acpi machine
  682. TRUE - this IS an acpi machine
  683. --*/
  684. {
  685. UNICODE_STRING unicodeString;
  686. OBJECT_ATTRIBUTES objectAttributes;
  687. HANDLE hKey;
  688. NTSTATUS status;
  689. PKEY_VALUE_PARTIAL_INFORMATION pvpi;
  690. UCHAR buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION)+sizeof(ULONG)+1];
  691. ULONG junk;
  692. PULONG pdw;
  693. ULONG start;
  694. RtlInitUnicodeString(&unicodeString, rgzAcpiKey);
  695. InitializeObjectAttributes(
  696. &objectAttributes,
  697. &unicodeString,
  698. OBJ_CASE_INSENSITIVE,
  699. NULL,
  700. NULL
  701. );
  702. status = ZwOpenKey(&hKey, KEY_READ, &objectAttributes);
  703. if (!NT_SUCCESS(status)) {
  704. return FALSE;
  705. }
  706. RtlInitUnicodeString(&unicodeString, rgzAcpiStart);
  707. pvpi = (PKEY_VALUE_PARTIAL_INFORMATION)buffer;
  708. status = ZwQueryValueKey(
  709. hKey,
  710. &unicodeString,
  711. KeyValuePartialInformation,
  712. pvpi,
  713. sizeof(buffer),
  714. &junk
  715. );
  716. if ( (NT_SUCCESS(status)) &&
  717. (pvpi->Type == REG_DWORD) &&
  718. (pvpi->DataLength == sizeof(ULONG)) )
  719. {
  720. pdw = (PULONG)&(pvpi->Data[0]);
  721. if (*pdw == 0) {
  722. ZwClose(hKey);
  723. return TRUE;
  724. }
  725. }
  726. ZwClose(hKey);
  727. return FALSE;
  728. }