- 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.
Modified: trunk/reactos/drivers/input/i8042prt/i8042prt.c
Modified: trunk/reactos/drivers/input/i8042prt/i8042prt.h
Modified: trunk/reactos/drivers/input/i8042prt/keyboard.c
Modified: trunk/reactos/drivers/input/i8042prt/mouse.c

Modified: trunk/reactos/drivers/input/i8042prt/i8042prt.c
--- trunk/reactos/drivers/input/i8042prt/i8042prt.c	2005-10-21 19:45:28 UTC (rev 18663)
+++ trunk/reactos/drivers/input/i8042prt/i8042prt.c	2005-10-21 20:28:05 UTC (rev 18664)
@@ -10,9 +10,7 @@
 
 /* INCLUDES ****************************************************************/
 
-#ifndef NDEBUG
 #define NDEBUG
-#endif
 #include <debug.h>
 
 #include "i8042prt.h"
@@ -78,7 +76,7 @@
 		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 @@
 	UCHAR Ignore;
 
 	while (STATUS_SUCCESS == I8042ReadData(&Ignore)) {
-		; /* drop */
+		DPRINT("Data flushed\n"); /* drop */
 	}
 }
 
@@ -146,25 +144,38 @@
 	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 @@
 		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 @@
 		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 @@
 	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 @@
 
 	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 @@
 		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 @@
 
 	return(STATUS_SUCCESS);
 }
-

Modified: trunk/reactos/drivers/input/i8042prt/i8042prt.h
--- trunk/reactos/drivers/input/i8042prt/i8042prt.h	2005-10-21 19:45:28 UTC (rev 18663)
+++ trunk/reactos/drivers/input/i8042prt/i8042prt.h	2005-10-21 20:28:05 UTC (rev 18664)
@@ -246,7 +246,18 @@
 #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 @@
 
 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);

Modified: trunk/reactos/drivers/input/i8042prt/keyboard.c
--- trunk/reactos/drivers/input/i8042prt/keyboard.c	2005-10-21 19:45:28 UTC (rev 18663)
+++ trunk/reactos/drivers/input/i8042prt/keyboard.c	2005-10-21 20:28:05 UTC (rev 18664)
@@ -11,13 +11,11 @@
 
 /* INCLUDES ****************************************************************/
 
-#ifndef NDEBUG
+#include "i8042prt.h"
+
 #define NDEBUG
-#endif
 #include <debug.h>
 
-#include "i8042prt.h"
-
 /* GLOBALS *******************************************************************/
 
 static UCHAR TypematicTable[] = {
@@ -559,34 +557,73 @@
  * 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 @@
 		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 @@
 {
 	NTSTATUS Status;
 	UCHAR Value;
+	UCHAR Value2;
 	ULONG RetryCount = 10;
 
 	DPRINT("Detecting keyboard\n");
@@ -635,12 +673,15 @@
 	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 @@
 		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);

Modified: trunk/reactos/drivers/input/i8042prt/mouse.c
--- trunk/reactos/drivers/input/i8042prt/mouse.c	2005-10-21 19:45:28 UTC (rev 18663)
+++ trunk/reactos/drivers/input/i8042prt/mouse.c	2005-10-21 20:28:05 UTC (rev 18664)
@@ -11,13 +11,11 @@
 
 /* INCLUDES ****************************************************************/
 
-#ifndef NDEBUG
+#include "i8042prt.h"
+
 #define NDEBUG
-#endif
 #include <debug.h>
 
-#include "i8042prt.h"
-
 /*
  * These functions are callbacks for filter driver custom interrupt
  * service routines.
@@ -897,3 +895,54 @@
 
 	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;
+}