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>
This commit is contained in:
parent
828f6b428e
commit
260dd9eb7e
|
@ -209,9 +209,139 @@ static bool keyboard_cmd(unsigned char cmd)
|
||||||
return false;
|
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)
|
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)
|
unsigned char keyboard_get_scancode(void)
|
||||||
|
@ -344,30 +474,11 @@ static struct console_input_driver cons = {
|
||||||
.input_type = CONSOLE_INPUT_TYPE_EC,
|
.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)
|
void keyboard_init(void)
|
||||||
{
|
{
|
||||||
|
if (keyboard_state != STATE_INIT)
|
||||||
|
return;
|
||||||
|
|
||||||
map = &keyboard_layouts[0];
|
map = &keyboard_layouts[0];
|
||||||
|
|
||||||
/* Initialized keyboard controller. */
|
/* Initialized keyboard controller. */
|
||||||
|
@ -377,21 +488,8 @@ void keyboard_init(void)
|
||||||
/* Enable first PS/2 port */
|
/* Enable first PS/2 port */
|
||||||
i8042_cmd(I8042_CMD_EN_KB);
|
i8042_cmd(I8042_CMD_EN_KB);
|
||||||
|
|
||||||
/* Disable scanning */
|
keyboard_state = STATE_DISABLE_SCAN;
|
||||||
keyboard_cmd(I8042_KBCMD_DEFAULT_DIS);
|
keyboard_time = timer_us(0);
|
||||||
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");
|
|
||||||
|
|
||||||
console_add_input_driver(&cons);
|
console_add_input_driver(&cons);
|
||||||
}
|
}
|
||||||
|
@ -418,4 +516,6 @@ void keyboard_disconnect(void)
|
||||||
|
|
||||||
/* Release keyboard controller driver */
|
/* Release keyboard controller driver */
|
||||||
i8042_close();
|
i8042_close();
|
||||||
|
|
||||||
|
keyboard_state = STATE_INIT;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue