kbddep.c revision 677833bc953b6cb418c701facbdcf4aa18d6c44e
/*++
Copyright (c) 1990, 1991, 1992, 1993 Microsoft Corporation
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 "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(INIT,I8xKeyboardConfiguration)
#pragma alloc_text(INIT,I8xKeyboardPeripheralCallout)
#pragma alloc_text(INIT,I8xInitializeKeyboard)
#endif
BOOLEAN
I8042KeyboardInterruptService(
IN PKINTERRUPT Interrupt,
IN PVOID Context
)
/*++
Routine Description:
This is the interrupt service routine for the keyboard device when
scan code set 1 is in use.
Arguments:
Interrupt - A pointer to the interrupt object for this interrupt.
Context - A pointer to the device object.
Return Value:
Returns TRUE if the interrupt was expected (and therefore processed);
otherwise, FALSE is returned.
--*/
{
UCHAR scanCode;
PDEVICE_EXTENSION deviceExtension;
PDEVICE_OBJECT deviceObject;
KEYBOARD_SCAN_STATE *scanState;
PKEYBOARD_INPUT_DATA input;
ULONG i;
UNREFERENCED_PARAMETER(Interrupt);
I8xPrint((2, "I8042PRT-I8042KeyboardInterruptService: enter\n"));
//
// Get the device extension.
//
deviceObject = (PDEVICE_OBJECT) Context;
deviceExtension = (PDEVICE_EXTENSION) deviceObject->DeviceExtension;
//
// 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.
//
if ((I8X_GET_STATUS_BYTE(deviceExtension->DeviceRegisters[CommandPort])
& (OUTPUT_BUFFER_FULL|MOUSE_OUTPUT_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)deviceExtension->Configuration.PollStatusIterations; i++) {
KeStallExecutionProcessor(1);
if ((I8X_GET_STATUS_BYTE(deviceExtension->DeviceRegisters[CommandPort])
& (OUTPUT_BUFFER_FULL|MOUSE_OUTPUT_BUFFER_FULL))
== (OUTPUT_BUFFER_FULL))
break;
}
if ((I8X_GET_STATUS_BYTE(deviceExtension->DeviceRegisters[CommandPort])
& (OUTPUT_BUFFER_FULL|MOUSE_OUTPUT_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.
//
I8xPrint((
1,
"I8042PRT-I8042KeyboardInterruptService: not our interrupt!\n"
));
if (deviceExtension->KeyboardEnableCount == 0) {
scanCode =
I8X_GET_DATA_BYTE(deviceExtension->DeviceRegisters[DataPort]);
}
return(FALSE);
}
}
//
// The interrupt is valid. Read the byte from the i8042 data port.
//
I8xGetByteAsynchronous(
(CCHAR) KeyboardDeviceType,
deviceExtension,
&scanCode
);
I8xPrint((
2,
"I8042PRT-I8042KeyboardInterruptService: scanCode 0x%x\n",
scanCode
));
//
// 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:
I8xPrint((
3,
"I8042PRT-I8042KeyboardInterruptService: RESEND, retries = %d\n",
deviceExtension->KeyboardExtension.ResendCount + 1
));
//
// If the timer count is zero, don't process the interrupt
// further. The timeout routine will complete this request.
//
if (deviceExtension->TimerCount == 0) {
break;
}
//
// Reset the timeout value to indicate no timeout.
//
deviceExtension->TimerCount = I8042_ASYNC_NO_TIMEOUT;
//
// If the maximum number of retries has not been exceeded,
// re-initiate the operation; otherwise, queue the DPC to
// complete this request.
//
if ((deviceExtension->KeyboardExtension.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->KeyboardExtension.ResendCount
< deviceExtension->Configuration.ResendIterations) {
deviceExtension->KeyboardExtension.ResendCount += 1;
I8xKeyboardInitiateIo((PVOID) deviceObject);
} else {
deviceExtension->KeyboardExtension.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:
I8xPrint((
3,
"I8042PRT-I8042KeyboardInterruptService: ACK, "
));
//
// If the timer count is zero, don't process the interrupt
// further. The timeout routine will complete this request.
//
if (deviceExtension->TimerCount == 0) {
break;
}
//
// Reset the timeout value to indicate no timeout.
//
deviceExtension->TimerCount = I8042_ASYNC_NO_TIMEOUT;
//
// Reset resend count.
//
deviceExtension->KeyboardExtension.ResendCount = 0;
if (deviceExtension->KeyboardExtension.CurrentOutput.State
== SendFirstByte) {
//
// We've successfully sent the first byte of a 2-byte
// command sequence. Initiate a send of the second byte.
//
I8xPrint((
3,
"now initiate send of last byte\n"
));
deviceExtension->KeyboardExtension.CurrentOutput.State =
SendLastByte;
I8xKeyboardInitiateIo((PVOID) deviceObject);
} else if (deviceExtension->KeyboardExtension.CurrentOutput.State
== SendLastByte) {
//
// We've successfully sent all bytes in the command sequence.
// Reset the current state and queue the completion DPC.
//
I8xPrint((
3,
"all bytes have been sent\n"
));
deviceExtension->KeyboardExtension.CurrentOutput.State = Idle;
IoRequestDpc(
deviceObject,
deviceObject->CurrentIrp,
NULL
);
} else {
I8xPrint((
1,
"unexpected, State is 0x%x\n",
deviceExtension->KeyboardExtension.CurrentOutput.State
));
//
// Queue a DPC to log an internal driver error.
//
KeInsertQueueDpc(
&deviceExtension->ErrorLogDpc,
(PIRP) NULL,
(PVOID) (ULONG) I8042_INVALID_ISR_STATE);
//
// Note: We don't ASSERT here, because there are some
// machines (e.g., Compaq 386/25) that send back an
// extra ACK in response to the SETLED sequence. We've
// noticed this when, for example, CAPSLOCK is pressed
// at the same time as a normal key. Just ignore
// random ACKs.
//
}
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:
I8xPrint((
3,
"I8042PRT-I8042KeyboardInterruptService: 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->KeyboardExtension.CurrentInput;
scanState = &deviceExtension->KeyboardExtension.CurrentScanState;
if (scanCode == (UCHAR) 0xFF) {
I8xPrint((
1,
"I8042PRT-I8042KeyboardInterruptService: 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;
I8xPrint((
3,
"I8042PRT-I8042KeyboardInterruptService: change state to GotE0\n"
));
break;
} else if (scanCode == (UCHAR) 0xE1) {
input->Flags |= KEY_E1;
*scanState = GotE1;
I8xPrint((
3,
"I8042PRT-I8042KeyboardInterruptService: 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 defined(JAPAN) && defined(_X86_)
// Fujitsu Sep.08.1994
// We want to write debugging information to the file except stop error.
if(deviceExtension->Dump1Keys != 0) {
LONG Dump1Keys;
UCHAR DumpKey,DumpKey2;
BOOLEAN onflag;
static 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 };
Dump1Keys = deviceExtension->Dump1Keys;
switch(deviceExtension->Dump2Key) {
case 124: // 'Print Screen'
DumpKey = 0xB7;
DumpKey2 = 0x54;
break;
default:
if(deviceExtension->Dump2Key <= 133)
DumpKey = KeyToScanTbl[deviceExtension->Dump2Key];
else
DumpKey = 0;
DumpKey2 = 0;
break;
}
if(scanCode <= (UCHAR) 0x7F) {
//
// make code
//
switch(scanCode) {
case 0x1D: // 'CTRL'
if(*scanState == Normal) // Left
deviceExtension->DumpFlags |= 0x20;
else if(*scanState == GotE0) // Right
deviceExtension->DumpFlags |= 0x02;
break;
case 0x38: // 'ALT'
if(*scanState == Normal) // Left
deviceExtension->DumpFlags |= 0x40;
else if(*scanState == GotE0) // Right
deviceExtension->DumpFlags |= 0x04;
break;
case 0x36: // Right 'Shift'
if(*scanState == Normal)
deviceExtension->DumpFlags |= 0x01;
break;
case 0x2A: // Left 'Shift'
if(*scanState == Normal)
deviceExtension->DumpFlags |= 0x10;
break;
default:
if((DumpKey & 0x80) == 0) {
if(*scanState == Normal
&& DumpKey == scanCode)
break;
}
else {
if(*scanState == GotE0
&& (DumpKey & 0x7F) == scanCode)
break;
}
if((DumpKey2 & 0x80) == 0) {
if(*scanState == Normal
&& DumpKey2 == scanCode)
break;
}
else {
if(*scanState == GotE0
&& (DumpKey2 & 0x7F) == scanCode)
break;
}
deviceExtension->DumpFlags = 0;
break;
}
}
else {
//
// break code
//
switch(scanCode & 0x7F) {
case 0x1D: // 'CTRL'
if(*scanState == Normal) // Left
deviceExtension->DumpFlags &= ~0x320;
else if(*scanState == GotE0) // Right
deviceExtension->DumpFlags &= ~0x302;
break;
case 0x38: // 'ALT'
if(*scanState == Normal) // Left
deviceExtension->DumpFlags &= ~0x340;
else if(*scanState == GotE0) // Right
deviceExtension->DumpFlags &= ~0x304;
break;
case 0x36: // Right 'Shift'
if(*scanState == Normal)
deviceExtension->DumpFlags &= ~0x301;
break;
case 0x2A: // Left 'Shift'
if(*scanState == Normal)
deviceExtension->DumpFlags &= ~0x310;
break;
default:
onflag = 0;
if((DumpKey & 0x80) == 0) {
if(*scanState == Normal
&& DumpKey == (scanCode & 0x7F))
onflag = 1;
}
else {
if(*scanState == GotE0
&& DumpKey == scanCode)
onflag = 1;
}
if((DumpKey2 & 0x80) == 0) {
if(*scanState == Normal
&& DumpKey2 == (scanCode & 0x7F))
onflag = 1;
}
else {
if(*scanState == GotE0
&& DumpKey2 == scanCode)
onflag = 1;
}
if(onflag) {
if((deviceExtension->DumpFlags & Dump1Keys) != Dump1Keys)
break;
if(deviceExtension->DumpFlags & 0x100)
deviceExtension->DumpFlags |= 0x200;
else
deviceExtension->DumpFlags |= 0x100;
break;
}
deviceExtension->DumpFlags = 0;
break;
}
}
Dump1Keys |= 0x300;
if(deviceExtension->DumpFlags == Dump1Keys) {
deviceExtension->DumpFlags = 0;
KeBugCheckEx(0x0000FFFF,0,0,0,0);
// make occured blue screen
}
}
#endif
if (scanCode > 0x7F) {
//
// Got a break code. Strip the high bit off
// to get the associated make code and set flags
// to indicate a break code.
//
I8xPrint((
3,
"I8042PRT-I8042KeyboardInterruptService: BREAK code\n"
));
input->MakeCode = scanCode & 0x7F;
input->Flags |= KEY_BREAK;
} else {
//
// Got a make code.
//
I8xPrint((
3,
"I8042PRT-I8042KeyboardInterruptService: 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 (*((PBOOLEAN)(*(PLONG)&KdDebuggerNotPresent))
== FALSE && !(input->Flags & KEY_BREAK)) {
if (ENHANCED_KEYBOARD(
deviceExtension->Configuration.KeyboardAttributes.KeyboardIdentifier
)) {
//
// Enhanced 101 keyboard, SysReq key is 0xE0 0x37.
//
if ((input->MakeCode == KEYBOARD_DEBUG_HOTKEY_ENH) &&
(input->Flags & KEY_E0)) {
__try {
if (**((PUCHAR *)&KdDebuggerEnabled) != FALSE) {
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 (**((PUCHAR *)&KdDebuggerEnabled) != FALSE) {
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,
(PVOID) (ULONG) I8042_INVALID_ISR_STATE);
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) {
if (deviceExtension->KeyboardEnableCount) {
deviceExtension->KeyboardExtension.CurrentInput.UnitId =
deviceExtension->KeyboardExtension.UnitId;
if (!I8xWriteDataToKeyboardQueue(
&deviceExtension->KeyboardExtension,
input
)) {
//
// 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.
//
I8xPrint((
1,
"I8042PRT-I8042KeyboardInterruptService: queue overflow\n"
));
if (deviceExtension->KeyboardExtension.OkayToLogOverflow) {
KeInsertQueueDpc(
&deviceExtension->ErrorLogDpc,
(PIRP) NULL,
(PVOID) (ULONG) I8042_KBD_BUFFER_OVERFLOW
);
deviceExtension->KeyboardExtension.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.
//
input->Flags = 0;
}
break;
}
I8xPrint((2, "I8042PRT-I8042KeyboardInterruptService: exit\n"));
return(TRUE);
}
//
// 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 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
};
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;
I8xPrint((2, "I8042PRT-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];
}
I8xPrint((2, "I8042PRT-I8xConvertTypematicParameters: exit\n"));
return(value);
}
NTSTATUS
I8xInitializeKeyboard(
IN PDEVICE_OBJECT DeviceObject
)
/*++
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;
PDEVICE_EXTENSION deviceExtension;
UCHAR byte;
I8042_TRANSMIT_CCB_CONTEXT transmitCCBContext;
ULONG i;
PIO_ERROR_LOG_PACKET errorLogEntry;
ULONG uniqueErrorValue;
NTSTATUS errorCode = STATUS_SUCCESS;
ULONG dumpCount;
#if !(defined(_X86_) || defined(_PPC_)) // IBMCPK: MIPS specific initialization
PI8042_CONFIGURATION_INFORMATION configuration;
PKEYBOARD_ID keyboardId;
#endif
LARGE_INTEGER startOfSpin, nextQuery, difference, tenSeconds;
BOOLEAN waitForAckOnReset = WAIT_FOR_ACKNOWLEDGE;
#define DUMP_COUNT 4
ULONG dumpData[DUMP_COUNT];
I8xPrint((2, "I8042PRT-I8xInitializeKeyboard: enter\n"));
for (i = 0; i < DUMP_COUNT; i++)
dumpData[i] = 0;
//
// Get the device extension.
//
deviceExtension = DeviceObject->DeviceExtension;
//
// Reset the keyboard.
//
StartOfReset:
status = I8xPutBytePolled(
(CCHAR) DataPort,
waitForAckOnReset,
(CCHAR) KeyboardDeviceType,
deviceExtension,
(UCHAR) KEYBOARD_RESET
);
if (!NT_SUCCESS(status)) {
I8xPrint((
1,
"I8042PRT-I8xInitializeKeyboard: failed keyboard reset, status 0x%x\n",
status
));
//
// Set up error log info.
//
errorCode = I8042_KBD_RESET_COMMAND_FAILED;
uniqueErrorValue = I8042_ERROR_VALUE_BASE + 510;
dumpData[0] = KBDMOU_COULD_NOT_SEND_COMMAND;
dumpData[1] = DataPort;
dumpData[2] = KEYBOARD_RESET;
dumpCount = 3;
//
// NOTE: The following line was commented out to work around a
// problem with the Gateway 4DX2/66V 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 workaround
// is to ignore the keyboard reset failure and continue.
//
// goto I8xInitializeKeyboardExit;
}
//
// 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. The stall value was determined by
// experimentation.
//
KeQueryTickCount(&startOfSpin);
for (i = 0; i < 11200; i++) {
status = I8xGetBytePolled(
(CCHAR) KeyboardDeviceType,
deviceExtension,
&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.
//
//
// Log a warning.
//
dumpData[0] = KBDMOU_INCORRECT_RESPONSE;
dumpData[1] = KeyboardDeviceType;
dumpData[2] = KEYBOARD_COMPLETE_SUCCESS;
dumpData[3] = byte;
I8xLogError(
DeviceObject,
I8042_KBD_RESET_RESPONSE_FAILED,
I8042_ERROR_VALUE_BASE + 515,
status,
dumpData,
4
);
break;
}
} else {
if (status == STATUS_IO_TIMEOUT) {
//
// Stall, and then try again to get a response from
// the reset.
//
KeStallExecutionProcessor(50);
KeQueryTickCount(&nextQuery);
difference.QuadPart = nextQuery.QuadPart - startOfSpin.QuadPart;
tenSeconds.QuadPart = 10*10*1000*1000;
ASSERT(KeQueryTimeIncrement() <= MAXLONG);
if (difference.QuadPart*KeQueryTimeIncrement() >=
tenSeconds.QuadPart) {
break;
}
} else {
break;
}
}
}
if (!NT_SUCCESS(status)) {
if (waitForAckOnReset == WAIT_FOR_ACKNOWLEDGE) {
waitForAckOnReset = NO_WAIT_FOR_ACKNOWLEDGE;
goto StartOfReset;
}
I8xPrint((
1,
"I8042PRT-I8xInitializeKeyboard: failed reset response, status 0x%x, byte 0x%x\n",
status,
byte
));
//
// Set up error log info.
//
errorCode = I8042_KBD_RESET_RESPONSE_FAILED;
uniqueErrorValue = I8042_ERROR_VALUE_BASE + 520;
dumpData[0] = KBDMOU_INCORRECT_RESPONSE;
dumpData[1] = KeyboardDeviceType;
dumpData[2] = KEYBOARD_COMPLETE_SUCCESS;
dumpData[3] = byte;
dumpCount = 4;
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(
deviceExtension,
(PVOID) &transmitCCBContext
);
if (!NT_SUCCESS(transmitCCBContext.Status)) {
//
// If failure then retry once. This is for Toshiba T3400CT.
//
I8xTransmitControllerCommand(
deviceExtension,
(PVOID) &transmitCCBContext
);
}
if (!NT_SUCCESS(transmitCCBContext.Status)) {
I8xPrint((
1,
"I8042PRT-I8xInitializeKeyboard: could not turn off translate\n"
));
status = transmitCCBContext.Status;
goto I8xInitializeKeyboardExit;
}
//
// Get a pointer to the keyboard identifier field.
//
id = &deviceExtension->Configuration.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,
deviceExtension,
(UCHAR) SET_KEYBOARD_TYPEMATIC
)) != STATUS_SUCCESS) {
I8xPrint((
1,
"I8042PRT-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,
status,
dumpData,
3
);
} else if ((status = I8xPutBytePolled(
(CCHAR) DataPort,
WAIT_FOR_ACKNOWLEDGE,
(CCHAR) KeyboardDeviceType,
deviceExtension,
I8xConvertTypematicParameters(
deviceExtension->Configuration.KeyRepeatCurrent.Rate,
deviceExtension->Configuration.KeyRepeatCurrent.Delay
))) != STATUS_SUCCESS) {
I8xPrint((
1,
"I8042PRT-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->Configuration.KeyRepeatCurrent.Rate,
deviceExtension->Configuration.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,
deviceExtension,
(UCHAR) SET_KEYBOARD_INDICATORS
)) != STATUS_SUCCESS) {
I8xPrint((
1,
"I8042PRT-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,
status,
dumpData,
3
);
} else if ((status = I8xPutBytePolled(
(CCHAR) DataPort,
WAIT_FOR_ACKNOWLEDGE,
(CCHAR) KeyboardDeviceType,
deviceExtension,
(UCHAR) deviceExtension->Configuration.KeyboardIndicators.LedFlags
)) != STATUS_SUCCESS) {
I8xPrint((
1,
"I8042PRT-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->Configuration.KeyboardIndicators.LedFlags;
I8xLogError(
DeviceObject,
I8042_SET_LED_FAILED,
I8042_ERROR_VALUE_BASE + 550,
status,
dumpData,
4
);
}
status = STATUS_SUCCESS;
#if !(defined(_X86_) || 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,
deviceExtension,
(UCHAR) SELECT_SCAN_CODE_SET
);
if (NT_SUCCESS(status)) {
//
// Send the associated parameter byte.
//
status = I8xPutBytePolled(
(CCHAR) DataPort,
WAIT_FOR_ACKNOWLEDGE,
(CCHAR) KeyboardDeviceType,
deviceExtension,
(UCHAR) 2
);
}
if (!NT_SUCCESS(status)) {
I8xPrint((
1,
"I8042PRT-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 = &deviceExtension->Configuration;
keyboardId = &configuration->KeyboardAttributes.KeyboardIdentifier;
keyboardId->Type = 3;
configuration->KeyboardAttributes.NumberOfFunctionKeys =
KeyboardTypeInformation[keyboardId->Type - 1].NumberOfFunctionKeys;
configuration->KeyboardAttributes.NumberOfIndicators =
KeyboardTypeInformation[keyboardId->Type - 1].NumberOfIndicators;
configuration->KeyboardAttributes.NumberOfKeysTotal =
KeyboardTypeInformation[keyboardId->Type - 1].NumberOfKeysTotal;
status = STATUS_SUCCESS;
}
}
#endif
#ifdef JAPAN
// NLS Keyboard Support Code.
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,
deviceExtension,
(UCHAR) SELECT_SCAN_CODE_SET
);
if (status != STATUS_SUCCESS) {
I8xPrint((
1,
"I8042PRT-I8xInitializeKeyboard: could not send Select Scan command\n"
));
I8xPrint((
0,
"I8042PRT-I8xInitializeKeyboard: WARNING - using scan set 82h\n"
));
deviceExtension->Configuration.KeyboardAttributes.KeyboardMode = 3;
} else {
//
// Send the associated parameter byte.
//
status = I8xPutBytePolled(
(CCHAR) DataPort,
WAIT_FOR_ACKNOWLEDGE,
(CCHAR) KeyboardDeviceType,
deviceExtension,
(UCHAR) 0x82
);
if (status != STATUS_SUCCESS) {
I8xPrint((
1,
"I8042PRT-I8xInitializeKeyboard: could not send Select Scan param\n"
));
I8xPrint((
0,
"I8042PRT-I8xInitializeKeyboard: WARNING - using scan set 82h\n"
));
deviceExtension->Configuration.KeyboardAttributes.KeyboardMode = 3;
}
}
}
#endif
if (deviceExtension->Configuration.KeyboardAttributes.KeyboardMode == 1) {
//
// 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(
deviceExtension,
(PVOID) &transmitCCBContext
);
if (!NT_SUCCESS(transmitCCBContext.Status)) {
I8xPrint((
1,
"I8042PRT-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,
deviceExtension,
(UCHAR) SELECT_SCAN_CODE_SET
);
if (!NT_SUCCESS(status)) {
I8xPrint((
1,
"I8042PRT-I8xInitializeKeyboard: could not send Select Scan command\n"
));
I8xPrint((
0,
"I8042PRT-I8xInitializeKeyboard: WARNING - using scan set 2\n"
));
deviceExtension->Configuration.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,
deviceExtension,
#ifdef JAPAN
// NLS Keyboard Support Code.
(UCHAR) (IBM02_KEYBOARD(*id) ? 0x81 : 1 )
#else
(UCHAR) 1
#endif
);
if (!NT_SUCCESS(status)) {
I8xPrint((
1,
"I8042PRT-I8xInitializeKeyboard: could not send Select Scan param\n"
));
I8xPrint((
0,
"I8042PRT-I8xInitializeKeyboard: WARNING - using scan set 2\n"
));
deviceExtension->Configuration.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 the keyboard initialization failed, log an error.
//
if (errorCode != STATUS_SUCCESS) {
errorLogEntry = (PIO_ERROR_LOG_PACKET)
IoAllocateErrorLogEntry(
DeviceObject,
(UCHAR) (sizeof(IO_ERROR_LOG_PACKET)
+ (dumpCount * sizeof(ULONG)))
);
if (errorLogEntry != NULL) {
errorLogEntry->ErrorCode = errorCode;
errorLogEntry->DumpDataSize = (USHORT) dumpCount * sizeof(ULONG);
errorLogEntry->SequenceNumber = 0;
errorLogEntry->MajorFunctionCode = 0;
errorLogEntry->IoControlCode = 0;
errorLogEntry->RetryCount = 0;
errorLogEntry->UniqueErrorValue = uniqueErrorValue;
errorLogEntry->FinalStatus = status;
for (i = 0; i < dumpCount; i++)
errorLogEntry->DumpData[i] = dumpData[i];
IoWriteErrorLogEntry(errorLogEntry);
}
}
//
// Initialize current keyboard set packet state.
//
deviceExtension->KeyboardExtension.CurrentOutput.State = Idle;
deviceExtension->KeyboardExtension.CurrentOutput.FirstByte = 0;
deviceExtension->KeyboardExtension.CurrentOutput.LastByte = 0;
I8xPrint((2, "I8042PRT-I8xInitializeKeyboard: exit\n"));
return(status);
}
VOID
I8xKeyboardConfiguration(
IN PINIT_EXTENSION InitializationData,
IN PUNICODE_STRING RegistryPath,
IN PUNICODE_STRING KeyboardDeviceName,
IN PUNICODE_STRING PointerDeviceName
)
/*++
Routine Description:
This routine retrieves the configuration information for the keyboard.
Arguments:
InitializationData - Pointer to the initialization data, including the
device extension.
RegistryPath - Pointer to the null-terminated Unicode name of the
registry path for this driver.
KeyboardDeviceName - Pointer to the Unicode string that will receive
the keyboard port device name.
PointerDeviceName - Pointer to the Unicode string that will receive
the pointer port device name.
Return Value:
None. As a side-effect, may set DeviceExtension->HardwarePresent.
--*/
{
PDEVICE_EXTENSION deviceExtension = &(InitializationData->DeviceExtension);
NTSTATUS status = STATUS_SUCCESS;
PI8042_CONFIGURATION_INFORMATION configuration;
INTERFACE_TYPE interfaceType;
CONFIGURATION_TYPE controllerType = KeyboardController;
CONFIGURATION_TYPE peripheralType = KeyboardPeripheral;
PKEYBOARD_ID keyboardId;
ULONG i;
for (i = 0; i < MaximumInterfaceType; i++) {
//
// Get the registry information for this device.
//
interfaceType = i;
status = IoQueryDeviceDescription(&interfaceType,
NULL,
&controllerType,
NULL,
&peripheralType,
NULL,
I8xKeyboardPeripheralCallout,
(PVOID) InitializationData);
if (deviceExtension->HardwarePresent & KEYBOARD_HARDWARE_PRESENT) {
//
// Get the service parameters (e.g., user-configurable number
// of resends, polling iterations, etc.).
//
I8xServiceParameters(
InitializationData,
RegistryPath,
KeyboardDeviceName,
PointerDeviceName
);
configuration = &(InitializationData->DeviceExtension.Configuration);
keyboardId = &configuration->KeyboardAttributes.KeyboardIdentifier;
if (!ENHANCED_KEYBOARD(*keyboardId)) {
I8xPrint((
1,
"I8042PRT-I8xKeyboardConfiguration: Old AT-style keyboard\n"
));
configuration->PollingIterations =
configuration->PollingIterationsMaximum;
}
//
// Initialize keyboard-specific configuration parameters.
//
configuration->KeyboardAttributes.NumberOfFunctionKeys =
KeyboardTypeInformation[keyboardId->Type - 1].NumberOfFunctionKeys;
configuration->KeyboardAttributes.NumberOfIndicators =
KeyboardTypeInformation[keyboardId->Type - 1].NumberOfIndicators;
configuration->KeyboardAttributes.NumberOfKeysTotal =
KeyboardTypeInformation[keyboardId->Type - 1].NumberOfKeysTotal;
configuration->KeyboardAttributes.KeyboardMode =
KEYBOARD_SCAN_CODE_SET;
configuration->KeyboardAttributes.KeyRepeatMinimum.Rate =
KEYBOARD_TYPEMATIC_RATE_MINIMUM;
configuration->KeyboardAttributes.KeyRepeatMinimum.Delay =
KEYBOARD_TYPEMATIC_DELAY_MINIMUM;
configuration->KeyboardAttributes.KeyRepeatMaximum.Rate =
KEYBOARD_TYPEMATIC_RATE_MAXIMUM;
configuration->KeyboardAttributes.KeyRepeatMaximum.Delay =
KEYBOARD_TYPEMATIC_DELAY_MAXIMUM;
configuration->KeyRepeatCurrent.Rate =
KEYBOARD_TYPEMATIC_RATE_DEFAULT;
configuration->KeyRepeatCurrent.Delay =
KEYBOARD_TYPEMATIC_DELAY_DEFAULT;
break;
} else {
I8xPrint((
1,
"I8042PRT-I8xKeyboardConfiguration: IoQueryDeviceDescription for bus type %d failed\n",
interfaceType
));
}
}
}
VOID
I8xKeyboardInitiateIo(
IN PVOID Context
)
/*++
Routine Description:
This routine is called synchronously from I8xKeyboardInitiateWrapper and
the ISR to initiate an I/O operation for the keyboard device.
Arguments:
Context - Pointer to the device object.
Return Value:
None.
--*/
{
PDEVICE_EXTENSION deviceExtension;
PDEVICE_OBJECT deviceObject;
KEYBOARD_SET_PACKET keyboardPacket;
I8xPrint((2, "I8042PRT-I8xKeyboardInitiateIo: enter\n"));
//
// Get the device extension.
//
deviceObject = (PDEVICE_OBJECT) Context;
deviceExtension = deviceObject->DeviceExtension;
//
// Set the timeout value.
//
deviceExtension->TimerCount = I8042_ASYNC_TIMEOUT;
//
// Get the current set request packet to work on.
//
keyboardPacket = deviceExtension->KeyboardExtension.CurrentOutput;
if (deviceExtension->KeyboardExtension.CurrentOutput.State
== SendFirstByte){
I8xPrint((
2,
"I8042PRT-I8xKeyboardInitiateIo: send first byte 0x%x\n",
keyboardPacket.FirstByte
));
//
// Send the first byte of a 2-byte command sequence to the
// keyboard controller, asynchronously.
//
I8xPutByteAsynchronous(
(CCHAR) DataPort,
deviceExtension,
keyboardPacket.FirstByte
);
} else if (deviceExtension->KeyboardExtension.CurrentOutput.State
== SendLastByte) {
I8xPrint((
2,
"I8042PRT-I8xKeyboardInitiateIo: send last byte 0x%x\n",
keyboardPacket.LastByte
));
//
// Send the last byte of a command sequence to the keyboard
// controller, asynchronously.
//
I8xPutByteAsynchronous(
(CCHAR) DataPort,
deviceExtension,
keyboardPacket.LastByte
);
} else {
I8xPrint((2, "I8042PRT-I8xKeyboardInitiateIo: INVALID REQUEST\n"));
//
// Queue a DPC to log an internal driver error.
//
KeInsertQueueDpc(
&deviceExtension->ErrorLogDpc,
(PIRP) NULL,
(PVOID) (ULONG) I8042_INVALID_INITIATE_STATE);
ASSERT(FALSE);
}
I8xPrint((2, "I8042PRT-I8xKeyboardInitiateIo: exit\n"));
return;
}
VOID
I8xKeyboardInitiateWrapper(
IN PVOID Context
)
/*++
Routine Description:
This routine is called from StartIo synchronously. It sets up the
CurrentOutput and ResendCount fields in the device extension, and
then calls I8xKeyboardInitiateIo to do the real work.
Arguments:
Context - Pointer to the context structure containing the first and
last bytes of the send sequence.
Return Value:
None.
--*/
{
PDEVICE_OBJECT deviceObject;
PDEVICE_EXTENSION deviceExtension;
//
// Get a pointer to the device object from the context argument.
//
deviceObject = ((PKEYBOARD_INITIATE_CONTEXT) Context)->DeviceObject;
//
// Set up CurrentOutput state for this operation.
//
deviceExtension = (PDEVICE_EXTENSION) deviceObject->DeviceExtension;
deviceExtension->KeyboardExtension.CurrentOutput.State = SendFirstByte;
deviceExtension->KeyboardExtension.CurrentOutput.FirstByte =
((PKEYBOARD_INITIATE_CONTEXT) Context)->FirstByte;
deviceExtension->KeyboardExtension.CurrentOutput.LastByte =
((PKEYBOARD_INITIATE_CONTEXT) Context)->LastByte;
//
// We're starting a new operation, so reset the resend count.
//
deviceExtension->KeyboardExtension.ResendCount = 0;
//
// Initiate the keyboard I/O operation. Note that we were called
// using KeSynchronizeExecution, so I8xKeyboardInitiateIo is also
// synchronized with the keyboard ISR.
//
I8xKeyboardInitiateIo((PVOID) deviceObject);
}
NTSTATUS
I8xKeyboardPeripheralCallout(
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.
--*/
{
PDEVICE_EXTENSION deviceExtension;
PINIT_EXTENSION initializationData;
PI8042_CONFIGURATION_INFORMATION configuration;
UNICODE_STRING unicodeIdentifier;
PUCHAR controllerData;
PUCHAR peripheralData;
NTSTATUS status = STATUS_SUCCESS;
ULONG i;
ULONG listCount;
PCM_PARTIAL_RESOURCE_DESCRIPTOR resourceDescriptor;
CM_PARTIAL_RESOURCE_DESCRIPTOR tmpResourceDescriptor;
PCM_KEYBOARD_DEVICE_DATA keyboardSpecificData;
BOOLEAN defaultInterruptShare;
KINTERRUPT_MODE defaultInterruptMode;
I8xPrint((
1,
"I8042PRT-I8xKeyboardPeripheralCallout: Path @ 0x%x, Bus Type 0x%x, Bus Number 0x%x\n",
PathName, BusType, BusNumber
));
I8xPrint((
1,
" Controller Type 0x%x, Controller Number 0x%x, Controller info @ 0x%x\n",
ControllerType, ControllerNumber, ControllerInformation
));
I8xPrint((
1,
" Peripheral Type 0x%x, Peripheral Number 0x%x, Peripheral info @ 0x%x\n",
PeripheralType, PeripheralNumber, PeripheralInformation
));
//
// Get the length of the peripheral identifier information.
//
unicodeIdentifier.Length = (USHORT)
(*(PeripheralInformation + IoQueryDeviceIdentifier))->DataLength;
//
// If we already have the configuration information for the
// keyboard peripheral, or if the peripheral identifier is missing,
// just return.
//
initializationData = (PINIT_EXTENSION) Context;
deviceExtension = &(initializationData->DeviceExtension);
if ((deviceExtension->HardwarePresent & KEYBOARD_HARDWARE_PRESENT)
|| (unicodeIdentifier.Length == 0)) {
return (status);
}
configuration = &deviceExtension->Configuration;
//
// Get the identifier information for the peripheral.
//
unicodeIdentifier.MaximumLength = unicodeIdentifier.Length;
unicodeIdentifier.Buffer = (PWSTR) (((PUCHAR)(*(PeripheralInformation +
IoQueryDeviceIdentifier))) +
(*(PeripheralInformation +
IoQueryDeviceIdentifier))->DataOffset);
I8xPrint((
1,
"I8042PRT-I8xKeyboardPeripheralCallout: Keyboard type %ws\n",
unicodeIdentifier.Buffer
));
deviceExtension->HardwarePresent |= KEYBOARD_HARDWARE_PRESENT;
//
// Initialize the Keyboard Type to unknown.
//
configuration->KeyboardAttributes.KeyboardIdentifier.Type = 0;
configuration->KeyboardAttributes.KeyboardIdentifier.Subtype = 0;
//
// Look through the peripheral's resource list for device-specific
// information. The keyboard-specific information is defined
// in sdk\inc\ntconfig.h.
//
if ((*(PeripheralInformation + IoQueryDeviceConfigurationData))->DataLength != 0){
peripheralData = ((PUCHAR) (*(PeripheralInformation +
IoQueryDeviceConfigurationData))) +
(*(PeripheralInformation +
IoQueryDeviceConfigurationData))->DataOffset;
peripheralData += FIELD_OFFSET(CM_FULL_RESOURCE_DESCRIPTOR,
PartialResourceList);
listCount = ((PCM_PARTIAL_RESOURCE_LIST) peripheralData)->Count;
resourceDescriptor =
((PCM_PARTIAL_RESOURCE_LIST) peripheralData)->PartialDescriptors;
for (i = 0; i < listCount; i++, resourceDescriptor++) {
switch(resourceDescriptor->Type) {
case CmResourceTypeDeviceSpecific:
//
// Get the keyboard type, subtype, and the initial
// settings for the LEDs.
//
keyboardSpecificData =
(PCM_KEYBOARD_DEVICE_DATA)(((PUCHAR)resourceDescriptor)
+ sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR));
if (keyboardSpecificData->Type<= NUM_KNOWN_KEYBOARD_TYPES){
configuration->KeyboardAttributes.KeyboardIdentifier.Type =
keyboardSpecificData->Type;
}
configuration->KeyboardAttributes.KeyboardIdentifier.Subtype =
keyboardSpecificData->Subtype;
configuration->KeyboardIndicators.LedFlags =
(keyboardSpecificData->KeyboardFlags >> 4) & 7;
break;
default:
break;
}
}
}
//
// If no keyboard-specific information (i.e., keyboard type, subtype,
// and initial LED settings) was found, use the keyboard driver
// defaults.
//
if (configuration->KeyboardAttributes.KeyboardIdentifier.Type == 0) {
#if defined(JAPAN) && defined(_X86_)
//Fujitsu Aug.23.1994
// if not connect keyboard hardware,Insert 106 keyboard type to each structure members
// for realizing "keyboard-less system".
configuration->KeyboardAttributes.KeyboardIdentifier.Type = 0x4;
configuration->KeyboardAttributes.KeyboardIdentifier.Subtype = 0;
configuration->KeyboardIndicators.LedFlags = KEYBOARD_INDICATORS_DEFAULT;
#endif
I8xPrint((
1,
"I8042PRT-I8xKeyboardPeripheralCallout: Using default keyboard type\n"
));
configuration->KeyboardAttributes.KeyboardIdentifier.Type =
KEYBOARD_TYPE_DEFAULT;
configuration->KeyboardIndicators.LedFlags =
KEYBOARD_INDICATORS_DEFAULT;
}
I8xPrint((
1,
"I8042PRT-I8xKeyboardPeripheralCallout: Keyboard device specific data --\n"
));
I8xPrint((
1,
" Type = %d, Subtype = %d, Initial LEDs = 0x%x\n",
configuration->KeyboardAttributes.KeyboardIdentifier.Type,
configuration->KeyboardAttributes.KeyboardIdentifier.Subtype,
configuration->KeyboardIndicators.LedFlags
));
//
// Get the bus information.
//
configuration->InterfaceType = BusType;
configuration->BusNumber = BusNumber;
configuration->FloatingSave = I8042_FLOATING_SAVE;
if (BusType == MicroChannel) {
defaultInterruptShare = TRUE;
defaultInterruptMode = LevelSensitive;
} else {
defaultInterruptShare = I8042_INTERRUPT_SHARE;
defaultInterruptMode = I8042_INTERRUPT_MODE;
}
//
// Look through the controller's resource list for interrupt and port
// configuration information.
//
if ((*(ControllerInformation + IoQueryDeviceConfigurationData))->DataLength != 0){
controllerData = ((PUCHAR) (*(ControllerInformation +
IoQueryDeviceConfigurationData))) +
(*(ControllerInformation +
IoQueryDeviceConfigurationData))->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:
//
// 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).
//
ASSERT(configuration->PortListCount < MaximumPortCount);
configuration->PortList[configuration->PortListCount] =
*resourceDescriptor;
configuration->PortList[configuration->PortListCount].ShareDisposition =
I8042_REGISTER_SHARE? CmResourceShareShared:
CmResourceShareDriverExclusive;
configuration->PortListCount += 1;
break;
case CmResourceTypeInterrupt:
//
// Copy the interrupt information.
//
configuration->KeyboardInterrupt = *resourceDescriptor;
configuration->KeyboardInterrupt.ShareDisposition =
defaultInterruptShare? CmResourceShareShared :
CmResourceShareDeviceExclusive;
break;
case CmResourceTypeDeviceSpecific:
break;
default:
break;
}
}
}
//
// If no interrupt configuration information was found, use the
// keyboard driver defaults.
//
if (!(configuration->KeyboardInterrupt.Type & CmResourceTypeInterrupt)) {
I8xPrint((
1,
"I8042PRT-I8xKeyboardPeripheralCallout: Using default keyboard interrupt config\n"
));
configuration->KeyboardInterrupt.Type = CmResourceTypeInterrupt;
configuration->KeyboardInterrupt.ShareDisposition =
defaultInterruptShare? CmResourceShareShared :
CmResourceShareDeviceExclusive;
configuration->KeyboardInterrupt.Flags =
(defaultInterruptMode == Latched)? CM_RESOURCE_INTERRUPT_LATCHED :
CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
configuration->KeyboardInterrupt.u.Interrupt.Level = KEYBOARD_IRQL;
configuration->KeyboardInterrupt.u.Interrupt.Vector = KEYBOARD_VECTOR;
}
I8xPrint((
1,
"I8042PRT-I8xKeyboardPeripheralCallout: Keyboard interrupt config --\n"
));
I8xPrint((
1,
" %s, %s, Irq = %d\n",
configuration->KeyboardInterrupt.ShareDisposition == CmResourceShareShared?
"Sharable" : "NonSharable",
configuration->KeyboardInterrupt.Flags == CM_RESOURCE_INTERRUPT_LATCHED?
"Latched" : "Level Sensitive",
configuration->KeyboardInterrupt.u.Interrupt.Vector
));
//
// If no port configuration information was found, use the
// keyboard driver defaults.
//
if (configuration->PortListCount == 0) {
//
// No port configuration information was found, so use
// the driver defaults.
//
I8xPrint((
1,
"I8042PRT-I8xKeyboardPeripheralCallout: Using default port config\n"
));
configuration->PortList[DataPort].Type = CmResourceTypePort;
configuration->PortList[DataPort].Flags = I8042_PORT_TYPE;
configuration->PortList[DataPort].ShareDisposition =
I8042_REGISTER_SHARE? CmResourceShareShared:
CmResourceShareDriverExclusive;
configuration->PortList[DataPort].u.Port.Start.LowPart =
I8042_PHYSICAL_BASE + I8042_DATA_REGISTER_OFFSET;
configuration->PortList[DataPort].u.Port.Start.HighPart = 0;
configuration->PortList[DataPort].u.Port.Length = I8042_REGISTER_LENGTH;
configuration->PortList[CommandPort].Type = CmResourceTypePort;
configuration->PortList[CommandPort].Flags = I8042_PORT_TYPE;
configuration->PortList[CommandPort].ShareDisposition =
I8042_REGISTER_SHARE? CmResourceShareShared:
CmResourceShareDriverExclusive;
configuration->PortList[CommandPort].u.Port.Start.LowPart =
I8042_PHYSICAL_BASE + I8042_COMMAND_REGISTER_OFFSET;
configuration->PortList[CommandPort].u.Port.Start.HighPart = 0;
configuration->PortList[CommandPort].u.Port.Length = I8042_REGISTER_LENGTH;
configuration->PortListCount = 2;
} else if (configuration->PortListCount == 1) {
//
// Kludge for Jazz machines. Their ARC firmware neglects to
// separate out the port addresses, so fix that up here.
//
configuration->PortList[DataPort].u.Port.Length = I8042_REGISTER_LENGTH;
configuration->PortList[CommandPort] = configuration->PortList[DataPort];
configuration->PortList[CommandPort].u.Port.Start.LowPart +=
I8042_COMMAND_REGISTER_OFFSET;
configuration->PortListCount += 1;
} else {
//
// Put the lowest port address range in the DataPort element of
// the port list.
//
if (configuration->PortList[CommandPort].u.Port.Start.LowPart
< configuration->PortList[DataPort].u.Port.Start.LowPart) {
tmpResourceDescriptor = configuration->PortList[DataPort];
configuration->PortList[DataPort] =
configuration->PortList[CommandPort];
configuration->PortList[CommandPort] = tmpResourceDescriptor;
}
}
#ifdef PNP_IDENTIFY
//
// We're going to use the keyboard based on this data,
// so make sure we can tell PNP that we've claimed it later on
//
initializationData->KeyboardConfig.InterfaceType = BusType;
initializationData->KeyboardConfig.InterfaceNumber = BusNumber;
initializationData->KeyboardConfig.ControllerType = ControllerType;
initializationData->KeyboardConfig.ControllerNumber = ControllerNumber;
initializationData->KeyboardConfig.PeripheralType = PeripheralType;
initializationData->KeyboardConfig.PeripheralNumber = PeripheralNumber;
#endif
for (i = 0; i < configuration->PortListCount; i++) {
I8xPrint((
1,
" %s, Ports 0x%x - 0x%x\n",
configuration->PortList[i].ShareDisposition
== CmResourceShareShared? "Sharable" : "NonSharable",
configuration->PortList[i].u.Port.Start.LowPart,
configuration->PortList[i].u.Port.Start.LowPart +
configuration->PortList[i].u.Port.Length - 1
));
}
return(status);
}