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
899 lines
20 KiB
/*++
|
|
|
|
Module Name:
|
|
|
|
apm.c
|
|
|
|
Abstract:
|
|
|
|
A collection of code that allows NT calls into APM.
|
|
The code in this routine depends on data being set up in the registry
|
|
|
|
Author:
|
|
|
|
Environment:
|
|
|
|
Kernel mode only.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include "ntosp.h"
|
|
#include "zwapi.h"
|
|
#include "apmp.h"
|
|
#include "apm.h"
|
|
#include "apmcrib.h"
|
|
#include "ntapmdbg.h"
|
|
#include "ntapmlog.h"
|
|
#include "ntapmp.h"
|
|
|
|
|
|
#define MAX_SEL 30 // attempts before giving up
|
|
|
|
ULONG ApmCallActive = 0;
|
|
ULONG ApmCallEax = 0;
|
|
ULONG ApmCallEbx = 0;
|
|
ULONG ApmCallEcx = 0;
|
|
|
|
WCHAR rgzMultiFunctionAdapter[] =
|
|
L"\\Registry\\Machine\\Hardware\\Description\\System\\MultifunctionAdapter";
|
|
WCHAR rgzConfigurationData[] = L"Configuration Data";
|
|
WCHAR rgzIdentifier[] = L"Identifier";
|
|
WCHAR rgzPCIIndetifier[] = L"PCI";
|
|
|
|
WCHAR rgzApmConnect[]= L"\\Registry\\Machine\\Hardware\\ApmConnect";
|
|
WCHAR rgzApmConnectValue[] = L"ApmConnectValue";
|
|
|
|
APM_CONNECT Apm;
|
|
|
|
//
|
|
// First time we get any non-recoverable error back
|
|
// from APM, record what sort of call hit it and what
|
|
// the error code was here
|
|
//
|
|
ULONG ApmLogErrorFunction = -1L;
|
|
ULONG ApmLogErrorCode = 0L;
|
|
|
|
ULONG ApmErrorLogSequence = 0xf3;
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE,ApmInitializeConnection)
|
|
#endif
|
|
|
|
//
|
|
// Internal prototypes
|
|
//
|
|
|
|
BOOLEAN
|
|
ApmpBuildGdtEntry (
|
|
IN ULONG Index,
|
|
PKGDTENTRY GdtEntry,
|
|
IN ULONG SegmentBase
|
|
);
|
|
|
|
|
|
VOID
|
|
NtApmLogError(
|
|
NTSTATUS ErrorCode,
|
|
UCHAR ErrorByte
|
|
);
|
|
|
|
|
|
NTSTATUS
|
|
ApmInitializeConnection (
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize data needed to call APM bios functions -- look in the
|
|
registry to find out if this machine has had its APM capability
|
|
detected.
|
|
|
|
NOTE: If you change the recognition code, change the
|
|
code to IsApmPresent as well!
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if we were able to connect to the APM BIOS.
|
|
|
|
--*/
|
|
{
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR PDesc;
|
|
PCM_FULL_RESOURCE_DESCRIPTOR Desc;
|
|
PKEY_VALUE_FULL_INFORMATION ValueInfo;
|
|
PAPM_REGISTRY_INFO ApmEntry;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
UNICODE_STRING unicodeString, ConfigName, IdentName;
|
|
KGDTENTRY GdtEntry;
|
|
NTSTATUS status;
|
|
BOOLEAN Error;
|
|
HANDLE hMFunc, hBus, hApmConnect;
|
|
USHORT Sel[MAX_SEL], TSel;
|
|
UCHAR buffer [sizeof(APM_REGISTRY_INFO) + 99];
|
|
WCHAR wstr[8];
|
|
ULONG i, j, Count, junk;
|
|
PWSTR p;
|
|
USHORT volatile Offset;
|
|
|
|
//
|
|
// Look in the registery for the "APM bus" data
|
|
//
|
|
|
|
RtlInitUnicodeString(&unicodeString, rgzMultiFunctionAdapter);
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&unicodeString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL, // handle
|
|
NULL
|
|
);
|
|
|
|
|
|
status = ZwOpenKey(&hMFunc, KEY_READ, &objectAttributes);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
unicodeString.Buffer = wstr;
|
|
unicodeString.MaximumLength = sizeof (wstr);
|
|
|
|
RtlInitUnicodeString(&ConfigName, rgzConfigurationData);
|
|
RtlInitUnicodeString(&IdentName, rgzIdentifier);
|
|
|
|
ValueInfo = (PKEY_VALUE_FULL_INFORMATION) buffer;
|
|
|
|
for (i=0; TRUE; i++) {
|
|
RtlIntegerToUnicodeString(i, 10, &unicodeString);
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&unicodeString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
hMFunc,
|
|
NULL
|
|
);
|
|
|
|
status = ZwOpenKey(&hBus, KEY_READ, &objectAttributes);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Out of Multifunction adapter entries...
|
|
//
|
|
|
|
ZwClose (hMFunc);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Check the Indentifier to see if this is a APM entry
|
|
//
|
|
|
|
status = ZwQueryValueKey (
|
|
hBus,
|
|
&IdentName,
|
|
KeyValueFullInformation,
|
|
ValueInfo,
|
|
sizeof (buffer),
|
|
&junk
|
|
);
|
|
|
|
if (!NT_SUCCESS (status)) {
|
|
ZwClose (hBus);
|
|
continue;
|
|
}
|
|
|
|
p = (PWSTR) ((PUCHAR) ValueInfo + ValueInfo->DataOffset);
|
|
if (p[0] != L'A' || p[1] != L'P' || p[2] != L'M' || p[3] != 0) {
|
|
ZwClose (hBus);
|
|
continue;
|
|
}
|
|
|
|
status = ZwQueryValueKey(
|
|
hBus,
|
|
&ConfigName,
|
|
KeyValueFullInformation,
|
|
ValueInfo,
|
|
sizeof (buffer),
|
|
&junk
|
|
);
|
|
|
|
ZwClose (hBus);
|
|
if (!NT_SUCCESS(status)) {
|
|
continue ;
|
|
}
|
|
|
|
Desc = (PCM_FULL_RESOURCE_DESCRIPTOR) ((PUCHAR)
|
|
ValueInfo + ValueInfo->DataOffset);
|
|
PDesc = (PCM_PARTIAL_RESOURCE_DESCRIPTOR) ((PUCHAR)
|
|
Desc->PartialResourceList.PartialDescriptors);
|
|
|
|
if (PDesc->Type == CmResourceTypeDeviceSpecific) {
|
|
// got it..
|
|
ApmEntry = (PAPM_REGISTRY_INFO) (PDesc+1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//DbgPrint("ApmEntry: %08lx\n", ApmEntry);
|
|
//DbgPrint("Signature: %c%c%c\n", ApmEntry->Signature[0], ApmEntry->Signature[1], ApmEntry->Signature[2]);
|
|
if ( (ApmEntry->Signature[0] != 'A') ||
|
|
(ApmEntry->Signature[1] != 'P') ||
|
|
(ApmEntry->Signature[2] != 'M') )
|
|
{
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//DbgPrint("ApmEntry->Valid: %0d\n", ApmEntry->Valid);
|
|
if (ApmEntry->Valid != 1) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Apm found - initialize the connection
|
|
//
|
|
|
|
KeInitializeSpinLock(&Apm.CallLock);
|
|
|
|
//
|
|
// Allocate a bunch of selectors
|
|
//
|
|
|
|
for (Count=0; Count < MAX_SEL; Count++) {
|
|
status = KeI386AllocateGdtSelectors (Sel+Count, 1);
|
|
if (!NT_SUCCESS(status)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Sort the selctors via bubble sort
|
|
//
|
|
|
|
for (i=0; i < Count; i++) {
|
|
for (j = i+1; j < Count; j++) {
|
|
if (Sel[j] < Sel[i]) {
|
|
TSel = Sel[i];
|
|
Sel[i] = Sel[j];
|
|
Sel[j] = TSel;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now look for 3 consecutive values
|
|
//
|
|
|
|
for (i=0; i < Count - 3; i++) {
|
|
if (Sel[i]+8 == Sel[i+1] && Sel[i]+16 == Sel[i+2]) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i >= Count - 3) {
|
|
DrDebug(APM_INFO,("APM: Could not allocate consecutive selectors\n"));
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Save the results
|
|
//
|
|
|
|
Apm.Selector[0] = Sel[i+0];
|
|
Apm.Selector[1] = Sel[i+1];
|
|
Apm.Selector[2] = Sel[i+2];
|
|
Sel[i+0] = 0;
|
|
Sel[i+1] = 0;
|
|
Sel[i+2] = 0;
|
|
|
|
//
|
|
// Free unused selectors
|
|
//
|
|
|
|
for (i=0; i < Count; i++) {
|
|
if (Sel[i]) {
|
|
KeI386ReleaseGdtSelectors (Sel+i, 1);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Initialize the selectors to use the APM bios
|
|
//
|
|
|
|
Error = FALSE;
|
|
|
|
//
|
|
// initialize 16 bit code selector
|
|
//
|
|
|
|
GdtEntry.LimitLow = 0xFFFF;
|
|
GdtEntry.HighWord.Bytes.Flags1 = 0;
|
|
GdtEntry.HighWord.Bytes.Flags2 = 0;
|
|
GdtEntry.HighWord.Bits.Pres = 1;
|
|
GdtEntry.HighWord.Bits.Dpl = DPL_SYSTEM;
|
|
GdtEntry.HighWord.Bits.Granularity = GRAN_BYTE;
|
|
GdtEntry.HighWord.Bits.Type = 31;
|
|
GdtEntry.HighWord.Bits.Default_Big = 0;
|
|
|
|
Error |= ApmpBuildGdtEntry (0, &GdtEntry, ApmEntry->Code16BitSegment);
|
|
|
|
//
|
|
// initialize 16 bit data selector
|
|
//
|
|
|
|
GdtEntry.LimitLow = 0xFFFF;
|
|
GdtEntry.HighWord.Bytes.Flags1 = 0;
|
|
GdtEntry.HighWord.Bytes.Flags2 = 0;
|
|
GdtEntry.HighWord.Bits.Pres = 1;
|
|
GdtEntry.HighWord.Bits.Dpl = DPL_SYSTEM;
|
|
GdtEntry.HighWord.Bits.Granularity = GRAN_BYTE;
|
|
GdtEntry.HighWord.Bits.Type = 19;
|
|
GdtEntry.HighWord.Bits.Default_Big = 1;
|
|
|
|
Error |= ApmpBuildGdtEntry (1, &GdtEntry, ApmEntry->Data16BitSegment);
|
|
|
|
//
|
|
// If we leave it like this, the compiler generates incorrect code!!!
|
|
// Apm.Code16BitOffset = ApmEntry->Code16BitOffset;
|
|
// So do this instead.
|
|
//
|
|
Offset = ApmEntry->Code16BitOffset;
|
|
Apm.Code16BitOffset = (ULONG) Offset;
|
|
|
|
//DbgPrint("Apm@%08lx ApmEntry@%08lx\n", &Apm, ApmEntry);
|
|
//DbgBreakPoint();
|
|
|
|
|
|
#if 0
|
|
//
|
|
// to make the poweroff path in the Hal about 20 times simpler,
|
|
// as well as make it work, pass our mappings on to the Hal, so
|
|
// it can use them.
|
|
//
|
|
RtlInitUnicodeString(&unicodeString, rgzApmConnect);
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&unicodeString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
status = ZwCreateKey(
|
|
&hApmConnect,
|
|
KEY_ALL_ACCESS,
|
|
&objectAttributes,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_VOLATILE,
|
|
&junk
|
|
);
|
|
RtlInitUnicodeString(&unicodeString, rgzApmConnectValue);
|
|
if (NT_SUCCESS(status)) {
|
|
status = ZwSetValueKey(
|
|
hApmConnect,
|
|
&unicodeString,
|
|
0,
|
|
REG_BINARY,
|
|
&Apm,
|
|
sizeof(APM_CONNECT)
|
|
);
|
|
ZwClose(hApmConnect);
|
|
}
|
|
#endif
|
|
|
|
return Error ? STATUS_UNSUCCESSFUL : STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
ApmpBuildGdtEntry (
|
|
IN ULONG Index,
|
|
PKGDTENTRY GdtEntry,
|
|
IN ULONG SegmentBase
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Build the Gdt Entry
|
|
|
|
Arguments:
|
|
|
|
Index Index of entry
|
|
GdtEntry
|
|
SegmentBase
|
|
|
|
Return Value:
|
|
|
|
TRUE if we encountered any error, FALSE if successful
|
|
|
|
--*/
|
|
{
|
|
PHYSICAL_ADDRESS PhysAddr;
|
|
ULONG SegBase;
|
|
PVOID VirtualAddress;
|
|
ULONG AddressSpace;
|
|
BOOLEAN flag;
|
|
|
|
//
|
|
// Convert Segment to phyiscal address
|
|
//
|
|
|
|
PhysAddr.LowPart = SegmentBase << 4;
|
|
PhysAddr.HighPart = 0;
|
|
|
|
//
|
|
// Translate physical address from ISA bus 0
|
|
//
|
|
|
|
AddressSpace = 0;
|
|
flag = HalTranslateBusAddress (
|
|
Isa, 0,
|
|
PhysAddr,
|
|
&AddressSpace,
|
|
&PhysAddr
|
|
);
|
|
|
|
if (AddressSpace != 0 || !flag) {
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Map into virtual address space
|
|
//
|
|
|
|
VirtualAddress = MmMapIoSpace (
|
|
PhysAddr,
|
|
0x10000, // 64k
|
|
TRUE
|
|
);
|
|
Apm.VirtualAddress[Index] = VirtualAddress;
|
|
|
|
//
|
|
// Map virtual address to selector:0 address
|
|
//
|
|
|
|
SegBase = (ULONG) VirtualAddress;
|
|
GdtEntry->BaseLow = (USHORT) (SegBase & 0xffff);
|
|
GdtEntry->HighWord.Bits.BaseMid = (UCHAR) (SegBase >> 16) & 0xff;
|
|
GdtEntry->HighWord.Bits.BaseHi = (UCHAR) (SegBase >> 24) & 0xff;
|
|
|
|
KeI386SetGdtSelector (Apm.Selector[Index], GdtEntry);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ApmFunction (
|
|
IN ULONG ApmFunctionCode,
|
|
IN OUT PULONG Ebx,
|
|
IN OUT PULONG Ecx
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Call APM BIOS with ApmFunctionCode and appropriate arguments
|
|
|
|
Arguments:
|
|
|
|
ApmFunctionCode Apm function code
|
|
Ebx Ebx param to APM BIOS
|
|
Ecx Ecx param to APM BIOS
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS with Ebx, Ebx
|
|
otherwise an NTSTATUS code
|
|
|
|
--*/
|
|
{
|
|
KIRQL OldIrql;
|
|
ULONG ApmStatus;
|
|
CONTEXT Regs;
|
|
|
|
|
|
if (!Apm.Selector[0]) {
|
|
|
|
//
|
|
// Attempting to call APM BIOS without a sucessfull connection
|
|
//
|
|
|
|
DrDebug(APM_INFO,("APM: ApmFunction - APM not initialized\n"));
|
|
DrDebug(APM_INFO,
|
|
("APM: ApmFunction failing function %x\n", ApmFunctionCode));
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//DbgPrint("APM: ApmFunction: %08lx Ebx: %08lx Ecx: %08lx\n", ApmFunctionCode, *Ebx, *Ecx);
|
|
|
|
|
|
//
|
|
// Serialize calls into the APM bios
|
|
//
|
|
KeAcquireSpinLock(&Apm.CallLock, &OldIrql);
|
|
ApmCallActive += 1;
|
|
|
|
//
|
|
// ASM interface to call the BIOS
|
|
//
|
|
|
|
//
|
|
// Fill in general registers for 16bit bios call.
|
|
// Note: only the following registers are passed. Specifically,
|
|
// SS and ESP are not passed and are generated by the system.
|
|
//
|
|
|
|
Regs.ContextFlags = CONTEXT_INTEGER | CONTEXT_SEGMENTS;
|
|
|
|
Regs.Eax = ApmFunctionCode;
|
|
Regs.Ebx = *Ebx;
|
|
Regs.Ecx = *Ecx;
|
|
Regs.Edx = 0;
|
|
Regs.Esi = 0;
|
|
Regs.Edi = 0;
|
|
Regs.SegGs = 0;
|
|
Regs.SegFs = 0;
|
|
Regs.SegEs = Apm.Selector[1];
|
|
Regs.SegDs = Apm.Selector[1];
|
|
Regs.SegCs = Apm.Selector[0];
|
|
Regs.Eip = Apm.Code16BitOffset;
|
|
Regs.EFlags = 0x200; // interrupts enabled
|
|
|
|
ApmCallEax = Regs.Eax;
|
|
ApmCallEbx = Regs.Ebx;
|
|
ApmCallEcx = Regs.Ecx;
|
|
|
|
//
|
|
// call the 16:16 bios function
|
|
//
|
|
|
|
KeI386Call16BitFunction (&Regs);
|
|
|
|
ApmCallActive -= 1;
|
|
|
|
//
|
|
// Release serialization
|
|
//
|
|
KeReleaseSpinLock(&Apm.CallLock, OldIrql);
|
|
|
|
//
|
|
// Get the results
|
|
//
|
|
|
|
ApmStatus = 0;
|
|
if (Regs.EFlags & 0x1) { // check carry flag
|
|
ApmStatus = (Regs.Eax >> 8) & 0xff;
|
|
}
|
|
|
|
*Ebx = Regs.Ebx;
|
|
*Ecx = Regs.Ecx;
|
|
|
|
//
|
|
// save for debug use
|
|
//
|
|
if (ApmStatus) {
|
|
if (ApmLogErrorCode != 0) {
|
|
ApmLogErrorFunction = ApmFunctionCode;
|
|
ApmLogErrorCode = ApmStatus;
|
|
}
|
|
}
|
|
|
|
//
|
|
// log specific errors of value to the user
|
|
//
|
|
if (ApmFunctionCode == APM_SET_POWER_STATE) {
|
|
if (ApmStatus != 0)
|
|
{
|
|
NtApmLogError(NTAPM_SET_POWER_FAILURE, (UCHAR)ApmStatus);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
DrDebug(APM_INFO,("APM: ApmFunction result is %x\n", ApmStatus));
|
|
return ApmStatus;
|
|
}
|
|
|
|
|
|
WCHAR ApmConvArray[] = {'0', '1','2','3','4','5','6','7','8','9','A','B','C','D','E','F',0};
|
|
VOID
|
|
NtApmLogError(
|
|
NTSTATUS ErrorCode,
|
|
UCHAR ErrorByte
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Report the incoming error to the event log.
|
|
|
|
Arguments:
|
|
|
|
ErrorCode - the ntstatus type value which will match the message template
|
|
and get reported to the user.
|
|
|
|
ErrorByte - the 1 byte value returned by APM bios
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
--*/
|
|
{
|
|
PIO_ERROR_LOG_PACKET errorLogPacket;
|
|
PUCHAR p;
|
|
PWCHAR pw;
|
|
|
|
errorLogPacket = IoAllocateErrorLogEntry(
|
|
NtApmDriverObject,
|
|
(UCHAR)(sizeof(IO_ERROR_LOG_PACKET)+8)
|
|
);
|
|
|
|
if (errorLogPacket != NULL) {
|
|
errorLogPacket->ErrorCode = ErrorCode;
|
|
errorLogPacket->SequenceNumber = ApmErrorLogSequence++;
|
|
errorLogPacket->FinalStatus = STATUS_UNSUCCESSFUL;
|
|
errorLogPacket->UniqueErrorValue = 0;
|
|
errorLogPacket->NumberOfStrings = 1;
|
|
errorLogPacket->RetryCount = 0;
|
|
errorLogPacket->MajorFunctionCode = 0;
|
|
errorLogPacket->DeviceOffset.HighPart = 0;
|
|
errorLogPacket->DeviceOffset.LowPart = 0;
|
|
errorLogPacket->DumpDataSize = 0;
|
|
|
|
//
|
|
// why our own conversion code? because we can't get the fine
|
|
// RTL routines to put the data in the right sized output buffer
|
|
//
|
|
p = (PUCHAR) &(errorLogPacket->DumpData[0]);
|
|
pw = (PWCHAR)p;
|
|
|
|
pw[0] = ApmConvArray[(ULONG)((ErrorByte & 0xf0)>>4)];
|
|
pw[1] = ApmConvArray[(ULONG)(ErrorByte & 0xf)];
|
|
pw[2] = L'\0';
|
|
|
|
errorLogPacket->StringOffset =
|
|
((PUCHAR)(&(errorLogPacket->DumpData[0]))) - ((PUCHAR)errorLogPacket);
|
|
IoWriteErrorLogEntry(errorLogPacket);
|
|
}
|
|
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ApmSuspendSystem (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Suspend the system
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if the computer was suspended & then resumed
|
|
|
|
--*/
|
|
{
|
|
ULONG Ebx, Ecx;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Use ApmFunction to suspend machine
|
|
//
|
|
|
|
DrDebug(APM_L2,("APM: ApmSuspendSystem: enter\n"));
|
|
Ebx = APM_DEVICE_ALL;
|
|
Ecx = APM_SET_SUSPEND;
|
|
Status = ApmFunction (APM_SET_POWER_STATE, &Ebx, &Ecx);
|
|
DrDebug(APM_L2,("APM: ApmSuspendSystem: exit\n"));
|
|
return Status;
|
|
}
|
|
|
|
|
|
VOID
|
|
ApmTurnOffSystem(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Turn the system off.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
|
|
--*/
|
|
{
|
|
ULONG Ebx, Ecx;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Use ApmFunction to put machine into StandBy mode
|
|
//
|
|
DrDebug(APM_L2,("APM: ApmTurnOffSystem: enter\n"));
|
|
Ebx = APM_DEVICE_ALL;
|
|
Ecx = APM_SET_OFF;
|
|
Status = ApmFunction (APM_SET_POWER_STATE, &Ebx, &Ecx);
|
|
DrDebug(APM_L2,("APM: ApmTurnOffSystem: exit\n"));
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
ApmInProgress(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine informs the BIOS to cool its jets for 5 seconds
|
|
while we continue to operate
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if the computer was suspended & then resumed
|
|
|
|
--*/
|
|
{
|
|
ULONG Ebx, Ecx;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Use ApmFunction to tell BIOS to cool its heals
|
|
//
|
|
|
|
Ebx = APM_DEVICE_ALL;
|
|
Ecx = APM_SET_PROCESSING;
|
|
Status = ApmFunction (APM_SET_POWER_STATE, &Ebx, &Ecx);
|
|
return;
|
|
}
|
|
|
|
|
|
ULONG
|
|
ApmCheckForEvent (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Poll for APM event
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
We return:
|
|
APM_DO_code from apmp.h
|
|
|
|
APM_DO_NOTHING 0
|
|
APM_DO_SUSPEND 1
|
|
APM_DO_STANDBY 2
|
|
APM_DO_FIXCLOCK 3
|
|
APM_DO_NOTIFY 4
|
|
APM_DO_CRITICAL_SUSPEND 5
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG Ebx, Ecx;
|
|
ULONG returnvalue;
|
|
|
|
//
|
|
// Read an event. Might get nothing.
|
|
//
|
|
|
|
returnvalue = APM_DO_NOTHING;
|
|
|
|
Ebx = 0;
|
|
Ecx = 0;
|
|
Status = ApmFunction (APM_GET_EVENT, &Ebx, &Ecx);
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
return returnvalue;
|
|
}
|
|
|
|
//
|
|
// Handle APM reported event
|
|
//
|
|
|
|
DrDebug(APM_L2,("APM: ApmCheckForEvent, code is %d\n", Ebx));
|
|
|
|
switch (Ebx) {
|
|
|
|
//
|
|
// say wer're working on it and set up for standby
|
|
//
|
|
case APM_SYS_STANDBY_REQUEST:
|
|
case APM_USR_STANDBY_REQUEST:
|
|
DrDebug(APM_L2,("APM: ApmCheckForEvent, standby request\n"));
|
|
ApmInProgress();
|
|
returnvalue = APM_DO_STANDBY;
|
|
break;
|
|
|
|
//
|
|
// say we're working on it and set up for suspend
|
|
//
|
|
case APM_SYS_SUSPEND_REQUEST:
|
|
case APM_USR_SUSPEND_REQUEST:
|
|
case APM_BATTERY_LOW_NOTICE:
|
|
DrDebug(APM_L2,
|
|
("APM: ApmCheckForEvent, suspend or battery low\n"));
|
|
ApmInProgress();
|
|
returnvalue = APM_DO_SUSPEND;
|
|
break;
|
|
|
|
//
|
|
// Say we're working on it, and setup for CRITICAL suspend
|
|
//
|
|
case APM_CRITICAL_SYSTEM_SUSPEND_REQUEST:
|
|
DrDebug(APM_L2, ("APM: Apmcheckforevent, critical suspend\n"));
|
|
ApmInProgress();
|
|
returnvalue = APM_DO_CRITICAL_SUSPEND;
|
|
break;
|
|
|
|
//
|
|
// ignore this because we have no idea what to do with it
|
|
//
|
|
case APM_CRITICAL_RESUME_NOTICE:
|
|
DrDebug(APM_L2,("APM: ApmCheckForEvent, critical resume\n"));
|
|
break;
|
|
|
|
|
|
case APM_UPDATE_TIME_EVENT:
|
|
DrDebug(APM_L2,("APM: ApmCheckForEvent, update time\n"));
|
|
returnvalue = APM_DO_FIXCLOCK;
|
|
break;
|
|
|
|
case APM_POWER_STATUS_CHANGE_NOTICE:
|
|
DrDebug(APM_L2,("APM: ApmCheckForEvent, update battery\n"));
|
|
returnvalue = APM_DO_NOTIFY;
|
|
break;
|
|
|
|
case APM_NORMAL_RESUME_NOTICE:
|
|
case APM_STANDBY_RESUME_NOTICE:
|
|
case APM_CAPABILITIES_CHANGE_NOTICE:
|
|
|
|
//
|
|
// ignore these because we don't care and there's nothing to do
|
|
//
|
|
|
|
DrDebug(APM_L2,
|
|
("APM: ApmCheckForEvent, non-interesting event\n"));
|
|
break;
|
|
|
|
default:
|
|
DrDebug(APM_L2,("APM: ApmCheckForEvent, out of range event\n"));
|
|
break;
|
|
} //switch
|
|
|
|
return returnvalue;
|
|
}
|
|
|