efi_loader: EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL
authorHeinrich Schuchardt <xypron.glpk@gmx.de>
Tue, 11 Sep 2018 20:38:08 +0000 (22:38 +0200)
committerAlexander Graf <agraf@suse.de>
Sun, 23 Sep 2018 19:55:30 +0000 (21:55 +0200)
This patch implements the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.

The implementation of notification functions is postponed to a later
patch.

Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
Signed-off-by: Alexander Graf <agraf@suse.de>
include/efi_api.h
lib/efi_loader/efi_console.c

index f1c4e59..d423521 100644 (file)
@@ -587,11 +587,67 @@ struct efi_simple_text_output_protocol {
        struct simple_text_output_mode *mode;
 };
 
+#define EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID \
+       EFI_GUID(0xdd9e7534, 0x7762, 0x4698, \
+                0x8c, 0x14, 0xf5, 0x85, 0x17, 0xa6, 0x25, 0xaa)
+
 struct efi_input_key {
        u16 scan_code;
        s16 unicode_char;
 };
 
+#define EFI_SHIFT_STATE_INVALID                0x00000000
+#define EFI_RIGHT_SHIFT_PRESSED                0x00000001
+#define EFI_LEFT_SHIFT_PRESSED         0x00000002
+#define EFI_RIGHT_CONTROL_PRESSED      0x00000004
+#define EFI_LEFT_CONTROL_PRESSED       0x00000008
+#define EFI_RIGHT_ALT_PRESSED          0x00000010
+#define EFI_LEFT_ALT_PRESSED           0x00000020
+#define EFI_RIGHT_LOGO_PRESSED         0x00000040
+#define EFI_LEFT_LOGO_PRESSED          0x00000080
+#define EFI_MENU_KEY_PRESSED           0x00000100
+#define EFI_SYS_REQ_PRESSED            0x00000200
+#define EFI_SHIFT_STATE_VALID          0x80000000
+
+#define EFI_TOGGLE_STATE_INVALID       0x00
+#define EFI_SCROLL_LOCK_ACTIVE         0x01
+#define EFI_NUM_LOCK_ACTIVE            0x02
+#define EFI_CAPS_LOCK_ACTIVE           0x04
+#define EFI_KEY_STATE_EXPOSED          0x40
+#define EFI_TOGGLE_STATE_VALID         0x80
+
+struct efi_key_state {
+       u32 key_shift_state;
+       u8 key_toggle_state;
+};
+
+struct efi_key_data {
+       struct efi_input_key key;
+       struct efi_key_state key_state;
+};
+
+struct efi_simple_text_input_ex_protocol {
+       efi_status_t (EFIAPI *reset) (
+               struct efi_simple_text_input_ex_protocol *this,
+               bool extended_verification);
+       efi_status_t (EFIAPI *read_key_stroke_ex) (
+               struct efi_simple_text_input_ex_protocol *this,
+               struct efi_key_data *key_data);
+       struct efi_event *wait_for_key_ex;
+       efi_status_t (EFIAPI *set_state) (
+               struct efi_simple_text_input_ex_protocol *this,
+               u8 key_toggle_state);
+       efi_status_t (EFIAPI *register_key_notify) (
+               struct efi_simple_text_input_ex_protocol *this,
+               struct efi_key_data *key_data,
+               efi_status_t (EFIAPI *key_notify_function)(
+                       struct efi_key_data *key_data),
+               void **notify_handle);
+       efi_status_t (EFIAPI *unregister_key_notify) (
+               struct efi_simple_text_input_ex_protocol *this,
+               void *notification_handle);
+};
+
 #define EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID \
        EFI_GUID(0x387477c1, 0x69c7, 0x11d2, \
                 0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
index e191e02..d17f0a1 100644 (file)
@@ -42,10 +42,12 @@ static struct cout_mode efi_cout_modes[] = {
        },
 };
 
-const efi_guid_t efi_guid_text_output_protocol =
-                       EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID;
+const efi_guid_t efi_guid_text_input_ex_protocol =
+                       EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
 const efi_guid_t efi_guid_text_input_protocol =
                        EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID;
+const efi_guid_t efi_guid_text_output_protocol =
+                       EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID;
 
 #define cESC '\x1b'
 #define ESC "\x1b"
@@ -391,19 +393,19 @@ struct efi_simple_text_output_protocol efi_con_out = {
 };
 
 static bool key_available;
-static struct efi_input_key next_key;
+static struct efi_key_data next_key;
 
 /**
- * skip_modifiers() - analyze modifiers (shift, alt, ctrl) for function keys
+ * analyze_modifiers() - analyze modifiers (shift, alt, ctrl) for function keys
  *
  * This gets called when we have already parsed CSI.
  *
  * @modifiers:  bitmask (shift, alt, ctrl)
  * @return:    the unmodified code
  */
-static char skip_modifiers(int *modifiers)
+static int analyze_modifiers(struct efi_key_state *key_state)
 {
-       char c, mod = 0, ret = 0;
+       int c, mod = 0, ret = 0;
 
        c = getc();
 
@@ -429,8 +431,17 @@ static char skip_modifiers(int *modifiers)
 out:
        if (mod)
                --mod;
-       if (modifiers)
-               *modifiers = mod;
+       key_state->key_shift_state = EFI_SHIFT_STATE_VALID;
+       if (mod) {
+               if (mod & 1)
+                       key_state->key_shift_state |= EFI_LEFT_SHIFT_PRESSED;
+               if (mod & 2)
+                       key_state->key_shift_state |= EFI_LEFT_ALT_PRESSED;
+               if (mod & 4)
+                       key_state->key_shift_state |= EFI_LEFT_CONTROL_PRESSED;
+               if (mod & 8)
+                       key_state->key_shift_state |= EFI_LEFT_LOGO_PRESSED;
+       }
        if (!ret)
                ret = c;
        return ret;
@@ -442,7 +453,7 @@ out:
  * @key:       - key received
  * Return:     - status code
  */
-static efi_status_t efi_cin_read_key(struct efi_input_key *key)
+static efi_status_t efi_cin_read_key(struct efi_key_data *key)
 {
        efi_status_t ret;
        struct efi_input_key pressed_key = {
@@ -454,6 +465,10 @@ static efi_status_t efi_cin_read_key(struct efi_input_key *key)
        ret = console_read_unicode(&ch);
        if (ret)
                return EFI_NOT_READY;
+
+       key->key_state.key_shift_state = EFI_SHIFT_STATE_INVALID;
+       key->key_state.key_toggle_state = EFI_TOGGLE_STATE_INVALID;
+
        /* We do not support multi-word codes */
        if (ch >= 0x10000)
                ch = '?';
@@ -490,7 +505,7 @@ static efi_status_t efi_cin_read_key(struct efi_input_key *key)
                                pressed_key.scan_code = 5;
                                break;
                        case '1':
-                               ch = skip_modifiers(NULL);
+                               ch = analyze_modifiers(&key->key_state);
                                switch (ch) {
                                case '1'...'5': /* F1 - F5 */
                                        pressed_key.scan_code = ch - '1' + 11;
@@ -510,7 +525,7 @@ static efi_status_t efi_cin_read_key(struct efi_input_key *key)
                                }
                                break;
                        case '2':
-                               ch = skip_modifiers(NULL);
+                               ch = analyze_modifiers(&key->key_state);
                                switch (ch) {
                                case '0'...'1': /* F9 - F10 */
                                        pressed_key.scan_code = ch - '0' + 19;
@@ -525,15 +540,15 @@ static efi_status_t efi_cin_read_key(struct efi_input_key *key)
                                break;
                        case '3': /* DEL */
                                pressed_key.scan_code = 8;
-                               skip_modifiers(NULL);
+                               analyze_modifiers(&key->key_state);
                                break;
                        case '5': /* PG UP */
                                pressed_key.scan_code = 9;
-                               skip_modifiers(NULL);
+                               analyze_modifiers(&key->key_state);
                                break;
                        case '6': /* PG DOWN */
                                pressed_key.scan_code = 10;
-                               skip_modifiers(NULL);
+                               analyze_modifiers(&key->key_state);
                                break;
                        }
                        break;
@@ -542,9 +557,28 @@ static efi_status_t efi_cin_read_key(struct efi_input_key *key)
                /* Backspace */
                ch = 0x08;
        }
-       if (!pressed_key.scan_code)
+       if (pressed_key.scan_code) {
+               key->key_state.key_shift_state |= EFI_SHIFT_STATE_VALID;
+       } else {
                pressed_key.unicode_char = ch;
-       *key = pressed_key;
+
+               /*
+                * Assume left control key for control characters typically
+                * entered using the control key.
+                */
+               if (ch >= 0x01 && ch <= 0x1f) {
+                       key->key_state.key_shift_state =
+                                       EFI_SHIFT_STATE_VALID;
+                       switch (ch) {
+                       case 0x01 ... 0x07:
+                       case 0x0b ... 0x0c:
+                       case 0x0e ... 0x1f:
+                               key->key_state.key_shift_state |=
+                                               EFI_LEFT_CONTROL_PRESSED;
+                       }
+               }
+       }
+       key->key = pressed_key;
 
        return EFI_SUCCESS;
 }
@@ -573,6 +607,170 @@ static void efi_cin_check(void)
 }
 
 /**
+ * efi_cin_empty_buffer() - empty input buffer
+ */
+static void efi_cin_empty_buffer(void)
+{
+       while (tstc())
+               getc();
+       key_available = false;
+}
+
+/**
+ * efi_cin_reset_ex() - reset console input
+ *
+ * @this:                      - the extended simple text input protocol
+ * @extended_verification:     - extended verification
+ *
+ * This function implements the reset service of the
+ * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: old value of the task priority level
+ */
+static efi_status_t EFIAPI efi_cin_reset_ex(
+               struct efi_simple_text_input_ex_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;
+       }
+
+       efi_cin_empty_buffer();
+out:
+       return EFI_EXIT(ret);
+}
+
+/**
+ * efi_cin_read_key_stroke_ex() - read key stroke
+ *
+ * @this:      instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
+ * @key_data:  key read from console
+ * Return:     status code
+ *
+ * This function implements the ReadKeyStrokeEx service of the
+ * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static efi_status_t EFIAPI efi_cin_read_key_stroke_ex(
+               struct efi_simple_text_input_ex_protocol *this,
+               struct efi_key_data *key_data)
+{
+       efi_status_t ret = EFI_SUCCESS;
+
+       EFI_ENTRY("%p, %p", this, key_data);
+
+       /* Check parameters */
+       if (!this || !key_data) {
+               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_data = next_key;
+       key_available = false;
+       efi_con_in.wait_for_key->is_signaled = false;
+out:
+       return EFI_EXIT(ret);
+}
+
+/**
+ * efi_cin_set_state() - set toggle key state
+ *
+ * @this:              instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
+ * @key_toggle_state:  key toggle state
+ * Return:             status code
+ *
+ * This function implements the SetState service of the
+ * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static efi_status_t EFIAPI efi_cin_set_state(
+               struct efi_simple_text_input_ex_protocol *this,
+               u8 key_toggle_state)
+{
+       EFI_ENTRY("%p, %u", this, key_toggle_state);
+       /*
+        * U-Boot supports multiple console input sources like serial and
+        * net console for which a key toggle state cannot be set at all.
+        *
+        * According to the UEFI specification it is allowable to not implement
+        * this service.
+        */
+       return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+/**
+ * efi_cin_register_key_notify() - register key notification function
+ *
+ * @this:                      instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
+ * @key_data:                  key to be notified
+ * @key_notify_function:       function to be called if the key is pressed
+ * @notify_handle:             handle for unregistering the notification
+ * Return:                     status code
+ *
+ * This function implements the SetState service of the
+ * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static efi_status_t EFIAPI efi_cin_register_key_notify(
+               struct efi_simple_text_input_ex_protocol *this,
+               struct efi_key_data *key_data,
+               efi_status_t (EFIAPI *key_notify_function)(
+                       struct efi_key_data *key_data),
+               void **notify_handle)
+{
+       EFI_ENTRY("%p, %p, %p, %p",
+                 this, key_data, key_notify_function, notify_handle);
+       return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+}
+
+/**
+ * efi_cin_unregister_key_notify() - unregister key notification function
+ *
+ * @this:                      instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
+ * @notification_handle:       handle received when registering
+ * Return:                     status code
+ *
+ * This function implements the SetState service of the
+ * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static efi_status_t EFIAPI efi_cin_unregister_key_notify(
+               struct efi_simple_text_input_ex_protocol *this,
+               void *notification_handle)
+{
+       EFI_ENTRY("%p, %p", this, notification_handle);
+       return EFI_EXIT(EFI_INVALID_PARAMETER);
+}
+
+
+/**
  * efi_cin_reset() - drain the input buffer
  *
  * @this:                      instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
@@ -599,16 +797,13 @@ static efi_status_t EFIAPI efi_cin_reset
                goto out;
        }
 
-       /* Empty input buffer */
-       while (tstc())
-               getc();
-       key_available = false;
+       efi_cin_empty_buffer();
 out:
        return EFI_EXIT(ret);
 }
 
 /**
- * efi_cin_reset() - drain the input buffer
+ * efi_cin_read_key_stroke() - read key stroke
  *
  * @this:      instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
  * @key:       key read from console
@@ -644,13 +839,22 @@ static efi_status_t EFIAPI efi_cin_read_key_stroke
                ret = EFI_NOT_READY;
                goto out;
        }
-       *key = next_key;
+       *key = next_key.key;
        key_available = false;
        efi_con_in.wait_for_key->is_signaled = false;
 out:
        return EFI_EXIT(ret);
 }
 
+static struct efi_simple_text_input_ex_protocol efi_con_in_ex = {
+       .reset = efi_cin_reset_ex,
+       .read_key_stroke_ex = efi_cin_read_key_stroke_ex,
+       .wait_for_key_ex = NULL,
+       .set_state = efi_cin_set_state,
+       .register_key_notify = efi_cin_register_key_notify,
+       .unregister_key_notify = efi_cin_unregister_key_notify,
+};
+
 struct efi_simple_text_input_protocol efi_con_in = {
        .reset = efi_cin_reset,
        .read_key_stroke = efi_cin_read_key_stroke,
@@ -721,6 +925,10 @@ int efi_console_register(void)
        if (r != EFI_SUCCESS)
                goto out_of_memory;
        systab.con_in_handle = efi_console_input_obj->handle;
+       r = efi_add_protocol(efi_console_input_obj->handle,
+                            &efi_guid_text_input_ex_protocol, &efi_con_in_ex);
+       if (r != EFI_SUCCESS)
+               goto out_of_memory;
 
        /* Create console events */
        r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK, efi_key_notify,
@@ -729,6 +937,7 @@ int efi_console_register(void)
                printf("ERROR: Failed to register WaitForKey event\n");
                return r;
        }
+       efi_con_in_ex.wait_for_key_ex = efi_con_in.wait_for_key;
        r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
                             efi_console_timer_notify, NULL, NULL,
                             &console_timer_event);