summaryrefslogtreecommitdiff
path: root/payloads
diff options
context:
space:
mode:
authorNico Huber <nico.h@gmx.de>2020-11-01 16:44:22 +0100
committerHung-Te Lin <hungte@chromium.org>2020-12-24 08:19:48 +0000
commit260dd9eb7e91de3a14ba095eccc7766c6ed61b2f (patch)
tree30a84873cf166fde663e347b5b179a2548fba81c /payloads
parent828f6b428e541363b386d579afc1740059d01739 (diff)
libpayload/keyboard: Turn init sequence into a state machine
We'll process the init sequence as part of the polling loop. This should have several advantages: * It eases error handling, i.e. we can return to an earlier state. * We don't have to stall initialization when a keyboard takes a little longer. * Generally, these keyboards can be hot-plugged (albeit not by design). Change-Id: I9cf5cf31eb420b3994bec20e56a72d37f3d2996e Signed-off-by: Nico Huber <nico.h@gmx.de> Reviewed-on: https://review.coreboot.org/c/coreboot/+/47086 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Angel Pons <th3fanbus@gmail.com>
Diffstat (limited to 'payloads')
-rw-r--r--payloads/libpayload/drivers/i8042/keyboard.c176
1 files changed, 138 insertions, 38 deletions
diff --git a/payloads/libpayload/drivers/i8042/keyboard.c b/payloads/libpayload/drivers/i8042/keyboard.c
index 9da3902168..dcba031268 100644
--- a/payloads/libpayload/drivers/i8042/keyboard.c
+++ b/payloads/libpayload/drivers/i8042/keyboard.c
@@ -209,9 +209,139 @@ static bool keyboard_cmd(unsigned char cmd)
return false;
}
+static bool set_scancode_set(const unsigned char set)
+{
+ bool ret;
+
+ if (set < 1 || set > 3)
+ return false;
+
+ ret = keyboard_cmd(I8042_KBCMD_SET_SCANCODE);
+ if (!ret) {
+ printf("ERROR: Keyboard set scancode failed!\n");
+ return ret;
+ }
+
+ ret = keyboard_cmd(set);
+ if (!ret) {
+ printf("ERROR: Keyboard scancode set#%u failed!\n", set);
+ return ret;
+ }
+
+ return ret;
+}
+
+static enum keyboard_state {
+ STATE_INIT = 0,
+ STATE_DISABLE_SCAN,
+ STATE_DRAIN_INPUT,
+ STATE_DISABLE_TRANSLATION,
+ STATE_CONFIGURE,
+ STATE_CONFIGURE_SET1,
+ STATE_ENABLE_TRANSLATION,
+ STATE_ENABLE_SCAN,
+ STATE_RUNNING,
+ STATE_IGNORE,
+} keyboard_state;
+
+static uint64_t keyboard_time;
+
+static void keyboard_poll(void)
+{
+ enum keyboard_state next_state = keyboard_state;
+ unsigned int i;
+
+ switch (keyboard_state) {
+
+ case STATE_INIT:
+ /* Wait until keyboard_init() has been called. */
+ break;
+
+ case STATE_DISABLE_SCAN:
+ (void)keyboard_cmd(I8042_KBCMD_DEFAULT_DIS);
+ next_state = STATE_DRAIN_INPUT;
+ break;
+
+ case STATE_DRAIN_INPUT:
+ /* Limit number of bytes drained per poll. */
+ for (i = 0; i < 50 && i8042_data_ready_ps2(); ++i)
+ (void)i8042_read_data_ps2();
+ if (i == 0)
+ next_state = STATE_DISABLE_TRANSLATION;
+ break;
+
+ case STATE_DISABLE_TRANSLATION:
+ /* Be opportunistic and assume it's disabled on failure. */
+ (void)i8042_set_kbd_translation(false);
+ next_state = STATE_CONFIGURE;
+ break;
+
+ case STATE_CONFIGURE:
+ if (set_scancode_set(2))
+ next_state = STATE_ENABLE_TRANSLATION;
+ else
+ next_state = STATE_CONFIGURE_SET1;
+ break;
+
+ case STATE_CONFIGURE_SET1:
+ if (!set_scancode_set(1)) {
+ printf("ERROR: Keyboard failed to set any scancode set.\n");
+ next_state = STATE_DISABLE_SCAN;
+ break;
+ }
+
+ next_state = STATE_ENABLE_SCAN;
+ break;
+
+ case STATE_ENABLE_TRANSLATION:
+ if (i8042_set_kbd_translation(true) != 0) {
+ printf("ERROR: Keyboard controller set translation failed!\n");
+ next_state = STATE_DISABLE_SCAN;
+ break;
+ }
+
+ next_state = STATE_ENABLE_SCAN;
+ break;
+
+ case STATE_ENABLE_SCAN:
+ if (!keyboard_cmd(I8042_KBCMD_EN)) {
+ printf("ERROR: Keyboard enable scanning failed!\n");
+ next_state = STATE_DISABLE_SCAN;
+ break;
+ }
+
+ next_state = STATE_RUNNING;
+ break;
+
+ case STATE_RUNNING:
+ /* TODO: Use echo command to detect detach. */
+ break;
+
+ case STATE_IGNORE:
+ /* TODO: Try again after timeout if it ever seems useful. */
+ break;
+
+ }
+
+ switch (next_state) {
+ case STATE_INIT:
+ case STATE_RUNNING:
+ case STATE_IGNORE:
+ break;
+ default:
+ if (timer_us(keyboard_time) > 30*1000*1000)
+ next_state = STATE_IGNORE;
+ break;
+ }
+
+ if (keyboard_state != next_state)
+ keyboard_state = next_state;
+}
+
bool keyboard_havechar(void)
{
- return i8042_data_ready_ps2();
+ keyboard_poll();
+ return keyboard_state == STATE_RUNNING && i8042_data_ready_ps2();
}
unsigned char keyboard_get_scancode(void)
@@ -344,30 +474,11 @@ static struct console_input_driver cons = {
.input_type = CONSOLE_INPUT_TYPE_EC,
};
-static bool set_scancode_set(const unsigned char set)
-{
- bool ret;
-
- if (set < 1 || set > 3)
- return false;
-
- ret = keyboard_cmd(I8042_KBCMD_SET_SCANCODE);
- if (!ret) {
- printf("ERROR: Keyboard set scancode failed!\n");
- return ret;
- }
-
- ret = keyboard_cmd(set);
- if (!ret) {
- printf("ERROR: Keyboard scancode set#%u failed!\n", set);
- return ret;
- }
-
- return ret;
-}
-
void keyboard_init(void)
{
+ if (keyboard_state != STATE_INIT)
+ return;
+
map = &keyboard_layouts[0];
/* Initialized keyboard controller. */
@@ -377,21 +488,8 @@ void keyboard_init(void)
/* Enable first PS/2 port */
i8042_cmd(I8042_CMD_EN_KB);
- /* Disable scanning */
- keyboard_cmd(I8042_KBCMD_DEFAULT_DIS);
- keyboard_drain_input();
-
- i8042_set_kbd_translation(false);
-
- if (set_scancode_set(2))
- i8042_set_kbd_translation(true);
- else if (!set_scancode_set(1))
- return; /* give up, leave keyboard input disabled */
-
- /* Enable scanning */
- const bool ret = keyboard_cmd(I8042_KBCMD_EN);
- if (!ret)
- printf("ERROR: Keyboard enable scanning failed!\n");
+ keyboard_state = STATE_DISABLE_SCAN;
+ keyboard_time = timer_us(0);
console_add_input_driver(&cons);
}
@@ -418,4 +516,6 @@ void keyboard_disconnect(void)
/* Release keyboard controller driver */
i8042_close();
+
+ keyboard_state = STATE_INIT;
}