You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2829 lines
93 KiB
2829 lines
93 KiB
/*++
|
|
|
|
Copyright (c) 1990-1998 Microsoft Corporation, All Rights Reserved
|
|
|
|
Module Name:
|
|
|
|
kbddep.c
|
|
|
|
Abstract:
|
|
|
|
The initialization and hardware-dependent portions of
|
|
the Intel i8042 port driver which are specific to the
|
|
keyboard.
|
|
|
|
Environment:
|
|
|
|
Kernel mode only.
|
|
|
|
Notes:
|
|
|
|
NOTES: (Future/outstanding issues)
|
|
|
|
- Powerfail not implemented.
|
|
|
|
- Consolidate duplicate code, where possible and appropriate.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "stdarg.h"
|
|
#include "stdio.h"
|
|
#include "string.h"
|
|
#include <ntddk.h>
|
|
#include <windef.h>
|
|
#include <imm.h>
|
|
#include "i8042prt.h"
|
|
#include "i8042log.h"
|
|
//
|
|
// Use the alloc_text pragma to specify the driver initialization routines
|
|
// (they can be paged out).
|
|
//
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, I8xKeyboardConfiguration)
|
|
#pragma alloc_text(PAGE, I8xInitializeKeyboard)
|
|
#pragma alloc_text(PAGE, I8xKeyboardServiceParameters)
|
|
#pragma alloc_text(PAGE, I8xServiceCrashDump)
|
|
|
|
#endif
|
|
|
|
#define BUFFER_FULL (OUTPUT_BUFFER_FULL|MOUSE_OUTPUT_BUFFER_FULL)
|
|
|
|
#define GET_MAKE_CODE(_sc_) (_sc_ & 0x7F)
|
|
|
|
//
|
|
// Tests for the top bit
|
|
//
|
|
#define IS_BREAK_CODE(_sc_) (_sc_ > (UCHAR) 0x7F)
|
|
#define IS_MAKE_CODE(_sc_) (_sc_ <= (UCHAR) 0x7F)
|
|
|
|
BOOLEAN
|
|
I8042KeyboardInterruptService(
|
|
IN PKINTERRUPT Interrupt,
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs the actual work. It either processes a keystroke or
|
|
the results from a write to the device.
|
|
|
|
Arguments:
|
|
|
|
CallIsrContext - Contains the interrupt object and device object.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the interrupt was truly ours
|
|
|
|
--*/
|
|
{
|
|
UCHAR scanCode, statusByte;
|
|
PPORT_KEYBOARD_EXTENSION deviceExtension;
|
|
KEYBOARD_SCAN_STATE *scanState;
|
|
PKEYBOARD_INPUT_DATA input;
|
|
ULONG i;
|
|
#ifdef FE_SB
|
|
PKEYBOARD_ID KeyboardId;
|
|
#endif
|
|
|
|
IsrPrint(DBG_KBISR_TRACE, ("enter\n"));
|
|
|
|
//
|
|
// Get the device extension.
|
|
//
|
|
deviceExtension = (PPORT_KEYBOARD_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// The interrupt will fire when we try to toggle the interrupts on the
|
|
// controller itself. Don't touch any of the ports in this state and the
|
|
// toggle will succeed.
|
|
//
|
|
if (deviceExtension->PowerState != PowerDeviceD0) {
|
|
return FALSE;
|
|
}
|
|
|
|
#ifdef FE_SB
|
|
//
|
|
// Get a pointer to keyboard id.
|
|
//
|
|
KeyboardId = &deviceExtension->KeyboardAttributes.KeyboardIdentifier;
|
|
#endif
|
|
|
|
//
|
|
// Verify that this device really interrupted. Check the status
|
|
// register. The Output Buffer Full bit should be set, and the
|
|
// Auxiliary Device Output Buffer Full bit should be clear.
|
|
//
|
|
statusByte =
|
|
I8X_GET_STATUS_BYTE(Globals.ControllerData->DeviceRegisters[CommandPort]);
|
|
if ((statusByte & BUFFER_FULL) != OUTPUT_BUFFER_FULL) {
|
|
|
|
//
|
|
// Stall and then try again. The Olivetti MIPS machine
|
|
// sometimes gets an interrupt before the status
|
|
// register is set. They do this for DOS compatibility (some
|
|
// DOS apps do things in polled mode, until they see a character
|
|
// in the keyboard buffer at which point they expect to get
|
|
// an interrupt???).
|
|
//
|
|
|
|
for (i = 0; i < (ULONG)Globals.ControllerData->Configuration.PollStatusIterations; i++) {
|
|
KeStallExecutionProcessor(1);
|
|
statusByte = I8X_GET_STATUS_BYTE(Globals.ControllerData->DeviceRegisters[CommandPort]);
|
|
if ((statusByte & BUFFER_FULL) == (OUTPUT_BUFFER_FULL)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
statusByte = I8X_GET_STATUS_BYTE(Globals.ControllerData->DeviceRegisters[CommandPort]);
|
|
if ((statusByte & BUFFER_FULL) != (OUTPUT_BUFFER_FULL)) {
|
|
|
|
//
|
|
// Not our interrupt.
|
|
//
|
|
// NOTE: If the keyboard has not yet been "enabled", go ahead
|
|
// and read a byte from the data port anyway.
|
|
// This fixes weirdness on some Gateway machines, where
|
|
// we get an interrupt sometime during driver initialization
|
|
// after the interrupt is connected, but the output buffer
|
|
// full bit never gets set.
|
|
//
|
|
|
|
IsrPrint(DBG_KBISR_ERROR|DBG_KBISR_INFO, ("not our interrupt!\n"));
|
|
|
|
if (deviceExtension->EnableCount == 0) {
|
|
scanCode =
|
|
I8X_GET_DATA_BYTE(Globals.ControllerData->DeviceRegisters[DataPort]);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The interrupt is valid. Read the byte from the i8042 data port.
|
|
//
|
|
|
|
I8xGetByteAsynchronous(
|
|
(CCHAR) KeyboardDeviceType,
|
|
&scanCode
|
|
);
|
|
|
|
deviceExtension->LastScanCode = deviceExtension->CurrentScanCode;
|
|
deviceExtension->CurrentScanCode = scanCode;
|
|
|
|
IsrPrint(DBG_KBISR_SCODE, ("scanCode 0x%x\n", scanCode));
|
|
|
|
if (deviceExtension->IsrHookCallback) {
|
|
BOOLEAN cont = FALSE, ret;
|
|
|
|
ret = (*deviceExtension->IsrHookCallback)(
|
|
deviceExtension->HookContext,
|
|
&deviceExtension->CurrentInput,
|
|
&deviceExtension->CurrentOutput,
|
|
statusByte,
|
|
&scanCode,
|
|
&cont,
|
|
&deviceExtension->CurrentScanState
|
|
);
|
|
|
|
if (!cont) {
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Take the appropriate action, depending on whether the byte read
|
|
// is a keyboard command response or a real scan code.
|
|
//
|
|
|
|
switch(scanCode) {
|
|
|
|
//
|
|
// The keyboard controller requests a resend. If the resend count
|
|
// has not been exceeded, re-initiate the I/O operation.
|
|
//
|
|
|
|
case RESEND:
|
|
|
|
IsrPrint(DBG_KBISR_INFO,
|
|
(" RESEND, retries = %d\n",
|
|
deviceExtension->ResendCount + 1
|
|
));
|
|
|
|
//
|
|
// If the timer count is zero, don't process the interrupt
|
|
// further. The timeout routine will complete this request.
|
|
//
|
|
|
|
if (Globals.ControllerData->TimerCount == 0) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Reset the timeout value to indicate no timeout.
|
|
//
|
|
|
|
Globals.ControllerData->TimerCount = I8042_ASYNC_NO_TIMEOUT;
|
|
|
|
//
|
|
// If the maximum number of retries has not been exceeded,
|
|
//
|
|
|
|
if ((deviceExtension->CurrentOutput.State == Idle)
|
|
|| (DeviceObject->CurrentIrp == NULL)) {
|
|
|
|
//
|
|
// We weren't sending a command or parameter to the hardware.
|
|
// This must be a scan code. I hear the Brazilian keyboard
|
|
// actually uses this.
|
|
//
|
|
|
|
goto ScanCodeCase;
|
|
|
|
} else if (deviceExtension->ResendCount
|
|
< Globals.ControllerData->Configuration.ResendIterations) {
|
|
|
|
//
|
|
// retard the byte count to resend the last byte
|
|
//
|
|
deviceExtension->CurrentOutput.CurrentByte -= 1;
|
|
deviceExtension->ResendCount += 1;
|
|
I8xInitiateIo(DeviceObject);
|
|
|
|
} else {
|
|
|
|
deviceExtension->CurrentOutput.State = Idle;
|
|
|
|
KeInsertQueueDpc(
|
|
&deviceExtension->RetriesExceededDpc,
|
|
DeviceObject->CurrentIrp,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
break;
|
|
|
|
//
|
|
// The keyboard controller has acknowledged a previous send.
|
|
// If there are more bytes to send for the current packet, initiate
|
|
// the next send operation. Otherwise, queue the completion DPC.
|
|
//
|
|
|
|
case ACKNOWLEDGE:
|
|
|
|
IsrPrint(DBG_KBISR_STATE, (": ACK, "));
|
|
|
|
//
|
|
// If the timer count is zero, don't process the interrupt
|
|
// further. The timeout routine will complete this request.
|
|
//
|
|
|
|
if (Globals.ControllerData->TimerCount == 0) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// We cannot clear the E0 or E1 bits b/c then the subsequent scan
|
|
// code will be misinterpreted. ie, the OS should have seen 0x2d
|
|
// with an extended bit, but instead it saw a plain 0x2d
|
|
//
|
|
// If the keyboard is using 0xE0 0x7A / 0xE0 0xFA as a make / break
|
|
// code, then tough luck...bad choice, we do not support it.
|
|
//
|
|
#if 0
|
|
//
|
|
// If the E0 or E1 is set, that means that this keyboard's
|
|
// manufacturer made a poor choice for a scan code, 0x7A, whose
|
|
// break code is 0xFA. Thankfully, they used the E0 or E1 prefix
|
|
// so we can tell the difference.
|
|
//
|
|
if (deviceExtension->CurrentInput.Flags & (KEY_E0 | KEY_E1)) {
|
|
|
|
//
|
|
// The following sequence can occur which requires the driver to
|
|
// ignore the spurious keystroke
|
|
//
|
|
// 1 write set typematic to the device (0xF3)
|
|
// 2 device responds with an ACK, ISR sees 0xFA
|
|
// 3 write typematic value to the device (0x??)
|
|
// 4 user hits an extended key (left arrow for instance), ISR sees 0xE0
|
|
// 5 device response with an ACK to the typematic value, ISR sees 0xFA
|
|
// before the actual scancode for the left arrow is sent to the ISR
|
|
//
|
|
|
|
//
|
|
// Make sure we are trully not writing out data to the device
|
|
//
|
|
if (Globals.ControllerData->TimerCount == I8042_ASYNC_NO_TIMEOUT &&
|
|
deviceExtension->CurrentOutput.State == Idle) {
|
|
IsrPrint(DBG_KBISR_INFO,
|
|
("BAD KEYBOARD: 0xFA used as a real scancode!\n"));
|
|
goto ScanCodeCase;
|
|
}
|
|
else {
|
|
//
|
|
// Spurious keystroke case.
|
|
//
|
|
// Clear the E0 / E1 flag. the 2nd byte of the scan code will
|
|
// never come through b/c it was preempted by the ACK for the
|
|
// write to the device
|
|
//
|
|
deviceExtension->CurrentInput.Flags &= ~(KEY_E0 | KEY_E1);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Reset the timeout value to indicate no timeout.
|
|
//
|
|
Globals.ControllerData->TimerCount = I8042_ASYNC_NO_TIMEOUT;
|
|
|
|
//
|
|
// Reset resend count.
|
|
//
|
|
deviceExtension->ResendCount = 0;
|
|
|
|
//
|
|
// Make sure we are writing to the device if we are going to write
|
|
// another byte or queue a DPC
|
|
//
|
|
if (deviceExtension->CurrentOutput.State == SendingBytes) {
|
|
if (deviceExtension->CurrentOutput.CurrentByte <
|
|
deviceExtension->CurrentOutput.ByteCount) {
|
|
|
|
//
|
|
// We've successfully sent the first byte of a 2-byte (or more)
|
|
// command sequence. Initiate a send of the second byte.
|
|
//
|
|
IsrPrint(DBG_KBISR_STATE,
|
|
("now initiate send of byte #%d\n",
|
|
deviceExtension->CurrentOutput.CurrentByte
|
|
));
|
|
|
|
I8xInitiateIo(DeviceObject);
|
|
}
|
|
else {
|
|
//
|
|
// We've successfully sent all bytes in the command sequence.
|
|
// Reset the current state and queue the completion DPC.
|
|
//
|
|
IsrPrint(DBG_KBISR_STATE,
|
|
("all bytes have been sent\n"
|
|
));
|
|
|
|
deviceExtension->CurrentOutput.State = Idle;
|
|
|
|
ASSERT(DeviceObject->CurrentIrp != NULL);
|
|
|
|
IoRequestDpc(
|
|
DeviceObject,
|
|
DeviceObject->CurrentIrp,
|
|
IntToPtr(IsrDpcCauseKeyboardWriteComplete)
|
|
);
|
|
}
|
|
}
|
|
break;
|
|
|
|
//
|
|
// Assume we've got a real, live scan code (or perhaps a keyboard
|
|
// overrun code, which we treat like a scan code). I.e., a key
|
|
// has been pressed or released. Queue the ISR DPC to process
|
|
// a complete scan code sequence.
|
|
//
|
|
|
|
ScanCodeCase:
|
|
default:
|
|
|
|
IsrPrint(DBG_KBISR_SCODE, ("real scan code\n"));
|
|
|
|
//
|
|
// Differentiate between an extended key sequence (first
|
|
// byte is E0, followed by a normal make or break byte), or
|
|
// a normal make code (one byte, the high bit is NOT set),
|
|
// or a normal break code (one byte, same as the make code
|
|
// but the high bit is set), or the key #126 byte sequence
|
|
// (requires special handling -- sequence is E11D459DC5).
|
|
//
|
|
// If there is a key detection error/overrun, the keyboard
|
|
// sends an overrun indicator (0xFF in scan code set 1).
|
|
// Map it to the overrun indicator expected by the Windows
|
|
// USER Raw Input Thread.
|
|
//
|
|
|
|
input = &deviceExtension->CurrentInput;
|
|
scanState = &deviceExtension->CurrentScanState;
|
|
|
|
if (scanCode == (UCHAR) 0xFF) {
|
|
IsrPrint(DBG_KBISR_ERROR, ("OVERRUN\n"));
|
|
input->MakeCode = KEYBOARD_OVERRUN_MAKE_CODE;
|
|
input->Flags = 0;
|
|
*scanState = Normal;
|
|
} else {
|
|
|
|
switch (*scanState) {
|
|
case Normal:
|
|
if (scanCode == (UCHAR) 0xE0) {
|
|
input->Flags |= KEY_E0;
|
|
*scanState = GotE0;
|
|
IsrPrint(DBG_KBISR_STATE, ("change state to GotE0\n"));
|
|
break;
|
|
} else if (scanCode == (UCHAR) 0xE1) {
|
|
input->Flags |= KEY_E1;
|
|
*scanState = GotE1;
|
|
IsrPrint(DBG_KBISR_STATE, ("change state to GotE1\n"));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Fall through to the GotE0/GotE1 case for the rest of the
|
|
// Normal case.
|
|
//
|
|
|
|
case GotE0:
|
|
case GotE1:
|
|
|
|
if (deviceExtension->CrashFlags != 0x0) {
|
|
I8xProcessCrashDump(deviceExtension,
|
|
scanCode,
|
|
*scanState);
|
|
}
|
|
|
|
if (IS_BREAK_CODE(scanCode)) {
|
|
SYS_BUTTON_ACTION action;
|
|
|
|
//
|
|
// Got a break code. Strip the high bit off
|
|
// to get the associated make code and set flags
|
|
// to indicate a break code.
|
|
//
|
|
|
|
IsrPrint(DBG_KBISR_SCODE, ("BREAK code\n"));
|
|
|
|
input->MakeCode = GET_MAKE_CODE(scanCode);
|
|
input->Flags |= KEY_BREAK;
|
|
|
|
if (input->Flags & KEY_E0) {
|
|
switch (input->MakeCode) {
|
|
case KEYBOARD_POWER_CODE:
|
|
if (deviceExtension->PowerCaps &
|
|
I8042_POWER_SYS_BUTTON) {
|
|
IsrPrint(DBG_KBISR_POWER, ("Send Power Button\n"));
|
|
action = SendAction;
|
|
}
|
|
else {
|
|
IsrPrint(DBG_KBISR_POWER, ("Update Power Button\n"));
|
|
action = UpdateAction;
|
|
}
|
|
break;
|
|
|
|
case KEYBOARD_SLEEP_CODE:
|
|
if (deviceExtension->PowerCaps &
|
|
I8042_SLEEP_SYS_BUTTON) {
|
|
IsrPrint(DBG_KBISR_POWER, ("Send Sleep Button\n"));
|
|
action = SendAction;
|
|
}
|
|
else {
|
|
IsrPrint(DBG_KBISR_POWER, ("Update Sleep Button\n"));
|
|
action = UpdateAction;
|
|
}
|
|
break;
|
|
|
|
case KEYBOARD_WAKE_CODE:
|
|
if (deviceExtension->PowerCaps &
|
|
I8042_WAKE_SYS_BUTTON) {
|
|
IsrPrint(DBG_KBISR_POWER, ("Send Wake Button\n"));
|
|
action = SendAction;
|
|
}
|
|
else {
|
|
IsrPrint(DBG_KBISR_POWER, ("Update Wake Button\n"));
|
|
action = UpdateAction;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
action = NoAction;
|
|
break;
|
|
}
|
|
|
|
if (action != NoAction) {
|
|
//
|
|
// Queue a DPC so that we can do the appropriate
|
|
// action
|
|
//
|
|
KeInsertQueueDpc(
|
|
&deviceExtension->SysButtonEventDpc,
|
|
(PVOID) action,
|
|
(PVOID) input->MakeCode
|
|
);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Got a make code.
|
|
//
|
|
|
|
IsrPrint(DBG_KBISR_SCODE, ("MAKE code\n"));
|
|
|
|
input->MakeCode = scanCode;
|
|
|
|
//
|
|
// If the input scan code is debug stop, then drop
|
|
// into the kernel debugger if it is active.
|
|
//
|
|
|
|
if ((KD_DEBUGGER_NOT_PRESENT == FALSE) && !(input->Flags & KEY_BREAK)) {
|
|
if (ENHANCED_KEYBOARD(
|
|
deviceExtension->KeyboardAttributes.KeyboardIdentifier
|
|
)) {
|
|
//
|
|
// Enhanced 101 keyboard, SysReq key is 0xE0 0x37.
|
|
//
|
|
|
|
if ((input->MakeCode == KEYBOARD_DEBUG_HOTKEY_ENH) &&
|
|
(input->Flags & KEY_E0)) {
|
|
try {
|
|
if ((KD_DEBUGGER_ENABLED != FALSE) &&
|
|
Globals.BreakOnSysRq) {
|
|
DbgBreakPointWithStatus(DBG_STATUS_SYSRQ);
|
|
}
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
}
|
|
}
|
|
//
|
|
// 84-key AT keyboard, SysReq key is 0xE0 0x54.
|
|
//
|
|
|
|
} else if ((input->MakeCode == KEYBOARD_DEBUG_HOTKEY_AT)) {
|
|
try {
|
|
if ((KD_DEBUGGER_ENABLED != FALSE)
|
|
&& Globals.BreakOnSysRq) {
|
|
DbgBreakPointWithStatus(DBG_STATUS_SYSRQ);
|
|
}
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Reset the state to Normal.
|
|
//
|
|
|
|
*scanState = Normal;
|
|
break;
|
|
|
|
default:
|
|
|
|
//
|
|
// Queue a DPC to log an internal driver error.
|
|
//
|
|
|
|
KeInsertQueueDpc(
|
|
&deviceExtension->ErrorLogDpc,
|
|
(PIRP) NULL,
|
|
LongToPtr(I8042_INVALID_ISR_STATE_KBD)
|
|
);
|
|
|
|
ASSERT(FALSE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// In the Normal state, if the keyboard device is enabled,
|
|
// add the data to the InputData queue and queue the ISR DPC.
|
|
//
|
|
if (*scanState == Normal) {
|
|
I8xQueueCurrentKeyboardInput(DeviceObject);
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
IsrPrint(DBG_KBISR_TRACE, ("exit\n"));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
I8xProcessCrashDump(
|
|
PPORT_KEYBOARD_EXTENSION DeviceExtension,
|
|
UCHAR ScanCode,
|
|
KEYBOARD_SCAN_STATE ScanState
|
|
)
|
|
{
|
|
LONG crashFlags;
|
|
BOOLEAN processFlags;
|
|
UCHAR crashScanCode, crashScanCode2;
|
|
|
|
crashFlags = DeviceExtension->CrashFlags;
|
|
crashScanCode = DeviceExtension->CrashScanCode;
|
|
crashScanCode2 = DeviceExtension->CrashScanCode2;
|
|
|
|
if (IS_MAKE_CODE(ScanCode)) {
|
|
//
|
|
// make code
|
|
//
|
|
// If it is one of the crash flag keys record it.
|
|
// If it is a crash dump key record it
|
|
// If it is neither, reset the current tracking state (CurrentCrashFlags)
|
|
//
|
|
switch (ScanCode) {
|
|
case CTRL_SCANCODE:
|
|
if (ScanState == Normal) { // Left
|
|
DeviceExtension->CurrentCrashFlags |= CRASH_L_CTRL;
|
|
}
|
|
else if (ScanState == GotE0) { // Right
|
|
DeviceExtension->CurrentCrashFlags |= CRASH_R_CTRL;
|
|
}
|
|
break;
|
|
|
|
case ALT_SCANCODE:
|
|
if (ScanState == Normal) { // Left
|
|
DeviceExtension->CurrentCrashFlags |= CRASH_L_ALT;
|
|
}
|
|
else if (ScanState == GotE0) { // Right
|
|
DeviceExtension->CurrentCrashFlags |= CRASH_R_ALT;
|
|
}
|
|
break;
|
|
|
|
case LEFT_SHIFT_SCANCODE:
|
|
if (ScanState == Normal) {
|
|
DeviceExtension->CurrentCrashFlags |= CRASH_L_SHIFT;
|
|
}
|
|
break;
|
|
|
|
case RIGHT_SHIFT_SCANCODE:
|
|
if (ScanState == Normal) {
|
|
DeviceExtension->CurrentCrashFlags |= CRASH_R_SHIFT;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (IS_MAKE_CODE(crashScanCode)) {
|
|
if (ScanState == Normal && crashScanCode == ScanCode) {
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
if (ScanState == GotE0 && GET_MAKE_CODE(crashScanCode) == ScanCode) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (IS_MAKE_CODE(crashScanCode2)) {
|
|
if (ScanState == Normal &&
|
|
crashScanCode2 == ScanCode) {
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
if (ScanState == GotE0 &&
|
|
GET_MAKE_CODE(crashScanCode2) == ScanCode) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Not a key we are interested in, reset our current state
|
|
//
|
|
DeviceExtension->CurrentCrashFlags = 0x0;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// break code
|
|
//
|
|
// If one of the modifer keys is released, our state is reset and all
|
|
// keys have to be pressed again.
|
|
// If it is a non modifier key, proceed with the processing if it is the
|
|
// crash dump key, otherwise reset our tracking state
|
|
//
|
|
switch (GET_MAKE_CODE(ScanCode)) {
|
|
case CTRL_SCANCODE:
|
|
if (ScanState == Normal) { // Left
|
|
DeviceExtension->CurrentCrashFlags &=
|
|
~(CRASH_BOTH_TIMES | CRASH_L_CTRL);
|
|
}
|
|
else if (ScanState == GotE0) { // Right
|
|
DeviceExtension->CurrentCrashFlags &=
|
|
~(CRASH_BOTH_TIMES | CRASH_R_CTRL);
|
|
}
|
|
break;
|
|
|
|
case ALT_SCANCODE:
|
|
if (ScanState == Normal) { // Left
|
|
DeviceExtension->CurrentCrashFlags &=
|
|
~(CRASH_BOTH_TIMES | CRASH_L_ALT);
|
|
}
|
|
else if (ScanState == GotE0) { // Right
|
|
DeviceExtension->CurrentCrashFlags &=
|
|
~(CRASH_BOTH_TIMES | CRASH_R_ALT);
|
|
}
|
|
break;
|
|
|
|
case RIGHT_SHIFT_SCANCODE:
|
|
if (ScanState == Normal) {
|
|
DeviceExtension->CurrentCrashFlags &=
|
|
~(CRASH_BOTH_TIMES | CRASH_R_SHIFT);
|
|
}
|
|
break;
|
|
|
|
case LEFT_SHIFT_SCANCODE:
|
|
if (ScanState == Normal) {
|
|
DeviceExtension->CurrentCrashFlags &=
|
|
~(CRASH_BOTH_TIMES | CRASH_L_SHIFT);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
processFlags = FALSE;
|
|
|
|
if (IS_MAKE_CODE(crashScanCode)) {
|
|
if (ScanState == Normal && crashScanCode == GET_MAKE_CODE(ScanCode))
|
|
processFlags = TRUE;
|
|
}
|
|
else {
|
|
if (ScanState == GotE0 && crashScanCode == ScanCode) {
|
|
processFlags = TRUE;
|
|
}
|
|
}
|
|
|
|
if (IS_MAKE_CODE(crashScanCode2)) {
|
|
if (ScanState == Normal && crashScanCode2 == GET_MAKE_CODE(ScanCode)) {
|
|
processFlags = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
if (ScanState == GotE0 && crashScanCode2 == ScanCode)
|
|
processFlags = TRUE;
|
|
}
|
|
|
|
//
|
|
// If this is the key we are interested in, continue, otherwise
|
|
// our tracking state is reset
|
|
//
|
|
if (processFlags) {
|
|
//
|
|
// test to see if all the needed modifier
|
|
// keys are down
|
|
//
|
|
if (crashFlags != (DeviceExtension->CurrentCrashFlags & crashFlags)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// record how many times we have seen
|
|
// this key
|
|
//
|
|
if (DeviceExtension->CurrentCrashFlags & CRASH_FIRST_TIME) {
|
|
DeviceExtension->CurrentCrashFlags |= CRASH_SECOND_TIME;
|
|
}
|
|
else {
|
|
DeviceExtension->CurrentCrashFlags |= CRASH_FIRST_TIME;
|
|
}
|
|
break;
|
|
}
|
|
|
|
DeviceExtension->CurrentCrashFlags = 0x0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
crashFlags |= CRASH_BOTH_TIMES;
|
|
|
|
if (DeviceExtension->CurrentCrashFlags == crashFlags) {
|
|
DeviceExtension->CurrentCrashFlags = 0x0;
|
|
|
|
//
|
|
// Bring down the system in a somewhat controlled manner
|
|
//
|
|
KeBugCheckEx(MANUALLY_INITIATED_CRASH, 0, 0, 0, 0);
|
|
}
|
|
}
|
|
|
|
//
|
|
// The following table is used to convert typematic rate (keys per
|
|
// second) into the value expected by the keyboard. The index into the
|
|
// array is the number of keys per second. The resulting value is
|
|
// the bit equate to send to the keyboard.
|
|
//
|
|
|
|
UCHAR
|
|
I8xConvertTypematicParameters(
|
|
IN USHORT Rate,
|
|
IN USHORT Delay
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine converts the typematic rate and delay to the form the
|
|
keyboard expects.
|
|
|
|
The byte passed to the keyboard looks like this:
|
|
|
|
- bit 7 is zero
|
|
- bits 5 and 6 indicate the delay
|
|
- bits 0-4 indicate the rate
|
|
|
|
The delay is equal to 1 plus the binary value of bits 6 and 5,
|
|
multiplied by 250 milliseconds.
|
|
|
|
The period (interval from one typematic output to the next) is
|
|
determined by the following equation:
|
|
|
|
Period = (8 + A) x (2^B) x 0.00417 seconds
|
|
where
|
|
A = binary value of bits 0-2
|
|
B = binary value of bits 3 and 4
|
|
|
|
|
|
Arguments:
|
|
|
|
Rate - Number of keys per second.
|
|
|
|
Delay - Number of milliseconds to delay before the key repeat starts.
|
|
|
|
Return Value:
|
|
|
|
The byte to pass to the keyboard.
|
|
|
|
--*/
|
|
|
|
{
|
|
UCHAR value;
|
|
UCHAR TypematicPeriod[] = {
|
|
31, // 0 keys per second
|
|
31, // 1 keys per second
|
|
28, // 2 keys per second, This is really 2.5, needed for NEXUS.
|
|
26, // 3 keys per second
|
|
23, // 4 keys per second
|
|
20, // 5 keys per second
|
|
18, // 6 keys per second
|
|
17, // 7 keys per second
|
|
15, // 8 keys per second
|
|
13, // 9 keys per second
|
|
12, // 10 keys per second
|
|
11, // 11 keys per second
|
|
10, // 12 keys per second
|
|
9, // 13 keys per second
|
|
9, // 14 keys per second
|
|
8, // 15 keys per second
|
|
7, // 16 keys per second
|
|
6, // 17 keys per second
|
|
5, // 18 keys per second
|
|
4, // 19 keys per second
|
|
4, // 20 keys per second
|
|
3, // 21 keys per second
|
|
3, // 22 keys per second
|
|
2, // 23 keys per second
|
|
2, // 24 keys per second
|
|
1, // 25 keys per second
|
|
1, // 26 keys per second
|
|
1 // 27 keys per second
|
|
// > 27 keys per second, use 0
|
|
};
|
|
|
|
Print(DBG_CALL_TRACE, ("I8xConvertTypematicParameters: enter\n"));
|
|
|
|
//
|
|
// Calculate the delay bits.
|
|
//
|
|
|
|
value = (UCHAR) ((Delay / 250) - 1);
|
|
|
|
//
|
|
// Put delay bits in the right place.
|
|
//
|
|
|
|
value <<= 5;
|
|
|
|
//
|
|
// Get the typematic period from the table. If keys per second
|
|
// is > 27, the typematic period value is zero.
|
|
//
|
|
|
|
if (Rate <= 27) {
|
|
value |= TypematicPeriod[Rate];
|
|
}
|
|
|
|
Print(DBG_CALL_TRACE, ("I8xConvertTypematicParameters: exit\n"));
|
|
|
|
return(value);
|
|
}
|
|
|
|
#define KB_INIT_FAILED_RESET 0x00000001
|
|
#define KB_INIT_FAILED_XLATE_OFF 0x00000010
|
|
#define KB_INIT_FAILED_XLATE_ON 0x00000020
|
|
#define KB_INIT_FAILED_SET_TYPEMATIC 0x00000100
|
|
#define KB_INIT_FAILED_SET_TYPEMATIC_PARAM 0x00000200
|
|
#define KB_INIT_FAILED_SET_LEDS 0x00001000
|
|
#define KB_INIT_FAILED_SET_LEDS_PARAM 0x00002000
|
|
#define KB_INIT_FAILED_SELECT_SS 0x00010000
|
|
#define KB_INIT_FAILED_SELECT_SS_PARAM 0x00020000
|
|
|
|
#if KEYBOARD_RECORD_INIT
|
|
|
|
ULONG KeyboardInitStatus;
|
|
#define SET_KB_INIT_FAILURE(flag) KeyboardInitStatus |= flag
|
|
#define KB_INIT_START() KeyboardInitStatus = 0x0;
|
|
|
|
#else
|
|
|
|
#define SET_KB_INIT_FAILURE(flag)
|
|
#define KB_INIT_START()
|
|
|
|
#endif // KEYBOARD_RECORD_INIT
|
|
|
|
NTSTATUS
|
|
I8xInitializeKeyboard(
|
|
IN PPORT_KEYBOARD_EXTENSION KeyboardExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the i8042 keyboard hardware. It is called
|
|
only at initialization, and does not synchronize access to the hardware.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object.
|
|
|
|
Return Value:
|
|
|
|
Returns status.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PKEYBOARD_ID id;
|
|
PPORT_KEYBOARD_EXTENSION deviceExtension;
|
|
PDEVICE_OBJECT deviceObject;
|
|
UCHAR byte,
|
|
failedResetResponseByte,
|
|
failedResetResponseByte2;
|
|
I8042_TRANSMIT_CCB_CONTEXT transmitCCBContext;
|
|
ULONG i;
|
|
ULONG limit;
|
|
NTSTATUS failedLedsStatus,
|
|
failedTypematicStatus,
|
|
failedResetStatus,
|
|
failedResetResponseStatus,
|
|
failedResetResponseStatus2;
|
|
PI8042_CONFIGURATION_INFORMATION configuration;
|
|
PKEYBOARD_ID keyboardId;
|
|
LARGE_INTEGER startOfSpin,
|
|
nextQuery,
|
|
difference,
|
|
resetRespTimeout,
|
|
li;
|
|
BOOLEAN waitForAckOnReset = WAIT_FOR_ACKNOWLEDGE,
|
|
translationOn = TRUE,
|
|
failedReset = FALSE,
|
|
failedResetResponse = FALSE,
|
|
failedResetResponse2 = FALSE,
|
|
failedTypematic = FALSE,
|
|
failedLeds = FALSE;
|
|
|
|
#define DUMP_COUNT 4
|
|
ULONG dumpData[DUMP_COUNT];
|
|
|
|
PAGED_CODE();
|
|
|
|
KB_INIT_START();
|
|
|
|
Print(DBG_SS_TRACE, ("I8xInitializeKeyboard, enter\n"));
|
|
|
|
for (i = 0; i < DUMP_COUNT; i++)
|
|
dumpData[i] = 0;
|
|
|
|
//
|
|
// Get the device extension.
|
|
//
|
|
deviceExtension = KeyboardExtension;
|
|
deviceObject = deviceExtension->Self;
|
|
|
|
//
|
|
// Reset the keyboard.
|
|
//
|
|
StartOfReset:
|
|
status = I8xPutBytePolled(
|
|
(CCHAR) DataPort,
|
|
waitForAckOnReset,
|
|
(CCHAR) KeyboardDeviceType,
|
|
(UCHAR) KEYBOARD_RESET
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
SET_KB_INIT_FAILURE(KB_INIT_FAILED_RESET);
|
|
failedReset = TRUE;
|
|
failedResetStatus = status;
|
|
|
|
if (KeyboardExtension->FailedReset == FAILED_RESET_STOP) {
|
|
//
|
|
// If the device was reported, but not responding, it is phantom
|
|
//
|
|
status = STATUS_DEVICE_NOT_CONNECTED;
|
|
SET_HW_FLAGS(PHANTOM_KEYBOARD_HARDWARE_REPORTED);
|
|
Print(DBG_SS_INFO,
|
|
("kb failed reset Reset failed, stopping immediately\n"));
|
|
goto I8xInitializeKeyboardExit;
|
|
}
|
|
else {
|
|
//
|
|
// NOTE: The Gateway 4DX2/66V has a problem when an old Compaq 286
|
|
// keyboard is attached. In this case, the keyboard reset
|
|
// is not acknowledged (at least, the system never
|
|
// receives the ack). Instead, the KEYBOARD_COMPLETE_SUCCESS
|
|
// byte is sitting in the i8042 output buffer. The fix
|
|
// is to ignore the keyboard reset failure and continue.
|
|
//
|
|
/* do nothing */;
|
|
Print(DBG_SS_INFO, ("kb failed reset, proceeding\n"));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the keyboard reset self-test response. A response byte of
|
|
// KEYBOARD_COMPLETE_SUCCESS indicates success; KEYBOARD_COMPLETE_FAILURE
|
|
// indicates failure.
|
|
//
|
|
// Note that it is usually necessary to stall a long time to get the
|
|
// keyboard reset/self-test to work.
|
|
//
|
|
li.QuadPart = -100;
|
|
|
|
resetRespTimeout.QuadPart = 10*10*1000*1000;
|
|
KeQueryTickCount(&startOfSpin);
|
|
|
|
while (TRUE) {
|
|
status = I8xGetBytePolled(
|
|
(CCHAR) KeyboardDeviceType,
|
|
&byte
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
if (byte == (UCHAR) KEYBOARD_COMPLETE_SUCCESS) {
|
|
//
|
|
// The reset completed successfully.
|
|
//
|
|
break;
|
|
}
|
|
else {
|
|
//
|
|
// There was some sort of failure during the reset
|
|
// self-test. Continue anyway.
|
|
//
|
|
failedResetResponse = TRUE;
|
|
failedResetResponseStatus = status;
|
|
failedResetResponseByte = byte;
|
|
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
if (status == STATUS_IO_TIMEOUT) {
|
|
//
|
|
// Stall, and then try again to get a response from
|
|
// the reset.
|
|
//
|
|
KeDelayExecutionThread(KernelMode,
|
|
FALSE,
|
|
&li);
|
|
|
|
KeQueryTickCount(&nextQuery);
|
|
|
|
difference.QuadPart = nextQuery.QuadPart - startOfSpin.QuadPart;
|
|
|
|
ASSERT(KeQueryTimeIncrement() <= MAXLONG);
|
|
if (difference.QuadPart*KeQueryTimeIncrement() >=
|
|
resetRespTimeout.QuadPart) {
|
|
Print(DBG_SS_ERROR, ("no reset response, quitting\n"));
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
if (waitForAckOnReset == WAIT_FOR_ACKNOWLEDGE) {
|
|
waitForAckOnReset = NO_WAIT_FOR_ACKNOWLEDGE;
|
|
goto StartOfReset;
|
|
}
|
|
|
|
failedResetResponse2 = TRUE;
|
|
failedResetResponseStatus2 = status;
|
|
failedResetResponseByte2 = byte;
|
|
|
|
goto I8xInitializeKeyboardExit;
|
|
}
|
|
|
|
//
|
|
// Turn off Keyboard Translate Mode. Call I8xTransmitControllerCommand
|
|
// to read the Controller Command Byte, modify the appropriate bits, and
|
|
// rewrite the Controller Command Byte.
|
|
//
|
|
transmitCCBContext.HardwareDisableEnableMask = 0;
|
|
transmitCCBContext.AndOperation = AND_OPERATION;
|
|
transmitCCBContext.ByteMask = (UCHAR) ~((UCHAR)CCB_KEYBOARD_TRANSLATE_MODE);
|
|
|
|
I8xTransmitControllerCommand(
|
|
(PVOID) &transmitCCBContext
|
|
);
|
|
|
|
if (!NT_SUCCESS(transmitCCBContext.Status)) {
|
|
//
|
|
// If failure then retry once. This is for Toshiba T3400CT.
|
|
//
|
|
I8xTransmitControllerCommand(
|
|
(PVOID) &transmitCCBContext
|
|
);
|
|
}
|
|
|
|
if (!NT_SUCCESS(transmitCCBContext.Status)) {
|
|
Print(DBG_SS_ERROR,
|
|
("I8xInitializeKeyboard: could not turn off translate\n"
|
|
));
|
|
status = transmitCCBContext.Status;
|
|
SET_KB_INIT_FAILURE(KB_INIT_FAILED_XLATE_OFF);
|
|
goto I8xInitializeKeyboardExit;
|
|
}
|
|
|
|
//
|
|
// Get a pointer to the keyboard identifier field.
|
|
//
|
|
|
|
id = &deviceExtension->KeyboardAttributes.KeyboardIdentifier;
|
|
|
|
//
|
|
// Set the typematic rate and delay. Send the Set Typematic Rate command
|
|
// to the keyboard, followed by the typematic rate/delay parameter byte.
|
|
// Note that it is often necessary to stall a long time to get this
|
|
// to work. The stall value was determined by experimentation. Some
|
|
// broken hardware does not accept this command, so ignore errors in the
|
|
// hope that the keyboard will work okay anyway.
|
|
//
|
|
//
|
|
|
|
if ((status = I8xPutBytePolled(
|
|
(CCHAR) DataPort,
|
|
WAIT_FOR_ACKNOWLEDGE,
|
|
(CCHAR) KeyboardDeviceType,
|
|
(UCHAR) SET_KEYBOARD_TYPEMATIC
|
|
)) != STATUS_SUCCESS) {
|
|
|
|
Print(DBG_SS_INFO, ("kb set typematic failed\n"));
|
|
|
|
SET_KB_INIT_FAILURE(KB_INIT_FAILED_SET_TYPEMATIC);
|
|
failedTypematic = TRUE;
|
|
failedTypematicStatus = status;
|
|
|
|
} else if ((status = I8xPutBytePolled(
|
|
(CCHAR) DataPort,
|
|
WAIT_FOR_ACKNOWLEDGE,
|
|
(CCHAR) KeyboardDeviceType,
|
|
I8xConvertTypematicParameters(
|
|
deviceExtension->KeyRepeatCurrent.Rate,
|
|
deviceExtension->KeyRepeatCurrent.Delay
|
|
))) != STATUS_SUCCESS) {
|
|
|
|
SET_KB_INIT_FAILURE(KB_INIT_FAILED_SET_TYPEMATIC_PARAM);
|
|
Print(DBG_SS_ERROR,
|
|
("I8xInitializeKeyboard: could not send typematic param\n"
|
|
));
|
|
|
|
//
|
|
// Log an error.
|
|
//
|
|
|
|
dumpData[0] = KBDMOU_COULD_NOT_SEND_PARAM;
|
|
dumpData[1] = DataPort;
|
|
dumpData[2] = SET_KEYBOARD_TYPEMATIC;
|
|
dumpData[3] =
|
|
I8xConvertTypematicParameters(
|
|
deviceExtension->KeyRepeatCurrent.Rate,
|
|
deviceExtension->KeyRepeatCurrent.Delay
|
|
);
|
|
|
|
I8xLogError(
|
|
deviceObject,
|
|
I8042_SET_TYPEMATIC_FAILED,
|
|
I8042_ERROR_VALUE_BASE + 540,
|
|
status,
|
|
dumpData,
|
|
4
|
|
);
|
|
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Set the keyboard indicator lights. Ignore errors.
|
|
//
|
|
|
|
if ((status = I8xPutBytePolled(
|
|
(CCHAR) DataPort,
|
|
WAIT_FOR_ACKNOWLEDGE,
|
|
(CCHAR) KeyboardDeviceType,
|
|
(UCHAR) SET_KEYBOARD_INDICATORS
|
|
)) != STATUS_SUCCESS) {
|
|
|
|
Print(DBG_SS_INFO, ("kb set LEDs failed\n"));
|
|
|
|
SET_KB_INIT_FAILURE(KB_INIT_FAILED_SET_LEDS);
|
|
failedLeds = TRUE;
|
|
failedLedsStatus = status;
|
|
|
|
} else if ((status = I8xPutBytePolled(
|
|
(CCHAR) DataPort,
|
|
WAIT_FOR_ACKNOWLEDGE,
|
|
(CCHAR) KeyboardDeviceType,
|
|
(UCHAR) deviceExtension->KeyboardIndicators.LedFlags
|
|
)) != STATUS_SUCCESS) {
|
|
|
|
SET_KB_INIT_FAILURE(KB_INIT_FAILED_SET_LEDS_PARAM);
|
|
|
|
Print(DBG_SS_ERROR,
|
|
("I8xInitializeKeyboard: could not send SET LEDS param\n"
|
|
));
|
|
|
|
//
|
|
// Log an error.
|
|
//
|
|
|
|
dumpData[0] = KBDMOU_COULD_NOT_SEND_PARAM;
|
|
dumpData[1] = DataPort;
|
|
dumpData[2] = SET_KEYBOARD_INDICATORS;
|
|
dumpData[3] =
|
|
deviceExtension->KeyboardIndicators.LedFlags;
|
|
|
|
I8xLogError(
|
|
deviceObject,
|
|
I8042_SET_LED_FAILED,
|
|
I8042_ERROR_VALUE_BASE + 550,
|
|
status,
|
|
dumpData,
|
|
4
|
|
);
|
|
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
#if !(defined(_X86_) || defined(_IA64_) || defined(_PPC_)) // IBMCPK: MIPS specific initialization
|
|
|
|
//
|
|
// NOTE: This code is necessary until the MIPS firmware stops
|
|
// selecting scan code set 3. Select scan code set 2 here.
|
|
// Since the translate bit is set, the net effect is that
|
|
// we will receive scan code set 1 bytes.
|
|
//
|
|
|
|
if (ENHANCED_KEYBOARD(*id)) {
|
|
status = I8xPutBytePolled(
|
|
(CCHAR) DataPort,
|
|
WAIT_FOR_ACKNOWLEDGE,
|
|
(CCHAR) KeyboardDeviceType,
|
|
(UCHAR) SELECT_SCAN_CODE_SET
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Send the associated parameter byte.
|
|
//
|
|
|
|
status = I8xPutBytePolled(
|
|
(CCHAR) DataPort,
|
|
WAIT_FOR_ACKNOWLEDGE,
|
|
(CCHAR) KeyboardDeviceType,
|
|
(UCHAR) 2
|
|
);
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
Print(DBG_SS_ERROR,
|
|
("I8xInitializeKeyboard: could not send Select Scan command\n"
|
|
));
|
|
|
|
//
|
|
// This failed so probably what we have here isn't an enhanced
|
|
// keyboard at all. Make this an old style keyboard.
|
|
//
|
|
|
|
configuration = &Globals.ControllerData->Configuration;
|
|
keyboardId = &deviceExtension->KeyboardAttributes.KeyboardIdentifier;
|
|
|
|
keyboardId->Type = 3;
|
|
|
|
deviceExtension->KeyboardAttributes.NumberOfFunctionKeys =
|
|
KeyboardTypeInformation[keyboardId->Type - 1].NumberOfFunctionKeys;
|
|
deviceExtension->KeyboardAttributes.NumberOfIndicators =
|
|
KeyboardTypeInformation[keyboardId->Type - 1].NumberOfIndicators;
|
|
deviceExtension->KeyboardAttributes.NumberOfKeysTotal =
|
|
KeyboardTypeInformation[keyboardId->Type - 1].NumberOfKeysTotal;
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(FE_SB)
|
|
|
|
if (IBM02_KEYBOARD(*id)) {
|
|
|
|
//
|
|
// IBM-J 5576-002 Keyboard should set local scan code set for
|
|
// supplied NLS key.
|
|
//
|
|
|
|
status = I8xPutBytePolled(
|
|
(CCHAR) DataPort,
|
|
WAIT_FOR_ACKNOWLEDGE,
|
|
(CCHAR) KeyboardDeviceType,
|
|
(UCHAR) SELECT_SCAN_CODE_SET
|
|
);
|
|
if (status != STATUS_SUCCESS) {
|
|
Print(DBG_SS_ERROR,
|
|
("I8xInitializeKeyboard: could not send Select Scan command\n"
|
|
));
|
|
Print(DBG_SS_ERROR,
|
|
("I8xInitializeKeyboard: WARNING - using scan set 82h\n"
|
|
));
|
|
deviceExtension->KeyboardAttributes.KeyboardMode = 3;
|
|
} else {
|
|
|
|
//
|
|
// Send the associated parameter byte.
|
|
//
|
|
|
|
status = I8xPutBytePolled(
|
|
(CCHAR) DataPort,
|
|
WAIT_FOR_ACKNOWLEDGE,
|
|
(CCHAR) KeyboardDeviceType,
|
|
(UCHAR) 0x82
|
|
);
|
|
if (status != STATUS_SUCCESS) {
|
|
Print(DBG_SS_ERROR,
|
|
("I8xInitializeKeyboard: could not send Select Scan param\n"
|
|
));
|
|
Print(DBG_SS_ERROR,
|
|
("I8xInitializeKeyboard: WARNING - using scan set 82h\n"
|
|
));
|
|
deviceExtension->KeyboardAttributes.KeyboardMode = 3;
|
|
}
|
|
}
|
|
}
|
|
#endif // FE_SB
|
|
|
|
if (deviceExtension->InitializationHookCallback) {
|
|
(*deviceExtension->InitializationHookCallback) (
|
|
deviceExtension->HookContext,
|
|
(PVOID) deviceObject,
|
|
(PI8042_SYNCH_READ_PORT) I8xKeyboardSynchReadPort,
|
|
(PI8042_SYNCH_WRITE_PORT) I8xKeyboardSynchWritePort,
|
|
&translationOn
|
|
);
|
|
}
|
|
|
|
if (deviceExtension->KeyboardAttributes.KeyboardMode == 1 &&
|
|
translationOn) {
|
|
|
|
//
|
|
// Turn translate back on. The keyboard should, by default, send
|
|
// scan code set 2. When the translate bit in the 8042 command byte
|
|
// is on, the 8042 translates the scan code set 2 bytes to scan code
|
|
// set 1 before sending them to the CPU. Scan code set 1 is
|
|
// the industry standard scan code set.
|
|
//
|
|
// N.B. It does not appear to be possible to change the translate
|
|
// bit on some models of PS/2.
|
|
//
|
|
|
|
transmitCCBContext.HardwareDisableEnableMask = 0;
|
|
transmitCCBContext.AndOperation = OR_OPERATION;
|
|
transmitCCBContext.ByteMask = (UCHAR) CCB_KEYBOARD_TRANSLATE_MODE;
|
|
|
|
I8xTransmitControllerCommand(
|
|
(PVOID) &transmitCCBContext
|
|
);
|
|
|
|
if (!NT_SUCCESS(transmitCCBContext.Status)) {
|
|
SET_KB_INIT_FAILURE(KB_INIT_FAILED_XLATE_ON);
|
|
Print(DBG_SS_ERROR,
|
|
("I8xInitializeKeyboard: couldn't turn on translate\n"
|
|
));
|
|
|
|
if (transmitCCBContext.Status == STATUS_DEVICE_DATA_ERROR) {
|
|
|
|
//
|
|
// Could not turn translate back on. This happens on some
|
|
// PS/2 machines. In this case, select scan code set 1
|
|
// for the keyboard, since the 8042 will not do the
|
|
// translation from the scan code set 2, which is what the
|
|
// KEYBOARD_RESET caused the keyboard to default to.
|
|
//
|
|
|
|
if (ENHANCED_KEYBOARD(*id)) {
|
|
status = I8xPutBytePolled(
|
|
(CCHAR) DataPort,
|
|
WAIT_FOR_ACKNOWLEDGE,
|
|
(CCHAR) KeyboardDeviceType,
|
|
(UCHAR) SELECT_SCAN_CODE_SET
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
SET_KB_INIT_FAILURE(KB_INIT_FAILED_SELECT_SS);
|
|
Print(DBG_SS_ERROR,
|
|
("I8xInitializeKeyboard: could not send Select Scan command\n"
|
|
));
|
|
Print(DBG_SS_ERROR,
|
|
("I8xInitializeKeyboard: WARNING - using scan set 2\n"
|
|
));
|
|
deviceExtension->KeyboardAttributes.KeyboardMode = 2;
|
|
//
|
|
// Log an error.
|
|
//
|
|
|
|
dumpData[0] = KBDMOU_COULD_NOT_SEND_COMMAND;
|
|
dumpData[1] = DataPort;
|
|
dumpData[2] = SELECT_SCAN_CODE_SET;
|
|
|
|
I8xLogError(
|
|
deviceObject,
|
|
I8042_SELECT_SCANSET_FAILED,
|
|
I8042_ERROR_VALUE_BASE + 555,
|
|
status,
|
|
dumpData,
|
|
3
|
|
);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Send the associated parameter byte.
|
|
//
|
|
|
|
status = I8xPutBytePolled(
|
|
(CCHAR) DataPort,
|
|
WAIT_FOR_ACKNOWLEDGE,
|
|
(CCHAR) KeyboardDeviceType,
|
|
#ifdef FE_SB // I8xInitializeKeyboard()
|
|
(UCHAR) (IBM02_KEYBOARD(*id) ? 0x81 : 1 )
|
|
#else
|
|
(UCHAR) 1
|
|
#endif // FE_SB
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
SET_KB_INIT_FAILURE(KB_INIT_FAILED_SELECT_SS_PARAM);
|
|
Print(DBG_SS_ERROR,
|
|
("I8xInitializeKeyboard: could not send Select Scan param\n"
|
|
));
|
|
Print(DBG_SS_ERROR,
|
|
("I8xInitializeKeyboard: WARNING - using scan set 2\n"
|
|
));
|
|
deviceExtension->KeyboardAttributes.KeyboardMode = 2;
|
|
//
|
|
// Log an error.
|
|
//
|
|
|
|
dumpData[0] = KBDMOU_COULD_NOT_SEND_PARAM;
|
|
dumpData[1] = DataPort;
|
|
dumpData[2] = SELECT_SCAN_CODE_SET;
|
|
dumpData[3] = 1;
|
|
|
|
I8xLogError(
|
|
deviceObject,
|
|
I8042_SELECT_SCANSET_FAILED,
|
|
I8042_ERROR_VALUE_BASE + 560,
|
|
status,
|
|
dumpData,
|
|
4
|
|
);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
status = transmitCCBContext.Status;
|
|
goto I8xInitializeKeyboardExit;
|
|
}
|
|
}
|
|
}
|
|
|
|
I8xInitializeKeyboardExit:
|
|
|
|
//
|
|
// If all 3 of these have failed, then we have a device that was reported
|
|
// present but is not plugged in. This usually happens on either an ACPI
|
|
// enabled machine (where it always reports the PS/2 kbd and mouse present)
|
|
// or on a machine which has legacy HID support (where the reported PS/2
|
|
// device(s) are really USB HID).
|
|
//
|
|
// If this is the case, then we will succeed the start and hide the device
|
|
// in the UI
|
|
//
|
|
if (failedReset && failedTypematic && failedLeds) {
|
|
if (KeyboardExtension->FailedReset == FAILED_RESET_PROCEED) {
|
|
OBJECT_ATTRIBUTES oa;
|
|
UNICODE_STRING string;
|
|
HANDLE hService, hParameters;
|
|
|
|
InitializeObjectAttributes(&oa,
|
|
&Globals.RegistryPath,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
(PSECURITY_DESCRIPTOR) NULL);
|
|
|
|
if (NT_SUCCESS(ZwOpenKey(&hService, KEY_ALL_ACCESS, &oa))) {
|
|
RtlInitUnicodeString(&string, L"Parameters");
|
|
|
|
InitializeObjectAttributes(&oa,
|
|
&string,
|
|
OBJ_CASE_INSENSITIVE,
|
|
hService,
|
|
(PSECURITY_DESCRIPTOR) NULL);
|
|
|
|
if (NT_SUCCESS(ZwOpenKey(&hParameters, KEY_ALL_ACCESS, &oa))) {
|
|
ULONG tmp;
|
|
|
|
RtlInitUnicodeString (&string, STR_FAILED_RESET);
|
|
tmp = FAILED_RESET_STOP;
|
|
|
|
Print(DBG_SS_INFO | DBG_SS_ERROR,
|
|
("Future failed kbd resets will stop init\n"));
|
|
|
|
ZwSetValueKey(hParameters,
|
|
&string,
|
|
0,
|
|
REG_DWORD,
|
|
&tmp,
|
|
sizeof(tmp));
|
|
|
|
ZwClose(hParameters);
|
|
}
|
|
|
|
ZwClose(hService);
|
|
}
|
|
}
|
|
|
|
Print(DBG_SS_INFO,
|
|
("kb, all 3 sets failed, assuming a phantom keyboard\n"));
|
|
|
|
status = STATUS_DEVICE_NOT_CONNECTED;
|
|
// errorCode = I8042_NO_KBD_DEVICE;
|
|
|
|
SET_HW_FLAGS(PHANTOM_KEYBOARD_HARDWARE_REPORTED);
|
|
|
|
if (Globals.ReportResetErrors) {
|
|
I8xLogError(deviceObject,
|
|
I8042_NO_KBD_DEVICE,
|
|
0,
|
|
status,
|
|
NULL,
|
|
0
|
|
);
|
|
}
|
|
}
|
|
else {
|
|
if (failedReset) {
|
|
Print(DBG_SS_ERROR,
|
|
("I8xInitializeKeyboard: failed keyboard reset, status 0x%x\n",
|
|
status
|
|
));
|
|
|
|
if (Globals.ReportResetErrors) {
|
|
dumpData[0] = KBDMOU_COULD_NOT_SEND_COMMAND;
|
|
dumpData[1] = DataPort;
|
|
dumpData[2] = KEYBOARD_RESET;
|
|
|
|
I8xLogError(deviceObject,
|
|
I8042_KBD_RESET_COMMAND_FAILED,
|
|
I8042_ERROR_VALUE_BASE + 510,
|
|
failedResetStatus,
|
|
dumpData,
|
|
3
|
|
);
|
|
}
|
|
}
|
|
|
|
if (failedResetResponse2) {
|
|
Print(DBG_SS_ERROR,
|
|
("I8xInitializeKeyboard, failed reset response, status 0x%x, byte 0x%x\n",
|
|
status,
|
|
byte
|
|
));
|
|
|
|
//
|
|
// Log a warning.
|
|
//
|
|
dumpData[0] = KBDMOU_INCORRECT_RESPONSE;
|
|
dumpData[1] = KeyboardDeviceType;
|
|
dumpData[2] = KEYBOARD_COMPLETE_SUCCESS;
|
|
dumpData[3] = failedResetResponse2;
|
|
|
|
I8xLogError(
|
|
deviceObject,
|
|
I8042_KBD_RESET_RESPONSE_FAILED,
|
|
I8042_ERROR_VALUE_BASE + 520,
|
|
failedResetResponseStatus2,
|
|
dumpData,
|
|
4
|
|
);
|
|
}
|
|
else if (failedResetResponse) {
|
|
Print(DBG_SS_ERROR,
|
|
("kb failed reset response\n")
|
|
);
|
|
|
|
//
|
|
// Log a warning.
|
|
//
|
|
dumpData[0] = KBDMOU_INCORRECT_RESPONSE;
|
|
dumpData[1] = KeyboardDeviceType;
|
|
dumpData[2] = KEYBOARD_COMPLETE_SUCCESS;
|
|
dumpData[3] = failedResetResponseByte;
|
|
|
|
I8xLogError(
|
|
deviceObject,
|
|
I8042_KBD_RESET_RESPONSE_FAILED,
|
|
I8042_ERROR_VALUE_BASE + 515,
|
|
failedResetResponseStatus,
|
|
dumpData,
|
|
4
|
|
);
|
|
}
|
|
|
|
if (failedTypematic) {
|
|
Print(DBG_SS_ERROR,
|
|
("I8xInitializeKeyboard: could not send SET TYPEMATIC cmd\n"
|
|
));
|
|
|
|
//
|
|
// Log an error.
|
|
//
|
|
dumpData[0] = KBDMOU_COULD_NOT_SEND_COMMAND;
|
|
dumpData[1] = DataPort;
|
|
dumpData[2] = SET_KEYBOARD_TYPEMATIC;
|
|
|
|
I8xLogError(
|
|
deviceObject,
|
|
I8042_SET_TYPEMATIC_FAILED,
|
|
I8042_ERROR_VALUE_BASE + 535,
|
|
failedTypematicStatus,
|
|
dumpData,
|
|
3
|
|
);
|
|
}
|
|
|
|
if (failedLeds) {
|
|
Print(DBG_SS_ERROR,
|
|
("I8xInitializeKeyboard: could not send SET LEDS cmd\n"
|
|
));
|
|
|
|
//
|
|
// Log an error.
|
|
//
|
|
|
|
dumpData[0] = KBDMOU_COULD_NOT_SEND_COMMAND;
|
|
dumpData[1] = DataPort;
|
|
dumpData[2] = SET_KEYBOARD_INDICATORS;
|
|
|
|
I8xLogError(
|
|
deviceObject,
|
|
I8042_SET_LED_FAILED,
|
|
I8042_ERROR_VALUE_BASE + 545,
|
|
failedLedsStatus,
|
|
dumpData,
|
|
3
|
|
);
|
|
}
|
|
}
|
|
|
|
if (DEVICE_START_SUCCESS(status)) {
|
|
SET_HW_FLAGS(KEYBOARD_HARDWARE_PRESENT |
|
|
KEYBOARD_HARDWARE_INITIALIZED);
|
|
}
|
|
else {
|
|
CLEAR_KEYBOARD_PRESENT();
|
|
}
|
|
|
|
//
|
|
// Initialize current keyboard set packet state.
|
|
//
|
|
deviceExtension->CurrentOutput.State = Idle;
|
|
deviceExtension->CurrentOutput.Bytes = NULL;
|
|
deviceExtension->CurrentOutput.ByteCount = 0;
|
|
|
|
Print(DBG_SS_TRACE, ("I8xInitializeKeyboard (0x%x)\n", status));
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
I8xKeyboardConfiguration(
|
|
IN PPORT_KEYBOARD_EXTENSION KeyboardExtension,
|
|
IN PCM_RESOURCE_LIST ResourceList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retrieves the configuration information for the keyboard.
|
|
|
|
Arguments:
|
|
|
|
KeyboardExtension - Keyboard extension
|
|
|
|
ResourceList - Translated resource list give to us via the start IRP
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if all the resources required are presented
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
PCM_PARTIAL_RESOURCE_LIST partialResList = NULL;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR firstResDesc = NULL,
|
|
currentResDesc = NULL;
|
|
PCM_FULL_RESOURCE_DESCRIPTOR fullResDesc = NULL;
|
|
PI8042_CONFIGURATION_INFORMATION configuration;
|
|
|
|
PKEYBOARD_ID keyboardId;
|
|
|
|
ULONG count,
|
|
i;
|
|
|
|
KINTERRUPT_MODE defaultInterruptMode;
|
|
BOOLEAN defaultInterruptShare;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (!ResourceList) {
|
|
Print(DBG_SS_INFO | DBG_SS_ERROR, ("keyboard with null resources\n"));
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
fullResDesc = ResourceList->List;
|
|
if (!fullResDesc) {
|
|
//
|
|
// this should never happen
|
|
//
|
|
ASSERT(fullResDesc != NULL);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
configuration = &Globals.ControllerData->Configuration;
|
|
|
|
partialResList = &fullResDesc->PartialResourceList;
|
|
currentResDesc = firstResDesc = partialResList->PartialDescriptors;
|
|
count = partialResList->Count;
|
|
|
|
configuration->FloatingSave = I8042_FLOATING_SAVE;
|
|
configuration->BusNumber = fullResDesc->BusNumber;
|
|
configuration->InterfaceType = fullResDesc->InterfaceType;
|
|
|
|
if (configuration->InterfaceType == MicroChannel) {
|
|
defaultInterruptShare = TRUE;
|
|
defaultInterruptMode = LevelSensitive;
|
|
}
|
|
else {
|
|
defaultInterruptShare = I8042_INTERRUPT_SHARE;
|
|
defaultInterruptMode = I8042_INTERRUPT_MODE;
|
|
}
|
|
|
|
for (i = 0; i < count; i++, currentResDesc++) {
|
|
switch (currentResDesc->Type) {
|
|
case CmResourceTypeMemory:
|
|
Globals.RegistersMapped = TRUE;
|
|
|
|
case CmResourceTypePort:
|
|
//
|
|
// Copy the port information. We will sort the port list
|
|
// into ascending order based on the starting port address
|
|
// later (note that we *know* there are a max of two port
|
|
// ranges for the i8042).
|
|
//
|
|
#if 0
|
|
if (currentResDesc->Flags == CM_RESOURCE_PORT_MEMORY) {
|
|
Globals.RegistersMapped = TRUE;
|
|
}
|
|
#endif
|
|
|
|
Print(DBG_SS_NOISE,
|
|
("port is %s.\n",
|
|
Globals.RegistersMapped ? "memory" : "io"));
|
|
|
|
if (configuration->PortListCount < MaximumPortCount) {
|
|
configuration->PortList[configuration->PortListCount] =
|
|
*currentResDesc;
|
|
configuration->PortList[configuration->PortListCount].ShareDisposition =
|
|
I8042_REGISTER_SHARE ? CmResourceShareShared:
|
|
CmResourceShareDriverExclusive;
|
|
configuration->PortListCount += 1;
|
|
}
|
|
else {
|
|
Print(DBG_SS_INFO | DBG_SS_ERROR,
|
|
("KB::PortListCount already at max (%d)\n",
|
|
configuration->PortListCount
|
|
)
|
|
);
|
|
}
|
|
break;
|
|
|
|
case CmResourceTypeInterrupt:
|
|
|
|
//
|
|
// Copy the interrupt information.
|
|
//
|
|
KeyboardExtension->InterruptDescriptor = *currentResDesc;
|
|
KeyboardExtension->InterruptDescriptor.ShareDisposition =
|
|
defaultInterruptShare ? CmResourceShareShared :
|
|
CmResourceShareDeviceExclusive;
|
|
|
|
break;
|
|
|
|
default:
|
|
Print(DBG_ALWAYS,
|
|
("resource type 0x%x unhandled...\n",
|
|
(LONG) currentResDesc->Type
|
|
));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (KeyboardExtension->InterruptDescriptor.Type & CmResourceTypeInterrupt) {
|
|
Print(DBG_SS_INFO,
|
|
("Keyboard interrupt config --\n"
|
|
" %s, %s, Irq = 0x%x\n",
|
|
KeyboardExtension->InterruptDescriptor.ShareDisposition ==
|
|
CmResourceShareShared ? "Sharable" : "NonSharable",
|
|
KeyboardExtension->InterruptDescriptor.Flags ==
|
|
CM_RESOURCE_INTERRUPT_LATCHED ? "Latched" : "Level Sensitive",
|
|
KeyboardExtension->InterruptDescriptor.u.Interrupt.Vector
|
|
));
|
|
}
|
|
//
|
|
// If no keyboard-specific information (i.e., keyboard type, subtype,
|
|
// and initial LED settings) was found, use the keyboard driver
|
|
// defaults.
|
|
//
|
|
if (KeyboardExtension->KeyboardAttributes.KeyboardIdentifier.Type == 0) {
|
|
|
|
Print(DBG_SS_INFO, ("Using default keyboard type\n"));
|
|
|
|
KeyboardExtension->KeyboardAttributes.KeyboardIdentifier.Type =
|
|
KEYBOARD_TYPE_DEFAULT;
|
|
KeyboardExtension->KeyboardIndicators.LedFlags =
|
|
KEYBOARD_INDICATORS_DEFAULT;
|
|
|
|
KeyboardExtension->KeyboardIdentifierEx.Type = KEYBOARD_TYPE_DEFAULT;
|
|
}
|
|
|
|
Print(DBG_SS_INFO,
|
|
("Keyboard device specific data --\n"
|
|
" Type = %d, Subtype = %d, Initial LEDs = 0x%x\n",
|
|
KeyboardExtension->KeyboardAttributes.KeyboardIdentifier.Type,
|
|
KeyboardExtension->KeyboardAttributes.KeyboardIdentifier.Subtype,
|
|
KeyboardExtension->KeyboardIndicators.LedFlags
|
|
));
|
|
|
|
keyboardId = &KeyboardExtension->KeyboardAttributes.KeyboardIdentifier;
|
|
if (!ENHANCED_KEYBOARD(*keyboardId)) {
|
|
Print(DBG_SS_INFO, ("Old AT-style keyboard\n"));
|
|
configuration->PollingIterations =
|
|
configuration->PollingIterationsMaximum;
|
|
}
|
|
|
|
//
|
|
// Initialize keyboard-specific configuration parameters.
|
|
//
|
|
|
|
if (FAREAST_KEYBOARD(*keyboardId)) {
|
|
ULONG iIndex = 0;
|
|
PKEYBOARD_TYPE_INFORMATION pKeyboardTypeInformation = NULL;
|
|
|
|
while (KeyboardFarEastOemInformation[iIndex].KeyboardId.Type) {
|
|
if ((KeyboardFarEastOemInformation[iIndex].KeyboardId.Type
|
|
== keyboardId->Type) &&
|
|
(KeyboardFarEastOemInformation[iIndex].KeyboardId.Subtype
|
|
== keyboardId->Subtype)) {
|
|
|
|
pKeyboardTypeInformation = (PKEYBOARD_TYPE_INFORMATION)
|
|
&(KeyboardFarEastOemInformation[iIndex].KeyboardTypeInformation);
|
|
break;
|
|
}
|
|
|
|
iIndex++;
|
|
}
|
|
|
|
if (pKeyboardTypeInformation == NULL) {
|
|
|
|
//
|
|
// Set default...
|
|
//
|
|
|
|
pKeyboardTypeInformation = (PKEYBOARD_TYPE_INFORMATION)
|
|
&(KeyboardTypeInformation[KEYBOARD_TYPE_DEFAULT-1]);
|
|
}
|
|
|
|
KeyboardExtension->KeyboardAttributes.NumberOfFunctionKeys =
|
|
pKeyboardTypeInformation->NumberOfFunctionKeys;
|
|
KeyboardExtension->KeyboardAttributes.NumberOfIndicators =
|
|
pKeyboardTypeInformation->NumberOfIndicators;
|
|
KeyboardExtension->KeyboardAttributes.NumberOfKeysTotal =
|
|
pKeyboardTypeInformation->NumberOfKeysTotal;
|
|
}
|
|
else {
|
|
KeyboardExtension->KeyboardAttributes.NumberOfFunctionKeys =
|
|
KeyboardTypeInformation[keyboardId->Type - 1].NumberOfFunctionKeys;
|
|
KeyboardExtension->KeyboardAttributes.NumberOfIndicators =
|
|
KeyboardTypeInformation[keyboardId->Type - 1].NumberOfIndicators;
|
|
KeyboardExtension->KeyboardAttributes.NumberOfKeysTotal =
|
|
KeyboardTypeInformation[keyboardId->Type - 1].NumberOfKeysTotal;
|
|
}
|
|
|
|
KeyboardExtension->KeyboardAttributes.KeyboardMode =
|
|
KEYBOARD_SCAN_CODE_SET;
|
|
|
|
KeyboardExtension->KeyboardAttributes.KeyRepeatMinimum.Rate =
|
|
KEYBOARD_TYPEMATIC_RATE_MINIMUM;
|
|
KeyboardExtension->KeyboardAttributes.KeyRepeatMinimum.Delay =
|
|
KEYBOARD_TYPEMATIC_DELAY_MINIMUM;
|
|
KeyboardExtension->KeyboardAttributes.KeyRepeatMaximum.Rate =
|
|
KEYBOARD_TYPEMATIC_RATE_MAXIMUM;
|
|
KeyboardExtension->KeyboardAttributes.KeyRepeatMaximum.Delay =
|
|
KEYBOARD_TYPEMATIC_DELAY_MAXIMUM;
|
|
KeyboardExtension->KeyRepeatCurrent.Rate =
|
|
KEYBOARD_TYPEMATIC_RATE_DEFAULT;
|
|
KeyboardExtension->KeyRepeatCurrent.Delay =
|
|
KEYBOARD_TYPEMATIC_DELAY_DEFAULT;
|
|
|
|
return status;
|
|
}
|
|
|
|
#if defined(_X86_)
|
|
ULONG
|
|
I8042ConversionStatusForOasys(
|
|
IN ULONG fOpen,
|
|
IN ULONG ConvStatus)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine convert ime open/close status and ime converion mode to
|
|
FMV oyayubi-shift keyboard device internal input mode.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
FMV oyayubi-shift keyboard's internal input mode.
|
|
|
|
--*/
|
|
{
|
|
ULONG ImeMode = 0;
|
|
|
|
if (fOpen) {
|
|
if (ConvStatus & IME_CMODE_ROMAN) {
|
|
if (ConvStatus & IME_CMODE_ALPHANUMERIC) {
|
|
//
|
|
// Alphanumeric, roman mode.
|
|
//
|
|
ImeMode = THUMB_ROMAN_ALPHA_CAPSON;
|
|
} else if (ConvStatus & IME_CMODE_KATAKANA) {
|
|
//
|
|
// Katakana, roman mode.
|
|
//
|
|
ImeMode = THUMB_ROMAN_KATAKANA;
|
|
} else if (ConvStatus & IME_CMODE_NATIVE) {
|
|
//
|
|
// Hiragana, roman mode.
|
|
//
|
|
ImeMode = THUMB_ROMAN_HIRAGANA;
|
|
} else {
|
|
ImeMode = THUMB_ROMAN_ALPHA_CAPSON;
|
|
}
|
|
} else {
|
|
if (ConvStatus & IME_CMODE_ALPHANUMERIC) {
|
|
//
|
|
// Alphanumeric, no-roman mode.
|
|
//
|
|
ImeMode = THUMB_NOROMAN_ALPHA_CAPSON;
|
|
} else if (ConvStatus & IME_CMODE_KATAKANA) {
|
|
//
|
|
// Katakana, no-roman mode.
|
|
//
|
|
ImeMode = THUMB_NOROMAN_KATAKANA;
|
|
} else if (ConvStatus & IME_CMODE_NATIVE) {
|
|
//
|
|
// Hiragana, no-roman mode.
|
|
//
|
|
ImeMode = THUMB_NOROMAN_HIRAGANA;
|
|
} else {
|
|
ImeMode = THUMB_NOROMAN_ALPHA_CAPSON;
|
|
}
|
|
}
|
|
} else {
|
|
//
|
|
// Ime close. In this case, internal mode is always this value.
|
|
// (the both LED off roman and kana)
|
|
//
|
|
ImeMode = THUMB_NOROMAN_ALPHA_CAPSON;
|
|
}
|
|
|
|
return ImeMode;
|
|
}
|
|
|
|
ULONG
|
|
I8042QueryIMEStatusForOasys(
|
|
IN PKEYBOARD_IME_STATUS KeyboardIMEStatus
|
|
)
|
|
{
|
|
ULONG InternalMode;
|
|
|
|
//
|
|
// Map to IME mode to hardware mode.
|
|
//
|
|
InternalMode = I8042ConversionStatusForOasys(
|
|
KeyboardIMEStatus->ImeOpen,
|
|
KeyboardIMEStatus->ImeConvMode
|
|
);
|
|
|
|
return InternalMode;
|
|
}
|
|
|
|
NTSTATUS
|
|
I8042SetIMEStatusForOasys(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN OUT PINITIATE_OUTPUT_CONTEXT InitiateContext
|
|
)
|
|
{
|
|
PKEYBOARD_IME_STATUS KeyboardIMEStatus;
|
|
PPORT_KEYBOARD_EXTENSION kbExtension;
|
|
ULONG InternalMode;
|
|
LARGE_INTEGER deltaTime;
|
|
|
|
kbExtension = DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// Get pointer to KEYBOARD_IME_STATUS buffer.
|
|
//
|
|
KeyboardIMEStatus = (PKEYBOARD_IME_STATUS)(Irp->AssociatedIrp.SystemBuffer);
|
|
|
|
//
|
|
// Map IME mode to keyboard hardware mode.
|
|
//
|
|
InternalMode = I8042QueryIMEStatusForOasys(KeyboardIMEStatus);
|
|
|
|
//
|
|
// Set up the context structure for the InitiateIo wrapper.
|
|
//
|
|
InitiateContext->Bytes = Globals.ControllerData->DefaultBuffer;
|
|
InitiateContext->DeviceObject = DeviceObject;
|
|
InitiateContext->ByteCount = 3;
|
|
InitiateContext->Bytes[0] = 0xF0;
|
|
InitiateContext->Bytes[1] = 0x8C;
|
|
InitiateContext->Bytes[2] = (UCHAR)InternalMode;
|
|
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
#endif // defined(_X86_)
|
|
|
|
VOID
|
|
I8xQueueCurrentKeyboardInput(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine queues the current input data to be processed by a
|
|
DPC outside the ISR
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PPORT_KEYBOARD_EXTENSION deviceExtension;
|
|
|
|
deviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
if (deviceExtension->EnableCount) {
|
|
|
|
if (!I8xWriteDataToKeyboardQueue(
|
|
deviceExtension,
|
|
&deviceExtension->CurrentInput
|
|
)) {
|
|
|
|
//
|
|
// The InputData queue overflowed. There is
|
|
// not much that can be done about it, so just
|
|
// continue (but don't queue the ISR DPC, since
|
|
// no new packets were added to the queue).
|
|
//
|
|
// Queue a DPC to log an overrun error.
|
|
//
|
|
|
|
IsrPrint(DBG_KBISR_ERROR, ("queue overflow\n"));
|
|
|
|
if (deviceExtension->OkayToLogOverflow) {
|
|
KeInsertQueueDpc(
|
|
&deviceExtension->ErrorLogDpc,
|
|
(PIRP) NULL,
|
|
LongToPtr(I8042_KBD_BUFFER_OVERFLOW)
|
|
);
|
|
deviceExtension->OkayToLogOverflow = FALSE;
|
|
}
|
|
|
|
} else if (deviceExtension->DpcInterlockKeyboard >= 0) {
|
|
|
|
//
|
|
// The ISR DPC is already executing. Tell the ISR DPC
|
|
// it has more work to do by incrementing
|
|
// DpcInterlockKeyboard.
|
|
//
|
|
|
|
deviceExtension->DpcInterlockKeyboard += 1;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Queue the ISR DPC.
|
|
//
|
|
|
|
KeInsertQueueDpc(
|
|
&deviceExtension->KeyboardIsrDpc,
|
|
DeviceObject->CurrentIrp,
|
|
NULL
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Reset the input state.
|
|
//
|
|
deviceExtension->CurrentInput.Flags = 0;
|
|
}
|
|
|
|
VOID
|
|
I8xServiceCrashDump(
|
|
IN PPORT_KEYBOARD_EXTENSION DeviceExtension,
|
|
IN PUNICODE_STRING RegistryPath
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retrieves this driver's service parameters information
|
|
from the registry.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Pointer to the device extension.
|
|
|
|
RegistryPath - Pointer to the null-terminated Unicode name of the
|
|
registry path for this driver.
|
|
|
|
Return Value:
|
|
|
|
None. As a side-effect, sets fields in DeviceExtension->Dump1Keys
|
|
& DeviceExtension->Dump2Key.
|
|
|
|
--*/
|
|
|
|
{
|
|
PRTL_QUERY_REGISTRY_TABLE parameters = NULL;
|
|
UNICODE_STRING parametersPath;
|
|
LONG defaultCrashFlags = 0;
|
|
LONG crashFlags;
|
|
LONG defaultKeyNumber = 0;
|
|
LONG keyNumber;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PWSTR path = NULL;
|
|
USHORT queriesPlusOne = 3;
|
|
|
|
const UCHAR keyToScanTbl[134] = {
|
|
0x00,0x29,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,
|
|
0x0A,0x0B,0x0C,0x0D,0x7D,0x0E,0x0F,0x10,0x11,0x12,
|
|
0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x00,
|
|
0x3A,0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,
|
|
0x27,0x28,0x2B,0x1C,0x2A,0x00,0x2C,0x2D,0x2E,0x2F,
|
|
0x30,0x31,0x32,0x33,0x34,0x35,0x73,0x36,0x1D,0x00,
|
|
0x38,0x39,0xB8,0x00,0x9D,0x00,0x00,0x00,0x00,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0xD2,0xD3,0x00,0x00,0xCB,
|
|
0xC7,0xCF,0x00,0xC8,0xD0,0xC9,0xD1,0x00,0x00,0xCD,
|
|
0x45,0x47,0x4B,0x4F,0x00,0xB5,0x48,0x4C,0x50,0x52,
|
|
0x37,0x49,0x4D,0x51,0x53,0x4A,0x4E,0x00,0x9C,0x00,
|
|
0x01,0x00,0x3B,0x3C,0x3D,0x3E,0x3F,0x40,0x41,0x42,
|
|
0x43,0x44,0x57,0x58,0x00,0x46,0x00,0x00,0x00,0x00,
|
|
0x00,0x7B,0x79,0x70 };
|
|
|
|
PAGED_CODE();
|
|
|
|
parametersPath.Buffer = NULL;
|
|
|
|
//
|
|
// Registry path is already null-terminated, so just use it.
|
|
//
|
|
|
|
path = RegistryPath->Buffer;
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Allocate the Rtl query table.
|
|
//
|
|
|
|
parameters = ExAllocatePool(
|
|
PagedPool,
|
|
sizeof(RTL_QUERY_REGISTRY_TABLE) * queriesPlusOne
|
|
);
|
|
|
|
if (!parameters) {
|
|
|
|
Print(DBG_SS_ERROR,
|
|
("I8xServiceCrashDump: Couldn't allocate table for Rtl query to parameters for %ws\n",
|
|
path
|
|
));
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
|
|
} else {
|
|
|
|
RtlZeroMemory(
|
|
parameters,
|
|
sizeof(RTL_QUERY_REGISTRY_TABLE) * queriesPlusOne
|
|
);
|
|
|
|
//
|
|
// Form a path to this driver's Parameters subkey.
|
|
//
|
|
|
|
RtlInitUnicodeString(
|
|
¶metersPath,
|
|
NULL
|
|
);
|
|
|
|
parametersPath.MaximumLength = RegistryPath->Length +
|
|
sizeof(L"\\Crashdump");
|
|
|
|
parametersPath.Buffer = ExAllocatePool(
|
|
PagedPool,
|
|
parametersPath.MaximumLength
|
|
);
|
|
|
|
if (!parametersPath.Buffer) {
|
|
|
|
Print(DBG_SS_ERROR,
|
|
("I8xServiceCrashDump: Couldn't allocate string for path to parameters for %ws\n",
|
|
path
|
|
));
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Form the parameters path.
|
|
//
|
|
|
|
RtlZeroMemory(
|
|
parametersPath.Buffer,
|
|
parametersPath.MaximumLength
|
|
);
|
|
RtlAppendUnicodeToString(
|
|
¶metersPath,
|
|
path
|
|
);
|
|
RtlAppendUnicodeToString(
|
|
¶metersPath,
|
|
L"\\Crashdump"
|
|
);
|
|
|
|
Print(DBG_SS_INFO,
|
|
("I8xServiceCrashDump: crashdump path is %ws\n",
|
|
parametersPath.Buffer
|
|
));
|
|
|
|
//
|
|
// Gather all of the "user specified" information from
|
|
// the registry.
|
|
//
|
|
|
|
parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[0].Name = L"Dump1Keys";
|
|
parameters[0].EntryContext = &crashFlags;
|
|
parameters[0].DefaultType = REG_DWORD;
|
|
parameters[0].DefaultData = &defaultCrashFlags;
|
|
parameters[0].DefaultLength = sizeof(LONG);
|
|
|
|
parameters[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[1].Name = L"Dump2Key";
|
|
parameters[1].EntryContext = &keyNumber;
|
|
parameters[1].DefaultType = REG_DWORD;
|
|
parameters[1].DefaultData = &defaultKeyNumber;
|
|
parameters[1].DefaultLength = sizeof(LONG);
|
|
|
|
status = RtlQueryRegistryValues(
|
|
RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
|
|
parametersPath.Buffer,
|
|
parameters,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
//
|
|
// Go ahead and assign driver defaults.
|
|
//
|
|
DeviceExtension->CrashFlags = defaultCrashFlags;
|
|
}
|
|
else {
|
|
DeviceExtension->CrashFlags = crashFlags;
|
|
}
|
|
|
|
if (DeviceExtension->CrashFlags) {
|
|
if (keyNumber == 124) {
|
|
DeviceExtension->CrashScanCode = KEYBOARD_DEBUG_HOTKEY_ENH | 0x80;
|
|
DeviceExtension->CrashScanCode2 = KEYBOARD_DEBUG_HOTKEY_AT;
|
|
}
|
|
else {
|
|
if(keyNumber <= 133) {
|
|
DeviceExtension->CrashScanCode = keyToScanTbl[keyNumber];
|
|
}
|
|
else {
|
|
DeviceExtension->CrashScanCode = 0;
|
|
}
|
|
|
|
DeviceExtension->CrashScanCode2 = 0;
|
|
}
|
|
}
|
|
|
|
Print(DBG_SS_NOISE,
|
|
("I8xServiceCrashDump: CrashFlags = 0x%x\n",
|
|
DeviceExtension->CrashFlags
|
|
));
|
|
Print(DBG_SS_NOISE,
|
|
("I8xServiceCrashDump: CrashScanCode = 0x%x, CrashScanCode2 = 0x%x\n",
|
|
(ULONG) DeviceExtension->CrashScanCode,
|
|
(ULONG) DeviceExtension->CrashScanCode2
|
|
));
|
|
|
|
//
|
|
// Free the allocated memory before returning.
|
|
//
|
|
if (parametersPath.Buffer)
|
|
ExFreePool(parametersPath.Buffer);
|
|
if (parameters)
|
|
ExFreePool(parameters);
|
|
}
|
|
|
|
VOID
|
|
I8xKeyboardServiceParameters(
|
|
IN PUNICODE_STRING RegistryPath,
|
|
IN PPORT_KEYBOARD_EXTENSION KeyboardExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retrieves this driver's service parameters information
|
|
from the registry. Overrides these values if they are present in the
|
|
devnode.
|
|
|
|
Arguments:
|
|
|
|
RegistryPath - Pointer to the null-terminated Unicode name of the
|
|
registry path for this driver.
|
|
|
|
KeyboardExtension - Keyboard extension
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PI8042_CONFIGURATION_INFORMATION configuration;
|
|
PRTL_QUERY_REGISTRY_TABLE parameters = NULL;
|
|
PWSTR path = NULL;
|
|
ULONG defaultDataQueueSize = DATA_QUEUE_SIZE;
|
|
ULONG invalidKeyboardSubtype = (ULONG) -1;
|
|
ULONG invalidKeyboardType = 0;
|
|
ULONG overrideKeyboardSubtype = (ULONG) -1;
|
|
ULONG overrideKeyboardType = 0;
|
|
ULONG pollStatusIterations = 0;
|
|
ULONG defaultPowerCaps = 0x0, powerCaps = 0x0;
|
|
ULONG failedReset = FAILED_RESET_DEFAULT,
|
|
defaultFailedReset = FAILED_RESET_DEFAULT;
|
|
ULONG i = 0;
|
|
UNICODE_STRING parametersPath;
|
|
HANDLE keyHandle;
|
|
ULONG defaultPollStatusIterations = I8042_POLLING_DEFAULT;
|
|
|
|
ULONG crashOnCtrlScroll = 0,
|
|
defaultCrashOnCtrlScroll = 0;
|
|
|
|
USHORT queries = 8;
|
|
|
|
PAGED_CODE();
|
|
|
|
#if I8042_VERBOSE
|
|
queries += 2;
|
|
#endif
|
|
|
|
configuration = &(Globals.ControllerData->Configuration);
|
|
parametersPath.Buffer = NULL;
|
|
|
|
//
|
|
// Registry path is already null-terminated, so just use it.
|
|
//
|
|
path = RegistryPath->Buffer;
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Allocate the Rtl query table.
|
|
//
|
|
parameters = ExAllocatePool(
|
|
PagedPool,
|
|
sizeof(RTL_QUERY_REGISTRY_TABLE) * (queries + 1)
|
|
);
|
|
|
|
if (!parameters) {
|
|
|
|
Print(DBG_SS_ERROR,
|
|
("%s: couldn't allocate table for Rtl query to %ws for %ws\n",
|
|
pFncServiceParameters,
|
|
pwParameters,
|
|
path
|
|
));
|
|
status = STATUS_UNSUCCESSFUL;
|
|
|
|
} else {
|
|
|
|
RtlZeroMemory(
|
|
parameters,
|
|
sizeof(RTL_QUERY_REGISTRY_TABLE) * (queries + 1)
|
|
);
|
|
|
|
//
|
|
// Form a path to this driver's Parameters subkey.
|
|
//
|
|
RtlInitUnicodeString( ¶metersPath, NULL );
|
|
parametersPath.MaximumLength = RegistryPath->Length +
|
|
(wcslen(pwParameters) * sizeof(WCHAR) ) + sizeof(UNICODE_NULL);
|
|
|
|
parametersPath.Buffer = ExAllocatePool(
|
|
PagedPool,
|
|
parametersPath.MaximumLength
|
|
);
|
|
|
|
if (!parametersPath.Buffer) {
|
|
|
|
Print(DBG_SS_ERROR,
|
|
("%s: Couldn't allocate string for path to %ws for %ws\n",
|
|
pFncServiceParameters,
|
|
pwParameters,
|
|
path
|
|
));
|
|
status = STATUS_UNSUCCESSFUL;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Form the parameters path.
|
|
//
|
|
|
|
RtlZeroMemory(
|
|
parametersPath.Buffer,
|
|
parametersPath.MaximumLength
|
|
);
|
|
RtlAppendUnicodeToString(
|
|
¶metersPath,
|
|
path
|
|
);
|
|
RtlAppendUnicodeToString(
|
|
¶metersPath,
|
|
pwParameters
|
|
);
|
|
|
|
//
|
|
// Gather all of the "user specified" information from
|
|
// the registry.
|
|
//
|
|
parameters[i].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[i].Name = pwKeyboardDataQueueSize;
|
|
parameters[i].EntryContext =
|
|
&KeyboardExtension->KeyboardAttributes.InputDataQueueLength;
|
|
parameters[i].DefaultType = REG_DWORD;
|
|
parameters[i].DefaultData = &defaultDataQueueSize;
|
|
parameters[i].DefaultLength = sizeof(ULONG);
|
|
|
|
parameters[++i].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[i].Name = pwOverrideKeyboardType;
|
|
parameters[i].EntryContext = &overrideKeyboardType;
|
|
parameters[i].DefaultType = REG_DWORD;
|
|
parameters[i].DefaultData = &invalidKeyboardType;
|
|
parameters[i].DefaultLength = sizeof(ULONG);
|
|
|
|
parameters[++i].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[i].Name = pwOverrideKeyboardSubtype;
|
|
parameters[i].EntryContext = &overrideKeyboardSubtype;
|
|
parameters[i].DefaultType = REG_DWORD;
|
|
parameters[i].DefaultData = &invalidKeyboardSubtype;
|
|
parameters[i].DefaultLength = sizeof(ULONG);
|
|
|
|
parameters[++i].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[i].Name = pwPollStatusIterations;
|
|
parameters[i].EntryContext = &pollStatusIterations;
|
|
parameters[i].DefaultType = REG_DWORD;
|
|
parameters[i].DefaultData = &defaultPollStatusIterations;
|
|
parameters[i].DefaultLength = sizeof(ULONG);
|
|
|
|
parameters[++i].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[i].Name = pwPowerCaps;
|
|
parameters[i].EntryContext = &powerCaps;
|
|
parameters[i].DefaultType = REG_DWORD;
|
|
parameters[i].DefaultData = &defaultPowerCaps;
|
|
parameters[i].DefaultLength = sizeof(ULONG);
|
|
|
|
parameters[++i].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[i].Name = L"CrashOnCtrlScroll";
|
|
parameters[i].EntryContext = &crashOnCtrlScroll;
|
|
parameters[i].DefaultType = REG_DWORD;
|
|
parameters[i].DefaultData = &defaultCrashOnCtrlScroll;
|
|
parameters[i].DefaultLength = sizeof(ULONG);
|
|
|
|
parameters[++i].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[i].Name = STR_FAILED_RESET;
|
|
parameters[i].EntryContext = &failedReset;
|
|
parameters[i].DefaultType = REG_DWORD;
|
|
parameters[i].DefaultData = &defaultFailedReset;
|
|
parameters[i].DefaultLength = sizeof(ULONG);
|
|
|
|
status = RtlQueryRegistryValues(
|
|
RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
|
|
parametersPath.Buffer,
|
|
parameters,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
Print(DBG_SS_INFO,
|
|
("kb RtlQueryRegistryValues failed (0x%x)\n",
|
|
status
|
|
));
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Go ahead and assign driver defaults.
|
|
//
|
|
configuration->PollStatusIterations = (USHORT)
|
|
defaultPollStatusIterations;
|
|
KeyboardExtension->KeyboardAttributes.InputDataQueueLength =
|
|
defaultDataQueueSize;
|
|
}
|
|
else {
|
|
configuration->PollStatusIterations = (USHORT) pollStatusIterations;
|
|
}
|
|
|
|
switch (failedReset) {
|
|
case FAILED_RESET_STOP:
|
|
case FAILED_RESET_PROCEED:
|
|
case FAILED_RESET_PROCEED_ALWAYS:
|
|
KeyboardExtension->FailedReset = (UCHAR) failedReset;
|
|
break;
|
|
|
|
default:
|
|
KeyboardExtension->FailedReset = FAILED_RESET_DEFAULT;
|
|
break;
|
|
}
|
|
|
|
Print(DBG_SS_NOISE, ("Failed reset is set to %d\n",
|
|
KeyboardExtension->FailedReset));
|
|
|
|
status = IoOpenDeviceRegistryKey(KeyboardExtension->PDO,
|
|
PLUGPLAY_REGKEY_DEVICE,
|
|
STANDARD_RIGHTS_READ,
|
|
&keyHandle
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
//
|
|
// If the value is not present in devnode, then the default is the value
|
|
// read in from the Services\i8042prt\Parameters key
|
|
//
|
|
ULONG prevInputDataQueueLength,
|
|
prevPowerCaps,
|
|
prevOverrideKeyboardType,
|
|
prevOverrideKeyboardSubtype,
|
|
prevPollStatusIterations;
|
|
|
|
prevInputDataQueueLength =
|
|
KeyboardExtension->KeyboardAttributes.InputDataQueueLength;
|
|
prevPowerCaps = powerCaps;
|
|
prevOverrideKeyboardType = overrideKeyboardType;
|
|
prevOverrideKeyboardSubtype = overrideKeyboardSubtype;
|
|
prevPollStatusIterations = pollStatusIterations;
|
|
|
|
RtlZeroMemory(
|
|
parameters,
|
|
sizeof(RTL_QUERY_REGISTRY_TABLE) * (queries + 1)
|
|
);
|
|
|
|
i = 0;
|
|
|
|
//
|
|
// Gather all of the "user specified" information from
|
|
// the registry (this time from the devnode)
|
|
//
|
|
parameters[i].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[i].Name = pwKeyboardDataQueueSize;
|
|
parameters[i].EntryContext =
|
|
&KeyboardExtension->KeyboardAttributes.InputDataQueueLength;
|
|
parameters[i].DefaultType = REG_DWORD;
|
|
parameters[i].DefaultData = &prevInputDataQueueLength;
|
|
parameters[i].DefaultLength = sizeof(ULONG);
|
|
|
|
parameters[++i].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[i].Name = pwOverrideKeyboardType;
|
|
parameters[i].EntryContext = &overrideKeyboardType;
|
|
parameters[i].DefaultType = REG_DWORD;
|
|
parameters[i].DefaultData = &prevOverrideKeyboardType;
|
|
parameters[i].DefaultLength = sizeof(ULONG);
|
|
|
|
parameters[++i].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[i].Name = pwOverrideKeyboardSubtype;
|
|
parameters[i].EntryContext = &overrideKeyboardSubtype;
|
|
parameters[i].DefaultType = REG_DWORD;
|
|
parameters[i].DefaultData = &prevOverrideKeyboardSubtype;
|
|
parameters[i].DefaultLength = sizeof(ULONG);
|
|
|
|
parameters[++i].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[i].Name = pwPollStatusIterations;
|
|
parameters[i].EntryContext = &pollStatusIterations;
|
|
parameters[i].DefaultType = REG_DWORD;
|
|
parameters[i].DefaultData = &prevPollStatusIterations;
|
|
parameters[i].DefaultLength = sizeof(ULONG);
|
|
|
|
parameters[++i].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[i].Name = pwPowerCaps,
|
|
parameters[i].EntryContext = &powerCaps;
|
|
parameters[i].DefaultType = REG_DWORD;
|
|
parameters[i].DefaultData = &prevPowerCaps;
|
|
parameters[i].DefaultLength = sizeof(ULONG);
|
|
|
|
status = RtlQueryRegistryValues(
|
|
RTL_REGISTRY_HANDLE,
|
|
(PWSTR) keyHandle,
|
|
parameters,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
Print(DBG_SS_INFO,
|
|
("kb RtlQueryRegistryValues (via handle) failed (0x%x)\n",
|
|
status
|
|
));
|
|
}
|
|
|
|
ZwClose(keyHandle);
|
|
}
|
|
else {
|
|
Print(DBG_SS_INFO | DBG_SS_ERROR,
|
|
("kb, opening devnode handle failed (0x%x)\n",
|
|
status
|
|
));
|
|
}
|
|
|
|
Print(DBG_SS_NOISE, ("I8xKeyboardServiceParameters results..\n"));
|
|
|
|
Print(DBG_SS_NOISE,
|
|
(pDumpDecimal,
|
|
pwPollStatusIterations,
|
|
configuration->PollStatusIterations
|
|
));
|
|
|
|
if (KeyboardExtension->KeyboardAttributes.InputDataQueueLength == 0) {
|
|
|
|
Print(DBG_SS_INFO | DBG_SS_ERROR,
|
|
("\toverriding %ws = 0x%x\n",
|
|
pwKeyboardDataQueueSize,
|
|
KeyboardExtension->KeyboardAttributes.InputDataQueueLength
|
|
));
|
|
|
|
KeyboardExtension->KeyboardAttributes.InputDataQueueLength =
|
|
defaultDataQueueSize;
|
|
|
|
}
|
|
KeyboardExtension->KeyboardAttributes.InputDataQueueLength *=
|
|
sizeof(KEYBOARD_INPUT_DATA);
|
|
|
|
KeyboardExtension->PowerCaps = (UCHAR) (powerCaps & I8042_SYS_BUTTONS);
|
|
Print(DBG_SS_NOISE, (pDumpHex, pwPowerCaps, KeyboardExtension->PowerCaps));
|
|
|
|
if (overrideKeyboardType != invalidKeyboardType) {
|
|
|
|
if (overrideKeyboardType <= NUM_KNOWN_KEYBOARD_TYPES) {
|
|
|
|
Print(DBG_SS_NOISE,
|
|
(pDumpDecimal,
|
|
pwOverrideKeyboardType,
|
|
overrideKeyboardType
|
|
));
|
|
|
|
KeyboardExtension->KeyboardAttributes.KeyboardIdentifier.Type =
|
|
(UCHAR) overrideKeyboardType;
|
|
|
|
} else {
|
|
|
|
Print(DBG_SS_NOISE,
|
|
(pDumpDecimal,
|
|
pwOverrideKeyboardType,
|
|
overrideKeyboardType
|
|
));
|
|
|
|
}
|
|
|
|
KeyboardExtension->KeyboardIdentifierEx.Type = overrideKeyboardType;
|
|
}
|
|
|
|
if (overrideKeyboardSubtype != invalidKeyboardSubtype) {
|
|
|
|
Print(DBG_SS_NOISE,
|
|
(pDumpDecimal,
|
|
pwOverrideKeyboardSubtype,
|
|
overrideKeyboardSubtype
|
|
));
|
|
|
|
KeyboardExtension->KeyboardAttributes.KeyboardIdentifier.Subtype =
|
|
(UCHAR) overrideKeyboardSubtype;
|
|
|
|
KeyboardExtension->KeyboardIdentifierEx.Subtype =
|
|
overrideKeyboardSubtype;
|
|
}
|
|
|
|
if (crashOnCtrlScroll) {
|
|
Print(DBG_SS_INFO, ("Crashing on Ctrl + Scroll Lock\n"));
|
|
|
|
KeyboardExtension->CrashFlags = CRASH_R_CTRL;
|
|
KeyboardExtension->CrashScanCode = SCROLL_LOCK_SCANCODE;
|
|
KeyboardExtension->CrashScanCode2 = 0x0;
|
|
}
|
|
|
|
//
|
|
// Free the allocated memory before returning.
|
|
//
|
|
if (parametersPath.Buffer)
|
|
ExFreePool(parametersPath.Buffer);
|
|
if (parameters)
|
|
ExFreePool(parameters);
|
|
}
|