46968b6
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
46968b6
From: =?UTF-8?q?Renaud=20M=C3=A9trich?= <rmetrich@redhat.com>
46968b6
Date: Fri, 18 Dec 2020 15:39:26 +0100
46968b6
Subject: [PATCH] Add 'at_keyboard_fallback_set' var to force the set manually
9fdaa79
MIME-Version: 1.0
9fdaa79
Content-Type: text/plain; charset=UTF-8
9fdaa79
Content-Transfer-Encoding: 8bit
46968b6
46968b6
This seems required with HP DL380p Gen 8 systems.
46968b6
Indeed, with this system, we can see the following sequence:
46968b6
46968b6
1. controller is queried to get current configuration (returns 0x30 which is quite standard)
46968b6
2. controller is queried to get the current keyboard set in used, using code 0xf0 (first part)
46968b6
3. controller answers with 0xfa which means "ACK" (== ok)
46968b6
4. then we send "0" to tell "we want to know which set your are supporting"
46968b6
5. controller answers with 0xfa ("ACK")
46968b6
6. controller should then give us 1, 2, 3 or 0x43, 0x41, 0x3f, but here it gives us 0xfe which means "NACK"
46968b6
46968b6
Since there seems no way to determine the current set, and in fact the
46968b6
controller expects set2 to be used, we need to rely on an environment
46968b6
variable.
46968b6
Everything has been tested on this system: using 0xFE (resend command),
46968b6
making sure we wait for ACK in the 2 steps "write_mode", etc.
46968b6
46968b6
Below is litterature I used to come up with "there is no other
46968b6
solution":
46968b6
- https://wiki.osdev.org/%228042%22_PS/2_Controller
46968b6
- http://www-ug.eecg.toronto.edu/msl/nios_devices/datasheets/PS2%20Keyboard%20Protocol.htm
46968b6
- http://www.s100computers.com/My%20System%20Pages/MSDOS%20Board/PC%20Keyboard.pdf
9fdaa79
9fdaa79
Signed-off-by: Renaud M├ętrich <rmetrich@redhat.com>
9fdaa79
Signed-off-by: Robbie Harwood <rharwood@redhat.com>
46968b6
---
46968b6
 grub-core/term/at_keyboard.c | 121 ++++++++++++++++++++++++++++++++++---------
46968b6
 1 file changed, 96 insertions(+), 25 deletions(-)
46968b6
46968b6
diff --git a/grub-core/term/at_keyboard.c b/grub-core/term/at_keyboard.c
e622855
index 2601438260..dac0f946fe 100644
46968b6
--- a/grub-core/term/at_keyboard.c
46968b6
+++ b/grub-core/term/at_keyboard.c
46968b6
@@ -31,6 +31,7 @@ GRUB_MOD_LICENSE ("GPLv3+");
46968b6
 static grub_uint8_t grub_keyboard_controller_orig;
46968b6
 static grub_uint8_t grub_keyboard_orig_set;
46968b6
 struct grub_ps2_state ps2_state;
46968b6
+static int fallback_set;
46968b6
 
46968b6
 static int ping_sent;
46968b6
 
46968b6
@@ -76,6 +77,8 @@ at_command (grub_uint8_t data)
46968b6
 	break;
46968b6
       return 0;
46968b6
     }
46968b6
+  if (i == GRUB_AT_TRIES)
46968b6
+    grub_dprintf ("atkeyb", "at_command() timed out! (stopped after %d tries)\n", i);
46968b6
   return (i != GRUB_AT_TRIES);
46968b6
 }
46968b6
 
46968b6
@@ -105,6 +108,21 @@ grub_keyboard_controller_read (void)
46968b6
 
46968b6
 #endif
46968b6
 
46968b6
+static int
46968b6
+resend_last_result (void)
46968b6
+{
46968b6
+  grub_uint8_t ret;
46968b6
+  keyboard_controller_wait_until_ready ();
46968b6
+  grub_dprintf ("atkeyb", "resend_last_result: sending 0xfe\n");
46968b6
+  grub_outb (0xfe, KEYBOARD_REG_DATA);
46968b6
+  ret = wait_ack ();
46968b6
+  grub_dprintf ("atkeyb", "resend_last_result: wait_ack() returned 0x%x\n", ret);
46968b6
+  keyboard_controller_wait_until_ready ();
46968b6
+  ret = grub_inb (KEYBOARD_REG_DATA);
46968b6
+  grub_dprintf ("atkeyb", "resend_last_result: read 0x%x from controller\n", ret);
46968b6
+  return ret;
46968b6
+}
46968b6
+
46968b6
 static int
46968b6
 write_mode (int mode)
46968b6
 {
46968b6
@@ -113,11 +131,14 @@ write_mode (int mode)
46968b6
     {
46968b6
       grub_uint8_t ack;
46968b6
       keyboard_controller_wait_until_ready ();
46968b6
+      grub_dprintf ("atkeyb", "write_mode: sending 0xf0\n");
46968b6
       grub_outb (0xf0, KEYBOARD_REG_DATA);
46968b6
       keyboard_controller_wait_until_ready ();
46968b6
+      grub_dprintf ("atkeyb", "write_mode: sending mode %d\n", mode);
46968b6
       grub_outb (mode, KEYBOARD_REG_DATA);
46968b6
       keyboard_controller_wait_until_ready ();
46968b6
       ack = wait_ack ();
46968b6
+      grub_dprintf ("atkeyb", "write_mode: wait_ack() returned 0x%x\n", ack);
46968b6
       if (ack == GRUB_AT_NACK)
46968b6
 	continue;
46968b6
       if (ack == GRUB_AT_ACK)
46968b6
@@ -125,6 +146,9 @@ write_mode (int mode)
46968b6
       return 0;
46968b6
     }
46968b6
 
46968b6
+  if (i == GRUB_AT_TRIES)
46968b6
+    grub_dprintf ("atkeyb", "write_mode() timed out! (stopped after %d tries)\n", i);
46968b6
+
46968b6
   return (i != GRUB_AT_TRIES);
46968b6
 }
46968b6
 
46968b6
@@ -132,31 +156,66 @@ static int
46968b6
 query_mode (void)
46968b6
 {
46968b6
   grub_uint8_t ret;
46968b6
+  grub_uint64_t endtime;
46968b6
+  unsigned i;
46968b6
   int e;
46968b6
+  char *envvar;
46968b6
 
46968b6
-  e = write_mode (0);
46968b6
-  if (!e) {
46968b6
-    grub_dprintf("atkeyb", "query_mode: write_mode(0) failed\n");
46968b6
-    return 0;
46968b6
-  }
46968b6
+  for (i = 0; i < GRUB_AT_TRIES; i++) {
46968b6
+    grub_dprintf ("atkeyb", "query_mode: sending command to controller\n");
46968b6
+    e = write_mode (0);
46968b6
+    if (!e) {
46968b6
+      grub_dprintf ("atkeyb", "query_mode: write_mode(0) failed\n");
46968b6
+      return 0;
46968b6
+    }
46968b6
 
46968b6
-  do {
46968b6
-    keyboard_controller_wait_until_ready ();
46968b6
-    ret = grub_inb (KEYBOARD_REG_DATA);
46968b6
-  } while (ret == GRUB_AT_ACK);
46968b6
-  /* QEMU translates the set even in no-translate mode.  */
46968b6
-  if (ret == 0x43 || ret == 1) {
46968b6
-    grub_dprintf("atkeyb", "query_mode: returning 1 (ret=0x%x)\n", ret);
46968b6
-    return 1;
46968b6
-  }
46968b6
-  if (ret == 0x41 || ret == 2) {
46968b6
-    grub_dprintf("atkeyb", "query_mode: returning 2 (ret=0x%x)\n", ret);
46968b6
-    return 2;
46968b6
+    endtime = grub_get_time_ms () + 20;
46968b6
+    do {
46968b6
+      keyboard_controller_wait_until_ready ();
46968b6
+      ret = grub_inb (KEYBOARD_REG_DATA);
46968b6
+      grub_dprintf ("atkeyb", "query_mode/loop: read 0x%x from controller\n", ret);
46968b6
+    } while ((ret == GRUB_AT_ACK || ret == GRUB_AT_NACK) && grub_get_time_ms () < endtime);
46968b6
+    if (ret == 0xfe) {
46968b6
+      grub_dprintf ("atkeyb", "query_mode: asking controller to resend last result\n");
46968b6
+      ret = resend_last_result();
46968b6
+      grub_dprintf ("atkeyb", "query_mode: read 0x%x from controller\n", ret);
46968b6
+    }
46968b6
+    /* QEMU translates the set even in no-translate mode.  */
46968b6
+    if (ret == 0x43 || ret == 1) {
46968b6
+      grub_dprintf ("atkeyb", "query_mode: controller returned 0x%x, returning 1\n", ret);
46968b6
+      return 1;
46968b6
+    }
46968b6
+    if (ret == 0x41 || ret == 2) {
46968b6
+      grub_dprintf ("atkeyb", "query_mode: controller returned 0x%x, returning 2\n", ret);
46968b6
+      return 2;
46968b6
+    }
46968b6
+    if (ret == 0x3f || ret == 3) {
46968b6
+      grub_dprintf ("atkeyb", "query_mode: controller returned 0x%x, returning 3\n", ret);
46968b6
+      return 3;
46968b6
+    }
46968b6
+    grub_dprintf ("atkeyb", "query_mode: controller returned unexpected value 0x%x, retrying\n", ret);
46968b6
   }
46968b6
-  if (ret == 0x3f || ret == 3) {
46968b6
-    grub_dprintf("atkeyb", "query_mode: returning 3 (ret=0x%x)\n", ret);
46968b6
-    return 3;
46968b6
+
46968b6
+  /*
46968b6
+   * Falling here means we tried querying and the controller returned something
46968b6
+   * we don't understand, try to use 'at_keyboard_fallback_set' if it exists,
46968b6
+   * otherwise return 0.
46968b6
+   */
46968b6
+  envvar = grub_env_get ("at_keyboard_fallback_set");
46968b6
+  if (envvar) {
46968b6
+    fallback_set = grub_strtoul (envvar, 0, 10);
46968b6
+    if ((grub_errno) || (fallback_set < 1) || (fallback_set > 3)) {
46968b6
+      grub_dprintf ("atkeyb", "WARNING: ignoring unexpected value '%s' for '%s' variable\n",
46968b6
+		    envvar, "at_keyboard_fallback_set");
46968b6
+      fallback_set = 0;
46968b6
+    } else {
46968b6
+      grub_dprintf ("atkeyb", "query_mode: '%s' specified in environment, returning %d\n",
46968b6
+		    "at_keyboard_fallback_set", fallback_set);
46968b6
+    }
46968b6
+    return fallback_set;
46968b6
   }
46968b6
+  grub_dprintf ("atkeyb", "WARNING: no '%s' specified in environment, returning 0\n",
46968b6
+		"at_keyboard_fallback_set");
46968b6
   return 0;
46968b6
 }
46968b6
 
46968b6
@@ -165,14 +224,25 @@ set_scancodes (void)
46968b6
 {
46968b6
   /* You must have visited computer museum. Keyboard without scancode set
46968b6
      knowledge. Assume XT. */
46968b6
-  if (!grub_keyboard_orig_set)
46968b6
-    {
46968b6
-      grub_dprintf ("atkeyb", "No sets support assumed\n");
46968b6
-      ps2_state.current_set = 1;
46968b6
+  if (!grub_keyboard_orig_set) {
46968b6
+    if (fallback_set) {
46968b6
+      grub_dprintf ("atkeyb", "No sets support assumed but set forced to %d\n", fallback_set);
46968b6
+      ps2_state.current_set = fallback_set;
46968b6
       return;
46968b6
     }
46968b6
+    grub_dprintf ("atkeyb", "No sets support assumed, forcing to set 1\n");
46968b6
+    ps2_state.current_set = 1;
46968b6
+    return;
46968b6
+  }
46968b6
 
46968b6
 #if !USE_SCANCODE_SET
46968b6
+  if (fallback_set) {
46968b6
+    grub_dprintf ("atkeyb", "queried set is %d but set forced to %d\n",
46968b6
+		  grub_keyboard_orig_set, fallback_set);
46968b6
+    ps2_state.current_set = fallback_set;
46968b6
+    return;
46968b6
+  }
46968b6
+
46968b6
   if ((grub_keyboard_controller_orig & KEYBOARD_AT_TRANSLATE) == KEYBOARD_AT_TRANSLATE) {
46968b6
     grub_dprintf ("atkeyb", "queried set is %d but keyboard in Translate mode, so actually in set 1\n", grub_keyboard_orig_set);
46968b6
     ps2_state.current_set = 1;
46968b6
@@ -261,6 +331,7 @@ grub_at_keyboard_getkey (struct grub_term_input *term __attribute__ ((unused)))
46968b6
 static void
46968b6
 grub_keyboard_controller_init (void)
46968b6
 {
46968b6
+  grub_dprintf ("atkeyb", "initializing the controller\n");
46968b6
   ps2_state.at_keyboard_status = 0;
46968b6
   /* Drain input buffer. */
46968b6
   while (1)
46968b6
@@ -282,6 +353,7 @@ grub_keyboard_controller_init (void)
46968b6
   grub_keyboard_controller_orig = grub_keyboard_controller_read ();
46968b6
   grub_dprintf ("atkeyb", "grub_keyboard_controller_orig = 0x%x\n", grub_keyboard_controller_orig);
46968b6
   grub_keyboard_orig_set = query_mode ();
46968b6
+  grub_dprintf ("atkeyb", "grub_keyboard_orig_set = %d\n", grub_keyboard_orig_set);
46968b6
 #endif
46968b6
   set_scancodes ();
46968b6
   keyboard_controller_led (ps2_state.led_status);
46968b6
@@ -329,7 +401,6 @@ grub_at_restore_hw (void)
46968b6
   return GRUB_ERR_NONE;
46968b6
 }
46968b6
 
46968b6
-
46968b6
 static struct grub_term_input grub_at_keyboard_term =
46968b6
   {
46968b6
     .name = "at_keyboard",