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.
506 lines
15 KiB
506 lines
15 KiB
/*++
|
|
|
|
Copyright (c) 1997-1998 Microsoft Corporation, All Rights Reserved
|
|
|
|
Module Name:
|
|
|
|
power.c
|
|
|
|
Abstract:
|
|
|
|
This module contains plug & play code for the I8042 Keyboard Filter Driver.
|
|
|
|
Environment:
|
|
|
|
Kernel mode.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "i8042prt.h"
|
|
#include "i8042log.h"
|
|
#include <initguid.h>
|
|
#include <poclass.h>
|
|
|
|
VOID
|
|
I8xUpdateSysButtonCaps(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PI8X_KEYBOARD_WORK_ITEM Item
|
|
);
|
|
|
|
VOID
|
|
I8xCompleteSysButtonEventWorker(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PI8X_KEYBOARD_WORK_ITEM Item
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, I8xKeyboardGetSysButtonCaps)
|
|
#pragma alloc_text(PAGE, I8xUpdateSysButtonCaps)
|
|
|
|
#if DELAY_SYSBUTTON_COMPLETION
|
|
#pragma alloc_text(PAGE, I8xCompleteSysButtonEventWorker)
|
|
#endif
|
|
|
|
#endif
|
|
|
|
VOID
|
|
I8xCompleteSysButtonIrp(
|
|
PIRP Irp,
|
|
ULONG Event,
|
|
NTSTATUS Status
|
|
)
|
|
{
|
|
Print(DBG_POWER_NOISE,
|
|
("completing sys button irp 0x%x, event %d, status 0x%x\n",
|
|
Irp, Event, Status));
|
|
|
|
ASSERT(IoSetCancelRoutine(Irp, NULL) == NULL);
|
|
|
|
*(PULONG) Irp->AssociatedIrp.SystemBuffer = Event;
|
|
Irp->IoStatus.Information = sizeof(Event);
|
|
Irp->IoStatus.Status = Status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
NTSTATUS
|
|
I8xKeyboardGetSysButtonCaps(
|
|
PPORT_KEYBOARD_EXTENSION KeyboardExtension,
|
|
PIRP Irp
|
|
)
|
|
{
|
|
PIO_STACK_LOCATION stack;
|
|
NTSTATUS status;
|
|
ULONG caps, size;
|
|
|
|
PAGED_CODE();
|
|
|
|
stack = IoGetCurrentIrpStackLocation(Irp);
|
|
size = 0x0;
|
|
|
|
if (stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG)) {
|
|
Print(DBG_POWER_ERROR, ("get caps, buffer too small\n"));
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
}
|
|
else {
|
|
|
|
caps = 0x0;
|
|
size = sizeof(caps);
|
|
|
|
if (KeyboardExtension->PowerCaps & I8042_POWER_SYS_BUTTON) {
|
|
Print(DBG_POWER_NOISE | DBG_IOCTL_NOISE,
|
|
("get cap: reporting power button\n"));
|
|
caps |= SYS_BUTTON_POWER;
|
|
}
|
|
if (KeyboardExtension->PowerCaps & I8042_SLEEP_SYS_BUTTON) {
|
|
Print(DBG_POWER_NOISE | DBG_IOCTL_NOISE,
|
|
("get cap: reporting sleep button\n"));
|
|
caps |= SYS_BUTTON_SLEEP;
|
|
}
|
|
if (KeyboardExtension->PowerCaps & I8042_WAKE_SYS_BUTTON) {
|
|
Print(DBG_POWER_NOISE | DBG_IOCTL_NOISE,
|
|
("get cap: reporting wake button\n"));
|
|
caps |= SYS_BUTTON_WAKE;
|
|
}
|
|
|
|
// can't do this b/c SYS_BUTTON_WAKE is == 0x0
|
|
// ASSERT(caps != 0x0);
|
|
*(PULONG) Irp->AssociatedIrp.SystemBuffer = caps;
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
Irp->IoStatus.Information = size;
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
return status;
|
|
}
|
|
|
|
#if DELAY_SYSBUTTON_COMPLETION
|
|
VOID
|
|
I8xCompleteSysButtonEventWorker(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PI8X_KEYBOARD_WORK_ITEM Item
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Check to see if, in the short time that we queued the work item and it
|
|
// firing, that the irp has been cancelled
|
|
//
|
|
if (Item->Irp->Cancel) {
|
|
status = STATUS_CANCELLED;
|
|
Item->MakeCode = 0x0;
|
|
}
|
|
|
|
I8xCompleteSysButtonIrp(Item->Irp, Item->MakeCode, status);
|
|
IoFreeWorkItem(Item->Item);
|
|
ExFreePool(Item);
|
|
}
|
|
#endif
|
|
|
|
NTSTATUS
|
|
I8xKeyboardGetSysButtonEvent(
|
|
PPORT_KEYBOARD_EXTENSION KeyboardExtension,
|
|
PIRP Irp
|
|
)
|
|
{
|
|
PIO_STACK_LOCATION stack;
|
|
PIRP oldIrp, pendingIrp;
|
|
NTSTATUS status;
|
|
ULONG event = 0x0;
|
|
KIRQL irql;
|
|
|
|
stack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
if (stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG)) {
|
|
Print(DBG_POWER_ERROR, ("get event, buffer too small\n"));
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = 0x0;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
return status;
|
|
}
|
|
else if (KeyboardExtension->PowerEvent) {
|
|
#if DELAY_SYSBUTTON_COMPLETION
|
|
PI8X_KEYBOARD_WORK_ITEM item;
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
item = (PI8X_KEYBOARD_WORK_ITEM)
|
|
ExAllocatePool(NonPagedPool, sizeof(I8X_KEYBOARD_WORK_ITEM));
|
|
|
|
if (item) {
|
|
item->Item = IoAllocateWorkItem(KeyboardExtension->Self);
|
|
if (item->Item) {
|
|
Print(DBG_POWER_NOISE, ("Queueing work item to complete event\n"));
|
|
|
|
item->MakeCode = KeyboardExtension->PowerEvent;
|
|
item->Irp = Irp;
|
|
|
|
//
|
|
// No need to set a cancel routine b/c we will always be
|
|
// completing the irp in very short period of time
|
|
//
|
|
IoMarkIrpPending(Irp);
|
|
|
|
IoQueueWorkItem(item->Item,
|
|
I8xCompleteSysButtonEventWorker,
|
|
DelayedWorkQueue,
|
|
item);
|
|
|
|
status = STATUS_PENDING;
|
|
}
|
|
else {
|
|
ExFreePool(item);
|
|
}
|
|
}
|
|
|
|
#else // DELAY_SYSBUTTON_COMPLETION
|
|
|
|
Print(DBG_POWER_INFO, ("completing event immediately\n"));
|
|
event = KeyboardExtension->PowerEvent;
|
|
status = STATUS_SUCCESS;
|
|
|
|
#endif // DELAY_SYSBUTTON_COMPLETION
|
|
|
|
KeyboardExtension->PowerEvent = 0x0;
|
|
}
|
|
else {
|
|
//
|
|
// See if the pending sys button is NULL. If it is, then Irp will
|
|
// put into the slot
|
|
//
|
|
KeAcquireSpinLock(&KeyboardExtension->SysButtonSpinLock, &irql);
|
|
|
|
if (KeyboardExtension->SysButtonEventIrp == NULL) {
|
|
Print(DBG_POWER_INFO, ("pending sys button event\n"));
|
|
|
|
KeyboardExtension->SysButtonEventIrp = Irp;
|
|
IoMarkIrpPending(Irp);
|
|
IoSetCancelRoutine(Irp, I8xSysButtonCancelRoutine);
|
|
|
|
status = STATUS_PENDING;
|
|
|
|
//
|
|
// We don't care if Irp->Cancel is TRUE. If it is, then the cancel
|
|
// routine will complete the irp an everything will be all set.
|
|
// Since status == STATUS_PENDING, nobody in this code path is going
|
|
// to touch the irp
|
|
//
|
|
}
|
|
else {
|
|
Print(DBG_POWER_ERROR | DBG_POWER_INFO,
|
|
("got 1+ get sys button event requests!\n"));
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
KeReleaseSpinLock(&KeyboardExtension->SysButtonSpinLock, irql);
|
|
}
|
|
|
|
if (status != STATUS_PENDING) {
|
|
Print(DBG_POWER_NOISE,
|
|
("completing get sys power event with 0x%x\n", status));
|
|
I8xCompleteSysButtonIrp(Irp, event, status);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
I8xKeyboardSysButtonEventDpc(
|
|
IN PKDPC Dpc,
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN SYS_BUTTON_ACTION Action,
|
|
IN ULONG MakeCode
|
|
)
|
|
{
|
|
PPORT_KEYBOARD_EXTENSION kbExtension = DeviceObject->DeviceExtension;
|
|
PI8X_KEYBOARD_WORK_ITEM item;
|
|
KIRQL irql;
|
|
ULONG event;
|
|
PIRP irp;
|
|
|
|
UNREFERENCED_PARAMETER(Dpc);
|
|
|
|
ASSERT(Action != NoAction);
|
|
|
|
//
|
|
// Check to see if we need to complete the IRP or actually register for a
|
|
// notification
|
|
//
|
|
switch (MakeCode) {
|
|
case KEYBOARD_POWER_CODE: event = SYS_BUTTON_POWER; break;
|
|
case KEYBOARD_SLEEP_CODE: event = SYS_BUTTON_SLEEP; break;
|
|
case KEYBOARD_WAKE_CODE: event = SYS_BUTTON_WAKE; break;
|
|
default: event = 0x0; TRAP();
|
|
}
|
|
|
|
if (Action == SendAction) {
|
|
|
|
Print(DBG_POWER_INFO, ("button event complete (0x%x)\n", event));
|
|
|
|
KeAcquireSpinLock(&kbExtension->SysButtonSpinLock, &irql);
|
|
|
|
irp = kbExtension->SysButtonEventIrp;
|
|
kbExtension->SysButtonEventIrp = NULL;
|
|
|
|
if (irp && (irp->Cancel || IoSetCancelRoutine(irp, NULL) == NULL)) {
|
|
irp = NULL;
|
|
}
|
|
|
|
KeReleaseSpinLock(&kbExtension->SysButtonSpinLock, irql);
|
|
|
|
if (irp) {
|
|
I8xCompleteSysButtonIrp(irp, event, STATUS_SUCCESS);
|
|
}
|
|
}
|
|
else {
|
|
ASSERT(Action == UpdateAction);
|
|
|
|
//
|
|
// Queue the work item. We need to write the value to the registry and
|
|
// set the device interface
|
|
//
|
|
item = (PI8X_KEYBOARD_WORK_ITEM)
|
|
ExAllocatePool(NonPagedPool, sizeof(I8X_KEYBOARD_WORK_ITEM));
|
|
|
|
if (item) {
|
|
item->Item = IoAllocateWorkItem(DeviceObject);
|
|
if (item->Item) {
|
|
Print(DBG_POWER_NOISE, ("Queueing work item to update caps\n"));
|
|
|
|
//
|
|
// Save this off so when we get the IOCTL, we can complete it immediately
|
|
//
|
|
kbExtension->PowerEvent |= (UCHAR) event;
|
|
item->MakeCode = MakeCode;
|
|
|
|
IoQueueWorkItem(item->Item,
|
|
I8xUpdateSysButtonCaps,
|
|
DelayedWorkQueue,
|
|
item);
|
|
}
|
|
else {
|
|
ExFreePool(item);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
I8xSysButtonCancelRoutine(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
PPORT_KEYBOARD_EXTENSION kbExtension = DeviceObject->DeviceExtension;
|
|
PIRP irp;
|
|
KIRQL irql;
|
|
|
|
Print(DBG_POWER_TRACE, ("SysButtonCancelRoutine\n"));
|
|
|
|
KeAcquireSpinLock(&kbExtension->SysButtonSpinLock, &irql);
|
|
|
|
irp = kbExtension->SysButtonEventIrp;
|
|
kbExtension->SysButtonEventIrp = NULL;
|
|
Print(DBG_POWER_INFO, ("pending event irp = 0x%x\n", irp));
|
|
|
|
KeReleaseSpinLock(&kbExtension->SysButtonSpinLock, irql);
|
|
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
Irp->IoStatus.Information = 0x0;
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
PIRP
|
|
I8xUpdateSysButtonCapsGetPendedIrp(
|
|
PPORT_KEYBOARD_EXTENSION KeyboardExtension
|
|
)
|
|
{
|
|
KIRQL irql;
|
|
PIRP irp;
|
|
|
|
KeAcquireSpinLock(&KeyboardExtension->SysButtonSpinLock, &irql);
|
|
|
|
irp = KeyboardExtension->SysButtonEventIrp;
|
|
KeyboardExtension->SysButtonEventIrp = NULL;
|
|
|
|
if (irp && IoSetCancelRoutine(irp, NULL) == NULL) {
|
|
//
|
|
// Cancel routine take care of the irp
|
|
//
|
|
irp = NULL;
|
|
}
|
|
|
|
KeReleaseSpinLock(&KeyboardExtension->SysButtonSpinLock, irql);
|
|
|
|
return irp;
|
|
}
|
|
|
|
VOID
|
|
I8xUpdateSysButtonCaps(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PI8X_KEYBOARD_WORK_ITEM Item
|
|
)
|
|
{
|
|
UNICODE_STRING strPowerCaps;
|
|
PPORT_KEYBOARD_EXTENSION kbExtension;
|
|
HANDLE devInstRegKey;
|
|
ULONG newPowerCaps;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PIRP irp;
|
|
|
|
PAGED_CODE();
|
|
|
|
kbExtension = (PPORT_KEYBOARD_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
if (Item->MakeCode != 0x0) {
|
|
if ((NT_SUCCESS(IoOpenDeviceRegistryKey(kbExtension->PDO,
|
|
PLUGPLAY_REGKEY_DEVICE,
|
|
STANDARD_RIGHTS_ALL,
|
|
&devInstRegKey)))) {
|
|
//
|
|
// Update the power caps
|
|
//
|
|
switch (Item->MakeCode) {
|
|
case KEYBOARD_POWER_CODE:
|
|
Print(DBG_POWER_NOISE, ("Adding Power Sys Button cap\n"));
|
|
kbExtension->PowerCaps |= I8042_POWER_SYS_BUTTON;
|
|
break;
|
|
|
|
case KEYBOARD_SLEEP_CODE:
|
|
Print(DBG_POWER_NOISE, ("Adding Power Sleep Button cap\n"));
|
|
kbExtension->PowerCaps |= I8042_SLEEP_SYS_BUTTON;
|
|
break;
|
|
|
|
case KEYBOARD_WAKE_CODE:
|
|
Print(DBG_POWER_NOISE, ("Adding Power Wake Button cap\n"));
|
|
kbExtension->PowerCaps |= I8042_WAKE_SYS_BUTTON;
|
|
break;
|
|
|
|
default:
|
|
Print(DBG_POWER_ERROR,
|
|
("Adding power cap, unknown makecode 0x%x\n",
|
|
(ULONG) Item->MakeCode
|
|
));
|
|
TRAP();
|
|
}
|
|
|
|
RtlInitUnicodeString(&strPowerCaps,
|
|
pwPowerCaps
|
|
);
|
|
|
|
newPowerCaps = kbExtension->PowerCaps;
|
|
|
|
ZwSetValueKey(devInstRegKey,
|
|
&strPowerCaps,
|
|
0,
|
|
REG_DWORD,
|
|
&newPowerCaps,
|
|
sizeof(newPowerCaps)
|
|
);
|
|
|
|
ZwClose(devInstRegKey);
|
|
|
|
if (!kbExtension->SysButtonInterfaceName.Buffer) {
|
|
//
|
|
// No prev caps so we must register and turn on the interface now
|
|
//
|
|
ASSERT(kbExtension->SysButtonEventIrp == NULL);
|
|
|
|
status = I8xRegisterDeviceInterface(kbExtension->PDO,
|
|
&GUID_DEVICE_SYS_BUTTON,
|
|
&kbExtension->SysButtonInterfaceName
|
|
);
|
|
|
|
Print(DBG_POWER_NOISE,
|
|
("Registering Interface for 1st time (0x%x)\n", status));
|
|
}
|
|
else {
|
|
//
|
|
// We better have a pending event irp already then!
|
|
//
|
|
Print(DBG_POWER_INFO, ("failing old sys button event irp\n"));
|
|
|
|
if ((irp = I8xUpdateSysButtonCapsGetPendedIrp(kbExtension))) {
|
|
//
|
|
// Complete the old irp, the PO subsystem will then
|
|
// remove this system button.
|
|
//
|
|
I8xCompleteSysButtonIrp(irp, 0x0, STATUS_DEVICE_NOT_CONNECTED);
|
|
}
|
|
|
|
//
|
|
// We need to reregister with the PO subsystem so that it will
|
|
// requery this interface
|
|
//
|
|
IoSetDeviceInterfaceState(&kbExtension->SysButtonInterfaceName,
|
|
FALSE);
|
|
|
|
IoSetDeviceInterfaceState(&kbExtension->SysButtonInterfaceName,
|
|
TRUE);
|
|
}
|
|
}
|
|
else {
|
|
Print(DBG_POWER_ERROR, ("could not open devnode key!\n"));
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// Must report the device interface
|
|
//
|
|
}
|
|
|
|
IoFreeWorkItem(Item->Item);
|
|
ExFreePool(Item);
|
|
}
|
|
|