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