f4c76c0
From 2eeb9c48e4dfd7cd22a214a9cb5dd37094278240 Mon Sep 17 00:00:00 2001
f4c76c0
From: Peter Jones <pjones@redhat.com>
f4c76c0
Date: Mon, 3 Feb 2014 15:21:46 -0500
f4c76c0
Subject: [PATCH 41/74] Make CTRL and ALT keys work as expected on EFI systems
f4c76c0
 (version 5).
f4c76c0
f4c76c0
This is version 4.
f4c76c0
f4c76c0
Changes from version 1:
f4c76c0
- handles SHIFT as a modifier
f4c76c0
- handles F11 and F12 keys
f4c76c0
- uses the handle provided by the system table to find our _EX protocol.
f4c76c0
f4c76c0
Changes from version 2:
f4c76c0
- eliminate duplicate keycode translation.
f4c76c0
f4c76c0
Changes from version 3:
f4c76c0
- Do not add the shift modifier for any ascii character between space
f4c76c0
  (0x20) and DEL (0x7f); the combination of the modifier and many of the
f4c76c0
  keys causes it not to be recognized at all.  Specifically, if we
f4c76c0
  include the modifier on any querty punctuation character, i.e.
f4c76c0
  anything the string "~!@#$%^&*()_+{}|:\"<>?" represents in C, it stops
f4c76c0
  being recognized whatsoever.
f4c76c0
f4c76c0
Changes from version 4:
f4c76c0
- Always initialize term->data from locate protocol (i.e. make it
f4c76c0
  unconditional.)
f4c76c0
f4c76c0
Signed-off-by: Peter Jones <pjones@redhat.com>
f4c76c0
---
f4c76c0
 grub-core/term/efi/console.c | 118 +++++++++++++++++++++++++++++++++++--------
f4c76c0
 include/grub/efi/api.h       |  65 +++++++++++++++++++++++-
f4c76c0
 2 files changed, 161 insertions(+), 22 deletions(-)
f4c76c0
f4c76c0
diff --git a/grub-core/term/efi/console.c b/grub-core/term/efi/console.c
f4c76c0
index a37eb84..677eab5 100644
f4c76c0
--- a/grub-core/term/efi/console.c
f4c76c0
+++ b/grub-core/term/efi/console.c
f4c76c0
@@ -104,26 +104,12 @@ const unsigned efi_codes[] =
f4c76c0
     GRUB_TERM_KEY_DC, GRUB_TERM_KEY_PPAGE, GRUB_TERM_KEY_NPAGE, GRUB_TERM_KEY_F1,
f4c76c0
     GRUB_TERM_KEY_F2, GRUB_TERM_KEY_F3, GRUB_TERM_KEY_F4, GRUB_TERM_KEY_F5,
f4c76c0
     GRUB_TERM_KEY_F6, GRUB_TERM_KEY_F7, GRUB_TERM_KEY_F8, GRUB_TERM_KEY_F9,
f4c76c0
-    GRUB_TERM_KEY_F10, 0, 0, '\e'
f4c76c0
+    GRUB_TERM_KEY_F10, GRUB_TERM_KEY_F10, GRUB_TERM_KEY_F11, '\e'
f4c76c0
   };
f4c76c0
 
f4c76c0
-
f4c76c0
 static int
f4c76c0
-grub_console_getkey (struct grub_term_input *term __attribute__ ((unused)))
f4c76c0
+grub_efi_translate_key (grub_efi_input_key_t key)
f4c76c0
 {
f4c76c0
-  grub_efi_simple_input_interface_t *i;
f4c76c0
-  grub_efi_input_key_t key;
f4c76c0
-  grub_efi_status_t status;
f4c76c0
-
f4c76c0
-  if (grub_efi_is_finished)
f4c76c0
-    return 0;
f4c76c0
-
f4c76c0
-  i = grub_efi_system_table->con_in;
f4c76c0
-  status = efi_call_2 (i->read_key_stroke, i, &key);
f4c76c0
-
f4c76c0
-  if (status != GRUB_EFI_SUCCESS)
f4c76c0
-    return GRUB_TERM_NO_KEY;
f4c76c0
-
f4c76c0
   if (key.scan_code == 0)
f4c76c0
     {
f4c76c0
       /* Some firmware implementations use VT100-style codes against the spec.
f4c76c0
@@ -139,9 +125,98 @@ grub_console_getkey (struct grub_term_input *term __attribute__ ((unused)))
f4c76c0
   else if (key.scan_code < ARRAY_SIZE (efi_codes))
f4c76c0
     return efi_codes[key.scan_code];
f4c76c0
 
f4c76c0
+  if (key.unicode_char >= 0x20 && key.unicode_char <= 0x7f)
f4c76c0
+    return key.unicode_char;
f4c76c0
+
f4c76c0
   return GRUB_TERM_NO_KEY;
f4c76c0
 }
f4c76c0
 
f4c76c0
+static int
f4c76c0
+grub_console_getkey_con (struct grub_term_input *term __attribute__ ((unused)))
f4c76c0
+{
f4c76c0
+  grub_efi_simple_input_interface_t *i;
f4c76c0
+  grub_efi_input_key_t key;
f4c76c0
+  grub_efi_status_t status;
f4c76c0
+
f4c76c0
+  i = grub_efi_system_table->con_in;
f4c76c0
+  status = efi_call_2 (i->read_key_stroke, i, &key);
f4c76c0
+
f4c76c0
+  if (status != GRUB_EFI_SUCCESS)
f4c76c0
+    return GRUB_TERM_NO_KEY;
f4c76c0
+
f4c76c0
+  return grub_efi_translate_key(key);
f4c76c0
+}
f4c76c0
+
f4c76c0
+static int
f4c76c0
+grub_console_getkey_ex(struct grub_term_input *term)
f4c76c0
+{
f4c76c0
+  grub_efi_key_data_t key_data;
f4c76c0
+  grub_efi_status_t status;
f4c76c0
+  grub_efi_uint32_t kss;
f4c76c0
+  int key = -1;
f4c76c0
+
f4c76c0
+  grub_efi_simple_text_input_ex_interface_t *text_input = term->data;
f4c76c0
+
f4c76c0
+  status = efi_call_2 (text_input->read_key_stroke, text_input, &key_data);
f4c76c0
+
f4c76c0
+  if (status != GRUB_EFI_SUCCESS)
f4c76c0
+    return GRUB_TERM_NO_KEY;
f4c76c0
+
f4c76c0
+  kss = key_data.key_state.key_shift_state;
f4c76c0
+  key = grub_efi_translate_key(key_data.key);
f4c76c0
+
f4c76c0
+  if (key == GRUB_TERM_NO_KEY)
f4c76c0
+    return GRUB_TERM_NO_KEY;
f4c76c0
+
f4c76c0
+  if (kss & GRUB_EFI_SHIFT_STATE_VALID)
f4c76c0
+    {
f4c76c0
+      if ((kss & GRUB_EFI_LEFT_SHIFT_PRESSED
f4c76c0
+	   || kss & GRUB_EFI_RIGHT_SHIFT_PRESSED)
f4c76c0
+	  && !(key >= 0x20 && key <= 0x7f))
f4c76c0
+	key |= GRUB_TERM_SHIFT;
f4c76c0
+      if (kss & GRUB_EFI_LEFT_ALT_PRESSED || kss & GRUB_EFI_RIGHT_ALT_PRESSED)
f4c76c0
+	key |= GRUB_TERM_ALT;
f4c76c0
+      if (kss & GRUB_EFI_LEFT_CONTROL_PRESSED
f4c76c0
+	  || kss & GRUB_EFI_RIGHT_CONTROL_PRESSED)
f4c76c0
+	key |= GRUB_TERM_CTRL;
f4c76c0
+    }
f4c76c0
+
f4c76c0
+  return key;
f4c76c0
+}
f4c76c0
+
f4c76c0
+static grub_err_t
f4c76c0
+grub_efi_console_input_init (struct grub_term_input *term)
f4c76c0
+{
f4c76c0
+  grub_efi_guid_t text_input_ex_guid =
f4c76c0
+    GRUB_EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
f4c76c0
+
f4c76c0
+  if (grub_efi_is_finished)
f4c76c0
+    return 0;
f4c76c0
+
f4c76c0
+  grub_efi_simple_text_input_ex_interface_t *text_input = term->data;
f4c76c0
+  if (text_input)
f4c76c0
+    return 0;
f4c76c0
+
f4c76c0
+  text_input = grub_efi_open_protocol(grub_efi_system_table->console_in_handler,
f4c76c0
+				      &text_input_ex_guid,
f4c76c0
+				      GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
f4c76c0
+  term->data = (void *)text_input;
f4c76c0
+
f4c76c0
+  return 0;
f4c76c0
+}
f4c76c0
+
f4c76c0
+static int
f4c76c0
+grub_console_getkey (struct grub_term_input *term)
f4c76c0
+{
f4c76c0
+  if (grub_efi_is_finished)
f4c76c0
+    return 0;
f4c76c0
+
f4c76c0
+  if (term->data)
f4c76c0
+    return grub_console_getkey_ex(term);
f4c76c0
+  else
f4c76c0
+    return grub_console_getkey_con(term);
f4c76c0
+}
f4c76c0
+
f4c76c0
 static struct grub_term_coordinate
f4c76c0
 grub_console_getwh (struct grub_term_output *term __attribute__ ((unused)))
f4c76c0
 {
f4c76c0
@@ -243,7 +318,7 @@ grub_console_setcursor (struct grub_term_output *term __attribute__ ((unused)),
f4c76c0
 }
f4c76c0
 
f4c76c0
 static grub_err_t
f4c76c0
-grub_efi_console_init (struct grub_term_output *term)
f4c76c0
+grub_efi_console_output_init (struct grub_term_output *term)
f4c76c0
 {
f4c76c0
   grub_efi_set_text_mode (1);
f4c76c0
   grub_console_setcursor (term, 1);
f4c76c0
@@ -251,7 +326,7 @@ grub_efi_console_init (struct grub_term_output *term)
f4c76c0
 }
f4c76c0
 
f4c76c0
 static grub_err_t
f4c76c0
-grub_efi_console_fini (struct grub_term_output *term)
f4c76c0
+grub_efi_console_output_fini (struct grub_term_output *term)
f4c76c0
 {
f4c76c0
   grub_console_setcursor (term, 0);
f4c76c0
   grub_efi_set_text_mode (0);
f4c76c0
@@ -262,13 +337,14 @@ static struct grub_term_input grub_console_term_input =
f4c76c0
   {
f4c76c0
     .name = "console",
f4c76c0
     .getkey = grub_console_getkey,
f4c76c0
+    .init = grub_efi_console_input_init,
f4c76c0
   };
f4c76c0
 
f4c76c0
 static struct grub_term_output grub_console_term_output =
f4c76c0
   {
f4c76c0
     .name = "console",
f4c76c0
-    .init = grub_efi_console_init,
f4c76c0
-    .fini = grub_efi_console_fini,
f4c76c0
+    .init = grub_efi_console_output_init,
f4c76c0
+    .fini = grub_efi_console_output_fini,
f4c76c0
     .putchar = grub_console_putchar,
f4c76c0
     .getwh = grub_console_getwh,
f4c76c0
     .getxy = grub_console_getxy,
f4c76c0
@@ -291,8 +367,8 @@ grub_console_init (void)
f4c76c0
       return;
f4c76c0
     }
f4c76c0
 
f4c76c0
-  grub_term_register_input ("console", &grub_console_term_input);
f4c76c0
   grub_term_register_output ("console", &grub_console_term_output);
f4c76c0
+  grub_term_register_input ("console", &grub_console_term_input);
f4c76c0
 }
f4c76c0
 
f4c76c0
 void
f4c76c0
diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h
f4c76c0
index 1a5e38c..029ee92 100644
f4c76c0
--- a/include/grub/efi/api.h
f4c76c0
+++ b/include/grub/efi/api.h
f4c76c0
@@ -111,7 +111,7 @@
f4c76c0
     { 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b } \
f4c76c0
   }
f4c76c0
 
f4c76c0
-#define EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID \
f4c76c0
+#define GRUB_EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID \
f4c76c0
   { 0xdd9e7534, 0x7762, 0x4698, \
f4c76c0
     { 0x8c, 0x14, 0xf5, 0x85, 0x17, 0xa6, 0x25, 0xaa } \
f4c76c0
   }
f4c76c0
@@ -952,6 +952,32 @@ struct grub_efi_input_key
f4c76c0
 };
f4c76c0
 typedef struct grub_efi_input_key grub_efi_input_key_t;
f4c76c0
 
f4c76c0
+typedef grub_efi_uint8_t grub_efi_key_toggle_state_t;
f4c76c0
+struct grub_efi_key_state
f4c76c0
+{
f4c76c0
+	grub_efi_uint32_t key_shift_state;
f4c76c0
+	grub_efi_key_toggle_state_t key_toggle_state;
f4c76c0
+};
f4c76c0
+typedef struct grub_efi_key_state grub_efi_key_state_t;
f4c76c0
+
f4c76c0
+#define GRUB_EFI_SHIFT_STATE_VALID     0x80000000
f4c76c0
+#define GRUB_EFI_RIGHT_SHIFT_PRESSED   0x00000001
f4c76c0
+#define GRUB_EFI_LEFT_SHIFT_PRESSED    0x00000002
f4c76c0
+#define GRUB_EFI_RIGHT_CONTROL_PRESSED 0x00000004
f4c76c0
+#define GRUB_EFI_LEFT_CONTROL_PRESSED  0x00000008
f4c76c0
+#define GRUB_EFI_RIGHT_ALT_PRESSED     0x00000010
f4c76c0
+#define GRUB_EFI_LEFT_ALT_PRESSED      0x00000020
f4c76c0
+#define GRUB_EFI_RIGHT_LOGO_PRESSED    0x00000040
f4c76c0
+#define GRUB_EFI_LEFT_LOGO_PRESSED     0x00000080
f4c76c0
+#define GRUB_EFI_MENU_KEY_PRESSED      0x00000100
f4c76c0
+#define GRUB_EFI_SYS_REQ_PRESSED       0x00000200
f4c76c0
+
f4c76c0
+#define GRUB_EFI_TOGGLE_STATE_VALID 0x80
f4c76c0
+#define GRUB_EFI_KEY_STATE_EXPOSED  0x40
f4c76c0
+#define GRUB_EFI_SCROLL_LOCK_ACTIVE 0x01
f4c76c0
+#define GRUB_EFI_NUM_LOCK_ACTIVE    0x02
f4c76c0
+#define GRUB_EFI_CAPS_LOCK_ACTIVE   0x04
f4c76c0
+
f4c76c0
 struct grub_efi_simple_text_output_mode
f4c76c0
 {
f4c76c0
   grub_efi_int32_t max_mode;
f4c76c0
@@ -1294,6 +1320,43 @@ struct grub_efi_simple_input_interface
f4c76c0
 };
f4c76c0
 typedef struct grub_efi_simple_input_interface grub_efi_simple_input_interface_t;
f4c76c0
 
f4c76c0
+struct grub_efi_key_data {
f4c76c0
+	grub_efi_input_key_t key;
f4c76c0
+	grub_efi_key_state_t key_state;
f4c76c0
+};
f4c76c0
+typedef struct grub_efi_key_data grub_efi_key_data_t;
f4c76c0
+
f4c76c0
+typedef grub_efi_status_t (*grub_efi_key_notify_function_t) (
f4c76c0
+	grub_efi_key_data_t *key_data
f4c76c0
+	);
f4c76c0
+
f4c76c0
+struct grub_efi_simple_text_input_ex_interface
f4c76c0
+{
f4c76c0
+	grub_efi_status_t
f4c76c0
+	(*reset) (struct grub_efi_simple_text_input_ex_interface *this,
f4c76c0
+		  grub_efi_boolean_t extended_verification);
f4c76c0
+
f4c76c0
+	grub_efi_status_t
f4c76c0
+	(*read_key_stroke) (struct grub_efi_simple_text_input_ex_interface *this,
f4c76c0
+			    grub_efi_key_data_t *key_data);
f4c76c0
+
f4c76c0
+	grub_efi_event_t wait_for_key;
f4c76c0
+
f4c76c0
+	grub_efi_status_t
f4c76c0
+	(*set_state) (struct grub_efi_simple_text_input_ex_interface *this,
f4c76c0
+		      grub_efi_key_toggle_state_t *key_toggle_state);
f4c76c0
+
f4c76c0
+	grub_efi_status_t
f4c76c0
+	(*register_key_notify) (struct grub_efi_simple_text_input_ex_interface *this,
f4c76c0
+				grub_efi_key_data_t *key_data,
f4c76c0
+				grub_efi_key_notify_function_t key_notification_function);
f4c76c0
+
f4c76c0
+	grub_efi_status_t
f4c76c0
+	(*unregister_key_notify) (struct grub_efi_simple_text_input_ex_interface *this,
f4c76c0
+				  void *notification_handle);
f4c76c0
+};
f4c76c0
+typedef struct grub_efi_simple_text_input_ex_interface grub_efi_simple_text_input_ex_interface_t;
f4c76c0
+
f4c76c0
 struct grub_efi_simple_text_output_interface
f4c76c0
 {
f4c76c0
   grub_efi_status_t
f4c76c0
-- 
f4c76c0
2.4.3
f4c76c0