windows-nt/Source/XPSP1/NT/drivers/input/pnpi8042/sysbtn.c
2020-09-26 16:20:57 +08:00

507 lines
14 KiB
C

/*++
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);
}