From 0dfd13a4c850000450ed9ab135d83e4753be0770 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 11 Sep 2018 22:38:05 +0200 Subject: [PATCH] efi_loader: rework event handling for console Preread the next key in the console timer event. The EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL requires to trigger registered key notification functions based on the prefetched key. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_console.c | 175 +++++++++++++++++++++++++++++++++---------- 1 file changed, 137 insertions(+), 38 deletions(-) diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index 6af0839..e191e02 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -390,21 +390,12 @@ struct efi_simple_text_output_protocol efi_con_out = { .mode = (void*)&efi_con_mode, }; -static efi_status_t EFIAPI efi_cin_reset( - struct efi_simple_text_input_protocol *this, - bool extended_verification) -{ - EFI_ENTRY("%p, %d", this, extended_verification); - - /* Empty input buffer */ - while (tstc()) - getc(); +static bool key_available; +static struct efi_input_key next_key; - return EFI_EXIT(EFI_SUCCESS); -} - -/* - * Analyze modifiers (shift, alt, ctrl) for function keys. +/** + * skip_modifiers() - analyze modifiers (shift, alt, ctrl) for function keys + * * This gets called when we have already parsed CSI. * * @modifiers: bitmask (shift, alt, ctrl) @@ -445,9 +436,13 @@ out: return ret; } -static efi_status_t EFIAPI efi_cin_read_key_stroke( - struct efi_simple_text_input_protocol *this, - struct efi_input_key *key) +/** + * efi_cin_read_key() - read a key from the console input + * + * @key: - key received + * Return: - status code + */ +static efi_status_t efi_cin_read_key(struct efi_input_key *key) { efi_status_t ret; struct efi_input_key pressed_key = { @@ -456,14 +451,9 @@ static efi_status_t EFIAPI efi_cin_read_key_stroke( }; s32 ch; - EFI_ENTRY("%p, %p", this, key); - - /* We don't do interrupts, so check for timers cooperatively */ - efi_timer_check(); - ret = console_read_unicode(&ch); if (ret) - return EFI_EXIT(EFI_NOT_READY); + return EFI_NOT_READY; /* We do not support multi-word codes */ if (ch >= 0x10000) ch = '?'; @@ -556,7 +546,109 @@ static efi_status_t EFIAPI efi_cin_read_key_stroke( pressed_key.unicode_char = ch; *key = pressed_key; - return EFI_EXIT(EFI_SUCCESS); + return EFI_SUCCESS; +} + +/** + * efi_cin_check() - check if keyboard input is available + */ +static void efi_cin_check(void) +{ + efi_status_t ret; + + if (key_available) { + efi_signal_event(efi_con_in.wait_for_key, true); + return; + } + + if (tstc()) { + ret = efi_cin_read_key(&next_key); + if (ret == EFI_SUCCESS) { + key_available = true; + + /* Queue the wait for key event */ + efi_signal_event(efi_con_in.wait_for_key, true); + } + } +} + +/** + * efi_cin_reset() - drain the input buffer + * + * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL + * @extended_verification: allow for exhaustive verification + * Return: status code + * + * This function implements the Reset service of the + * EFI_SIMPLE_TEXT_INPUT_PROTOCOL. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + */ +static efi_status_t EFIAPI efi_cin_reset + (struct efi_simple_text_input_protocol *this, + bool extended_verification) +{ + efi_status_t ret = EFI_SUCCESS; + + EFI_ENTRY("%p, %d", this, extended_verification); + + /* Check parameters */ + if (!this) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + /* Empty input buffer */ + while (tstc()) + getc(); + key_available = false; +out: + return EFI_EXIT(ret); +} + +/** + * efi_cin_reset() - drain the input buffer + * + * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL + * @key: key read from console + * Return: status code + * + * This function implements the ReadKeyStroke service of the + * EFI_SIMPLE_TEXT_INPUT_PROTOCOL. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + */ +static efi_status_t EFIAPI efi_cin_read_key_stroke + (struct efi_simple_text_input_protocol *this, + struct efi_input_key *key) +{ + efi_status_t ret = EFI_SUCCESS; + + EFI_ENTRY("%p, %p", this, key); + + /* Check parameters */ + if (!this || !key) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + /* We don't do interrupts, so check for timers cooperatively */ + efi_timer_check(); + + /* Enable console input after ExitBootServices */ + efi_cin_check(); + + if (!key_available) { + ret = EFI_NOT_READY; + goto out; + } + *key = next_key; + key_available = false; + efi_con_in.wait_for_key->is_signaled = false; +out: + return EFI_EXIT(ret); } struct efi_simple_text_input_protocol efi_con_in = { @@ -567,31 +659,38 @@ struct efi_simple_text_input_protocol efi_con_in = { static struct efi_event *console_timer_event; -static void EFIAPI efi_key_notify(struct efi_event *event, void *context) -{ -} - /* - * Notification function of the console timer event. + * efi_console_timer_notify() - notify the console timer event * - * event: console timer event - * context: not used + * @event: console timer event + * @context: not used */ static void EFIAPI efi_console_timer_notify(struct efi_event *event, void *context) { EFI_ENTRY("%p, %p", event, context); + efi_cin_check(); + EFI_EXIT(EFI_SUCCESS); +} - /* Check if input is available */ - if (tstc()) { - /* Queue the wait for key event */ - efi_con_in.wait_for_key->is_signaled = true; - efi_signal_event(efi_con_in.wait_for_key, true); - } +/** + * efi_key_notify() - notify the wait for key event + * + * @event: wait for key event + * @context: not used + */ +static void EFIAPI efi_key_notify(struct efi_event *event, void *context) +{ + EFI_ENTRY("%p, %p", event, context); + efi_cin_check(); EFI_EXIT(EFI_SUCCESS); } -/* This gets called from do_bootefi_exec(). */ +/** + * efi_console_register() - install the console protocols + * + * This function is called from do_bootefi_exec(). + */ int efi_console_register(void) { efi_status_t r;