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