www.pudn.com > pnpi8042.rar > pnp.c


/*++ 
 
Copyright (c) 1997-1998 Microsoft Corporation, All Rights Reserved 
 
Module Name: 
 
    pnp.c 
 
Abstract: 
 
    This module contains general PnP and Power code for the i8042prt Driver. 
 
Environment: 
 
    Kernel mode. 
 
Revision History: 
 
--*/ 
#include "i8042prt.h" 
#include "i8042log.h" 
 
#ifdef ALLOC_PRAGMA 
#pragma alloc_text(PAGE, I8xAddDevice) 
#pragma alloc_text(PAGE, I8xFilterResourceRequirements) 
#pragma alloc_text(PAGE, I8xFindPortCallout) 
#pragma alloc_text(PAGE, I8xManuallyRemoveDevice) 
#pragma alloc_text(PAGE, I8xPnP) 
#pragma alloc_text(PAGE, I8xPower) 
#pragma alloc_text(PAGE, I8xRegisterDeviceInterface) 
#pragma alloc_text(PAGE, I8xRemovePort) 
#pragma alloc_text(PAGE, I8xSendIrpSynchronously)  
#endif 
 
NTSTATUS 
I8xAddDevice ( 
    IN PDRIVER_OBJECT   Driver, 
    IN PDEVICE_OBJECT   PDO 
    ) 
/*++ 
 
Routine Description: 
 
    Adds a device to the stack and sets up the appropriate flags and  
    device extension for the newly created device. 
     
Arguments: 
 
    Driver - The driver object 
    PDO    - the device that we are attaching ourselves on top of 
     
Return Value: 
 
    NTSTATUS result code. 
 
--*/ 
{ 
    PCOMMON_DATA             commonData; 
    PIO_ERROR_LOG_PACKET     errorLogEntry; 
    PDEVICE_OBJECT           device; 
    NTSTATUS                 status = STATUS_SUCCESS; 
    ULONG                    maxSize; 
 
    PAGED_CODE(); 
 
    Print(DBG_PNP_TRACE, ("enter Add Device \n")); 
 
    maxSize = sizeof(PORT_KEYBOARD_EXTENSION) > sizeof(PORT_MOUSE_EXTENSION) ? 
              sizeof(PORT_KEYBOARD_EXTENSION) : 
              sizeof(PORT_MOUSE_EXTENSION); 
 
    status = IoCreateDevice(Driver,                 // driver 
                            maxSize,                // size of extension 
                            NULL,                   // device name 
                            FILE_DEVICE_8042_PORT,  // device type  ?? unknown at this time!!! 
                            0,                      // device characteristics 
                            FALSE,                  // exclusive 
                            &device                 // new device 
                            ); 
 
    if (!NT_SUCCESS(status)) { 
        return (status); 
    } 
 
    RtlZeroMemory(device->DeviceExtension, maxSize); 
 
    commonData = GET_COMMON_DATA(device->DeviceExtension); 
    commonData->TopOfStack = IoAttachDeviceToDeviceStack(device, PDO); 
 
    if (commonData->TopOfStack == NULL) { 
        // 
        // Not good; in only extreme cases will this fail 
        // 
        errorLogEntry = (PIO_ERROR_LOG_PACKET) 
            IoAllocateErrorLogEntry(Driver, (UCHAR)sizeof(IO_ERROR_LOG_PACKET)); 
        if (errorLogEntry) { 
            errorLogEntry->ErrorCode = I8042_ATTACH_DEVICE_FAILED; 
            errorLogEntry->DumpDataSize = 0; 
            errorLogEntry->SequenceNumber = 0; 
            errorLogEntry->MajorFunctionCode = 0; 
            errorLogEntry->IoControlCode = 0; 
            errorLogEntry->RetryCount = 0; 
            errorLogEntry->UniqueErrorValue = 0; 
            errorLogEntry->FinalStatus =  STATUS_DEVICE_NOT_CONNECTED; 
 
            IoWriteErrorLogEntry (errorLogEntry); 
        } 
 
        IoDeleteDevice (device); 
        return STATUS_DEVICE_NOT_CONNECTED;  
    } 
 
    ASSERT(commonData->TopOfStack); 
 
    commonData->Self =          device; 
    commonData->PDO =           PDO; 
    commonData->PowerState =    PowerDeviceD0; 
 
    KeInitializeSpinLock(&commonData->InterruptSpinLock); 
 
    // 
    // Initialize the data consumption timer 
    // 
    KeInitializeTimer(&commonData->DataConsumptionTimer); 
 
    // 
    // Initialize the port DPC queue to log overrun and internal 
    // device errors. 
    // 
    KeInitializeDpc( 
        &commonData->ErrorLogDpc, 
        (PKDEFERRED_ROUTINE) I8042ErrorLogDpc, 
        device 
        ); 
 
    // 
    // Initialize the device completion DPC for requests that exceed the 
    // maximum number of retries. 
    // 
    KeInitializeDpc( 
        &commonData->RetriesExceededDpc, 
        (PKDEFERRED_ROUTINE) I8042RetriesExceededDpc, 
        device 
        ); 
 
    // 
    // Initialize the device completion DPC for requests that have timed out 
    // 
    KeInitializeDpc( 
        &commonData->TimeOutDpc, 
        (PKDEFERRED_ROUTINE) I8042TimeOutDpc, 
        device 
        ); 
 
    // 
    // Initialize the port completion DPC object in the device extension. 
    // This DPC routine handles the completion of successful set requests. 
    // 
    IoInitializeDpcRequest(device, I8042CompletionDpc); 
 
    IoInitializeRemoveLock(&commonData->RemoveLock, 
                           I8042_POOL_TAG, 
                           0, 
                           0); 
 
    device->Flags |= DO_BUFFERED_IO; 
    device->Flags |= DO_POWER_PAGABLE; 
    device->Flags &= ~DO_DEVICE_INITIALIZING; 
 
    Print(DBG_PNP_TRACE, ("Add Device (0x%x)\n", status)); 
 
    return status; 
} 
 
NTSTATUS 
I8xSendIrpSynchronously ( 
    IN PDEVICE_OBJECT DeviceObject, 
    IN PIRP Irp, 
    IN BOOLEAN Strict 
    ) 
/*++ 
 
Routine Description: 
 
    Generic routine to send an irp DeviceObject and wait for its return up the 
    device stack. 
     
Arguments: 
 
    DeviceObject - The device object to which we want to send the Irp 
     
    Irp - The Irp we want to send 
     
Return Value: 
 
    return code from the Irp 
--*/ 
{ 
    KEVENT   event; 
    NTSTATUS status; 
 
    PAGED_CODE(); 
 
    KeInitializeEvent(&event, 
                      SynchronizationEvent, 
                      FALSE 
                      ); 
 
    IoCopyCurrentIrpStackLocationToNext(Irp); 
 
    IoSetCompletionRoutine(Irp, 
                           I8xPnPComplete, 
                           &event, 
                           TRUE, 
                           TRUE, 
                           TRUE 
                           ); 
 
    status = IoCallDriver(DeviceObject, Irp); 
 
    // 
    // Wait for lower drivers to be done with the Irp 
    // 
    if (status == STATUS_PENDING) { 
       KeWaitForSingleObject(&event, 
                             Executive, 
                             KernelMode, 
                             FALSE, 
                             NULL 
                             ); 
       status = Irp->IoStatus.Status; 
    } 
 
    if (!Strict &&  
        (status == STATUS_NOT_SUPPORTED || 
         status == STATUS_INVALID_DEVICE_REQUEST)) { 
        status = STATUS_SUCCESS; 
    } 
 
    return status; 
} 
 
NTSTATUS 
I8xPnPComplete ( 
    IN PDEVICE_OBJECT DeviceObject, 
    IN PIRP Irp, 
    IN PKEVENT Event 
    ) 
/*++ 
 
Routine Description: 
 
    Completion routine for all PnP IRPs 
     
Arguments: 
 
    DeviceObject - Pointer to the DeviceObject 
 
    Irp - Pointer to the request packet 
     
    Event - The event to set once processing is complete  
 
Return Value: 
 
    STATUS_MORE_PROCESSING_REQUIRED 
 
--*/ 
{ 
    UNREFERENCED_PARAMETER (DeviceObject); 
    UNREFERENCED_PARAMETER (Irp); 
 
    // 
    // Since this completion routines sole purpose in life is to synchronize 
    // Irp, we know that unless something else happens that the IoCallDriver 
    // will unwind AFTER the we have complete this Irp.  Therefore we should 
    // NOT bubble up the pending bit. 
    // 
    // if (Irp->PendingReturned) { 
    //     IoMarkIrpPending(Irp); 
    // } 
    // 
 
    KeSetEvent(Event, 0, FALSE); 
    return STATUS_MORE_PROCESSING_REQUIRED; 
} 
 
NTSTATUS 
I8xPnP ( 
    IN PDEVICE_OBJECT DeviceObject, 
    IN PIRP Irp 
    ) 
/*++ 
 
Routine Description: 
 
    This is the dispatch routine for PnP requests 
Arguments: 
 
    DeviceObject - Pointer to the device object 
 
    Irp - Pointer to the request packet 
 
 
Return Value: 
 
    STATUS_SUCCESSFUL if successful, 
    an valid NTSTATUS error code otherwise 
 
--*/ 
{ 
    PPORT_KEYBOARD_EXTENSION   kbExtension; 
    PPORT_MOUSE_EXTENSION      mouseExtension; 
    PCOMMON_DATA               commonData; 
    PIO_STACK_LOCATION         stack; 
    NTSTATUS                   status = STATUS_SUCCESS; 
    KIRQL                      oldIrql; 
 
    PAGED_CODE(); 
 
    commonData = GET_COMMON_DATA(DeviceObject->DeviceExtension); 
    stack = IoGetCurrentIrpStackLocation(Irp); 
 
    status = IoAcquireRemoveLock(&commonData->RemoveLock, Irp); 
    if (!NT_SUCCESS(status)) { 
        Irp->IoStatus.Status = status; 
        Irp->IoStatus.Information = 0; 
        IoCompleteRequest(Irp, IO_NO_INCREMENT); 
 
        return status; 
    } 
 
    Print(DBG_PNP_TRACE, 
          ("I8xPnP (%s),  enter (min func=0x%x)\n", 
          commonData->IsKeyboard ? "kb" : "mou", 
          (ULONG) stack->MinorFunction 
          )); 
 
    switch (stack->MinorFunction) { 
    case IRP_MN_START_DEVICE: 
 
        // 
        // The device is starting. 
        // 
        // We cannot touch the device (send it any non pnp irps) until a 
        // start device has been passed down to the lower drivers. 
        // 
        status = I8xSendIrpSynchronously(commonData->TopOfStack, Irp, TRUE); 
 
        if (NT_SUCCESS(status) && NT_SUCCESS(Irp->IoStatus.Status)) { 
            // 
            // As we are successfully now back from our start device 
            // we can do work. 
 
            ExAcquireFastMutexUnsafe(&Globals.DispatchMutex); 
 
            if (commonData->Started) { 
                Print(DBG_PNP_ERROR, 
                      ("received 1+ starts on %s\n", 
                      commonData->IsKeyboard ? "kb" : "mouse" 
                      )); 
            } 
            else { 
                // 
                // commonData->IsKeyboard is set during 
                //  IOCTL_INTERNAL_KEYBOARD_CONNECT to TRUE and  
                //  IOCTL_INTERNAL_MOUSE_CONNECT to FALSE 
                // 
                if (IS_KEYBOARD(commonData)) { 
                    status = I8xKeyboardStartDevice( 
                      (PPORT_KEYBOARD_EXTENSION) DeviceObject->DeviceExtension, 
                      stack->Parameters.StartDevice.AllocatedResourcesTranslated 
                      ); 
                } 
                else { 
                    status = I8xMouseStartDevice( 
                      (PPORT_MOUSE_EXTENSION) DeviceObject->DeviceExtension, 
                      stack->Parameters.StartDevice.AllocatedResourcesTranslated 
                      ); 
                } 
     
                if (NT_SUCCESS(status)) { 
                    InterlockedIncrement(&Globals.StartedDevices); 
                    commonData->Started = TRUE; 
                } 
            } 
 
            ExReleaseFastMutexUnsafe(&Globals.DispatchMutex); 
        } 
 
        // 
        // We must now complete the IRP, since we stopped it in the 
        // completetion routine with MORE_PROCESSING_REQUIRED. 
        // 
        Irp->IoStatus.Status = status; 
        Irp->IoStatus.Information = 0; 
        IoCompleteRequest(Irp, IO_NO_INCREMENT); 
 
        break; 
 
    case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:  
        // 
        // The general rule of thumb for handling this minor code is this:   
        //    add resources when the irp is going down the stack and 
        //    remove resources when the irp is coming back up the stack 
        // 
        // The irp has the original resources on the way down. 
        // 
        status = I8xSendIrpSynchronously(commonData->TopOfStack, Irp, FALSE); 
 
        if (NT_SUCCESS(status)) { 
            status = I8xFilterResourceRequirements(DeviceObject, 
                                                   Irp 
                                                   ); 
        } 
        else { 
           Print(DBG_PNP_ERROR, 
                 ("error pending filter res req event (0x%x)\n", 
                 status 
                 )); 
        } 
    
        // 
        // Irp->IoStatus.Information will contain the new i/o resource  
        // requirements list so leave it alone 
        // 
        Irp->IoStatus.Status = status; 
        IoCompleteRequest(Irp, IO_NO_INCREMENT); 
 
        break; 
     
    case IRP_MN_QUERY_PNP_DEVICE_STATE:  
 
        status = I8xSendIrpSynchronously(commonData->TopOfStack, Irp, FALSE); 
        if (NT_SUCCESS(status)) { 
            (PNP_DEVICE_STATE) Irp->IoStatus.Information |= 
                commonData->PnpDeviceState; 
        } 
        else { 
            Print(DBG_PNP_ERROR, 
                  ("error pending query pnp device state event (0x%x)\n", 
                  status 
                  )); 
 
        } 
    
        // 
        // Irp->IoStatus.Information will contain the new i/o resource  
        // requirements list so leave it alone 
        // 
        Irp->IoStatus.Status = status; 
        IoCompleteRequest(Irp, IO_NO_INCREMENT); 
 
        break; 
 
    // 
    // Don't let either of the requests succeed, otherwise the kb/mouse 
    // might be rendered useless. 
    // 
    //  NOTE: this behavior is particular to i8042prt.  Any other driver, 
    //        especially any other keyboard or port driver, should  
    //        succeed the query remove or stop.  i8042prt has this different  
    //        behavior because of the shared I/O ports but independent interrupts. 
    // 
    //        FURTHERMORE, if you allow the query to succeed, it should be sent 
    //        down the stack (see sermouse.sys for an example of how to do this) 
    // 
    case IRP_MN_QUERY_REMOVE_DEVICE: 
    case IRP_MN_QUERY_STOP_DEVICE: 
        status = (MANUALLY_REMOVED(commonData) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL); 
 
        // 
        // If we succeed the irp, we must send it down the stack 
        // 
        if (NT_SUCCESS(status)) { 
            IoSkipCurrentIrpStackLocation(Irp); 
            status = IoCallDriver(commonData->TopOfStack, Irp); 
        } 
        else { 
            Irp->IoStatus.Status = status;  
            Irp->IoStatus.Information = 0;     
            IoCompleteRequest(Irp, IO_NO_INCREMENT); 
        } 
        break; 
 
    // 
    // PnP rules dictate we send the IRP down to the PDO first 
    // 
    case IRP_MN_CANCEL_REMOVE_DEVICE: 
    case IRP_MN_CANCEL_STOP_DEVICE: 
        status = I8xSendIrpSynchronously(commonData->TopOfStack, Irp, FALSE); 
 
        Irp->IoStatus.Status = status; 
        Irp->IoStatus.Information = 0;     
        IoCompleteRequest(Irp, IO_NO_INCREMENT); 
 
        break; 
 
    // case IRP_MN_SURPRISE_REMOVAL: 
    case IRP_MN_REMOVE_DEVICE: 
        Print(DBG_PNP_INFO, 
              ("(surprise) remove device (0x%x function 0x%x)\n", 
              commonData->Self, 
              (ULONG) stack->MinorFunction)); 
 
        if (commonData->Initialized) { 
            IoWMIRegistrationControl(commonData->Self, 
                                     WMIREG_ACTION_DEREGISTER 
                                     ); 
        } 
 
        if (commonData->Started) { 
             InterlockedDecrement(&Globals.StartedDevices); 
        } 
 
        // 
        // Wait for any pending I/O to drain 
        // 
        IoReleaseRemoveLockAndWait(&commonData->RemoveLock, Irp); 
 
        ExAcquireFastMutexUnsafe(&Globals.DispatchMutex); 
        if (IS_KEYBOARD(commonData)) { 
            I8xKeyboardRemoveDevice(DeviceObject); 
        } 
        else { 
            I8xMouseRemoveDevice(DeviceObject); 
        } 
        ExReleaseFastMutexUnsafe(&Globals.DispatchMutex); 
 
        // 
        // Set these flags so that when a surprise remove is sent, it will be 
        // handled just like a remove, and when the remove comes, no other  
        // removal type actions will occur. 
        // 
        commonData->Started = FALSE; 
        commonData->Initialized = FALSE; 
 
        Irp->IoStatus.Status = STATUS_SUCCESS; 
        IoSkipCurrentIrpStackLocation(Irp); 
        status = IoCallDriver(commonData->TopOfStack, Irp); 
 
        IoDetachDevice(commonData->TopOfStack);  
        IoDeleteDevice(DeviceObject); 
         
        return status; 
 
    case IRP_MN_QUERY_CAPABILITIES: 
 
        // 
        // Change the device caps to not allow wait wake requests on level 
        // triggered interrupts for mice because when an errant mouse movement 
        // occurs while we are going to sleep, the interrupt will remain 
        // triggered indefinitely. 
        // 
        // If the mouse does not have a level triggered interrupt, just let the 
        // irp go by... 
        // 
        if (commonData->Started && 
            IS_MOUSE(commonData) && IS_LEVEL_TRIGGERED(commonData)) { 
 
            Print(DBG_PNP_NOISE, ("query caps, mouse is level triggered\n")); 
 
            status = I8xSendIrpSynchronously(commonData->TopOfStack, Irp, TRUE); 
            if (NT_SUCCESS(status) && NT_SUCCESS(Irp->IoStatus.Status)) { 
                PDEVICE_CAPABILITIES devCaps; 
 
                Print(DBG_PNP_INFO, ("query caps, removing wake caps\n")); 
 
                stack = IoGetCurrentIrpStackLocation(Irp); 
                devCaps = stack->Parameters.DeviceCapabilities.Capabilities; 
 
                ASSERT(devCaps); 
 
                if (devCaps) { 
                    Print(DBG_PNP_NOISE, 
                          ("old DeviceWake was D%d and SystemWake was S%d.\n", 
                          devCaps->DeviceWake-1, devCaps->SystemWake-1 
                          )) ; 
 
                    devCaps->DeviceWake = PowerDeviceUnspecified; 
                    devCaps->SystemWake = PowerSystemUnspecified; 
                } 
            } 
 
            IoCompleteRequest(Irp, IO_NO_INCREMENT); 
            break; 
        } 
 
    case IRP_MN_STOP_DEVICE: 
    case IRP_MN_QUERY_DEVICE_RELATIONS: 
    case IRP_MN_QUERY_INTERFACE: 
    case IRP_MN_QUERY_DEVICE_TEXT: 
    case IRP_MN_QUERY_RESOURCES: 
    case IRP_MN_QUERY_RESOURCE_REQUIREMENTS: 
    case IRP_MN_READ_CONFIG: 
    case IRP_MN_WRITE_CONFIG: 
    case IRP_MN_EJECT: 
    case IRP_MN_SET_LOCK: 
    case IRP_MN_QUERY_ID: 
    default: 
        // 
        // Here the driver below i8042prt might modify the behavior of these IRPS 
        // Please see PlugPlay documentation for use of these IRPs. 
        // 
        IoSkipCurrentIrpStackLocation(Irp); 
        status = IoCallDriver(commonData->TopOfStack, Irp); 
        break; 
    } 
 
    Print(DBG_PNP_TRACE, 
          ("I8xPnP (%s) exit (status=0x%x)\n", 
          commonData->IsKeyboard ? "kb" : "mou", 
          status 
          )); 
 
    IoReleaseRemoveLock(&commonData->RemoveLock, Irp); 
 
    return status; 
} 
 
LONG 
I8xManuallyRemoveDevice( 
    PCOMMON_DATA CommonData 
    ) 
/*++ 
 
Routine Description: 
 
    Invalidates CommonData->PDO's device state and sets the manually removed  
    flag 
     
Arguments: 
 
    CommonData - represent either the keyboard or mouse 
     
Return Value: 
 
    new device count for that particular type of device 
     
--*/ 
{ 
    LONG deviceCount; 
 
    PAGED_CODE(); 
 
    if (IS_KEYBOARD(CommonData)) { 
 
        deviceCount = InterlockedDecrement(&Globals.AddedKeyboards); 
        if (deviceCount < 1) { 
            Print(DBG_PNP_INFO, ("clear kb (manually remove)\n")); 
            CLEAR_KEYBOARD_PRESENT(); 
        } 
 
    } else { 
 
        deviceCount = InterlockedDecrement(&Globals.AddedMice); 
        if (deviceCount < 1) { 
            Print(DBG_PNP_INFO, ("clear mou (manually remove)\n")); 
            CLEAR_MOUSE_PRESENT(); 
        } 
         
    } 
 
    CommonData->PnpDeviceState |= PNP_DEVICE_REMOVED | PNP_DEVICE_DONT_DISPLAY_IN_UI; 
    IoInvalidateDeviceState(CommonData->PDO); 
 
    return deviceCount; 
} 
 
#define PhysAddrCmp(a,b) ( (a).LowPart == (b).LowPart && (a).HighPart == (b).HighPart ) 
 
BOOLEAN 
I8xRemovePort( 
    IN PIO_RESOURCE_DESCRIPTOR ResDesc 
    ) 
/*++ 
 
Routine Description: 
 
    If the physical address contained in the ResDesc is not in the list of  
    previously seen physicall addresses, it is placed within the list. 
     
Arguments: 
 
    ResDesc - contains the physical address 
 
Return Value: 
 
    TRUE  - if the physical address was found in the list 
    FALSE - if the physical address was not found in the list (and thus inserted 
            into it) 
--*/ 
{ 
    ULONG               i; 
    PHYSICAL_ADDRESS   address; 
 
    PAGED_CODE(); 
 
    if (Globals.ControllerData->KnownPortsCount == -1) { 
        return FALSE; 
    } 
 
    address =  ResDesc->u.Port.MinimumAddress; 
    for (i = 0; i < Globals.ControllerData->KnownPortsCount; i++) { 
        if (PhysAddrCmp(address, Globals.ControllerData->KnownPorts[i])) { 
            return TRUE; 
        } 
    } 
 
    if (Globals.ControllerData->KnownPortsCount < MaximumPortCount) { 
        Globals.ControllerData->KnownPorts[ 
            Globals.ControllerData->KnownPortsCount++] = address; 
    } 
 
    Print(DBG_PNP_INFO, 
          ("Saw port [0x%08x %08x] - [0x%08x %08x]\n", 
          address.HighPart, 
          address.LowPart, 
          ResDesc->u.Port.MaximumAddress.HighPart, 
          ResDesc->u.Port.MaximumAddress.LowPart 
          )); 
 
    return FALSE; 
} 
 
NTSTATUS 
I8xFindPortCallout( 
    IN PVOID                        Context, 
    IN PUNICODE_STRING              PathName, 
    IN INTERFACE_TYPE               BusType, 
    IN ULONG                        BusNumber, 
    IN PKEY_VALUE_FULL_INFORMATION *BusInformation, 
    IN CONFIGURATION_TYPE           ControllerType, 
    IN ULONG                        ControllerNumber, 
    IN PKEY_VALUE_FULL_INFORMATION *ControllerInformation, 
    IN CONFIGURATION_TYPE           PeripheralType, 
    IN ULONG                        PeripheralNumber, 
    IN PKEY_VALUE_FULL_INFORMATION *PeripheralInformation 
    ) 
/*++ 
 
Routine Description: 
 
    This is the callout routine sent as a parameter to 
    IoQueryDeviceDescription.  It grabs the keyboard controller and 
    peripheral configuration information. 
 
Arguments: 
 
    Context - Context parameter that was passed in by the routine 
        that called IoQueryDeviceDescription. 
 
    PathName - The full pathname for the registry key. 
 
    BusType - Bus interface type (Isa, Eisa, Mca, etc.). 
 
    BusNumber - The bus sub-key (0, 1, etc.). 
 
    BusInformation - Pointer to the array of pointers to the full value 
        information for the bus. 
 
    ControllerType - The controller type (should be KeyboardController). 
 
    ControllerNumber - The controller sub-key (0, 1, etc.). 
 
    ControllerInformation - Pointer to the array of pointers to the full 
        value information for the controller key. 
 
    PeripheralType - The peripheral type (should be KeyboardPeripheral). 
 
    PeripheralNumber - The peripheral sub-key. 
 
    PeripheralInformation - Pointer to the array of pointers to the full 
        value information for the peripheral key. 
 
 
Return Value: 
 
    None.  If successful, will have the following side-effects: 
 
        - Sets DeviceObject->DeviceExtension->HardwarePresent. 
        - Sets configuration fields in 
          DeviceObject->DeviceExtension->Configuration. 
 
--*/ 
{ 
    PUCHAR                          controllerData; 
    NTSTATUS                        status = STATUS_UNSUCCESSFUL; 
    ULONG                           i, 
                                    listCount, 
                                    portCount = 0; 
    PIO_RESOURCE_LIST               pResList = (PIO_RESOURCE_LIST) Context; 
    PIO_RESOURCE_DESCRIPTOR         pResDesc; 
    PKEY_VALUE_FULL_INFORMATION     controllerInfo = NULL; 
    PCM_PARTIAL_RESOURCE_DESCRIPTOR resourceDescriptor; 
 
    PAGED_CODE(); 
 
    UNREFERENCED_PARAMETER(PathName); 
    UNREFERENCED_PARAMETER(BusType); 
    UNREFERENCED_PARAMETER(BusNumber); 
    UNREFERENCED_PARAMETER(BusInformation); 
    UNREFERENCED_PARAMETER(ControllerType); 
    UNREFERENCED_PARAMETER(ControllerNumber); 
    UNREFERENCED_PARAMETER(PeripheralType); 
    UNREFERENCED_PARAMETER(PeripheralNumber); 
    UNREFERENCED_PARAMETER(PeripheralInformation); 
 
    pResDesc = pResList->Descriptors + pResList->Count; 
    controllerInfo = ControllerInformation[IoQueryDeviceConfigurationData]; 
 
    Print(DBG_PNP_TRACE, ("I8xFindPortCallout enter\n")); 
 
    if (controllerInfo->DataLength != 0) { 
        controllerData = ((PUCHAR) controllerInfo) + controllerInfo->DataOffset; 
        controllerData += FIELD_OFFSET(CM_FULL_RESOURCE_DESCRIPTOR, 
                                       PartialResourceList); 
 
        listCount = ((PCM_PARTIAL_RESOURCE_LIST) controllerData)->Count; 
 
        resourceDescriptor = 
            ((PCM_PARTIAL_RESOURCE_LIST) controllerData)->PartialDescriptors; 
 
        for (i = 0; i < listCount; i++, resourceDescriptor++) { 
            switch(resourceDescriptor->Type) { 
            case CmResourceTypePort: 
                 
                if (portCount < 2) { 
 
                    Print(DBG_PNP_INFO,  
                          ("found port [0x%x 0x%x] with length %d\n", 
                          resourceDescriptor->u.Port.Start.HighPart, 
                          resourceDescriptor->u.Port.Start.LowPart, 
                          resourceDescriptor->u.Port.Length 
                          )); 
 
                    pResDesc->Type = resourceDescriptor->Type; 
                    pResDesc->Flags = resourceDescriptor->Flags; 
                    pResDesc->ShareDisposition = CmResourceShareDeviceExclusive; 
 
                    pResDesc->u.Port.Alignment = 1; 
                    pResDesc->u.Port.Length = 
                        resourceDescriptor->u.Port.Length; 
                    pResDesc->u.Port.MinimumAddress.QuadPart = 
                        resourceDescriptor->u.Port.Start.QuadPart; 
                    pResDesc->u.Port.MaximumAddress.QuadPart =  
                        pResDesc->u.Port.MinimumAddress.QuadPart + 
                        pResDesc->u.Port.Length - 1; 
 
                    pResList->Count++; 
 
                    // 
                    // We want to record the ports we stole from the kb as seen 
                    // so that if the keyboard is started later, we can trim 
                    // its resources and not have a resource conflict... 
                    // 
                    // ...we are getting too smart for ourselves here :] 
                    // 
                    I8xRemovePort(pResDesc); 
                    pResDesc++; 
                } 
 
                status = STATUS_SUCCESS; 
 
                break; 
 
            default: 
                Print(DBG_PNP_NOISE, ("type 0x%x found\n", 
                                      (LONG) resourceDescriptor->Type)); 
                break; 
            } 
        } 
 
    } 
 
    Print(DBG_PNP_TRACE, ("I8xFindPortCallout exit (0x%x)\n", status)); 
    return status; 
} 
 
NTSTATUS 
I8xFilterResourceRequirements( 
    IN PDEVICE_OBJECT DeviceObject, 
    IN PIRP Irp 
    ) 
/*++ 
 
Routine Description: 
 
    Iterates through the resource requirements list contained in the IRP and removes 
    any duplicate requests for I/O ports.  (This is a common problem on the Alphas.) 
     
    No removal is performed if more than one resource requirements list is present. 
     
Arguments: 
 
    DeviceObject - A pointer to the device object 
 
    Irp - A pointer to the request packet which contains the resource req. list. 
 
 
Return Value: 
 
    None. 
     
--*/ 
{ 
    PIO_RESOURCE_REQUIREMENTS_LIST  pReqList = NULL, 
                                    pNewReqList = NULL; 
    PIO_RESOURCE_LIST               pResList = NULL, 
                                    pNewResList = NULL; 
    PIO_RESOURCE_DESCRIPTOR         pResDesc = NULL, 
                                    pNewResDesc = NULL; 
    ULONG                           i = 0, j = 0, 
                                    removeCount, 
                                    reqCount, 
                                    size; 
    BOOLEAN                         foundInt = FALSE, 
                                    foundPorts = FALSE; 
 
    PIO_STACK_LOCATION  stack; 
 
    PAGED_CODE(); 
 
    ASSERT(DeviceObject); 
    ASSERT(DeviceObject->DeviceExtension); 
 
    Print(DBG_PNP_NOISE, 
          ("Received IRP_MN_FILTER_RESOURCE_REQUIREMENTS for %s\n", 
          (GET_COMMON_DATA(DeviceObject->DeviceExtension))->IsKeyboard ? "kb" : "mouse" 
          )); 
 
    stack = IoGetCurrentIrpStackLocation(Irp); 
 
    // 
    // The list can be in either the information field, or in the current 
    //  stack location.  The Information field has a higher precedence over 
    //  the stack location. 
    // 
    if (Irp->IoStatus.Information == 0) { 
        pReqList = 
            stack->Parameters.FilterResourceRequirements.IoResourceRequirementList; 
        Irp->IoStatus.Information = (ULONG_PTR) pReqList; 
    } 
    else { 
        pReqList = (PIO_RESOURCE_REQUIREMENTS_LIST) Irp->IoStatus.Information; 
    } 
 
    if (!pReqList) { 
        //  
        // Not much can be done here except return 
        // 
        Print(DBG_PNP_MASK & ~ DBG_PNP_TRACE,  
              ("(%s) NULL resource list in I8xFilterResourceRequirements\n", 
              (GET_COMMON_DATA(DeviceObject->DeviceExtension))->IsKeyboard ? 
                  "kb" : "mou" 
              )); 
 
        return STATUS_SUCCESS; 
    } 
 
    ASSERT(Irp->IoStatus.Information != 0); 
    ASSERT(pReqList != 0); 
 
    reqCount = pReqList->AlternativeLists; 
 
    // 
    // Only one AlternativeList is supported.  If there is more than one list, 
    // then there is now way of knowing which list will be chosen.  Also, if 
    // there are multiple lists, then chances are that a list with no i/o port 
    // conflicts will be chosen. 
    // 
    if (reqCount > 1) { 
        return STATUS_SUCCESS; 
    } 
 
    pResList = pReqList->List; 
    removeCount = 0; 
 
    for (j = 0; j < pResList->Count; j++) { 
        pResDesc = &pResList->Descriptors[j]; 
        switch (pResDesc->Type) { 
        case CmResourceTypePort: 
            Print(DBG_PNP_INFO,  
                  ("option = 0x%x, flags = 0x%x\n", 
                  (LONG) pResDesc->Option, 
                  (LONG) pResDesc->Flags 
                  )); 
 
            if (I8xRemovePort(pResDesc)) { 
                // 
                // Increment the remove count and tag this resource as 
                // one that we don't want to copy to the new list 
                // 
                removeCount++; 
                pResDesc->Type = I8X_REMOVE_RESOURCE; 
            } 
 
            foundPorts = TRUE; 
            break; 
 
        case CmResourceTypeInterrupt: 
            if (Globals.ControllerData->Configuration.SharedInterrupts) { 
                if (pResDesc->ShareDisposition != CmResourceShareShared) { 
                    Print(DBG_PNP_INFO, ("forcing non shared int to shared\n")); 
                } 
                pResDesc->ShareDisposition = CmResourceShareShared; 
            } 
 
            foundInt = TRUE; 
            break; 
 
        default: 
            break; 
        } 
    } 
 
    if (removeCount) { 
        size = pReqList->ListSize; 
 
        //  
        // One element of the array is already allocated (via the struct  
        //  definition) so make sure that we are allocating at least that  
        //  much memory. 
        // 
 
        ASSERT(pResList->Count >= removeCount); 
        if (pResList->Count > 1) { 
            size -= removeCount * sizeof(IO_RESOURCE_DESCRIPTOR); 
        } 
 
        pNewReqList = 
            (PIO_RESOURCE_REQUIREMENTS_LIST) ExAllocatePool(PagedPool, size); 
 
        if (!pNewReqList) { 
            // 
            // This is not good, but the system doesn't really need to know about 
            //  this, so just fix up our munging and return the original list 
            // 
            pReqList = stack->Parameters.FilterResourceRequirements.IoResourceRequirementList; 
            reqCount = pReqList->AlternativeLists; 
            removeCount = 0; 
        
            for (i = 0; i < reqCount; i++) { 
                pResList = &pReqList->List[i]; 
        
                for (j = 0; j < pResList->Count; j++) { 
                    pResDesc = &pResList->Descriptors[j]; 
                    if (pResDesc->Type == I8X_REMOVE_RESOURCE) { 
                        pResDesc->Type = CmResourceTypePort; 
                    } 
                } 
             
            } 
 
            return STATUS_SUCCESS; 
        } 
 
        // 
        // Clear out the newly allocated list 
        // 
        RtlZeroMemory(pNewReqList, 
                      size 
                      ); 
 
        // 
        // Copy the list header information except for the IO resource list 
        // itself 
        // 
        RtlCopyMemory(pNewReqList, 
                      pReqList, 
                      sizeof(IO_RESOURCE_REQUIREMENTS_LIST) -  
                        sizeof(IO_RESOURCE_LIST) 
                      ); 
        pNewReqList->ListSize = size; 
 
        pResList = pReqList->List; 
        pNewResList = pNewReqList->List; 
 
        // 
        // Copy the list header information except for the IO resource 
        // descriptor list itself 
        // 
        RtlCopyMemory(pNewResList, 
                      pResList, 
                      sizeof(IO_RESOURCE_LIST) - 
                        sizeof(IO_RESOURCE_DESCRIPTOR) 
                      ); 
 
        pNewResList->Count = 0; 
        pNewResDesc = pNewResList->Descriptors; 
 
        for (j = 0; j < pResList->Count; j++) { 
            pResDesc = &pResList->Descriptors[j]; 
            if (pResDesc->Type != I8X_REMOVE_RESOURCE) { 
                // 
                // Keep this resource, so copy it into the new list and 
                // incement the count and the location for the next 
                // IO resource descriptor 
                // 
                *pNewResDesc = *pResDesc; 
                pNewResDesc++; 
                pNewResList->Count++; 
 
                Print(DBG_PNP_INFO, 
                     ("List #%d, Descriptor #%d ... keeping res type %d\n", 
                     i, j, 
                     (ULONG) pResDesc->Type 
                     )); 
            } 
            else { 
                // 
                // Decrement the remove count so we can assert it is 
                //  zero once we are done 
                // 
                Print(DBG_PNP_INFO, 
                      ("Removing port [0x%08x %08x] - [0x%#08x %08x]\n", 
                      pResDesc->u.Port.MinimumAddress.HighPart, 
                      pResDesc->u.Port.MinimumAddress.LowPart, 
                      pResDesc->u.Port.MaximumAddress.HighPart, 
                      pResDesc->u.Port.MaximumAddress.LowPart 
                      )); 
                removeCount--; 
              } 
        } 
 
        ASSERT(removeCount == 0); 
 
        // 
        // There have been bugs where the old list was being used.  Zero it out to 
        //  make sure that no conflicts arise.  (Not to mention the fact that some 
        //  other code is accessing freed memory 
        // 
        RtlZeroMemory(pReqList, 
                      pReqList->ListSize 
                      ); 
 
        // 
        // Free the old list and place the new one in its place 
        // 
        ExFreePool(pReqList); 
        stack->Parameters.FilterResourceRequirements.IoResourceRequirementList = 
            pNewReqList; 
        Irp->IoStatus.Information = (ULONG_PTR) pNewReqList; 
    } 
    else if (!KEYBOARD_PRESENT() && !foundPorts && foundInt) { 
        INTERFACE_TYPE                      interfaceType; 
        NTSTATUS                            status; 
        ULONG                               prevCount; 
        CONFIGURATION_TYPE                  controllerType = KeyboardController; 
        CONFIGURATION_TYPE                  peripheralType = KeyboardPeripheral; 
 
        ASSERT( MOUSE_PRESENT() ); 
 
        Print(DBG_PNP_INFO, ("Adding ports to res list!\n")); 
 
        // 
        // We will now yank the resources from the keyboard to start the mouse 
        // solo 
        // 
        size = pReqList->ListSize + 2 * sizeof(IO_RESOURCE_DESCRIPTOR); 
        pNewReqList = (PIO_RESOURCE_REQUIREMENTS_LIST) 
                        ExAllocatePool( 
                            PagedPool, 
                            size 
                            ); 
 
        if (!pNewReqList) { 
            return STATUS_SUCCESS; 
        } 
 
        // 
        // Clear out the newly allocated list 
        // 
        RtlZeroMemory(pNewReqList, 
                      size 
                      ); 
 
        // 
        // Copy the entire old list 
        // 
        RtlCopyMemory(pNewReqList, 
                      pReqList, 
                      pReqList->ListSize 
                      ); 
 
        pResList = pReqList->List; 
        pNewResList = pNewReqList->List; 
 
        prevCount = pNewResList->Count; 
        for (i = 0; i < MaximumInterfaceType; i++) { 
 
            // 
            // Get the registry information for this device. 
            // 
            interfaceType = i; 
            status = IoQueryDeviceDescription( 
                &interfaceType, 
                NULL, 
                &controllerType, 
                NULL, 
                &peripheralType, 
                NULL, 
                I8xFindPortCallout, 
                (PVOID) pNewResList 
                ); 
 
            if (NT_SUCCESS(status) || prevCount != pNewResList->Count) { 
                break; 
            } 
        } 
 
        if (NT_SUCCESS(status) || prevCount != pNewResList->Count) { 
            pNewReqList->ListSize = size - (2 - (pNewResList->Count - prevCount)); 
     
            // 
            // Free the old list and place the new one in its place 
            // 
            ExFreePool(pReqList); 
            stack->Parameters.FilterResourceRequirements.IoResourceRequirementList = 
                pNewReqList; 
            Irp->IoStatus.Information = (ULONG_PTR) pNewReqList; 
        } 
        else { 
            ExFreePool(pNewReqList); 
        } 
    } 
 
    return STATUS_SUCCESS; 
} 
 
 
NTSTATUS 
I8xRegisterDeviceInterface( 
    PDEVICE_OBJECT PDO, 
    CONST GUID * Guid, 
    PUNICODE_STRING SymbolicName 
    ) 
{ 
    NTSTATUS status; 
 
    PAGED_CODE(); 
 
    status = IoRegisterDeviceInterface( 
                PDO, 
                Guid, 
                NULL, 
                SymbolicName  
                ); 
 
    if (NT_SUCCESS(status)) { 
        status = IoSetDeviceInterfaceState(SymbolicName, 
                                           TRUE 
                                           ); 
    } 
 
    return status; 
} 
 
void 
I8xSetPowerFlag( 
    IN ULONG Flag, 
    IN BOOLEAN Set 
    ) 
{ 
    KIRQL irql; 
 
    KeAcquireSpinLock(&Globals.ControllerData->PowerSpinLock, &irql); 
    if (Set) { 
        Globals.PowerFlags |= Flag; 
    } 
    else { 
        Globals.PowerFlags &= ~Flag; 
    } 
    KeReleaseSpinLock(&Globals.ControllerData->PowerSpinLock, irql); 
} 
 
BOOLEAN 
I8xCheckPowerFlag( 
    ULONG Flag 
    ) 
{ 
    KIRQL irql; 
    BOOLEAN rVal = FALSE; 
 
    KeAcquireSpinLock(&Globals.ControllerData->PowerSpinLock, &irql); 
    if (Globals.PowerFlags & Flag) { 
        rVal = TRUE; 
    } 
    KeReleaseSpinLock(&Globals.ControllerData->PowerSpinLock, irql); 
     
    return rVal; 
} 
 
NTSTATUS 
I8xPower ( 
    IN PDEVICE_OBJECT DeviceObject, 
    IN PIRP Irp 
    ) 
/*++ 
 
Routine Description: 
 
    This is the dispatch routine for power requests.   
 
Arguments: 
 
    DeviceObject - Pointer to the device object. 
 
    Irp - Pointer to the request packet. 
 
Return Value: 
 
    STATUS_SUCCESSFUL if successful, 
    an valid NTSTATUS error code otherwise 
 
--*/ 
{ 
    PCOMMON_DATA        commonData; 
    PIO_STACK_LOCATION  stack; 
    NTSTATUS            status = STATUS_SUCCESS; 
 
    PAGED_CODE(); 
 
    commonData = GET_COMMON_DATA(DeviceObject->DeviceExtension); 
 
    stack = IoGetCurrentIrpStackLocation(Irp); 
 
    Print(DBG_POWER_TRACE, 
          ("Power (%s), enter\n", 
          commonData->IsKeyboard ? "keyboard" : 
                                   "mouse" 
          )); 
 
    // 
    // A power irp can be sent to the device before we have been started or 
    // initialized.  Since the code below relies on StartDevice() to have 
    // executed, just fire and forget the irp 
    // 
    if (!commonData->Started || !commonData->Initialized) { 
        PoStartNextPowerIrp(Irp); 
        Irp->IoStatus.Status = STATUS_SUCCESS; 
        IoSkipCurrentIrpStackLocation(Irp); 
        return PoCallDriver(commonData->TopOfStack, Irp); 
    } 
 
    switch(stack->MinorFunction) { 
    case IRP_MN_WAIT_WAKE: 
        Print(DBG_POWER_NOISE, ("Got IRP_MN_WAIT_WAKE\n" )); 
 
        // 
        // Fail all wait wake requests on level triggered interrupts for mice 
        // because when an errant mouse movement occurs while we are going to 
        // sleep, it will keep the interrupt triggered indefinitely. 
        // 
        // We should not even get into this situation because the caps of the  
        // mouse should have been altered to not report wait wake  
        // 
        if (IS_MOUSE(commonData) && IS_LEVEL_TRIGGERED(commonData)) { 
 
            PoStartNextPowerIrp(Irp); 
            status = Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; 
            IoCompleteRequest(Irp, IO_NO_INCREMENT); 
 
            Print(DBG_POWER_INFO | DBG_POWER_ERROR, 
                  ("failing a wait wake request on a level triggered mouse\n")); 
 
            return status; 
        } 
 
        break; 
 
    case IRP_MN_POWER_SEQUENCE: 
        Print(DBG_POWER_NOISE, ("Got IRP_MN_POWER_SEQUENCE\n" )); 
        break; 
 
    case IRP_MN_SET_POWER: 
        Print(DBG_POWER_NOISE, ("Got IRP_MN_SET_POWER\n" )); 
 
        // 
        // Don't handle anything but DevicePowerState changes 
        // 
        if (stack->Parameters.Power.Type != DevicePowerState) { 
            commonData->SystemState = stack->Parameters.Power.State.SystemState; 
 
            Print(DBG_POWER_INFO, ("system power irp, S%d\n", commonData->SystemState-1)); 
            break; 
        } 
 
        // 
        // Check for no change in state, and if none, do nothing.  This state 
        // can occur when the device is armed for wake.  We will get a D0 in  
        // response to the WW irp completing and then another D0 corresponding 
        // to the S0 irp sent to the stack. 
        // 
        if (stack->Parameters.Power.State.DeviceState == 
            commonData->PowerState) { 
            Print(DBG_POWER_INFO, 
                  ("no change in state (PowerDeviceD%d)\n", 
                  commonData->PowerState-1 
                  )); 
            break; 
        } 
 
        switch (stack->Parameters.Power.State.DeviceState) { 
        case PowerDeviceD0: 
            Print(DBG_POWER_INFO, ("Powering up to PowerDeviceD0\n")); 
 
            IoAcquireRemoveLock(&commonData->RemoveLock, Irp); 
 
            if (IS_KEYBOARD(commonData)) { 
                I8xSetPowerFlag(KBD_POWERED_UP_STARTED, TRUE); 
            } 
            else { 
                I8xSetPowerFlag(MOU_POWERED_UP_STARTED, TRUE); 
            } 
                                 
            // 
            // PoSetPowerState will be called in I8xReinitalizeHardware for each 
            // device once all the devices have powered back up 
            // 
            IoCopyCurrentIrpStackLocationToNext(Irp); 
            IoSetCompletionRoutine(Irp, 
                                   I8xPowerUpToD0Complete, 
                                   NULL, 
                                   TRUE,                // on success 
                                   TRUE,                // on error 
                                   TRUE                 // on cancel 
                                   ); 
 
            // 
            // PoStartNextPowerIrp() gets called when the irp gets completed  
            // in either the completion routine or the resulting work item 
            // 
            // It is OK to call PoCallDriver and return pending b/c we are  
            // pending the irp in the completion routine and we may change 
            // the completion status if we can't alloc pool.  If we return the 
            // value from PoCallDriver, we are tied to that status value on the 
            // way back up. 
            //                 
            IoMarkIrpPending(Irp); 
            PoCallDriver(commonData->TopOfStack, Irp); 
            return STATUS_PENDING; 
 
        case PowerDeviceD1: 
        case PowerDeviceD2: 
        case PowerDeviceD3: 
            Print(DBG_POWER_INFO, 
                  ("Powering down to PowerDeviceD%d\n", 
                  stack->Parameters.Power.State.DeviceState-1 
                  )); 
 
            // 
            // If WORK_ITEM_QUEUED is set, that means that a work item is 
            // either queued to be run, or running now so we don't want to yank 
            // any devices underneath from the work item 
            // 
            if (I8xCheckPowerFlag(WORK_ITEM_QUEUED)) { 
                Print(DBG_POWER_INFO | DBG_POWER_ERROR, 
                      ("denying power down request because work item is running\n" 
                      )); 
 
                PoStartNextPowerIrp(Irp); 
                status = Irp->IoStatus.Status = STATUS_POWER_STATE_INVALID; 
                IoCompleteRequest(Irp, IO_NO_INCREMENT); 
 
                return status; 
            } 
 
            if (IS_KEYBOARD(commonData)) { 
                I8xSetPowerFlag(KBD_POWERED_DOWN, TRUE); 
            } 
            else { 
                I8xSetPowerFlag(MOU_POWERED_DOWN, TRUE); 
            } 
 
            PoSetPowerState(DeviceObject, 
                            stack->Parameters.Power.Type, 
                            stack->Parameters.Power.State 
                            ); 
 
            // 
            // Disconnect level triggered interupts on mice when we go into  
            // low power so errant mouse movement doesn't leave the interrupt 
            // signalled for long periods of time 
            // 
            if (IS_MOUSE(commonData) && IS_LEVEL_TRIGGERED(commonData)) { 
                PKINTERRUPT interrupt = commonData->InterruptObject; 
 
                Print(DBG_POWER_NOISE, 
                      ("disconnecting interrupt on level triggered mouse\n") 
                      ); 
 
                commonData->InterruptObject = NULL; 
                if (interrupt) { 
                    IoDisconnectInterrupt(interrupt); 
                } 
            } 
 
            commonData->PowerState = stack->Parameters.Power.State.DeviceState; 
            commonData->ShutdownType = stack->Parameters.Power.ShutdownType; 
 
            // 
            // For what we are doing, we don't need a completion routine 
            // since we don't race on the power requests. 
            // 
            Irp->IoStatus.Status = STATUS_SUCCESS; 
            PoStartNextPowerIrp(Irp); 
            IoSkipCurrentIrpStackLocation(Irp); 
            return  PoCallDriver(commonData->TopOfStack, Irp); 
 
        default: 
            Print(DBG_POWER_INFO, ("unknown state\n")); 
            break; 
        } 
        break; 
 
    case IRP_MN_QUERY_POWER: 
        Print(DBG_POWER_NOISE, ("Got IRP_MN_QUERY_POWER\n" )); 
        break; 
 
    default: 
        Print(DBG_POWER_NOISE, 
              ("Got unhandled minor function (%d)\n", 
              stack->MinorFunction 
              )); 
        break; 
    } 
 
    Print(DBG_POWER_TRACE, ("Power, exit\n")); 
 
    PoStartNextPowerIrp(Irp); 
 
    IoSkipCurrentIrpStackLocation(Irp); 
    return PoCallDriver(commonData->TopOfStack, Irp); 
} 
 
NTSTATUS 
I8xPowerUpToD0Complete( 
    IN PDEVICE_OBJECT DeviceObject, 
    IN PIRP Irp, 
    IN PVOID Context 
    ) 
/*++ 
 
Routine Description: 
 
    Reinitializes the i8042 haardware after any type of hibernation/sleep. 
     
Arguments: 
 
    DeviceObject - Pointer to the device object 
 
    Irp - Pointer to the request 
     
    Context - Context passed in from the funciton that set the completion 
              routine. UNUSED. 
 
 
Return Value: 
 
    STATUS_SUCCESSFUL if successful, 
    an valid NTSTATUS error code otherwise 
 
--*/ 
{ 
    NTSTATUS            status = STATUS_SUCCESS; 
    PCOMMON_DATA        commonData; 
    PPOWER_UP_WORK_ITEM item; 
    KIRQL               irql; 
    UCHAR               poweredDownDevices = 0, 
                        poweredUpDevices = 0, 
                        failedDevices = 0; 
    BOOLEAN             queueItem = FALSE, 
                        clearFlags = FALSE, 
                        failMouIrp = FALSE;  
    PIRP                mouIrp = NULL, 
                        kbdIrp = NULL; 
 
    UNREFERENCED_PARAMETER(Context); 
 
    commonData = GET_COMMON_DATA(DeviceObject->DeviceExtension); 
 
    Print(DBG_POWER_TRACE, 
          ("PowerUpToD0Complete (%s), Enter\n", 
          commonData->IsKeyboard ? "kb" : "mouse" 
          )); 
 
 
    // 
    // We can use a regular work item because we have a non completed power irp 
    // which has an outstanding reference to this stack. 
    // 
    item = (PPOWER_UP_WORK_ITEM) ExAllocatePool(NonPagedPool, 
                                                sizeof(POWER_UP_WORK_ITEM)); 
 
    KeAcquireSpinLock(&Globals.ControllerData->PowerSpinLock, &irql); 
 
    Print(DBG_POWER_TRACE, 
          ("Power up to D0 completion enter, power flags 0x%x\n", 
          Globals.PowerFlags)); 
 
    if (NT_SUCCESS(Irp->IoStatus.Status)) { 
        commonData->OutstandingPowerIrp = Irp; 
        status = STATUS_MORE_PROCESSING_REQUIRED; 
 
        if (IS_KEYBOARD(commonData)) { 
            KEYBOARD_POWERED_UP_SUCCESSFULLY();  
        } 
        else { 
            MOUSE_POWERED_UP_SUCCESSFULLY(); 
        } 
    } 
    else { 
        if (IS_KEYBOARD(commonData)) { 
            KEYBOARD_POWERED_UP_FAILURE(); 
        } 
        else { 
            MOUSE_POWERED_UP_FAILURE(); 
        } 
    } 
 
    if (KEYBOARD_POWERED_DOWN_SUCCESS()) { 
        Print(DBG_POWER_NOISE, ("--kbd powered down successfully\n")); 
        poweredDownDevices++; 
    } 
    if (MOUSE_POWERED_DOWN_SUCCESS()) { 
        Print(DBG_POWER_NOISE, ("--mou powered down successfully\n")); 
        poweredDownDevices++; 
    } 
 
    if (KEYBOARD_POWERED_UP_SUCCESS()) { 
        Print(DBG_POWER_NOISE, ("++kbd powered up successfully\n")); 
        poweredUpDevices++; 
    } 
    if (MOUSE_POWERED_UP_SUCCESS()) { 
        Print(DBG_POWER_NOISE, ("++mou powered up successfully\n")); 
        poweredUpDevices++; 
    } 
 
    if (KEYBOARD_POWERED_UP_FAILED()) { 
        Print(DBG_POWER_NOISE|DBG_POWER_ERROR, (">>kbd powered down failed\n")); 
        failedDevices++; 
    } 
    if (MOUSE_POWERED_UP_FAILED()) { 
        Print(DBG_POWER_NOISE|DBG_POWER_ERROR, (">>mou powered down failed\n")); 
        failedDevices++; 
    } 
 
    Print(DBG_POWER_INFO, 
          ("up %d, down %d, failed %d, flags 0x%x\n", 
          (ULONG) poweredUpDevices,  
          (ULONG) poweredDownDevices,  
          (ULONG) failedDevices,  
          Globals.PowerFlags)); 
 
    if ((poweredUpDevices + failedDevices) == poweredDownDevices) { 
        if (poweredUpDevices > 0) { 
            // 
            // The ports are associated with the keyboard.  If it has failed to 
            // power up while the mouse succeeded, we still need to fail the 
            // mouse b/c there is no hardware to talk to 
            // 
            if (failedDevices > 0 && KEYBOARD_POWERED_UP_FAILED()) { 
                ASSERT(MOUSE_POWERED_UP_SUCCESS()); 
                ASSERT(Globals.KeyboardExtension->OutstandingPowerIrp == NULL); 
 
                mouIrp = Globals.MouseExtension->OutstandingPowerIrp; 
                Globals.MouseExtension->OutstandingPowerIrp = NULL; 
                Globals.PowerFlags &= ~MOU_POWER_FLAGS; 
                clearFlags =  TRUE; 
 
                if (mouIrp != Irp) { 
                    // 
                    // we have queued the irp, complete it later in this 
                    // function under a special case 
                    // 
                    failMouIrp = TRUE; 
                } 
                else { 
                    // 
                    // The mouse irp is the current irp.  We have already 
                    // completed the kbd irp in our previous processing.  Set 
                    // the irp status to some unsuccessful value so that we will 
                    // call PoStartNextPowerIrp later in this function.  Also 
                    // set status to != STATUS_MORE_PROCESSING_REQUIRED so the  
                    // irp will be completed when the function exits. 
                    // 
                    status = mouIrp->IoStatus.Status = STATUS_UNSUCCESSFUL; 
                } 
 
            } 
            else { 
                Print(DBG_POWER_INFO, ("at least one device powered up!\n")); 
                queueItem = TRUE; 
            } 
        } 
        else { 
            Print(DBG_POWER_INFO, 
                  ("all devices failed power up, 0x%x\n", 
                   Globals.PowerFlags)); 
 
            clearFlags = TRUE; 
        } 
    } 
    else { 
        // 
        // the other device is still powered down, wait for it to power back 
        // up before processing power states  
        // 
        Print(DBG_POWER_INFO, 
              ("queueing, waiting for 2nd dev obj to power cycle\n")); 
    } 
 
    if (queueItem || clearFlags) { 
        // 
        // Extract the irp from each successfully started device and clear the 
        // associated power flags for the device 
        // 
        if (MOUSE_POWERED_UP_SUCCESS()) { 
            mouIrp = Globals.MouseExtension->OutstandingPowerIrp; 
            Globals.MouseExtension->OutstandingPowerIrp = NULL; 
 
            ASSERT(!TEST_PWR_FLAGS(MOU_POWERED_UP_FAILURE)); 
            Globals.PowerFlags &= ~MOU_POWER_FLAGS; 
        } 
        else { 
            Globals.PowerFlags &= ~(MOU_POWERED_UP_FAILURE); 
        } 
 
        if (KEYBOARD_POWERED_UP_SUCCESS()) { 
            kbdIrp = Globals.KeyboardExtension->OutstandingPowerIrp; 
            Globals.KeyboardExtension->OutstandingPowerIrp = NULL; 
 
            ASSERT(!TEST_PWR_FLAGS(KBD_POWERED_UP_FAILURE)); 
            Globals.PowerFlags &= ~(KBD_POWER_FLAGS); 
        } 
        else { 
             Globals.PowerFlags &= ~(KBD_POWERED_UP_FAILURE); 
        } 
 
        // 
        // Mark that the work item is queued.  This is used to make sure that 2 
        // work items are not queued concucrrently 
        // 
        if (item && queueItem) { 
            Print(DBG_POWER_INFO, ("setting work item queued flag\n")); 
 
            Globals.PowerFlags |= WORK_ITEM_QUEUED; 
        } 
    } 
 
    KeReleaseSpinLock(&Globals.ControllerData->PowerSpinLock, irql); 
 
    if (queueItem) { 
        if (item == NULL) { 
            // 
            // complete any queued power irps 
            // 
            Print(DBG_POWER_INFO | DBG_POWER_ERROR, 
                  ("failed to alloc work item\n")); 
 
            // 
            // what about PoSetPowerState? 
            // 
            if (mouIrp != NULL) { 
                Print(DBG_POWER_ERROR | DBG_POWER_INFO, 
                      ("completing mouse power irp 0x%x", mouIrp)); 
 
                mouIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; 
                mouIrp->IoStatus.Information = 0x0; 
 
                PoStartNextPowerIrp(mouIrp); 
                IoCompleteRequest(mouIrp, IO_NO_INCREMENT); 
                IoReleaseRemoveLock(&Globals.MouseExtension->RemoveLock,  
                                    mouIrp); 
                mouIrp = NULL; 
            } 
 
            if (kbdIrp != NULL) { 
                Print(DBG_POWER_ERROR | DBG_POWER_INFO, 
                      ("completing kbd power irp 0x%x", kbdIrp)); 
 
                kbdIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; 
                kbdIrp->IoStatus.Information = 0x0; 
 
                PoStartNextPowerIrp(kbdIrp); 
                IoCompleteRequest(kbdIrp, IO_NO_INCREMENT); 
                IoReleaseRemoveLock(&Globals.KeyboardExtension->RemoveLock,  
                                    kbdIrp); 
                kbdIrp = NULL; 
            } 
 
            // 
            // The passed in Irp has just been completed; by returning more 
            // processing required, it will not be double completed 
            // 
            return STATUS_MORE_PROCESSING_REQUIRED; 
        } 
        else { 
            RtlZeroMemory(item, sizeof(*item)); 
 
            if (MOUSE_STARTED()) { 
                SET_RECORD_STATE(Globals.MouseExtension, 
                                 RECORD_RESUME_FROM_POWER); 
            } 
     
            Print(DBG_POWER_INFO, ("queueing work item for init\n")); 
     
            item->KeyboardPowerIrp = kbdIrp; 
            item->MousePowerIrp = mouIrp; 
 
            ExInitializeWorkItem(&item->Item, I8xReinitializeHardware, item); 
            ExQueueWorkItem(&item->Item, DelayedWorkQueue); 
        } 
    } 
    else if (item != NULL) { 
        Print(DBG_POWER_NOISE,("freeing unused item %p\n", item)); 
        ExFreePool(item); 
        item = NULL; 
    } 
 
    if (failMouIrp) { 
        Print(DBG_POWER_INFO | DBG_POWER_ERROR, 
              ("failing successful mouse irp %p because kbd failed power up\n", 
               mouIrp)); 
 
        PoStartNextPowerIrp(mouIrp); 
        IoCompleteRequest(mouIrp, IO_NO_INCREMENT); 
        IoReleaseRemoveLock(&Globals.MouseExtension->RemoveLock, mouIrp); 
        mouIrp = NULL; 
    } 
 
    if (!NT_SUCCESS(Irp->IoStatus.Status)) { 
        Print(DBG_POWER_INFO | DBG_POWER_ERROR, 
              ("irp %p failed, starting next\n", Irp)); 
 
        PoStartNextPowerIrp(Irp); 
        Irp = NULL; 
        ASSERT(status != STATUS_MORE_PROCESSING_REQUIRED); 
    } 
 
    return status; 
}