www.pudn.com > pnpi8042.rar > sysbtn.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  
#include  
 
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); 
}