- Enable/disable keyboard by writing the controller command byte instead of

issuing keyboard commands, the keyboard commands seem to confuse some kvm
  switches.
- Use STATUS_IO_TIMEOUT consistently, STATUS_TIMEOUT is not an error condition
- Introduce symbolic constants for controller command byte bits
- Try to detect mouse early and if its not present, reset the keyboard
  controller. Some controllers seem to get locked up when writing to a
  non-present mouse.
- Don't check the timeout status bit when reading from the controller. It is
  often wrong.
- Don't treat failure to read keyboard id as a fatal error, we only read it
  to set KeyboardIsAT which we then don't use. Still try to detect the keyboard
  type, but don't fail if it doesn't work.
Fixes bug 688. Thanks to Hartmut, Filip and WaxDragon for testing.

svn path=/trunk/; revision=18664
This commit is contained in:
Gé van Geldorp 2005-10-21 20:28:05 +00:00
parent a861531b6c
commit 6940e61dd6
4 changed files with 194 additions and 47 deletions

View file

@ -10,9 +10,7 @@
/* INCLUDES ****************************************************************/
#ifndef NDEBUG
#define NDEBUG
#endif
#include <debug.h>
#include "i8042prt.h"
@ -78,7 +76,7 @@ NTSTATUS I8042ReadData(UCHAR *Data)
DPRINT("Read: %x (status: %x)\n", Data[0], Status);
// If the data is valid (not timeout, not parity error)
if (0 == (Status & (KBD_GTO | KBD_PERR)))
if (0 == (Status & KBD_PERR))
return STATUS_SUCCESS;
}
return STATUS_UNSUCCESSFUL;
@ -115,7 +113,7 @@ VOID I8042Flush()
UCHAR Ignore;
while (STATUS_SUCCESS == I8042ReadData(&Ignore)) {
; /* drop */
DPRINT("Data flushed\n"); /* drop */
}
}
@ -146,25 +144,38 @@ NTSTATUS STDCALL I8042SynchWritePort(PDEVICE_EXTENSION DevExt,
do {
if (Port)
if (!I8042Write(DevExt, I8042_DATA_PORT, Port))
return STATUS_TIMEOUT;
{
DPRINT1("Failed to write Port\n");
return STATUS_IO_TIMEOUT;
}
if (!I8042Write(DevExt, I8042_DATA_PORT, Value))
return STATUS_TIMEOUT;
{
DPRINT1("Failed to write Value\n");
return STATUS_IO_TIMEOUT;
}
if (WaitForAck) {
Status = I8042ReadDataWait(DevExt, &Ack);
if (!NT_SUCCESS(Status))
{
DPRINT1("Failed to read Ack\n");
return Status;
}
if (Ack == KBD_ACK)
return STATUS_SUCCESS;
if (Ack != KBD_RESEND)
{
DPRINT1("Unexpected Ack 0x%x\n", Ack);
return STATUS_UNEXPECTED_IO_ERROR;
}
} else {
return STATUS_SUCCESS;
}
DPRINT("Reiterating\n");
ResendIterations--;
} while (ResendIterations);
return STATUS_TIMEOUT;
return STATUS_IO_TIMEOUT;
}
/*
@ -241,7 +252,7 @@ NTSTATUS STDCALL I8042StartPacket(PDEVICE_EXTENSION DevExt,
DevExt->PacketPort = 0;
if (!I8042PacketWrite(DevExt)) {
Status = STATUS_TIMEOUT;
Status = STATUS_IO_TIMEOUT;
DevExt->Packet.State = Idle;
DevExt->PacketResult = STATUS_ABANDONED;
goto startpacketdone;
@ -273,7 +284,7 @@ BOOLEAN STDCALL I8042PacketIsr(PDEVICE_EXTENSION DevExt,
if (DevExt->PacketResends > DevExt->Settings.ResendIterations) {
DevExt->Packet.State = Idle;
DevExt->PacketComplete = TRUE;
DevExt->PacketResult = STATUS_TIMEOUT;
DevExt->PacketResult = STATUS_IO_TIMEOUT;
DevExt->PacketResends = 0;
return TRUE;
}
@ -301,7 +312,7 @@ BOOLEAN STDCALL I8042PacketIsr(PDEVICE_EXTENSION DevExt,
if (!I8042PacketWrite(DevExt)) {
DevExt->Packet.State = Idle;
DevExt->PacketComplete = TRUE;
DevExt->PacketResult = STATUS_TIMEOUT;
DevExt->PacketResult = STATUS_IO_TIMEOUT;
return TRUE;
}
DevExt->Packet.CurrentByte++;
@ -519,23 +530,53 @@ static NTSTATUS STDCALL I8042BasicDetect(PDEVICE_EXTENSION DevExt)
I8042Flush();
if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_SELF_TEST))
return STATUS_TIMEOUT;
if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_SELF_TEST)) {
DPRINT1("Writing KBD_SELF_TEST command failed\n");
return STATUS_IO_TIMEOUT;
}
// Wait longer?
Counter = 3;
do {
Status = I8042ReadDataWait(DevExt, &Value);
} while ((Counter--) && (STATUS_TIMEOUT == Status));
} while ((Counter--) && (STATUS_IO_TIMEOUT == Status));
if (!NT_SUCCESS(Status))
if (!NT_SUCCESS(Status)) {
DPRINT1("Failed to read KBD_SELF_TEST response, status 0x%x\n",
Status);
return Status;
}
if (Value != 0x55) {
DPRINT1("Got %x instead of 55\n", Value);
return STATUS_IO_DEVICE_ERROR;
}
if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_READ_MODE)) {
DPRINT1("Can't read i8042 mode\n");
return FALSE;
}
Status = I8042ReadDataWait(DevExt, &Value);
if (!NT_SUCCESS(Status)) {
DPRINT1("No response after read i8042 mode\n");
return FALSE;
}
Value |= CCB_KBD_DISAB | CCB_MOUSE_DISAB; /* disable keyboard/mouse */
Value &= ~(CCB_KBD_INT_ENAB | CCB_MOUSE_INT_ENAB);
/* don't enable keyboard and mouse interrupts */
if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_WRITE_MODE)) {
DPRINT1("Can't set i8042 mode\n");
return FALSE;
}
if (!I8042Write(DevExt, I8042_DATA_PORT, Value)) {
DPRINT1("Can't send i8042 mode\n");
return FALSE;
}
if (I8042Write(DevExt, I8042_CTRL_PORT, KBD_LINE_TEST))
{
Status = I8042ReadDataWait(DevExt, &Value);
@ -563,6 +604,11 @@ static NTSTATUS STDCALL I8042Initialize(PDEVICE_EXTENSION DevExt)
return Status;
}
if (DevExt->MouseExists) {
DPRINT("Aux port detected\n");
DevExt->MouseExists = I8042DetectMouse(DevExt);
}
if (!DevExt->KeyboardExists) {
DPRINT("Keyboard port not detected\n");
if (DevExt->Settings.Headless)
@ -820,4 +866,3 @@ NTSTATUS STDCALL DriverEntry(PDRIVER_OBJECT DriverObject,
return(STATUS_SUCCESS);
}

View file

@ -246,6 +246,17 @@ typedef struct _I8042_HOOK_WORKITEM
#define KBD_GTO 0x40
#define KBD_PERR 0x80
/*
* Controller command byte bits
*/
#define CCB_KBD_INT_ENAB 0x01
#define CCB_MOUSE_INT_ENAB 0x02
#define CCB_SYSTEM_FLAG 0x04
#define CCB_IGN_KEY_LOCK 0x08
#define CCB_KBD_DISAB 0x10
#define CCB_MOUSE_DISAB 0x20
#define CCB_TRANSLATE 0x40
/*
* LED bits
@ -359,6 +370,7 @@ VOID STDCALL I8042MouseHandle(PDEVICE_EXTENSION DevExt,
BOOLEAN STDCALL I8042MouseEnable(PDEVICE_EXTENSION DevExt);
BOOLEAN STDCALL I8042MouseDisable(PDEVICE_EXTENSION DevExt);
BOOLEAN STDCALL I8042DetectMouse(PDEVICE_EXTENSION DevExt);
/* ps2pp.c */
VOID I8042MouseHandlePs2pp(PDEVICE_EXTENSION DevExt, UCHAR Input);

View file

@ -11,13 +11,11 @@
/* INCLUDES ****************************************************************/
#ifndef NDEBUG
#define NDEBUG
#endif
#include <debug.h>
#include "i8042prt.h"
#define NDEBUG
#include <debug.h>
/* GLOBALS *******************************************************************/
static UCHAR TypematicTable[] = {
@ -559,34 +557,73 @@ intcontfailure:
* some really old broken keyboard controllers which I hope won't be
* necessary.
*
* Anyway, disabling the keyboard helps the detection and it also
* clears the keyboard buffer and sets defaults which is what we
* want.
* We change the command byte, sending KBD_ENABLE/DISABLE seems to confuse
* some kvm switches.
*/
BOOLEAN STDCALL I8042KeyboardEnable(PDEVICE_EXTENSION DevExt)
{
DPRINT("Enabling keyboard\n");
if (STATUS_SUCCESS != I8042SynchWritePort(DevExt,
0,
KBD_ENABLE,
TRUE)) {
DPRINT("Can't enable keyboard\n");
UCHAR Value;
NTSTATUS Status;
DPRINT("Enable keyboard\n");
if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_READ_MODE)) {
DPRINT1("Can't read i8042 mode\n");
return FALSE;
}
Status = I8042ReadDataWait(DevExt, &Value);
if (!NT_SUCCESS(Status)) {
DPRINT1("No response after read i8042 mode\n");
return FALSE;
}
Value &= ~CCB_KBD_DISAB; // don't disable keyboard
if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_WRITE_MODE)) {
DPRINT1("Can't set i8042 mode\n");
return FALSE;
}
if (!I8042Write(DevExt, I8042_DATA_PORT, Value)) {
DPRINT1("Can't send i8042 mode\n");
return FALSE;
}
return TRUE;
}
static BOOLEAN STDCALL I8042KeyboardDefaultsAndDisable(PDEVICE_EXTENSION DevExt)
{
UCHAR Value;
NTSTATUS Status;
DPRINT("Disabling keyboard\n");
if (STATUS_SUCCESS != I8042SynchWritePort(DevExt,
0,
KBD_DISABLE,
TRUE)) {
DPRINT("Can't disable keyboard\n");
if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_READ_MODE)) {
DPRINT1("Can't read i8042 mode\n");
return FALSE;
}
Status = I8042ReadDataWait(DevExt, &Value);
if (!NT_SUCCESS(Status)) {
DPRINT1("No response after read i8042 mode\n");
return FALSE;
}
Value |= CCB_KBD_DISAB; // disable keyboard
if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_WRITE_MODE)) {
DPRINT1("Can't set i8042 mode\n");
return FALSE;
}
if (!I8042Write(DevExt, I8042_DATA_PORT, Value)) {
DPRINT1("Can't send i8042 mode\n");
return FALSE;
}
return TRUE;
}
@ -608,8 +645,8 @@ BOOLEAN STDCALL I8042KeyboardEnableInterrupt(PDEVICE_EXTENSION DevExt)
return FALSE;
}
Value &= ~(0x10); // don't disable keyboard
Value |= 0x01; // enable keyboard interrupts
Value &= ~CCB_KBD_DISAB; // don't disable keyboard
Value |= CCB_KBD_INT_ENAB; // enable keyboard interrupts
if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_WRITE_MODE)) {
DPRINT1("Can't set i8042 mode\n");
@ -628,6 +665,7 @@ BOOLEAN STDCALL I8042DetectKeyboard(PDEVICE_EXTENSION DevExt)
{
NTSTATUS Status;
UCHAR Value;
UCHAR Value2;
ULONG RetryCount = 10;
DPRINT("Detecting keyboard\n");
@ -635,12 +673,15 @@ BOOLEAN STDCALL I8042DetectKeyboard(PDEVICE_EXTENSION DevExt)
I8042KeyboardDefaultsAndDisable(DevExt);
do {
I8042Flush();
Status = I8042SynchWritePort(DevExt, 0, KBD_GET_ID, TRUE);
} while (STATUS_TIMEOUT == Status && RetryCount--);
} while (STATUS_IO_TIMEOUT == Status && RetryCount--);
if (!NT_SUCCESS(Status)) {
DPRINT1("Can't write GET_ID (%x)\n", Status);
return FALSE;
/* Could be an AT keyboard */
DevExt->KeyboardIsAT = TRUE;
goto detectsetleds;
}
Status = I8042ReadDataWait(DevExt, &Value);
@ -658,17 +699,17 @@ BOOLEAN STDCALL I8042DetectKeyboard(PDEVICE_EXTENSION DevExt)
return FALSE;
}
DPRINT("Keyboard ID: %x", Value);
Status = I8042ReadDataWait(DevExt, &Value);
Status = I8042ReadDataWait(DevExt, &Value2);
if (!NT_SUCCESS(Status)) {
DPRINT("Partial ID\n");
return FALSE;
}
DPRINT ("%x\n", Value);
DPRINT("Keyboard ID: 0x%x 0x%x\n", Value, Value2);
detectsetleds:
I8042Flush(); /* Flush any bytes left over from GET_ID */
Status = I8042SynchWritePort(DevExt, 0, KBD_SET_LEDS, TRUE);
if (!NT_SUCCESS(Status)) {
DPRINT("Can't write SET_LEDS (%x)\n", Status);

View file

@ -11,13 +11,11 @@
/* INCLUDES ****************************************************************/
#ifndef NDEBUG
#define NDEBUG
#endif
#include <debug.h>
#include "i8042prt.h"
#define NDEBUG
#include <debug.h>
/*
* These functions are callbacks for filter driver custom interrupt
* service routines.
@ -897,3 +895,54 @@ BOOLEAN STDCALL I8042MouseDisable(PDEVICE_EXTENSION DevExt)
return TRUE;
}
BOOLEAN STDCALL I8042DetectMouse(PDEVICE_EXTENSION DevExt)
{
BOOLEAN Ok = TRUE;
NTSTATUS Status;
UCHAR Value;
UCHAR ExpectedReply[] = { 0xFA, 0xAA, 0x00 };
unsigned ReplyByte;
if (! I8042Write(DevExt, I8042_CTRL_PORT, 0xD4) ||
! I8042Write(DevExt, I8042_DATA_PORT, 0xFF))
{
DPRINT1("Failed to write reset command to mouse\n");
Ok = FALSE;
}
for (ReplyByte = 0;
ReplyByte < sizeof(ExpectedReply) / sizeof(ExpectedReply[0]) && Ok;
ReplyByte++)
{
Status = I8042ReadDataWait(DevExt, &Value);
if (! NT_SUCCESS(Status))
{
DPRINT1("No ACK after mouse reset, status 0x%08x\n",
Status);
Ok = FALSE;
}
else if (Value != ExpectedReply[ReplyByte])
{
DPRINT1("Unexpected reply: 0x%02x (expected 0x%02x)\n",
Value, ExpectedReply[ReplyByte]);
Ok = FALSE;
}
}
if (! Ok)
{
/* There is probably no mouse present. On some systems,
the probe locks the entire keyboard controller. Let's
try to get access to the keyboard again by sending a
reset */
I8042Flush();
I8042Write(DevExt, I8042_CTRL_PORT, KBD_SELF_TEST);
I8042ReadDataWait(DevExt, &Value);
I8042Flush();
}
DPRINT("Mouse %sdetected\n", Ok ? "" : "not ");
return Ok;
}