1156 lines
26 KiB
C
1156 lines
26 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1997 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
cmbutton.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
Control Method Button support
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Stephane Plante (splante)
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
NT Kernel Model Driver only
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
July 7, 1997 - Complete Rewrite
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "pch.h"
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(PAGE, ACPICMButtonStart)
|
|||
|
#pragma alloc_text(PAGE, ACPICMLidStart)
|
|||
|
#pragma alloc_text(PAGE, ACPICMPowerButtonStart)
|
|||
|
#pragma alloc_text(PAGE, ACPICMSleepButtonStart)
|
|||
|
#endif
|
|||
|
|
|||
|
VOID
|
|||
|
ACPICMButtonNotify (
|
|||
|
IN PVOID Context,
|
|||
|
IN ULONG EventData
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
AMLI device notification handler for control method button device
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - fixed feature button device object
|
|||
|
EventData - The event code the device is notified with
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension;
|
|||
|
PDEVICE_OBJECT deviceObject = (PDEVICE_OBJECT) Context;
|
|||
|
ULONG capabilities;
|
|||
|
|
|||
|
deviceExtension = ACPIInternalGetDeviceExtension(deviceObject);
|
|||
|
|
|||
|
//
|
|||
|
// Handle event type
|
|||
|
//
|
|||
|
switch (EventData) {
|
|||
|
case 0x80:
|
|||
|
#if 0
|
|||
|
KeBugCheckEx(
|
|||
|
ACPI_BIOS_ERROR,
|
|||
|
0,
|
|||
|
0,
|
|||
|
0,
|
|||
|
0
|
|||
|
);
|
|||
|
#endif
|
|||
|
capabilities = deviceExtension->Button.Capabilities;
|
|||
|
if (capabilities & SYS_BUTTON_LID) {
|
|||
|
|
|||
|
//
|
|||
|
// Get worker to check LID's status
|
|||
|
//
|
|||
|
ACPISetDeviceWorker( deviceExtension, LID_SIGNAL_EVENT);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Notify button was pressed
|
|||
|
//
|
|||
|
ACPIButtonEvent(
|
|||
|
deviceObject,
|
|||
|
capabilities & ~SYS_BUTTON_WAKE,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case 2:
|
|||
|
|
|||
|
//
|
|||
|
// Signal wake button
|
|||
|
//
|
|||
|
ACPIButtonEvent (deviceObject, SYS_BUTTON_WAKE, NULL);
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
ACPIDevPrint( (
|
|||
|
ACPI_PRINT_WARNING,
|
|||
|
deviceExtension,
|
|||
|
"ACPICMButtonNotify: Unknown CM butt notify code %d\n",
|
|||
|
EventData
|
|||
|
) );
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ACPICMButtonSetPower(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This is the main routine for setting power to a button. It dispatches a
|
|||
|
WAIT_WAKE irp (if necessary) then calls the real worker function to
|
|||
|
put the button in the proper state
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - The button device object
|
|||
|
Irp - The request that we are handling
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
PDEVICE_EXTENSION deviceExtension = ACPIInternalGetDeviceExtension(DeviceObject);
|
|||
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation( Irp );
|
|||
|
SYSTEM_POWER_STATE systemState;
|
|||
|
|
|||
|
//
|
|||
|
// If this is request to go a specific D-state, pass that along and
|
|||
|
// return immediately --- there is nothing for us to do in this case
|
|||
|
//
|
|||
|
if (irpStack->Parameters.Power.Type == DevicePowerState) {
|
|||
|
|
|||
|
goto ACPICMButtonSetPowerExit;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// HACKHACK --- Some Vendors can't get their act together and need to
|
|||
|
// have _PSW(On) when the system boots, otherwise they cannot deliver
|
|||
|
// Button Press notification, so to accomodate those vendors, we have
|
|||
|
// enabled _PSW(On) for all button devices except lid switchs. So,
|
|||
|
// if we aren't a lid switch, then do nothing
|
|||
|
//
|
|||
|
if ( !(deviceExtension->Button.Capabilities & SYS_BUTTON_LID) ) {
|
|||
|
|
|||
|
goto ACPICMButtonSetPowerExit;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If we don't support wake on the device, then there is nothing to do
|
|||
|
//
|
|||
|
if ( !(deviceExtension->Flags & DEV_CAP_WAKE) ) {
|
|||
|
|
|||
|
goto ACPICMButtonSetPowerExit;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// What system state are we going to go to?
|
|||
|
//
|
|||
|
systemState = irpStack->Parameters.Power.State.SystemState;
|
|||
|
if (systemState == PowerSystemWorking) {
|
|||
|
|
|||
|
//
|
|||
|
// If we are transitioning back into D0, then we want to cancel
|
|||
|
// any outstanding WAIT_WAKE requests that we have
|
|||
|
//
|
|||
|
status = ACPICMButtonWaitWakeCancel( deviceExtension );
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
|
|||
|
ACPIDevPrint( (
|
|||
|
ACPI_PRINT_FAILURE,
|
|||
|
deviceExtension,
|
|||
|
"%08lx: ACPICMButtonWaitWakeCancel = %08lx\n",
|
|||
|
Irp,
|
|||
|
status
|
|||
|
) );
|
|||
|
goto ACPICMButtonSetPowerExit;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Can we wake the system from this state?
|
|||
|
//
|
|||
|
if (deviceExtension->PowerInfo.SystemWakeLevel < systemState) {
|
|||
|
|
|||
|
goto ACPICMButtonSetPowerExit;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Do not enable this behaviour by default
|
|||
|
//
|
|||
|
if ( (deviceExtension->Flags & DEV_PROP_NO_LID_ACTION) ) {
|
|||
|
|
|||
|
goto ACPICMButtonSetPowerExit;
|
|||
|
|
|||
|
#if 0
|
|||
|
//
|
|||
|
// If we are a lid switch and if the lid isn't closed
|
|||
|
// right now, then do not enable wake support.
|
|||
|
//
|
|||
|
if ( (deviceExtension->Button.LidState != 0) ) {
|
|||
|
|
|||
|
//
|
|||
|
// The lid is open
|
|||
|
//
|
|||
|
goto ACPICMButtonSetPowerExit;
|
|||
|
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Send a WAIT_WAKE irp to ourselves
|
|||
|
//
|
|||
|
status = PoRequestPowerIrp(
|
|||
|
DeviceObject,
|
|||
|
IRP_MN_WAIT_WAKE,
|
|||
|
irpStack->Parameters.Power.State,
|
|||
|
ACPICMButtonWaitWakeComplete,
|
|||
|
NULL,
|
|||
|
NULL
|
|||
|
);
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
|
|||
|
ACPIDevPrint( (
|
|||
|
ACPI_PRINT_FAILURE,
|
|||
|
deviceExtension,
|
|||
|
"(%08lx): ACPICMButtonSetPower - PoRequestPowerIrp = %08lx\n",
|
|||
|
Irp,
|
|||
|
status
|
|||
|
) );
|
|||
|
goto ACPICMButtonSetPowerExit;
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
ACPICMButtonSetPowerExit:
|
|||
|
|
|||
|
//
|
|||
|
// Pass the irp to the normal dispatch point
|
|||
|
//
|
|||
|
return ACPIBusIrpSetPower(
|
|||
|
DeviceObject,
|
|||
|
Irp
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ACPICMButtonStart (
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp,
|
|||
|
IN ULONG ButtonType
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This is the main routine for starting a button. We remember what type
|
|||
|
of button we have then we start the button as we would any other device.
|
|||
|
|
|||
|
We actually register device interfaces and the like in the worker function
|
|||
|
that the completion routine schedules for us.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - The device that is starting
|
|||
|
Irp - The start irp request
|
|||
|
ButtonType - What kind of button this is
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = ACPIInternalGetDeviceExtension(DeviceObject);
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// Initialize device support
|
|||
|
//
|
|||
|
KeInitializeSpinLock (&deviceExtension->Button.SpinLock);
|
|||
|
deviceExtension->Button.Capabilities = ButtonType;
|
|||
|
|
|||
|
//
|
|||
|
// Start the device
|
|||
|
//
|
|||
|
status = ACPIInitStartDevice(
|
|||
|
DeviceObject,
|
|||
|
NULL,
|
|||
|
ACPICMButtonStartCompletion,
|
|||
|
Irp,
|
|||
|
Irp
|
|||
|
);
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
|
|||
|
return STATUS_PENDING;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
return status;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
ACPICMButtonStartCompletion(
|
|||
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|||
|
IN PVOID Context,
|
|||
|
IN NTSTATUS Status
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This is the callback routine that is invoked when we have finished
|
|||
|
programming the resources.
|
|||
|
|
|||
|
This routine queues the irp to a worker thread so that we can do the
|
|||
|
rest of the start device code. It will complete the irp, however, if
|
|||
|
the success is not STATUS_SUCCESS.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceExtension - Extension of the device that was started
|
|||
|
Context - The Irp
|
|||
|
Status - The result
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PIRP irp = (PIRP) Context;
|
|||
|
PWORK_QUEUE_CONTEXT workContext = &(DeviceExtension->Pdo.WorkContext);
|
|||
|
|
|||
|
irp->IoStatus.Status = Status;
|
|||
|
if (NT_SUCCESS(Status)) {
|
|||
|
|
|||
|
DeviceExtension->DeviceState = Started;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation( irp );
|
|||
|
UCHAR minorFunction = irpStack->MinorFunction;
|
|||
|
|
|||
|
//
|
|||
|
// Complete the irp --- we can do this at DPC level without problem
|
|||
|
//
|
|||
|
IoCompleteRequest( irp, IO_NO_INCREMENT );
|
|||
|
|
|||
|
//
|
|||
|
// Let the world know
|
|||
|
//
|
|||
|
ACPIDevPrint( (
|
|||
|
ACPI_PRINT_IRP,
|
|||
|
DeviceExtension,
|
|||
|
"(0x%08lx): %s = 0x%08lx\n",
|
|||
|
irp,
|
|||
|
ACPIDebugGetIrpText(IRP_MJ_PNP, minorFunction),
|
|||
|
Status
|
|||
|
) );
|
|||
|
return;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// We can't run EnableDisableRegions at DPC level,
|
|||
|
// so queue a worker item.
|
|||
|
//
|
|||
|
ExInitializeWorkItem(
|
|||
|
&(workContext->Item),
|
|||
|
ACPICMButtonStartWorker,
|
|||
|
workContext
|
|||
|
);
|
|||
|
workContext->DeviceObject = DeviceExtension->DeviceObject;
|
|||
|
workContext->Irp = irp;
|
|||
|
ExQueueWorkItem(
|
|||
|
&(workContext->Item),
|
|||
|
DelayedWorkQueue
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
ACPICMButtonStartWorker(
|
|||
|
IN PVOID Context
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called at PASSIVE_LEVEL after we turned on the device
|
|||
|
|
|||
|
It registers any interfaces we might need to use
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Context - Contains the arguments passed to the START_DEVICE function
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
PDEVICE_EXTENSION deviceExtension;
|
|||
|
PDEVICE_OBJECT deviceObject;
|
|||
|
PIRP irp;
|
|||
|
PIO_STACK_LOCATION irpStack;
|
|||
|
PWORK_QUEUE_CONTEXT workContext = (PWORK_QUEUE_CONTEXT) Context;
|
|||
|
UCHAR minorFunction;
|
|||
|
|
|||
|
//
|
|||
|
// Grab the parameters that we need out of the Context
|
|||
|
//
|
|||
|
deviceObject = workContext->DeviceObject;
|
|||
|
deviceExtension = ACPIInternalGetDeviceExtension(deviceObject);
|
|||
|
irp = workContext->Irp;
|
|||
|
irpStack = IoGetCurrentIrpStackLocation( irp );
|
|||
|
minorFunction = irpStack->MinorFunction;
|
|||
|
status = irp->IoStatus.Status;
|
|||
|
|
|||
|
//
|
|||
|
// Update the status of the device
|
|||
|
//
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
|
|||
|
goto ACPICMButtonStartWorkerExit;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If this is a lid switch, find out what the current state of
|
|||
|
// switch is
|
|||
|
//
|
|||
|
if (deviceExtension->Button.Capabilities & SYS_BUTTON_LID) {
|
|||
|
|
|||
|
//
|
|||
|
// Register the callback. Ignore the return value as we will
|
|||
|
// don't really care if registration was successfull or not
|
|||
|
//
|
|||
|
status = ACPIInternalRegisterPowerCallBack(
|
|||
|
deviceExtension,
|
|||
|
(PCALLBACK_FUNCTION) ACPICMLidPowerStateCallBack
|
|||
|
);
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Force a callback to make sure that we initialize the lid to the
|
|||
|
// proper policy
|
|||
|
//
|
|||
|
ACPICMLidPowerStateCallBack(
|
|||
|
deviceExtension,
|
|||
|
PO_CB_SYSTEM_POWER_POLICY,
|
|||
|
0
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Note: Setting the events as 0x0 should just cause the
|
|||
|
// system to run ACPICMLidWorker() without causing any side
|
|||
|
// effects (like telling the system to go to sleep
|
|||
|
//
|
|||
|
ACPISetDeviceWorker( deviceExtension, 0 );
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
IO_STATUS_BLOCK ioStatus;
|
|||
|
KIRQL oldIrql;
|
|||
|
POWER_STATE powerState;
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the ioStatus block to enable the device's waitwake
|
|||
|
//
|
|||
|
ioStatus.Status = STATUS_SUCCESS;
|
|||
|
ioStatus.Information = 0;
|
|||
|
|
|||
|
//
|
|||
|
// This is the S-state that we will try to wake the system from
|
|||
|
//
|
|||
|
KeAcquireSpinLock( &AcpiPowerLock, &oldIrql );
|
|||
|
powerState.SystemState = deviceExtension->PowerInfo.SystemWakeLevel;
|
|||
|
KeReleaseSpinLock( &AcpiPowerLock, oldIrql );
|
|||
|
|
|||
|
//
|
|||
|
// Start the WaitWake Loop
|
|||
|
//
|
|||
|
status = ACPIInternalWaitWakeLoop(
|
|||
|
deviceObject,
|
|||
|
IRP_MN_WAIT_WAKE,
|
|||
|
powerState,
|
|||
|
NULL,
|
|||
|
&ioStatus
|
|||
|
);
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
|
|||
|
ACPIDevPrint( (
|
|||
|
ACPI_PRINT_FAILURE,
|
|||
|
deviceExtension,
|
|||
|
" - ACPIInternalWaitWakeLoop = %08lx\n",
|
|||
|
status
|
|||
|
) );
|
|||
|
goto ACPICMButtonStartWorkerExit;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Register for device notifies on this device
|
|||
|
//
|
|||
|
ACPIRegisterForDeviceNotifications(
|
|||
|
deviceObject,
|
|||
|
(PDEVICE_NOTIFY_CALLBACK) ACPICMButtonNotify,
|
|||
|
(PVOID) deviceObject
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Register device as supporting system button ioctl
|
|||
|
//
|
|||
|
status = ACPIInternalSetDeviceInterface(
|
|||
|
deviceObject,
|
|||
|
(LPGUID) &GUID_DEVICE_SYS_BUTTON
|
|||
|
);
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
|
|||
|
ACPIDevPrint( (
|
|||
|
ACPI_PRINT_WARNING,
|
|||
|
deviceExtension,
|
|||
|
"ACPICMButtonStartWorker: ACPIInternalSetDeviceInterface = %08lx\n",
|
|||
|
status
|
|||
|
) );
|
|||
|
goto ACPICMButtonStartWorkerExit;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
ACPICMButtonStartWorkerExit:
|
|||
|
|
|||
|
//
|
|||
|
// Complete the request
|
|||
|
//
|
|||
|
irp->IoStatus.Status = status;
|
|||
|
irp->IoStatus.Information = (ULONG_PTR) NULL;
|
|||
|
IoCompleteRequest( irp, IO_NO_INCREMENT );
|
|||
|
|
|||
|
//
|
|||
|
// Let the world know
|
|||
|
//
|
|||
|
ACPIDevPrint( (
|
|||
|
ACPI_PRINT_IRP,
|
|||
|
deviceExtension,
|
|||
|
"(0x%08lx): %s = 0x%08lx\n",
|
|||
|
irp,
|
|||
|
ACPIDebugGetIrpText(IRP_MJ_PNP, minorFunction),
|
|||
|
status
|
|||
|
) );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ACPICMButtonWaitWakeCancel(
|
|||
|
IN PDEVICE_EXTENSION DeviceExtension
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine cancels any outstanding WAIT_WAKE irp on the given
|
|||
|
device extension.
|
|||
|
|
|||
|
The way that this code works is rather slimy. It is based on the
|
|||
|
assumption that the way that the Irp is cancelled doesn't really
|
|||
|
matter since the completion routine doesn't do anything interesting.
|
|||
|
So, the choice is that the driver can keep track of each WAIT WAKE
|
|||
|
irp the extension is associated with in the extension, write some
|
|||
|
complicated synchronization code to make sure that we don't cancel
|
|||
|
an IRP that could fire a WAIT WAKE, etc, etc, or we can simply fake
|
|||
|
a call that tells the OS that the device woke the system
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceExtension - The deviceExtension to cancel
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
{ return OSNotifyDeviceWake( DeviceExtension->AcpiObject );
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ACPICMButtonWaitWakeComplete(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN UCHAR MinorFunction,
|
|||
|
IN POWER_STATE PowerState,
|
|||
|
IN PVOID Context,
|
|||
|
IN PIO_STATUS_BLOCK IoStatus
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called when the button has awoken the system
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - The device object which woke the computer
|
|||
|
MinorFunction - IRP_MN_WAIT_WAKE
|
|||
|
PowerState - The state that it woke the computer from
|
|||
|
Context - Not used
|
|||
|
IoStatus - The result of the request
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = ACPIInternalGetDeviceExtension(DeviceObject);
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER( MinorFunction );
|
|||
|
UNREFERENCED_PARAMETER( PowerState );
|
|||
|
UNREFERENCED_PARAMETER( Context );
|
|||
|
|
|||
|
if (!NT_SUCCESS(IoStatus->Status)) {
|
|||
|
|
|||
|
ACPIDevPrint( (
|
|||
|
ACPI_PRINT_FAILURE,
|
|||
|
deviceExtension,
|
|||
|
"ACPICMButtonWaitWakeComplete - %08lx\n",
|
|||
|
IoStatus->Status
|
|||
|
) );
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
ACPIDevPrint( (
|
|||
|
ACPI_PRINT_WAKE,
|
|||
|
deviceExtension,
|
|||
|
"ACPICMButtonWaitWakeComplete - %08lx\n",
|
|||
|
IoStatus->Status
|
|||
|
) );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return IoStatus->Status;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
ACPICMLidPowerStateCallBack(
|
|||
|
IN PVOID CallBackContext,
|
|||
|
IN PVOID Argument1,
|
|||
|
IN PVOID Argument2
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called whenever the system changes the power policy.
|
|||
|
|
|||
|
The purpose of this routine is to see wether or not the user placed
|
|||
|
an action on closing the lid. If there is, then we arm the behaviour
|
|||
|
that the lid should always wake up the computer. Otherwise, opening the
|
|||
|
lid should do nothing
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
CallBackContext - The DeviceExtension for the lid switch
|
|||
|
Argument1 - The action that is being undertaken
|
|||
|
Argument2 - Unused
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) CallBackContext;
|
|||
|
SYSTEM_POWER_POLICY powerPolicy;
|
|||
|
ULONG action = PtrToUlong(Argument1);
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER( Argument2 );
|
|||
|
|
|||
|
//
|
|||
|
// We are looking for a PO_CB_SYSTEM_POWER_POLICY change
|
|||
|
//
|
|||
|
if (action != PO_CB_SYSTEM_POWER_POLICY) {
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get the information that we desired
|
|||
|
//
|
|||
|
status = ZwPowerInformation(
|
|||
|
SystemPowerPolicyCurrent,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
&powerPolicy,
|
|||
|
sizeof(SYSTEM_POWER_POLICY)
|
|||
|
);
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
|
|||
|
ACPIDevPrint( (
|
|||
|
ACPI_PRINT_FAILURE,
|
|||
|
deviceExtension,
|
|||
|
"ACPICMLidPowerStateCallBack - Failed ZwPowerInformation %8x\n",
|
|||
|
status
|
|||
|
) );
|
|||
|
return;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Is there an action for the lid?
|
|||
|
//
|
|||
|
if (powerPolicy.LidClose.Action == PowerActionNone ||
|
|||
|
powerPolicy.LidClose.Action == PowerActionReserved) {
|
|||
|
|
|||
|
ACPIInternalUpdateFlags(
|
|||
|
&(deviceExtension->Flags),
|
|||
|
DEV_PROP_NO_LID_ACTION,
|
|||
|
FALSE
|
|||
|
);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
ACPIInternalUpdateFlags(
|
|||
|
&(deviceExtension->Flags),
|
|||
|
DEV_PROP_NO_LID_ACTION,
|
|||
|
TRUE
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ACPICMLidSetPower(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This is the main routine for setting power to a lid. It dispatches a
|
|||
|
WAIT_WAKE irp (if necessary) then calls the real worker function to
|
|||
|
put the button in the proper state
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - The button device object
|
|||
|
Irp - The request that we are handling
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
PDEVICE_EXTENSION deviceExtension = ACPIInternalGetDeviceExtension(DeviceObject);
|
|||
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation( Irp );
|
|||
|
PULONG lidState;
|
|||
|
|
|||
|
//
|
|||
|
// If this is request to go a specific D-state, pass that along and
|
|||
|
// return immediately --- there is nothing for us to do in this case
|
|||
|
//
|
|||
|
if (irpStack->Parameters.Power.Type == DevicePowerState) {
|
|||
|
|
|||
|
return ACPIBusIrpSetDevicePower( DeviceObject, Irp, irpStack );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// HACKHACK
|
|||
|
//
|
|||
|
// We are going to want to know what the state of the lid is. We will
|
|||
|
// end up calling the interpreter at DPC level, so where we store the
|
|||
|
// lidState cannot be on the local stack. One nice place that we
|
|||
|
// can use is the Parameters.Power.Type field, since we already know
|
|||
|
// what the answer should be
|
|||
|
//
|
|||
|
lidState = (PULONG)&(irpStack->Parameters.Power.Type);
|
|||
|
|
|||
|
//
|
|||
|
// Mark the irp as pending
|
|||
|
//
|
|||
|
IoMarkIrpPending( Irp );
|
|||
|
|
|||
|
//
|
|||
|
// Evalute the integer
|
|||
|
//
|
|||
|
status = ACPIGetIntegerAsync(
|
|||
|
deviceExtension,
|
|||
|
PACKED_LID,
|
|||
|
ACPICMLidSetPowerCompletion,
|
|||
|
Irp,
|
|||
|
lidState,
|
|||
|
NULL
|
|||
|
);
|
|||
|
if (status != STATUS_PENDING) {
|
|||
|
|
|||
|
ACPICMLidSetPowerCompletion(
|
|||
|
NULL,
|
|||
|
status,
|
|||
|
NULL,
|
|||
|
Irp
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Always return STATUS_PENDING --- if we complete the irp with
|
|||
|
// another status code, we will do so in another (maybe) context...
|
|||
|
//
|
|||
|
return STATUS_PENDING;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
EXPORT
|
|||
|
ACPICMLidSetPowerCompletion(
|
|||
|
IN PNSOBJ AcpiObject,
|
|||
|
IN NTSTATUS Status,
|
|||
|
IN POBJDATA Result,
|
|||
|
IN PVOID Context
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called when the system has finished fetching the
|
|||
|
current lid state for the switch
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
AcpiObject - The object that we ran (ie: _LID)
|
|||
|
Status - Did the operation succeed
|
|||
|
Result - The result of the operation
|
|||
|
Context - IRP
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
BOOLEAN noticeStateChange = FALSE;
|
|||
|
KIRQL oldIrql;
|
|||
|
PDEVICE_EXTENSION deviceExtension;
|
|||
|
PDEVICE_OBJECT deviceObject;
|
|||
|
PIO_STACK_LOCATION irpStack;
|
|||
|
PIRP irp = (PIRP) Context;
|
|||
|
PULONG lidStateLocation;
|
|||
|
ULONG lidState;
|
|||
|
|
|||
|
//
|
|||
|
// Get the current Irp Stack location
|
|||
|
//
|
|||
|
irpStack = IoGetCurrentIrpStackLocation( irp );
|
|||
|
|
|||
|
//
|
|||
|
// Get the current device extension
|
|||
|
//
|
|||
|
deviceObject = irpStack->DeviceObject;
|
|||
|
deviceExtension = ACPIInternalGetDeviceExtension(deviceObject);
|
|||
|
|
|||
|
//
|
|||
|
// Go and find the place were to told the OS to write the answer to the
|
|||
|
// _LID request. We should also reset this stack location to the proper
|
|||
|
// value
|
|||
|
//
|
|||
|
lidStateLocation = (PULONG)&(irpStack->Parameters.Power.Type);
|
|||
|
lidState = *lidStateLocation;
|
|||
|
*lidStateLocation = (ULONG) SystemPowerState;
|
|||
|
|
|||
|
//
|
|||
|
// Did we succeed?
|
|||
|
//
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
|
|||
|
//
|
|||
|
// Note that we choose to pass the irp back to something
|
|||
|
// that will not send it a WAIT_WAKE irp
|
|||
|
//
|
|||
|
*lidStateLocation = (ULONG) SystemPowerState;
|
|||
|
ACPIBusIrpSetSystemPower( deviceObject, irp, irpStack );
|
|||
|
return;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Make sure that the lid state is a one or a zero
|
|||
|
//
|
|||
|
lidState = (lidState ? 1 : 0);
|
|||
|
|
|||
|
//
|
|||
|
// Grab the button spinlock
|
|||
|
//
|
|||
|
KeAcquireSpinLock( &(deviceExtension->Button.SpinLock), &oldIrql );
|
|||
|
|
|||
|
//
|
|||
|
// Did we the lid change state? Note that because we don't want the
|
|||
|
// user sleeping the machine, closing the lid, then the machine
|
|||
|
// waking up because of Wake-On-LAN causing the machine to go back
|
|||
|
// to sleep, the only state change that we care about is if the
|
|||
|
// lid went from the closed state to the open state
|
|||
|
//
|
|||
|
if (deviceExtension->Button.LidState == FALSE &&
|
|||
|
lidState == 1) {
|
|||
|
|
|||
|
noticeStateChange = TRUE;
|
|||
|
|
|||
|
}
|
|||
|
deviceExtension->Button.LidState = (BOOLEAN) lidState;
|
|||
|
|
|||
|
//
|
|||
|
// Done with the lock
|
|||
|
//
|
|||
|
KeReleaseSpinLock( &(deviceExtension->Button.SpinLock), oldIrql );
|
|||
|
|
|||
|
//
|
|||
|
// Did we notice a lid state change?
|
|||
|
//
|
|||
|
if (noticeStateChange) {
|
|||
|
|
|||
|
ACPIButtonEvent (
|
|||
|
deviceObject,
|
|||
|
SYS_BUTTON_WAKE,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// At this point, we are done, and we can pass the request off to
|
|||
|
// the proper dispatch point. Note that we will choose something that
|
|||
|
// can fire a WAIT_WAKE irp
|
|||
|
//
|
|||
|
ACPICMButtonSetPower( deviceObject, irp );
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ACPICMLidStart (
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This is the start routine for any lid device
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - The device that is starting
|
|||
|
Irp - The start irp request
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PAGED_CODE();
|
|||
|
return ACPICMButtonStart (
|
|||
|
DeviceObject,
|
|||
|
Irp,
|
|||
|
SYS_BUTTON_LID
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
ACPICMLidWorker (
|
|||
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|||
|
IN ULONG Events
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Worker thread function to get the current lid status
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
deviceExtension - The Device Extension for the lid
|
|||
|
Events - The event that occured
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
VOID
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
KIRQL oldIrql;
|
|||
|
NTSTATUS status;
|
|||
|
ULONG lidState;
|
|||
|
|
|||
|
//
|
|||
|
// Get the current lid status
|
|||
|
//
|
|||
|
status = ACPIGetIntegerSync(
|
|||
|
DeviceExtension,
|
|||
|
PACKED_LID,
|
|||
|
&lidState,
|
|||
|
NULL
|
|||
|
);
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
|
|||
|
ACPIDevPrint( (
|
|||
|
ACPI_PRINT_FAILURE,
|
|||
|
DeviceExtension,
|
|||
|
" ACPICMLidWorker - ACPIGetIntegerSync = %08lx\n",
|
|||
|
status
|
|||
|
) );
|
|||
|
return;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// force the value to either a 1 or a 0
|
|||
|
//
|
|||
|
lidState = lidState ? 1 : 0;
|
|||
|
|
|||
|
//
|
|||
|
// We need a spinlock since we can access/set this data from multiple
|
|||
|
// places
|
|||
|
//
|
|||
|
KeAcquireSpinLock( &(DeviceExtension->Button.SpinLock), &oldIrql );
|
|||
|
|
|||
|
//
|
|||
|
// Set the new lid state
|
|||
|
//
|
|||
|
DeviceExtension->Button.LidState = (BOOLEAN) lidState;
|
|||
|
|
|||
|
//
|
|||
|
// Done with the lock
|
|||
|
//
|
|||
|
KeReleaseSpinLock( &(DeviceExtension->Button.SpinLock), oldIrql );
|
|||
|
|
|||
|
//
|
|||
|
// Further processing depends on what events are set
|
|||
|
//
|
|||
|
if (Events & LID_SIGNAL_EVENT) {
|
|||
|
|
|||
|
//
|
|||
|
// Signal the event
|
|||
|
//
|
|||
|
ACPIButtonEvent (
|
|||
|
DeviceExtension->DeviceObject,
|
|||
|
lidState ? SYS_BUTTON_WAKE : SYS_BUTTON_LID,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ACPICMPowerButtonStart (
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This is the start routine for any power button
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - The device that is starting
|
|||
|
Irp - The start irp request
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PAGED_CODE();
|
|||
|
return ACPICMButtonStart (
|
|||
|
DeviceObject,
|
|||
|
Irp,
|
|||
|
SYS_BUTTON_POWER | SYS_BUTTON_WAKE
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ACPICMSleepButtonStart (
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This is the start routine for any sleep button
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - The device that is starting
|
|||
|
Irp - The start irp request
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PAGED_CODE();
|
|||
|
return ACPICMButtonStart (
|
|||
|
DeviceObject,
|
|||
|
Irp,
|
|||
|
SYS_BUTTON_SLEEP | SYS_BUTTON_WAKE
|
|||
|
);
|
|||
|
}
|
|||
|
|