/*++ Copyright (c) 1997-1998 Microsoft Corporation, All Rights Reserved Module Name: kbdpnp.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 #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, I8xKeyboardConnectInterrupt) #pragma alloc_text(PAGE, I8xKeyboardInitializeHardware) #pragma alloc_text(PAGE, I8xKeyboardStartDevice) #pragma alloc_text(PAGE, I8xKeyboardRemoveDevice) #endif NTSTATUS I8xKeyboardConnectInterrupt( PPORT_KEYBOARD_EXTENSION KeyboardExtension ) /*++ Routine Description: Calls IoConnectInterupt to connect the keyboard interrupt Arguments: KeyboardExtension - Keyboard Extension SyncConnectContext - Structure to be filled in if synchronization needs to take place between this interrupt and the mouse interrupt. Return Value: STATUS_SUCCESSFUL if successful, --*/ { NTSTATUS status = STATUS_SUCCESS; ULONG dumpData[1]; PI8042_CONFIGURATION_INFORMATION configuration; PDEVICE_OBJECT self; PAGED_CODE(); // // If the devices were started in totally disparate manner, make sure we // retry to connect the interrupt (and fail and NULL out InterruptObject) // if (KeyboardExtension->InterruptObject) { return STATUS_SUCCESS; } configuration = &Globals.ControllerData->Configuration; self = KeyboardExtension->Self; Print(DBG_SS_NOISE, ("I8xKeyboardConnectInterrupt:\n" "\tFDO = 0x%x\n" "\tVector = 0x%x\n" "\tIrql = 0x%x\n" "\tSynchIrql = 0x%x\n" "\tIntterupt Mode = %s\n" "\tShared int: %s\n" "\tAffinity = 0x%x\n" "\tFloating Save = %s\n", self, (ULONG) KeyboardExtension->InterruptDescriptor.u.Interrupt.Vector, (ULONG) KeyboardExtension->InterruptDescriptor.u.Interrupt.Level, (ULONG) configuration->InterruptSynchIrql, KeyboardExtension->InterruptDescriptor.Flags == CM_RESOURCE_INTERRUPT_LATCHED ? "Latched" : "LevelSensitive", (ULONG) KeyboardExtension->InterruptDescriptor.ShareDisposition == CmResourceShareShared ? "true" : "false", (ULONG) KeyboardExtension->InterruptDescriptor.u.Interrupt.Affinity, configuration->FloatingSave ? "yes" : "no" )); KeyboardExtension->IsIsrActivated = TRUE; // // Connect the interrupt and set everything in motion // status = IoConnectInterrupt( &(KeyboardExtension->InterruptObject), (PKSERVICE_ROUTINE) I8042KeyboardInterruptService, self, &KeyboardExtension->InterruptSpinLock, KeyboardExtension->InterruptDescriptor.u.Interrupt.Vector, (KIRQL) KeyboardExtension->InterruptDescriptor.u.Interrupt.Level, configuration->InterruptSynchIrql, KeyboardExtension->InterruptDescriptor.Flags == CM_RESOURCE_INTERRUPT_LATCHED ? Latched : LevelSensitive, (BOOLEAN) (KeyboardExtension->InterruptDescriptor.ShareDisposition == CmResourceShareShared), KeyboardExtension->InterruptDescriptor.u.Interrupt.Affinity, configuration->FloatingSave ); if (NT_SUCCESS(status)) { INTERNAL_I8042_START_INFORMATION startInfo; PDEVICE_OBJECT topOfStack = IoGetAttachedDeviceReference(self); ASSERT(KeyboardExtension->InterruptObject != NULL); ASSERT(topOfStack); RtlZeroMemory(&startInfo, sizeof(INTERNAL_I8042_START_INFORMATION)); startInfo.Size = sizeof(INTERNAL_I8042_START_INFORMATION); startInfo.InterruptObject = KeyboardExtension->InterruptObject; I8xSendIoctl(topOfStack, IOCTL_INTERNAL_I8042_KEYBOARD_START_INFORMATION, &startInfo, sizeof(INTERNAL_I8042_START_INFORMATION) ); ObDereferenceObject(topOfStack); } else { // // Failed to install. Free up resources before exiting. // Print(DBG_SS_ERROR, ("Could not connect keyboard isr!!!\n")); dumpData[0] = KeyboardExtension->InterruptDescriptor.u.Interrupt.Level; // // Log the error // I8xLogError(self, I8042_NO_INTERRUPT_CONNECTED_KBD, I8042_ERROR_VALUE_BASE + 80, STATUS_INSUFFICIENT_RESOURCES, dumpData, 1 ); I8xManuallyRemoveDevice(GET_COMMON_DATA(KeyboardExtension)); } return status; } NTSTATUS I8xKeyboardInitializeHardware( PPORT_KEYBOARD_EXTENSION KeyboardExtension, PPORT_MOUSE_EXTENSION MouseExtension ) /*++ Routine Description: Called if the keyboard is the last device to initialized with respect to the mouse (if it is present at all). It calls the initialization function and then connects the (possibly) two interrupts, synchronizing the lower IRQL'ed interrupt to the higher one. Arguments: KeyboardExtension - Keyboard Extension SyncConnectContext - Structure to be filled in if synchronization needs to take place between this interrupt and the mouse interrupt. Return Value: STATUS_SUCCESSFUL if successful, --*/ { NTSTATUS keyboardStatus = STATUS_UNSUCCESSFUL, mouseStatus = STATUS_UNSUCCESSFUL, status; BOOLEAN mouThoughtPresent; PAGED_CODE(); // // Initialize the i8042 for all devices present on it. // If either device is unresponsive, then XXX_PRESENT() will be false // mouThoughtPresent = MOUSE_PRESENT(); status = I8xInitializeHardwareAtBoot(&keyboardStatus, &mouseStatus); // // failure here means that we couldn't toggle the interrupts on the i8042 // if (!NT_SUCCESS(status)) { return status; } if (DEVICE_START_SUCCESS(mouseStatus)) { // // Any errors will be logged by I8xMouseConnectInterruptAndEnable // status = I8xMouseConnectInterruptAndEnable( MouseExtension, mouseStatus == STATUS_DEVICE_NOT_CONNECTED ? FALSE : TRUE ); // // mou couldn't connect, make sure our count of started devices reflects // this // if (!NT_SUCCESS(status)) { Print(DBG_SS_ERROR, ("thought mou was present, is not (2)!\n")); } } else { // // We thought the mouse was present, but it is not, make sure that started // devices reflects this // // if (mouThoughtPresent) { // InterlockedDecrement(&Globals.StartedDevices); // } } // // The keyboard could be present but not have been initialized // in I8xInitializeHardware // if (DEVICE_START_SUCCESS(keyboardStatus)) { // // I8xKeyboardConnectInterrupt will log any errors if unsuccessful // keyboardStatus = I8xKeyboardConnectInterrupt(KeyboardExtension); } return keyboardStatus; } NTSTATUS I8xKeyboardStartDevice( IN OUT PPORT_KEYBOARD_EXTENSION KeyboardExtension, IN PCM_RESOURCE_LIST ResourceList ) /*++ Routine Description: Configures the keyboard's device extension (ie allocation of pool, initialization of DPCs, etc). If the keyboard is the last device to start, it will also initialize the hardware and connect all the interrupts. Arguments: KeyboardExtension - Keyboard extesnion ResourceList - Translated resource list for this device Return Value: STATUS_SUCCESSFUL if successful, --*/ { ULONG dumpData[1]; NTSTATUS status = STATUS_SUCCESS; PDEVICE_OBJECT self; I8042_INITIALIZE_DATA_CONTEXT initializeDataContext; BOOLEAN tryMouseInit = FALSE; PAGED_CODE(); Print(DBG_SS_TRACE, ("I8xKeyboardStartDevice, enter\n")); // // Check to see if kb has been started. If so, fail this start // if (KEYBOARD_INITIALIZED()) { Print(DBG_SS_ERROR, ("too many kbs!\n")); // // This is not really necessary because the value won't ever be checked // in the context of seeing if all the keyboards were bogus, but it is // done so that Globals.AddedKeyboards == # of actual started keyboards // InterlockedDecrement(&Globals.AddedKeyboards); status = STATUS_NO_SUCH_DEVICE; goto I8xKeyboardStartDeviceExit; } else if (KeyboardExtension->ConnectData.ClassService == NULL) { // // We are never really going to get here because if we don't have the // class driver on top of us, extension->IsKeyboard will be false and // we will think that the device is a mouse, but for completeness // // No class driver on top of us == BAD BAD BAD // // Fail the start of this device in the hope that there is another stack // that is correctly formed. Another side affect of having no class // driver is that the AddedKeyboards count is not incremented for this // device // Print(DBG_SS_ERROR, ("Keyboard started with out a service cb!\n")); return STATUS_INVALID_DEVICE_STATE; } status = I8xKeyboardConfiguration(KeyboardExtension, ResourceList ); if (!NT_SUCCESS(status)) { if (I8xManuallyRemoveDevice(GET_COMMON_DATA(KeyboardExtension)) < 1) { tryMouseInit = TRUE; } goto I8xKeyboardStartDeviceExit; } ASSERT( KEYBOARD_PRESENT() ); Globals.KeyboardExtension = KeyboardExtension; self = KeyboardExtension->Self; if ((KIRQL) KeyboardExtension->InterruptDescriptor.u.Interrupt.Level > Globals.ControllerData->Configuration.InterruptSynchIrql) { Globals.ControllerData->Configuration.InterruptSynchIrql = (KIRQL) KeyboardExtension->InterruptDescriptor.u.Interrupt.Level; } // // Initialize crash dump configuration // KeyboardExtension->CrashFlags = 0; KeyboardExtension->CurrentCrashFlags = 0; KeyboardExtension->CrashScanCode = (UCHAR) 0; KeyboardExtension->CrashScanCode2 = (UCHAR) 0; I8xKeyboardServiceParameters( &Globals.RegistryPath, KeyboardExtension ); // // These may have been set by a value in the Parameters key. It overrides // the "Crash Dump" key // if (KeyboardExtension->CrashFlags == 0) { // // Get the crashdump information. // I8xServiceCrashDump(KeyboardExtension, &Globals.RegistryPath ); } // // Allocate memory for the keyboard data queue. // KeyboardExtension->InputData = ExAllocatePool( NonPagedPool, KeyboardExtension->KeyboardAttributes.InputDataQueueLength ); if (!KeyboardExtension->InputData) { // // Could not allocate memory for the keyboard data queue. // Print(DBG_SS_ERROR, ("I8xStartDevice: Could not allocate keyboard input data queue\n" )); dumpData[0] = KeyboardExtension->KeyboardAttributes.InputDataQueueLength; // // Log the error // I8xLogError(self, I8042_NO_BUFFER_ALLOCATED_KBD, I8042_ERROR_VALUE_BASE + 50, STATUS_INSUFFICIENT_RESOURCES, dumpData, 1 ); status = STATUS_INSUFFICIENT_RESOURCES; tryMouseInit = TRUE; goto I8xKeyboardStartDeviceExit; } else { KeyboardExtension->DataEnd = (PKEYBOARD_INPUT_DATA) ((PCHAR) (KeyboardExtension->InputData) + KeyboardExtension->KeyboardAttributes.InputDataQueueLength); // // Zero the keyboard input data ring buffer. // RtlZeroMemory( KeyboardExtension->InputData, KeyboardExtension->KeyboardAttributes.InputDataQueueLength ); initializeDataContext.DeviceExtension = KeyboardExtension; initializeDataContext.DeviceType = KeyboardDeviceType; I8xInitializeDataQueue(&initializeDataContext); } KeyboardExtension->DpcInterlockKeyboard = -1; // // Initialize the ISR DPC . The ISR DPC // is responsible for calling the connected class driver's callback // routine to process the input data queue. // KeInitializeDpc( &KeyboardExtension->KeyboardIsrDpc, (PKDEFERRED_ROUTINE) I8042KeyboardIsrDpc, self ); KeInitializeDpc( &KeyboardExtension->KeyboardIsrDpcRetry, (PKDEFERRED_ROUTINE) I8042KeyboardIsrDpc, self ); KeInitializeDpc( &KeyboardExtension->SysButtonEventDpc, (PKDEFERRED_ROUTINE) I8xKeyboardSysButtonEventDpc, self ); KeInitializeSpinLock(&KeyboardExtension->SysButtonSpinLock); if (KeyboardExtension->PowerCaps) { I8xRegisterDeviceInterface(KeyboardExtension->PDO, &GUID_DEVICE_SYS_BUTTON, &KeyboardExtension->SysButtonInterfaceName ); } I8xInitWmi(GET_COMMON_DATA(KeyboardExtension)); KeyboardExtension->Initialized = TRUE; // // This is not the last device to started on the i8042, defer h/w init // until the last device is started // if (MOUSE_PRESENT() && !MOUSE_STARTED()) { // // A mouse is present, but it has not been started yet // Print(DBG_SS_INFO, ("skipping init until mouse\n")); } else { status = I8xKeyboardInitializeHardware(KeyboardExtension, Globals.MouseExtension); } I8xKeyboardStartDeviceExit: if (tryMouseInit && MOUSE_STARTED() && !MOUSE_INITIALIZED()) { Print(DBG_SS_INFO, ("keyboard may have failed, trying to init mouse\n")); I8xMouseInitializeHardware(KeyboardExtension, Globals.MouseExtension ); } Print(DBG_SS_INFO, ("I8xKeyboardStartDevice %s\n", NT_SUCCESS(status) ? "successful" : "unsuccessful" )); Print(DBG_SS_TRACE, ("I8xKeyboardStartDevice exit (0x%x)\n", status)); return status; } VOID I8xKeyboardRemoveDeviceInitialized( PPORT_KEYBOARD_EXTENSION KeyboardExtension ) { PIRP irp = NULL; KIRQL irql; KeAcquireSpinLock(&KeyboardExtension->SysButtonSpinLock, &irql); KeRemoveQueueDpc(&KeyboardExtension->SysButtonEventDpc); irp = KeyboardExtension->SysButtonEventIrp; KeyboardExtension->SysButtonEventIrp = NULL; if (irp && (irp->Cancel || IoSetCancelRoutine(irp, NULL) == NULL)) { irp = NULL; } KeReleaseSpinLock(&KeyboardExtension->SysButtonSpinLock, irql); if (irp) { I8xCompleteSysButtonIrp(irp, 0x0, STATUS_DELETE_PENDING); } } VOID I8xKeyboardRemoveDevice( PDEVICE_OBJECT DeviceObject ) /*++ Routine Description: Removes the device. This will only occur if the device removed itself. Disconnects the interrupt, removes the synchronization flag for the mouse if present, and frees any memory associated with the device. Arguments: DeviceObject - The device object for the keyboard Return Value: STATUS_SUCCESSFUL if successful, --*/ { PPORT_KEYBOARD_EXTENSION keyboardExtension = DeviceObject->DeviceExtension; PIRP irp; Print(DBG_PNP_INFO, ("I8xKeyboardRemoveDevice enter\n")); PAGED_CODE(); if (Globals.KeyboardExtension == keyboardExtension && keyboardExtension) { CLEAR_KEYBOARD_PRESENT(); Globals.KeyboardExtension = NULL; } if (keyboardExtension->InterruptObject) { IoDisconnectInterrupt(keyboardExtension->InterruptObject); keyboardExtension->InterruptObject = NULL; } if (keyboardExtension->InputData) { ExFreePool(keyboardExtension->InputData); keyboardExtension->InputData = 0; } // // See if we have gotten a sys button key press in the midst of a removal. // If so, then the request will be in PendingCompletion...Irp, otherwise we // might have the irp in Pending...Irp // if (keyboardExtension->Initialized) { I8xKeyboardRemoveDeviceInitialized(keyboardExtension); } if (keyboardExtension->SysButtonInterfaceName.Buffer) { IoSetDeviceInterfaceState(&keyboardExtension->SysButtonInterfaceName, FALSE ); RtlFreeUnicodeString(&keyboardExtension->SysButtonInterfaceName); } }