| 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 |
| |
| 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 |
| |
| 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 69d99b61df5..c805cccbdde 100644 |
| |
| |
| @@ -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; |
| |
| @@ -76,6 +77,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); |
| } |
| |
| @@ -105,6 +108,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) |
| { |
| @@ -113,11 +131,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) |
| @@ -125,6 +146,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); |
| } |
| |
| @@ -132,31 +156,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; |
| } |
| |
| @@ -165,14 +224,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; |
| @@ -261,6 +331,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) |
| @@ -282,6 +353,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); |
| @@ -329,7 +401,6 @@ grub_at_restore_hw (void) |
| return GRUB_ERR_NONE; |
| } |
| |
| - |
| static struct grub_term_input grub_at_keyboard_term = |
| { |
| .name = "at_keyboard", |