efi_loader: rework event handling for console
authorHeinrich Schuchardt <xypron.glpk@gmx.de>
Tue, 11 Sep 2018 20:38:05 +0000 (22:38 +0200)
committerAlexander Graf <agraf@suse.de>
Sun, 23 Sep 2018 19:55:30 +0000 (21:55 +0200)
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 <xypron.glpk@gmx.de>
Signed-off-by: Alexander Graf <agraf@suse.de>
lib/efi_loader/efi_console.c

index 6af0839..e191e02 100644 (file)
@@ -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;