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.

899 lines
20 KiB

  1. /*++
  2. Module Name:
  3. apm.c
  4. Abstract:
  5. A collection of code that allows NT calls into APM.
  6. The code in this routine depends on data being set up in the registry
  7. Author:
  8. Environment:
  9. Kernel mode only.
  10. Revision History:
  11. --*/
  12. #include "ntosp.h"
  13. #include "zwapi.h"
  14. #include "apmp.h"
  15. #include "apm.h"
  16. #include "apmcrib.h"
  17. #include "ntapmdbg.h"
  18. #include "ntapmlog.h"
  19. #include "ntapmp.h"
  20. #define MAX_SEL 30 // attempts before giving up
  21. ULONG ApmCallActive = 0;
  22. ULONG ApmCallEax = 0;
  23. ULONG ApmCallEbx = 0;
  24. ULONG ApmCallEcx = 0;
  25. WCHAR rgzMultiFunctionAdapter[] =
  26. L"\\Registry\\Machine\\Hardware\\Description\\System\\MultifunctionAdapter";
  27. WCHAR rgzConfigurationData[] = L"Configuration Data";
  28. WCHAR rgzIdentifier[] = L"Identifier";
  29. WCHAR rgzPCIIndetifier[] = L"PCI";
  30. WCHAR rgzApmConnect[]= L"\\Registry\\Machine\\Hardware\\ApmConnect";
  31. WCHAR rgzApmConnectValue[] = L"ApmConnectValue";
  32. APM_CONNECT Apm;
  33. //
  34. // First time we get any non-recoverable error back
  35. // from APM, record what sort of call hit it and what
  36. // the error code was here
  37. //
  38. ULONG ApmLogErrorFunction = -1L;
  39. ULONG ApmLogErrorCode = 0L;
  40. ULONG ApmErrorLogSequence = 0xf3;
  41. #ifdef ALLOC_PRAGMA
  42. #pragma alloc_text(PAGE,ApmInitializeConnection)
  43. #endif
  44. //
  45. // Internal prototypes
  46. //
  47. BOOLEAN
  48. ApmpBuildGdtEntry (
  49. IN ULONG Index,
  50. PKGDTENTRY GdtEntry,
  51. IN ULONG SegmentBase
  52. );
  53. VOID
  54. NtApmLogError(
  55. NTSTATUS ErrorCode,
  56. UCHAR ErrorByte
  57. );
  58. NTSTATUS
  59. ApmInitializeConnection (
  60. VOID
  61. )
  62. /*++
  63. Routine Description:
  64. Initialize data needed to call APM bios functions -- look in the
  65. registry to find out if this machine has had its APM capability
  66. detected.
  67. NOTE: If you change the recognition code, change the
  68. code to IsApmPresent as well!
  69. Arguments:
  70. None
  71. Return Value:
  72. STATUS_SUCCESS if we were able to connect to the APM BIOS.
  73. --*/
  74. {
  75. PCM_PARTIAL_RESOURCE_DESCRIPTOR PDesc;
  76. PCM_FULL_RESOURCE_DESCRIPTOR Desc;
  77. PKEY_VALUE_FULL_INFORMATION ValueInfo;
  78. PAPM_REGISTRY_INFO ApmEntry;
  79. OBJECT_ATTRIBUTES objectAttributes;
  80. UNICODE_STRING unicodeString, ConfigName, IdentName;
  81. KGDTENTRY GdtEntry;
  82. NTSTATUS status;
  83. BOOLEAN Error;
  84. HANDLE hMFunc, hBus, hApmConnect;
  85. USHORT Sel[MAX_SEL], TSel;
  86. UCHAR buffer [sizeof(APM_REGISTRY_INFO) + 99];
  87. WCHAR wstr[8];
  88. ULONG i, j, Count, junk;
  89. PWSTR p;
  90. USHORT volatile Offset;
  91. //
  92. // Look in the registery for the "APM bus" data
  93. //
  94. RtlInitUnicodeString(&unicodeString, rgzMultiFunctionAdapter);
  95. InitializeObjectAttributes(
  96. &objectAttributes,
  97. &unicodeString,
  98. OBJ_CASE_INSENSITIVE,
  99. NULL, // handle
  100. NULL
  101. );
  102. status = ZwOpenKey(&hMFunc, KEY_READ, &objectAttributes);
  103. if (!NT_SUCCESS(status)) {
  104. return status;
  105. }
  106. unicodeString.Buffer = wstr;
  107. unicodeString.MaximumLength = sizeof (wstr);
  108. RtlInitUnicodeString(&ConfigName, rgzConfigurationData);
  109. RtlInitUnicodeString(&IdentName, rgzIdentifier);
  110. ValueInfo = (PKEY_VALUE_FULL_INFORMATION) buffer;
  111. for (i=0; TRUE; i++) {
  112. RtlIntegerToUnicodeString(i, 10, &unicodeString);
  113. InitializeObjectAttributes(
  114. &objectAttributes,
  115. &unicodeString,
  116. OBJ_CASE_INSENSITIVE,
  117. hMFunc,
  118. NULL
  119. );
  120. status = ZwOpenKey(&hBus, KEY_READ, &objectAttributes);
  121. if (!NT_SUCCESS(status)) {
  122. //
  123. // Out of Multifunction adapter entries...
  124. //
  125. ZwClose (hMFunc);
  126. return STATUS_UNSUCCESSFUL;
  127. }
  128. //
  129. // Check the Indentifier to see if this is a APM entry
  130. //
  131. status = ZwQueryValueKey (
  132. hBus,
  133. &IdentName,
  134. KeyValueFullInformation,
  135. ValueInfo,
  136. sizeof (buffer),
  137. &junk
  138. );
  139. if (!NT_SUCCESS (status)) {
  140. ZwClose (hBus);
  141. continue;
  142. }
  143. p = (PWSTR) ((PUCHAR) ValueInfo + ValueInfo->DataOffset);
  144. if (p[0] != L'A' || p[1] != L'P' || p[2] != L'M' || p[3] != 0) {
  145. ZwClose (hBus);
  146. continue;
  147. }
  148. status = ZwQueryValueKey(
  149. hBus,
  150. &ConfigName,
  151. KeyValueFullInformation,
  152. ValueInfo,
  153. sizeof (buffer),
  154. &junk
  155. );
  156. ZwClose (hBus);
  157. if (!NT_SUCCESS(status)) {
  158. continue ;
  159. }
  160. Desc = (PCM_FULL_RESOURCE_DESCRIPTOR) ((PUCHAR)
  161. ValueInfo + ValueInfo->DataOffset);
  162. PDesc = (PCM_PARTIAL_RESOURCE_DESCRIPTOR) ((PUCHAR)
  163. Desc->PartialResourceList.PartialDescriptors);
  164. if (PDesc->Type == CmResourceTypeDeviceSpecific) {
  165. // got it..
  166. ApmEntry = (PAPM_REGISTRY_INFO) (PDesc+1);
  167. break;
  168. }
  169. }
  170. //DbgPrint("ApmEntry: %08lx\n", ApmEntry);
  171. //DbgPrint("Signature: %c%c%c\n", ApmEntry->Signature[0], ApmEntry->Signature[1], ApmEntry->Signature[2]);
  172. if ( (ApmEntry->Signature[0] != 'A') ||
  173. (ApmEntry->Signature[1] != 'P') ||
  174. (ApmEntry->Signature[2] != 'M') )
  175. {
  176. return STATUS_UNSUCCESSFUL;
  177. }
  178. //DbgPrint("ApmEntry->Valid: %0d\n", ApmEntry->Valid);
  179. if (ApmEntry->Valid != 1) {
  180. return STATUS_UNSUCCESSFUL;
  181. }
  182. //
  183. // Apm found - initialize the connection
  184. //
  185. KeInitializeSpinLock(&Apm.CallLock);
  186. //
  187. // Allocate a bunch of selectors
  188. //
  189. for (Count=0; Count < MAX_SEL; Count++) {
  190. status = KeI386AllocateGdtSelectors (Sel+Count, 1);
  191. if (!NT_SUCCESS(status)) {
  192. break;
  193. }
  194. }
  195. //
  196. // Sort the selctors via bubble sort
  197. //
  198. for (i=0; i < Count; i++) {
  199. for (j = i+1; j < Count; j++) {
  200. if (Sel[j] < Sel[i]) {
  201. TSel = Sel[i];
  202. Sel[i] = Sel[j];
  203. Sel[j] = TSel;
  204. }
  205. }
  206. }
  207. //
  208. // Now look for 3 consecutive values
  209. //
  210. for (i=0; i < Count - 3; i++) {
  211. if (Sel[i]+8 == Sel[i+1] && Sel[i]+16 == Sel[i+2]) {
  212. break;
  213. }
  214. }
  215. if (i >= Count - 3) {
  216. DrDebug(APM_INFO,("APM: Could not allocate consecutive selectors\n"));
  217. return STATUS_UNSUCCESSFUL;
  218. }
  219. //
  220. // Save the results
  221. //
  222. Apm.Selector[0] = Sel[i+0];
  223. Apm.Selector[1] = Sel[i+1];
  224. Apm.Selector[2] = Sel[i+2];
  225. Sel[i+0] = 0;
  226. Sel[i+1] = 0;
  227. Sel[i+2] = 0;
  228. //
  229. // Free unused selectors
  230. //
  231. for (i=0; i < Count; i++) {
  232. if (Sel[i]) {
  233. KeI386ReleaseGdtSelectors (Sel+i, 1);
  234. }
  235. }
  236. //
  237. // Initialize the selectors to use the APM bios
  238. //
  239. Error = FALSE;
  240. //
  241. // initialize 16 bit code selector
  242. //
  243. GdtEntry.LimitLow = 0xFFFF;
  244. GdtEntry.HighWord.Bytes.Flags1 = 0;
  245. GdtEntry.HighWord.Bytes.Flags2 = 0;
  246. GdtEntry.HighWord.Bits.Pres = 1;
  247. GdtEntry.HighWord.Bits.Dpl = DPL_SYSTEM;
  248. GdtEntry.HighWord.Bits.Granularity = GRAN_BYTE;
  249. GdtEntry.HighWord.Bits.Type = 31;
  250. GdtEntry.HighWord.Bits.Default_Big = 0;
  251. Error |= ApmpBuildGdtEntry (0, &GdtEntry, ApmEntry->Code16BitSegment);
  252. //
  253. // initialize 16 bit data selector
  254. //
  255. GdtEntry.LimitLow = 0xFFFF;
  256. GdtEntry.HighWord.Bytes.Flags1 = 0;
  257. GdtEntry.HighWord.Bytes.Flags2 = 0;
  258. GdtEntry.HighWord.Bits.Pres = 1;
  259. GdtEntry.HighWord.Bits.Dpl = DPL_SYSTEM;
  260. GdtEntry.HighWord.Bits.Granularity = GRAN_BYTE;
  261. GdtEntry.HighWord.Bits.Type = 19;
  262. GdtEntry.HighWord.Bits.Default_Big = 1;
  263. Error |= ApmpBuildGdtEntry (1, &GdtEntry, ApmEntry->Data16BitSegment);
  264. //
  265. // If we leave it like this, the compiler generates incorrect code!!!
  266. // Apm.Code16BitOffset = ApmEntry->Code16BitOffset;
  267. // So do this instead.
  268. //
  269. Offset = ApmEntry->Code16BitOffset;
  270. Apm.Code16BitOffset = (ULONG) Offset;
  271. //DbgPrint("Apm@%08lx ApmEntry@%08lx\n", &Apm, ApmEntry);
  272. //DbgBreakPoint();
  273. #if 0
  274. //
  275. // to make the poweroff path in the Hal about 20 times simpler,
  276. // as well as make it work, pass our mappings on to the Hal, so
  277. // it can use them.
  278. //
  279. RtlInitUnicodeString(&unicodeString, rgzApmConnect);
  280. InitializeObjectAttributes(
  281. &objectAttributes,
  282. &unicodeString,
  283. OBJ_CASE_INSENSITIVE,
  284. NULL,
  285. NULL
  286. );
  287. status = ZwCreateKey(
  288. &hApmConnect,
  289. KEY_ALL_ACCESS,
  290. &objectAttributes,
  291. 0,
  292. NULL,
  293. REG_OPTION_VOLATILE,
  294. &junk
  295. );
  296. RtlInitUnicodeString(&unicodeString, rgzApmConnectValue);
  297. if (NT_SUCCESS(status)) {
  298. status = ZwSetValueKey(
  299. hApmConnect,
  300. &unicodeString,
  301. 0,
  302. REG_BINARY,
  303. &Apm,
  304. sizeof(APM_CONNECT)
  305. );
  306. ZwClose(hApmConnect);
  307. }
  308. #endif
  309. return Error ? STATUS_UNSUCCESSFUL : STATUS_SUCCESS;
  310. }
  311. BOOLEAN
  312. ApmpBuildGdtEntry (
  313. IN ULONG Index,
  314. PKGDTENTRY GdtEntry,
  315. IN ULONG SegmentBase
  316. )
  317. /*++
  318. Routine Description:
  319. Build the Gdt Entry
  320. Arguments:
  321. Index Index of entry
  322. GdtEntry
  323. SegmentBase
  324. Return Value:
  325. TRUE if we encountered any error, FALSE if successful
  326. --*/
  327. {
  328. PHYSICAL_ADDRESS PhysAddr;
  329. ULONG SegBase;
  330. PVOID VirtualAddress;
  331. ULONG AddressSpace;
  332. BOOLEAN flag;
  333. //
  334. // Convert Segment to phyiscal address
  335. //
  336. PhysAddr.LowPart = SegmentBase << 4;
  337. PhysAddr.HighPart = 0;
  338. //
  339. // Translate physical address from ISA bus 0
  340. //
  341. AddressSpace = 0;
  342. flag = HalTranslateBusAddress (
  343. Isa, 0,
  344. PhysAddr,
  345. &AddressSpace,
  346. &PhysAddr
  347. );
  348. if (AddressSpace != 0 || !flag) {
  349. return TRUE;
  350. }
  351. //
  352. // Map into virtual address space
  353. //
  354. VirtualAddress = MmMapIoSpace (
  355. PhysAddr,
  356. 0x10000, // 64k
  357. TRUE
  358. );
  359. Apm.VirtualAddress[Index] = VirtualAddress;
  360. //
  361. // Map virtual address to selector:0 address
  362. //
  363. SegBase = (ULONG) VirtualAddress;
  364. GdtEntry->BaseLow = (USHORT) (SegBase & 0xffff);
  365. GdtEntry->HighWord.Bits.BaseMid = (UCHAR) (SegBase >> 16) & 0xff;
  366. GdtEntry->HighWord.Bits.BaseHi = (UCHAR) (SegBase >> 24) & 0xff;
  367. KeI386SetGdtSelector (Apm.Selector[Index], GdtEntry);
  368. return FALSE;
  369. }
  370. NTSTATUS
  371. ApmFunction (
  372. IN ULONG ApmFunctionCode,
  373. IN OUT PULONG Ebx,
  374. IN OUT PULONG Ecx
  375. )
  376. /*++
  377. Routine Description:
  378. Call APM BIOS with ApmFunctionCode and appropriate arguments
  379. Arguments:
  380. ApmFunctionCode Apm function code
  381. Ebx Ebx param to APM BIOS
  382. Ecx Ecx param to APM BIOS
  383. Return Value:
  384. STATUS_SUCCESS with Ebx, Ebx
  385. otherwise an NTSTATUS code
  386. --*/
  387. {
  388. KIRQL OldIrql;
  389. ULONG ApmStatus;
  390. CONTEXT Regs;
  391. if (!Apm.Selector[0]) {
  392. //
  393. // Attempting to call APM BIOS without a sucessfull connection
  394. //
  395. DrDebug(APM_INFO,("APM: ApmFunction - APM not initialized\n"));
  396. DrDebug(APM_INFO,
  397. ("APM: ApmFunction failing function %x\n", ApmFunctionCode));
  398. return STATUS_UNSUCCESSFUL;
  399. }
  400. //DbgPrint("APM: ApmFunction: %08lx Ebx: %08lx Ecx: %08lx\n", ApmFunctionCode, *Ebx, *Ecx);
  401. //
  402. // Serialize calls into the APM bios
  403. //
  404. KeAcquireSpinLock(&Apm.CallLock, &OldIrql);
  405. ApmCallActive += 1;
  406. //
  407. // ASM interface to call the BIOS
  408. //
  409. //
  410. // Fill in general registers for 16bit bios call.
  411. // Note: only the following registers are passed. Specifically,
  412. // SS and ESP are not passed and are generated by the system.
  413. //
  414. Regs.ContextFlags = CONTEXT_INTEGER | CONTEXT_SEGMENTS;
  415. Regs.Eax = ApmFunctionCode;
  416. Regs.Ebx = *Ebx;
  417. Regs.Ecx = *Ecx;
  418. Regs.Edx = 0;
  419. Regs.Esi = 0;
  420. Regs.Edi = 0;
  421. Regs.SegGs = 0;
  422. Regs.SegFs = 0;
  423. Regs.SegEs = Apm.Selector[1];
  424. Regs.SegDs = Apm.Selector[1];
  425. Regs.SegCs = Apm.Selector[0];
  426. Regs.Eip = Apm.Code16BitOffset;
  427. Regs.EFlags = 0x200; // interrupts enabled
  428. ApmCallEax = Regs.Eax;
  429. ApmCallEbx = Regs.Ebx;
  430. ApmCallEcx = Regs.Ecx;
  431. //
  432. // call the 16:16 bios function
  433. //
  434. KeI386Call16BitFunction (&Regs);
  435. ApmCallActive -= 1;
  436. //
  437. // Release serialization
  438. //
  439. KeReleaseSpinLock(&Apm.CallLock, OldIrql);
  440. //
  441. // Get the results
  442. //
  443. ApmStatus = 0;
  444. if (Regs.EFlags & 0x1) { // check carry flag
  445. ApmStatus = (Regs.Eax >> 8) & 0xff;
  446. }
  447. *Ebx = Regs.Ebx;
  448. *Ecx = Regs.Ecx;
  449. //
  450. // save for debug use
  451. //
  452. if (ApmStatus) {
  453. if (ApmLogErrorCode != 0) {
  454. ApmLogErrorFunction = ApmFunctionCode;
  455. ApmLogErrorCode = ApmStatus;
  456. }
  457. }
  458. //
  459. // log specific errors of value to the user
  460. //
  461. if (ApmFunctionCode == APM_SET_POWER_STATE) {
  462. if (ApmStatus != 0)
  463. {
  464. NtApmLogError(NTAPM_SET_POWER_FAILURE, (UCHAR)ApmStatus);
  465. }
  466. }
  467. DrDebug(APM_INFO,("APM: ApmFunction result is %x\n", ApmStatus));
  468. return ApmStatus;
  469. }
  470. WCHAR ApmConvArray[] = {'0', '1','2','3','4','5','6','7','8','9','A','B','C','D','E','F',0};
  471. VOID
  472. NtApmLogError(
  473. NTSTATUS ErrorCode,
  474. UCHAR ErrorByte
  475. )
  476. /*++
  477. Routine Description:
  478. Report the incoming error to the event log.
  479. Arguments:
  480. ErrorCode - the ntstatus type value which will match the message template
  481. and get reported to the user.
  482. ErrorByte - the 1 byte value returned by APM bios
  483. Return Value:
  484. None.
  485. --*/
  486. {
  487. PIO_ERROR_LOG_PACKET errorLogPacket;
  488. PUCHAR p;
  489. PWCHAR pw;
  490. errorLogPacket = IoAllocateErrorLogEntry(
  491. NtApmDriverObject,
  492. (UCHAR)(sizeof(IO_ERROR_LOG_PACKET)+8)
  493. );
  494. if (errorLogPacket != NULL) {
  495. errorLogPacket->ErrorCode = ErrorCode;
  496. errorLogPacket->SequenceNumber = ApmErrorLogSequence++;
  497. errorLogPacket->FinalStatus = STATUS_UNSUCCESSFUL;
  498. errorLogPacket->UniqueErrorValue = 0;
  499. errorLogPacket->NumberOfStrings = 1;
  500. errorLogPacket->RetryCount = 0;
  501. errorLogPacket->MajorFunctionCode = 0;
  502. errorLogPacket->DeviceOffset.HighPart = 0;
  503. errorLogPacket->DeviceOffset.LowPart = 0;
  504. errorLogPacket->DumpDataSize = 0;
  505. //
  506. // why our own conversion code? because we can't get the fine
  507. // RTL routines to put the data in the right sized output buffer
  508. //
  509. p = (PUCHAR) &(errorLogPacket->DumpData[0]);
  510. pw = (PWCHAR)p;
  511. pw[0] = ApmConvArray[(ULONG)((ErrorByte & 0xf0)>>4)];
  512. pw[1] = ApmConvArray[(ULONG)(ErrorByte & 0xf)];
  513. pw[2] = L'\0';
  514. errorLogPacket->StringOffset =
  515. ((PUCHAR)(&(errorLogPacket->DumpData[0]))) - ((PUCHAR)errorLogPacket);
  516. IoWriteErrorLogEntry(errorLogPacket);
  517. }
  518. return;
  519. }
  520. NTSTATUS
  521. ApmSuspendSystem (
  522. VOID
  523. )
  524. /*++
  525. Routine Description:
  526. Suspend the system
  527. Arguments:
  528. none
  529. Return Value:
  530. STATUS_SUCCESS if the computer was suspended & then resumed
  531. --*/
  532. {
  533. ULONG Ebx, Ecx;
  534. NTSTATUS Status;
  535. //
  536. // Use ApmFunction to suspend machine
  537. //
  538. DrDebug(APM_L2,("APM: ApmSuspendSystem: enter\n"));
  539. Ebx = APM_DEVICE_ALL;
  540. Ecx = APM_SET_SUSPEND;
  541. Status = ApmFunction (APM_SET_POWER_STATE, &Ebx, &Ecx);
  542. DrDebug(APM_L2,("APM: ApmSuspendSystem: exit\n"));
  543. return Status;
  544. }
  545. VOID
  546. ApmTurnOffSystem(
  547. VOID
  548. )
  549. /*++
  550. Routine Description:
  551. Turn the system off.
  552. Arguments:
  553. none
  554. --*/
  555. {
  556. ULONG Ebx, Ecx;
  557. NTSTATUS Status;
  558. //
  559. // Use ApmFunction to put machine into StandBy mode
  560. //
  561. DrDebug(APM_L2,("APM: ApmTurnOffSystem: enter\n"));
  562. Ebx = APM_DEVICE_ALL;
  563. Ecx = APM_SET_OFF;
  564. Status = ApmFunction (APM_SET_POWER_STATE, &Ebx, &Ecx);
  565. DrDebug(APM_L2,("APM: ApmTurnOffSystem: exit\n"));
  566. return;
  567. }
  568. VOID
  569. ApmInProgress(
  570. VOID
  571. )
  572. /*++
  573. Routine Description:
  574. This routine informs the BIOS to cool its jets for 5 seconds
  575. while we continue to operate
  576. Arguments:
  577. none
  578. Return Value:
  579. STATUS_SUCCESS if the computer was suspended & then resumed
  580. --*/
  581. {
  582. ULONG Ebx, Ecx;
  583. NTSTATUS Status;
  584. //
  585. // Use ApmFunction to tell BIOS to cool its heals
  586. //
  587. Ebx = APM_DEVICE_ALL;
  588. Ecx = APM_SET_PROCESSING;
  589. Status = ApmFunction (APM_SET_POWER_STATE, &Ebx, &Ecx);
  590. return;
  591. }
  592. ULONG
  593. ApmCheckForEvent (
  594. VOID
  595. )
  596. /*++
  597. Routine Description:
  598. Poll for APM event
  599. Arguments:
  600. Return Value:
  601. We return:
  602. APM_DO_code from apmp.h
  603. APM_DO_NOTHING 0
  604. APM_DO_SUSPEND 1
  605. APM_DO_STANDBY 2
  606. APM_DO_FIXCLOCK 3
  607. APM_DO_NOTIFY 4
  608. APM_DO_CRITICAL_SUSPEND 5
  609. --*/
  610. {
  611. NTSTATUS Status;
  612. ULONG Ebx, Ecx;
  613. ULONG returnvalue;
  614. //
  615. // Read an event. Might get nothing.
  616. //
  617. returnvalue = APM_DO_NOTHING;
  618. Ebx = 0;
  619. Ecx = 0;
  620. Status = ApmFunction (APM_GET_EVENT, &Ebx, &Ecx);
  621. if (Status != STATUS_SUCCESS) {
  622. return returnvalue;
  623. }
  624. //
  625. // Handle APM reported event
  626. //
  627. DrDebug(APM_L2,("APM: ApmCheckForEvent, code is %d\n", Ebx));
  628. switch (Ebx) {
  629. //
  630. // say wer're working on it and set up for standby
  631. //
  632. case APM_SYS_STANDBY_REQUEST:
  633. case APM_USR_STANDBY_REQUEST:
  634. DrDebug(APM_L2,("APM: ApmCheckForEvent, standby request\n"));
  635. ApmInProgress();
  636. returnvalue = APM_DO_STANDBY;
  637. break;
  638. //
  639. // say we're working on it and set up for suspend
  640. //
  641. case APM_SYS_SUSPEND_REQUEST:
  642. case APM_USR_SUSPEND_REQUEST:
  643. case APM_BATTERY_LOW_NOTICE:
  644. DrDebug(APM_L2,
  645. ("APM: ApmCheckForEvent, suspend or battery low\n"));
  646. ApmInProgress();
  647. returnvalue = APM_DO_SUSPEND;
  648. break;
  649. //
  650. // Say we're working on it, and setup for CRITICAL suspend
  651. //
  652. case APM_CRITICAL_SYSTEM_SUSPEND_REQUEST:
  653. DrDebug(APM_L2, ("APM: Apmcheckforevent, critical suspend\n"));
  654. ApmInProgress();
  655. returnvalue = APM_DO_CRITICAL_SUSPEND;
  656. break;
  657. //
  658. // ignore this because we have no idea what to do with it
  659. //
  660. case APM_CRITICAL_RESUME_NOTICE:
  661. DrDebug(APM_L2,("APM: ApmCheckForEvent, critical resume\n"));
  662. break;
  663. case APM_UPDATE_TIME_EVENT:
  664. DrDebug(APM_L2,("APM: ApmCheckForEvent, update time\n"));
  665. returnvalue = APM_DO_FIXCLOCK;
  666. break;
  667. case APM_POWER_STATUS_CHANGE_NOTICE:
  668. DrDebug(APM_L2,("APM: ApmCheckForEvent, update battery\n"));
  669. returnvalue = APM_DO_NOTIFY;
  670. break;
  671. case APM_NORMAL_RESUME_NOTICE:
  672. case APM_STANDBY_RESUME_NOTICE:
  673. case APM_CAPABILITIES_CHANGE_NOTICE:
  674. //
  675. // ignore these because we don't care and there's nothing to do
  676. //
  677. DrDebug(APM_L2,
  678. ("APM: ApmCheckForEvent, non-interesting event\n"));
  679. break;
  680. default:
  681. DrDebug(APM_L2,("APM: ApmCheckForEvent, out of range event\n"));
  682. break;
  683. } //switch
  684. return returnvalue;
  685. }