960 lines
24 KiB
C
960 lines
24 KiB
C
|
/*++
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
ntapm.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
OS source for ntapm.sys
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Kernel mode
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
|
|||
|
|
|||
|
#include "ntddk.h"
|
|||
|
#include "ntpoapi.h"
|
|||
|
#include "string.h"
|
|||
|
#include "ntcrib.h"
|
|||
|
#include "ntapmdbg.h"
|
|||
|
#include "apm.h"
|
|||
|
#include "apmp.h"
|
|||
|
#include "ntapmp.h"
|
|||
|
#include <ntapm.h>
|
|||
|
#include <poclass.h>
|
|||
|
#include <ntapmlog.h>
|
|||
|
|
|||
|
//
|
|||
|
// Global debug flag. There are 3 separate groupings, see ntapmdbg.h for
|
|||
|
// break out.
|
|||
|
//
|
|||
|
|
|||
|
ULONG NtApmDebugFlag = 0;
|
|||
|
|
|||
|
ULONG ApmWorks = 0;
|
|||
|
|
|||
|
WCHAR rgzApmActiveFlag[] =
|
|||
|
L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\ApmActive";
|
|||
|
WCHAR rgzApmFlag[] =
|
|||
|
L"Active";
|
|||
|
|
|||
|
WCHAR rgzAcpiKey[] =
|
|||
|
L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\ACPI";
|
|||
|
WCHAR rgzAcpiStart[] =
|
|||
|
L"Start";
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Define driver entry routine.
|
|||
|
//
|
|||
|
|
|||
|
NTSTATUS DriverEntry(
|
|||
|
PDRIVER_OBJECT DriverObject,
|
|||
|
PUNICODE_STRING RegistryPath
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS ApmDispatch(
|
|||
|
PDEVICE_OBJECT DeviceObject,
|
|||
|
PIRP Irp
|
|||
|
);
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
IsAcpiMachine(
|
|||
|
VOID
|
|||
|
);
|
|||
|
|
|||
|
ULONG DoApmPoll();
|
|||
|
NTSTATUS DoApmInitMachine();
|
|||
|
|
|||
|
|
|||
|
|
|||
|
VOID (*BattChangeNotify)() = NULL;
|
|||
|
|
|||
|
|
|||
|
#define POLL_INTERVAL (500) // 500 milliseconds == 1/2 second
|
|||
|
|
|||
|
#define APM_POLL_MULTIPLY (4) // only call ApmInProgress once every 4 Poll intervals
|
|||
|
// which with current values is once every 2 seconds
|
|||
|
|
|||
|
#define APM_SPIN_LIMIT (6) // 6 spin passes, each with a call to ApmInProgress,
|
|||
|
// at APM_POLL_MULTIPLY * POLL_INTERVAL time spacing.
|
|||
|
// Current values (500, 4, 6) should yield APM bios
|
|||
|
// waiting from 12s to 17s, depending on how large
|
|||
|
// or small their value of 5s is.
|
|||
|
|
|||
|
volatile BOOLEAN OperationDone = FALSE; // used to make some sync between SuspendPollThread
|
|||
|
// and ApmSleep and ApmOff work.
|
|||
|
|
|||
|
//
|
|||
|
// Our own driver object. This is rude, but this is a very weird
|
|||
|
// and special driver. We will pass this to our APM library to
|
|||
|
// allow error logging to work. Note that we don't actually have
|
|||
|
// an active IRP around when the error occurs.
|
|||
|
//
|
|||
|
PDRIVER_OBJECT NtApmDriverObject = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Define the local routines used by this driver module.
|
|||
|
//
|
|||
|
|
|||
|
VOID SuspendPollThread(PVOID Dummy);
|
|||
|
VOID ApmSleep(VOID);
|
|||
|
VOID ApmOff(VOID);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
KTIMER PollTimer;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
DriverEntry(
|
|||
|
IN PDRIVER_OBJECT DriverObject,
|
|||
|
IN PUNICODE_STRING RegistryPath
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This is the initialization routine for the laptop driver.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DriverObject - Pointer to driver object created by the system.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The function value is the final status from the initialization operation.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
ULONG MajorVersion;
|
|||
|
ULONG MinorVersion;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// refuse to load on machines with more than 1 cpu
|
|||
|
//
|
|||
|
if (KeNumberProcessors != 1) {
|
|||
|
DrDebug(SYS_INFO, ("ntapm: more than 1 cpu, ntapm will exit\n"));
|
|||
|
return STATUS_UNSUCCESSFUL;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// refuse to load if version number is not 5.1 or 5.0
|
|||
|
// NOTE WELL: This is a manual version check, do NOT put a system
|
|||
|
// constant in here. This driver depends on hacks in
|
|||
|
// the kernel that will someday go away...
|
|||
|
//
|
|||
|
PsGetVersion(&MajorVersion, &MinorVersion, NULL, NULL);
|
|||
|
if ( !
|
|||
|
(
|
|||
|
((MajorVersion == 5) && (MinorVersion == 0)) ||
|
|||
|
((MajorVersion == 5) && (MinorVersion == 1))
|
|||
|
)
|
|||
|
)
|
|||
|
{
|
|||
|
DrDebug(SYS_INFO, ("ntapm: system version number != 5.1, exit\n"));
|
|||
|
return STATUS_UNSUCCESSFUL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// refuse to load if ACPI.SYS should be running
|
|||
|
//
|
|||
|
if (IsAcpiMachine()) {
|
|||
|
DrDebug(SYS_INFO, ("ntapm: this is an acpi machine apm exiting\n"));
|
|||
|
return STATUS_UNSUCCESSFUL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// init the driver object
|
|||
|
//
|
|||
|
DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = ApmDispatch;
|
|||
|
DriverObject->MajorFunction[IRP_MJ_CREATE] = ApmDispatch;
|
|||
|
DriverObject->MajorFunction[IRP_MJ_CLOSE] = ApmDispatch;
|
|||
|
DriverObject->MajorFunction[IRP_MJ_PNP] = NtApm_PnP;
|
|||
|
DriverObject->MajorFunction[IRP_MJ_POWER] = NtApm_Power;
|
|||
|
DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = ApmDispatch;
|
|||
|
DriverObject->DriverExtension->AddDevice = NtApm_AddDevice;
|
|||
|
NtApmDriverObject = DriverObject;
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN ApmAddHelperDone = FALSE;
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ApmAddHelper(
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
We do these things in the Add routine so that we cannot fail
|
|||
|
and leave the Kernel/Hal/Apm chain in a corrupt state.
|
|||
|
|
|||
|
This includes linking up with the Hal.
|
|||
|
|
|||
|
Turns out the caller doesn't know if this work has already
|
|||
|
been done, so disallow doing it more than once here.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The function value is the final status from the initialization operation.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
UCHAR HalTable[HAL_APM_TABLE_SIZE];
|
|||
|
PPM_DISPATCH_TABLE InTable;
|
|||
|
HANDLE ThreadHandle;
|
|||
|
HANDLE hKey;
|
|||
|
NTSTATUS status;
|
|||
|
ULONG flagvalue;
|
|||
|
OBJECT_ATTRIBUTES objectAttributes;
|
|||
|
UNICODE_STRING unicodeString;
|
|||
|
ULONG battresult;
|
|||
|
|
|||
|
if (ApmAddHelperDone) {
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
ApmAddHelperDone = TRUE;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// call ApmInitMachine so that Bios, etc, can be engaged
|
|||
|
// no suspends can happen before this call.
|
|||
|
//
|
|||
|
if (! NT_SUCCESS(DoApmInitMachine()) ) {
|
|||
|
DrDebug(SYS_INFO, ("ntapm: DoApmInitMachine failed\n"));
|
|||
|
return STATUS_UNSUCCESSFUL;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// call the hal
|
|||
|
//
|
|||
|
InTable = (PPM_DISPATCH_TABLE)HalTable;
|
|||
|
InTable->Signature = HAL_APM_SIGNATURE;
|
|||
|
InTable->Version = HAL_APM_VERSION;
|
|||
|
|
|||
|
//
|
|||
|
// In theory, APM should be fired up by now.
|
|||
|
// So call off into it to see if there is any sign
|
|||
|
// of a battery on the box.
|
|||
|
//
|
|||
|
// If we do not see a battery, then do NOT enable
|
|||
|
// S3, but do allow S4. This keeps people's machines
|
|||
|
// from puking on failed S3 calls (almost always desktops)
|
|||
|
// while allowing auto-shutdown at the end of hibernate to work.
|
|||
|
//
|
|||
|
battresult = DoApmReportBatteryStatus();
|
|||
|
if (battresult & NTAPM_NO_SYS_BATT) {
|
|||
|
//
|
|||
|
// it appears that the machine does not have
|
|||
|
// a battery, or least APM doesn't report one.
|
|||
|
//
|
|||
|
InTable->Function[HAL_APM_SLEEP_VECTOR] = NULL;
|
|||
|
} else {
|
|||
|
InTable->Function[HAL_APM_SLEEP_VECTOR] = &ApmSleep;
|
|||
|
}
|
|||
|
|
|||
|
InTable->Function[HAL_APM_OFF_VECTOR] = &ApmOff;
|
|||
|
|
|||
|
status = HalInitPowerManagement(InTable, NULL);
|
|||
|
|
|||
|
if (! NT_SUCCESS(status)) {
|
|||
|
DrDebug(SYS_INFO, ("ntapm: HalInitPowerManagement failed\n"));
|
|||
|
return STATUS_UNSUCCESSFUL;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// From this point on, INIT MUST succeed, otherwise we'll leave
|
|||
|
// the Hal with hanging pointers. So long as ApmSleep and ApmOff
|
|||
|
// are present in memory, things will be OK (though suspend may
|
|||
|
// not work, the box won't bugcheck.)
|
|||
|
//
|
|||
|
// init periodic timer, init suspend done event, init suspend dpc,
|
|||
|
// create and start poll thread
|
|||
|
//
|
|||
|
status = PsCreateSystemThread(&ThreadHandle,
|
|||
|
(ACCESS_MASK) 0L,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
&SuspendPollThread,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// the create didn't work, turns out that some apm functions
|
|||
|
// will still work, so just keep going.
|
|||
|
//
|
|||
|
if (! NT_SUCCESS(status)) {
|
|||
|
DrDebug(SYS_INFO, ("ntapm: could not create thread, but continunuing\n"));
|
|||
|
// return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
KeInitializeTimerEx(&PollTimer, SynchronizationTimer);
|
|||
|
|
|||
|
//
|
|||
|
// set a flag in the registry so that code with special hacks
|
|||
|
// based on apm being active can tell we're here and at least
|
|||
|
// nominally running
|
|||
|
//
|
|||
|
RtlInitUnicodeString(&unicodeString, rgzApmActiveFlag);
|
|||
|
InitializeObjectAttributes(
|
|||
|
&objectAttributes,
|
|||
|
&unicodeString,
|
|||
|
OBJ_CASE_INSENSITIVE,
|
|||
|
NULL, // handle
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
status = ZwCreateKey(
|
|||
|
&hKey,
|
|||
|
KEY_WRITE,
|
|||
|
&objectAttributes,
|
|||
|
0,
|
|||
|
NULL,
|
|||
|
REG_OPTION_VOLATILE,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
RtlInitUnicodeString(&unicodeString, rgzApmFlag);
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
flagvalue = 1;
|
|||
|
ZwSetValueKey(
|
|||
|
hKey,
|
|||
|
&unicodeString,
|
|||
|
0,
|
|||
|
REG_DWORD,
|
|||
|
&flagvalue,
|
|||
|
sizeof(ULONG)
|
|||
|
);
|
|||
|
ZwClose(hKey);
|
|||
|
}
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ApmDispatch(
|
|||
|
PDEVICE_OBJECT DeviceObject,
|
|||
|
PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
When an application calls the Laptop driver, it comes here.
|
|||
|
This is NOT the dispatch point for PNP or Power calls.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - Pointer to the device object for this driver.
|
|||
|
|
|||
|
Irp - Pointer to the request packet representing the I/O request.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The function value is the status of the operation.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
PIO_STACK_LOCATION irpSp;
|
|||
|
PVOID outbuffer;
|
|||
|
ULONG outlength;
|
|||
|
PFILE_OBJECT fileObject;
|
|||
|
ULONG percent;
|
|||
|
BOOLEAN acon;
|
|||
|
PNTAPM_LINK pparms;
|
|||
|
PULONG p;
|
|||
|
ULONG t;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER( DeviceObject );
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Get a pointer to the current stack location in the IRP. This is where
|
|||
|
// the function codes and parameters are stored.
|
|||
|
//
|
|||
|
|
|||
|
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|||
|
|
|||
|
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
|||
|
Irp->IoStatus.Information = 0;
|
|||
|
switch (irpSp->MajorFunction) {
|
|||
|
|
|||
|
//
|
|||
|
// device control
|
|||
|
//
|
|||
|
case IRP_MJ_INTERNAL_DEVICE_CONTROL:
|
|||
|
//
|
|||
|
// Only one valid command, which is to set (or null out) the
|
|||
|
// the link call pointers.
|
|||
|
//
|
|||
|
if (irpSp->MinorFunction == 0) {
|
|||
|
pparms = (PNTAPM_LINK) &(irpSp->Parameters.Others);
|
|||
|
if ((pparms->Signature == NTAPM_LINK_SIGNATURE) &&
|
|||
|
(pparms->Version == NTAPM_LINK_VERSION))
|
|||
|
{
|
|||
|
t = (ULONG) (&DoApmReportBatteryStatus);
|
|||
|
p = (PULONG)(pparms->BattLevelPtr);
|
|||
|
*p = t;
|
|||
|
BattChangeNotify = (PVOID)(pparms->ChangeNotify);
|
|||
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
Irp->IoStatus.Information = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
//
|
|||
|
// for all other operations, including create/open and close,
|
|||
|
// simply report failure, no matter what the operation is
|
|||
|
//
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Copy the final status into the return status, complete the request and
|
|||
|
// get out of here.
|
|||
|
//
|
|||
|
status = Irp->IoStatus.Status;
|
|||
|
IoCompleteRequest( Irp, 0 );
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
SuspendPollThread(
|
|||
|
PVOID Dummy
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is the laptop suspend polling thread.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Dummy Ignored parameter
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
LARGE_INTEGER DueTime;
|
|||
|
ULONG LocalSuspendFlag;
|
|||
|
ULONG LocalClockFlag;
|
|||
|
KIRQL OldIrql;
|
|||
|
POWER_ACTION SystemAction;
|
|||
|
SYSTEM_POWER_STATE MinSystemState;
|
|||
|
ULONG Flags;
|
|||
|
ULONG ApmEvent;
|
|||
|
ULONG count, count2;
|
|||
|
LONG i, j;
|
|||
|
ULONG BatteryResult, PriorBatteryResult;
|
|||
|
ULONG BattPresentMask, PriorBattPresentMask;
|
|||
|
BOOLEAN DoANotify;
|
|||
|
|
|||
|
PriorBatteryResult = BatteryResult = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Start the poll timer going, we'll wait for 1 second,
|
|||
|
// then POLL_INTERVAL milliseconds after that
|
|||
|
//
|
|||
|
|
|||
|
DueTime.HighPart = 0;
|
|||
|
DueTime.LowPart = 10*1000*1000; // 10 million * 100nano = 1 second
|
|||
|
KeSetTimerEx(&PollTimer, DueTime, POLL_INTERVAL, NULL);
|
|||
|
|
|||
|
while (1) {
|
|||
|
|
|||
|
KeWaitForSingleObject(&PollTimer, Executive, KernelMode, TRUE, NULL);
|
|||
|
|
|||
|
//
|
|||
|
// Call APM to poll for us
|
|||
|
//
|
|||
|
|
|||
|
Flags = 0; // clear all flags
|
|||
|
|
|||
|
switch (DoApmPoll()) {
|
|||
|
|
|||
|
case APM_DO_CRITICAL_SUSPEND:
|
|||
|
//
|
|||
|
// Here we force the Flags to have the
|
|||
|
// CRITICAL flag set, other than that it's the same thing
|
|||
|
// as for normal suspend and standby
|
|||
|
//
|
|||
|
Flags = POWER_ACTION_CRITICAL;
|
|||
|
|
|||
|
/* FALL FALL FALL */
|
|||
|
|
|||
|
case APM_DO_SUSPEND:
|
|||
|
case APM_DO_STANDBY:
|
|||
|
//
|
|||
|
// For either Suspend or Standby, call the
|
|||
|
// the system and tell it to suspend us
|
|||
|
//
|
|||
|
DrDebug(SYS_INFO, ("ntapm: about to call OS to suspend\n"));
|
|||
|
SystemAction = PowerActionSleep;
|
|||
|
MinSystemState = PowerSystemSleeping3;
|
|||
|
OperationDone = FALSE;
|
|||
|
ZwInitiatePowerAction(
|
|||
|
SystemAction,
|
|||
|
MinSystemState,
|
|||
|
Flags,
|
|||
|
TRUE // async
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// If we just call ZwInitiatePowerAction, most machines
|
|||
|
// will work, but a few get impatient and try to suspend
|
|||
|
// out from under us before the OS comes back round and
|
|||
|
// does the suspend. So, we need to call ApmInProgress
|
|||
|
// every so often to make these bioses wait.
|
|||
|
//
|
|||
|
// BUT, if the system is truly wedged, or the suspend fails,
|
|||
|
// we don't want to spin calling ApmInProgress forever, so
|
|||
|
// limit the number of times we do that. And once the
|
|||
|
// operation is about to happen, stop.
|
|||
|
//
|
|||
|
// Since we're not polling while we're waiting for something
|
|||
|
// to happen, we'll use the poll timer...
|
|||
|
//
|
|||
|
|
|||
|
if (OperationDone) goto Done;
|
|||
|
|
|||
|
ApmInProgress();
|
|||
|
for (count = 0; count < APM_SPIN_LIMIT; count++) {
|
|||
|
for (count2 = 0; count2 < APM_POLL_MULTIPLY; count2++) {
|
|||
|
KeWaitForSingleObject(&PollTimer, Executive, KernelMode, TRUE, NULL);
|
|||
|
}
|
|||
|
if (OperationDone) goto Done;
|
|||
|
ApmInProgress();
|
|||
|
}
|
|||
|
|
|||
|
DrDebug(SYS_INFO, ("ntapm: back from suspend\n"));
|
|||
|
Done:
|
|||
|
break;
|
|||
|
|
|||
|
case APM_DO_NOTIFY:
|
|||
|
//
|
|||
|
// Call out to battery driver with Notify op here
|
|||
|
//
|
|||
|
if (BattChangeNotify) {
|
|||
|
//DrDebug(SYS_INFO, ("ntapm: about to make notify call\n"));
|
|||
|
BattChangeNotify();
|
|||
|
//DrDebug(SYS_INFO, ("ntapm: back from notify call\n"));
|
|||
|
PriorBatteryResult = DoApmReportBatteryStatus();
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case APM_DO_FIXCLOCK:
|
|||
|
case APM_DO_NOTHING:
|
|||
|
default:
|
|||
|
//
|
|||
|
// fixing the clock is too scary with other power
|
|||
|
// code doing it, so we don't do it here.
|
|||
|
//
|
|||
|
// nothing is nothing
|
|||
|
//
|
|||
|
// if we don't understand, do nothing
|
|||
|
// (remember, bios will force op under us if it's critical)
|
|||
|
//
|
|||
|
|
|||
|
if (BattChangeNotify) {
|
|||
|
|
|||
|
//
|
|||
|
// we hereby redefine "nothing" to be "check on the
|
|||
|
// status of the bleeding battery" since not all bioses
|
|||
|
// tell us what is going on in a timely fashion
|
|||
|
//
|
|||
|
DoANotify = FALSE;
|
|||
|
BatteryResult = DoApmReportBatteryStatus();
|
|||
|
|
|||
|
if ((BatteryResult & NTAPM_ACON) !=
|
|||
|
(PriorBatteryResult & NTAPM_ACON))
|
|||
|
{
|
|||
|
DoANotify = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
if ((BatteryResult & NTAPM_BATTERY_STATE) !=
|
|||
|
(PriorBatteryResult & NTAPM_BATTERY_STATE))
|
|||
|
{
|
|||
|
DoANotify = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
i = BatteryResult & NTAPM_POWER_PERCENT;
|
|||
|
j = PriorBatteryResult & NTAPM_POWER_PERCENT;
|
|||
|
|
|||
|
if (( (i - j) > 25 ) ||
|
|||
|
( (j - i) > 25 ))
|
|||
|
{
|
|||
|
DoANotify = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
PriorBattPresentMask = PriorBatteryResult & (NTAPM_NO_BATT | NTAPM_NO_SYS_BATT);
|
|||
|
BattPresentMask = BatteryResult & (NTAPM_NO_BATT | NTAPM_NO_SYS_BATT);
|
|||
|
if (BattPresentMask != PriorBattPresentMask) {
|
|||
|
//
|
|||
|
// battery either went or reappeared
|
|||
|
//
|
|||
|
DoANotify = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
PriorBatteryResult = BatteryResult;
|
|||
|
|
|||
|
if (DoANotify) {
|
|||
|
ASSERT(BattChangeNotify);
|
|||
|
BattChangeNotify();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
} // switch
|
|||
|
} // while
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
ApmSleep(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
When the OS calls the Hal's S3 vector, the hal calls us here.
|
|||
|
We call APM to put the box to sleep
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
OperationDone = TRUE;
|
|||
|
if (ApmWorks) {
|
|||
|
|
|||
|
DrDebug(SYS_L2,("ntapm: apmsleep: calling apm to sleep\n"));
|
|||
|
|
|||
|
ApmSuspendSystem();
|
|||
|
|
|||
|
DrDebug(SYS_L2,("ntapm: apmsleep: back from apm call\n"));
|
|||
|
|
|||
|
} else { // ApmWorks == FALSE
|
|||
|
|
|||
|
DrDebug(SYS_INFO, ("ntapm: ApmSleep: no APM attached, Exit\n"));
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
ApmOff(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
When the OS calls the Hal's S4 or S5 routines, the hal calls us here.
|
|||
|
We turn the machine off.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
OperationDone = TRUE;
|
|||
|
if (ApmWorks) {
|
|||
|
|
|||
|
DrDebug(SYS_L2,("ntapm: ApmOff: calling APM\n"));
|
|||
|
|
|||
|
ApmTurnOffSystem();
|
|||
|
|
|||
|
DrDebug(SYS_INFO,("ntapm: ApmOff: we are back from Off, uh oh!\n"));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
DoApmInitMachine(
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine makes the BIOS ready to interact with laptop.sys.
|
|||
|
This code works with APM.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
ULONG Ebx, Ecx;
|
|||
|
|
|||
|
DrDebug(SYS_INIT,("ApmInitMachine: enter\n"));
|
|||
|
|
|||
|
Status = ApmInitializeConnection ();
|
|||
|
|
|||
|
if (NT_SUCCESS(Status)) {
|
|||
|
|
|||
|
DrDebug(SYS_INIT,("ApmInitMachine: Connection established!\n"));
|
|||
|
|
|||
|
//
|
|||
|
// Note that ntdetect (2nd version) will have set apm bios
|
|||
|
// to min of (machine version) and (1.2)
|
|||
|
// (so a 1.1 bios will be set to 1.1, a 1.2 to 1.2, a 1.3 to 1.2
|
|||
|
//
|
|||
|
|
|||
|
ApmWorks = 1;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
DrDebug(SYS_INIT,("ApmInitMachine: No connection made!\n"));
|
|||
|
|
|||
|
ApmWorks = 0;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
DrDebug(SYS_INIT,("ApmInitMachine: exit\n"));
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
ULONG
|
|||
|
DoApmPoll(
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called in the ntapm.sys polling loop to poll
|
|||
|
for APM events. It returns APM_DO_NOTHING unless there is
|
|||
|
actually something meaningful for us to do. (That is, things
|
|||
|
we don't want and/or don't understand are filtered down to
|
|||
|
APM_DO_NOTHING)
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
APM event code.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
DrDebug(SYS_L2,("ApmPoll: enter\n"));
|
|||
|
|
|||
|
if (ApmWorks) {
|
|||
|
|
|||
|
return ApmCheckForEvent();
|
|||
|
|
|||
|
} else { // ApmWorks == FALSE
|
|||
|
|
|||
|
DrDebug(SYS_L2,("ApmPoll: no APM attachment, exit\n"));
|
|||
|
return APM_DO_NOTHING;
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
ULONG
|
|||
|
DoApmReportBatteryStatus()
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine queries the BIOS/HW for the state of the power connection
|
|||
|
and the current battery level.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ULONG, fields defined by NTAPM_POWER_STATE and NTAPM_POWER_PERCENT
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
ULONG percent = 100;
|
|||
|
ULONG ac = 1;
|
|||
|
ULONG Status = 0;
|
|||
|
ULONG Ebx = 0;
|
|||
|
ULONG Ecx = 0;
|
|||
|
ULONG flags = 0;
|
|||
|
ULONG result = 0;
|
|||
|
|
|||
|
|
|||
|
DrDebug(SYS_L2,("ntapm: DoApmReportBatteryStatus: enter\n"));
|
|||
|
if (ApmWorks) {
|
|||
|
|
|||
|
//
|
|||
|
// Call APM BIOS and get power status
|
|||
|
//
|
|||
|
|
|||
|
Ebx = 1;
|
|||
|
Ecx = 0;
|
|||
|
Status = ApmFunction (APM_GET_POWER_STATUS, &Ebx, &Ecx);
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
|
|||
|
//
|
|||
|
// If we cannot read the power, jam in 50% and power off!
|
|||
|
//
|
|||
|
DrDebug(SYS_INFO,("ntapm: DoApmReportBatteryStatus: Can't get power!\n"));
|
|||
|
percent = 50;
|
|||
|
ac = 0;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Get battery/AC state -- anything but full 'on-line' means on
|
|||
|
// battery
|
|||
|
//
|
|||
|
|
|||
|
ac = (Ebx & APM_LINEMASK) >> APM_LINEMASK_SHIFT;
|
|||
|
if (ac != APM_GET_LINE_ONLINE) {
|
|||
|
ac = 0;
|
|||
|
}
|
|||
|
percent = Ecx & APM_PERCENT_MASK;
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
DrDebug(SYS_INFO,("ntapm: DoApmReportBatteryStatus: no APM attachment\n"));
|
|||
|
DrDebug(SYS_INFO,("ntapm: Return AC OFF 50% Life\n"));
|
|||
|
percent = 50;
|
|||
|
ac = FALSE;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
flags = 0;
|
|||
|
result = 0;
|
|||
|
|
|||
|
if (Ecx & APM_NO_BATT) {
|
|||
|
result |= NTAPM_NO_BATT;
|
|||
|
}
|
|||
|
|
|||
|
if (Ecx & APM_NO_SYS_BATT) {
|
|||
|
result |= NTAPM_NO_SYS_BATT;
|
|||
|
}
|
|||
|
|
|||
|
if ((percent == 255) || (Ecx & APM_NO_BATT) || (Ecx & APM_NO_SYS_BATT)) {
|
|||
|
percent = 0;
|
|||
|
} else if (percent > 100) {
|
|||
|
percent = 100;
|
|||
|
}
|
|||
|
|
|||
|
if ((Ecx & APM_BATT_CHARGING) && (percent < 100)) {
|
|||
|
flags |= BATTERY_CHARGING;
|
|||
|
} else {
|
|||
|
flags |= BATTERY_DISCHARGING;
|
|||
|
}
|
|||
|
|
|||
|
if (Ecx & APM_BATT_CRITICAL) {
|
|||
|
flags |= BATTERY_CRITICAL;
|
|||
|
percent = 1;
|
|||
|
}
|
|||
|
|
|||
|
if (ac) {
|
|||
|
result |= NTAPM_ACON;
|
|||
|
flags |= BATTERY_POWER_ON_LINE;
|
|||
|
}
|
|||
|
|
|||
|
result |= (flags << NTAPM_BATTERY_STATE_SHIFT);
|
|||
|
|
|||
|
|
|||
|
result |= percent;
|
|||
|
|
|||
|
DrDebug(SYS_L2,("ntapm: BatteryLevel: %08lx Percent: %d flags: %1x ac: %1x\n",
|
|||
|
result, percent, flags, ac));
|
|||
|
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
IsAcpiMachine(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
IsAcpiMachine reports whether the OS thinks this is an ACPI
|
|||
|
machine or not.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
FALSE - this is NOT an acpi machine
|
|||
|
|
|||
|
TRUE - this IS an acpi machine
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
UNICODE_STRING unicodeString;
|
|||
|
OBJECT_ATTRIBUTES objectAttributes;
|
|||
|
HANDLE hKey;
|
|||
|
NTSTATUS status;
|
|||
|
PKEY_VALUE_PARTIAL_INFORMATION pvpi;
|
|||
|
UCHAR buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION)+sizeof(ULONG)+1];
|
|||
|
ULONG junk;
|
|||
|
PULONG pdw;
|
|||
|
ULONG start;
|
|||
|
|
|||
|
|
|||
|
RtlInitUnicodeString(&unicodeString, rgzAcpiKey);
|
|||
|
InitializeObjectAttributes(
|
|||
|
&objectAttributes,
|
|||
|
&unicodeString,
|
|||
|
OBJ_CASE_INSENSITIVE,
|
|||
|
NULL,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
status = ZwOpenKey(&hKey, KEY_READ, &objectAttributes);
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
RtlInitUnicodeString(&unicodeString, rgzAcpiStart);
|
|||
|
pvpi = (PKEY_VALUE_PARTIAL_INFORMATION)buffer;
|
|||
|
status = ZwQueryValueKey(
|
|||
|
hKey,
|
|||
|
&unicodeString,
|
|||
|
KeyValuePartialInformation,
|
|||
|
pvpi,
|
|||
|
sizeof(buffer),
|
|||
|
&junk
|
|||
|
);
|
|||
|
|
|||
|
if ( (NT_SUCCESS(status)) &&
|
|||
|
(pvpi->Type == REG_DWORD) &&
|
|||
|
(pvpi->DataLength == sizeof(ULONG)) )
|
|||
|
{
|
|||
|
pdw = (PULONG)&(pvpi->Data[0]);
|
|||
|
if (*pdw == 0) {
|
|||
|
ZwClose(hKey);
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
ZwClose(hKey);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|