mail archive of the barebox mailing list
 help / color / mirror / Atom feed
From: Ahmad Fatoum <a.fatoum@pengutronix.de>
To: barebox@lists.infradead.org
Cc: Michael Olbrich <mol@pengutronix.de>,
	Ahmad Fatoum <a.fatoum@pengutronix.de>
Subject: [PATCH 2/2] serial: efi-stdio: Add simple_text_input_ex_protocol backend
Date: Mon, 21 Oct 2019 11:23:41 +0200	[thread overview]
Message-ID: <20191021092341.27633-2-a.fatoum@pengutronix.de> (raw)
In-Reply-To: <20191021092341.27633-1-a.fatoum@pengutronix.de>

EFI implementations differ in how simple_text_input_protocol treats
ctrl+alpha combinations. OVMF (used with QEMU) returns a Unicode control
character, the UEFI on the Dell Latitude 7490 on the other hand ignores
the ctrl completely and returns just the letter rendering utilities
like barebox edit unusable.

To fix this, the simple_text_input_ex_protocol can be leveraged as it
additionally provides the state of modifier keys. Extend efi-stdio to use
it where possible.

Cc: Michael Olbrich <mol@pengutronix.de>
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/serial/efi-stdio.c | 75 +++++++++++++++++++++++++++++++++-----
 drivers/serial/efi-stdio.h | 58 +++++++++++++++++++++++++++++
 2 files changed, 123 insertions(+), 10 deletions(-)
 create mode 100644 drivers/serial/efi-stdio.h

diff --git a/drivers/serial/efi-stdio.c b/drivers/serial/efi-stdio.c
index a8cc967b32a2..9e825181e6f9 100644
--- a/drivers/serial/efi-stdio.c
+++ b/drivers/serial/efi-stdio.c
@@ -26,6 +26,8 @@
 #include <readkey.h>
 #include <linux/ctype.h>
 #include <efi/efi.h>
+#include <efi/efi-device.h>
+#include "efi-stdio.h"
 
 #define EFI_SHIFT_STATE_VALID           0x80000000
 #define EFI_RIGHT_CONTROL_PRESSED       0x00000004
@@ -71,6 +73,7 @@
 struct efi_console_priv {
 	struct efi_simple_text_output_protocol *out;
 	struct efi_simple_input_interface *in;
+	struct efi_simple_text_input_ex_protocol *inex;
 	struct console_device cdev;
 	int lastkey;
 	u16 efi_console_buffer[CONFIG_CBSIZE];
@@ -105,29 +108,65 @@ static struct efi_ctrlkey ctrlkeys[] = {
 	{ 0x17, 27 /* escape key */ },
 };
 
+static int xlate_keypress(struct efi_input_key *k)
+{
+	int i;
+
+	/* 32 bit modifier keys + 16 bit scan code + 16 bit unicode */
+	for (i = 0; i < ARRAY_SIZE(ctrlkeys); i++) {
+		if (ctrlkeys[i].scan_code == k->scan_code)
+			return ctrlkeys[i].bb_key;
+
+	}
+
+	return k->unicode_char & 0xff;
+}
+
 static int efi_read_key(struct efi_console_priv *priv, bool wait)
 {
 	unsigned long index;
 	efi_status_t efiret;
-	struct efi_input_key k;
-	int i;
+	struct efi_key_data kd;
 
 	/* wait until key is pressed */
 	if (wait)
 		BS->wait_for_event(1, priv->in->wait_for_key, &index);
 
-        efiret = priv->in->read_key_stroke(priv->in, &k);
-        if (EFI_ERROR(efiret))
-		return -efi_errno(efiret);
+	if (priv->inex) {
+		efiret = priv->inex->read_key_stroke_ex(priv->inex, &kd);
 
-	/* 32 bit modifier keys + 16 bit scan code + 16 bit unicode */
-	for (i = 0; i < ARRAY_SIZE(ctrlkeys); i++) {
-		if (ctrlkeys[i].scan_code == k.scan_code)
-			return ctrlkeys[i].bb_key;
+		if (efiret == EFI_NOT_READY)
+			return -EAGAIN;
+
+		if (!EFI_ERROR(efiret)) {
+			if ((kd.state.shift_state & EFI_SHIFT_STATE_VALID) &&
+			    (kd.state.shift_state & EFI_CONTROL_PRESSED)) {
+				int ch = tolower(kd.key.unicode_char & 0xff);
+
+				if (isalpha(ch))
+					return CHAR_CTRL(ch);
+				if (ch == '\0') /* ctrl is pressed on its own */
+					return -EAGAIN;
+			}
 
+			if (kd.key.unicode_char || kd.key.scan_code)
+				return xlate_keypress(&kd.key);
+
+			/* Some broken firmwares offer simple_text_input_ex_protocol,
+			 * but never handle any key. Treat those as if
+			 * read_key_stroke_ex failed and fall through
+			 * to the basic simple_text_input_protocol.
+			 */
+			dev_dbg(priv->cdev.dev, "Falling back to simple_text_input_protocol\n");
+		}
 	}
 
-	return k.unicode_char & 0xff;
+	efiret = priv->in->read_key_stroke(priv->in, &kd.key);
+
+	if (EFI_ERROR(efiret))
+		return -efi_errno(efiret);
+
+	return xlate_keypress(&kd.key);
 }
 
 static void efi_console_putc(struct console_device *cdev, char c)
@@ -332,8 +371,12 @@ static void efi_set_mode(struct efi_console_priv *priv)
 
 static int efi_console_probe(struct device_d *dev)
 {
+	efi_guid_t inex_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
+	struct efi_simple_text_input_ex_protocol *inex;
 	struct console_device *cdev;
 	struct efi_console_priv *priv;
+	efi_status_t efiret;
+
 	int i;
 
 	priv = xzalloc(sizeof(*priv));
@@ -341,6 +384,18 @@ static int efi_console_probe(struct device_d *dev)
 	priv->out = efi_sys_table->con_out;
 	priv->in = efi_sys_table->con_in;
 
+	efiret = BS->open_protocol((void *)efi_sys_table->con_in_handle,
+			     &inex_guid,
+			     (void **)&inex,
+			     efi_parent_image,
+			     0,
+			     EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+
+	if (!EFI_ERROR(efiret)) {
+		priv->inex = inex;
+		dev_dbg(dev, "Using simple_text_input_ex_protocol\n");
+	}
+
 	priv->current_color = EFI_WHITE;
 
 	efi_set_mode(priv);
diff --git a/drivers/serial/efi-stdio.h b/drivers/serial/efi-stdio.h
new file mode 100644
index 000000000000..1fa417c706c1
--- /dev/null
+++ b/drivers/serial/efi-stdio.h
@@ -0,0 +1,58 @@
+#ifndef EFI_STDIO_H_
+#define EFI_STDIO_H_
+
+#include <efi.h>
+
+struct efi_simple_text_input_ex_protocol;
+
+typedef efi_status_t (EFIAPI *efi_input_reset_ex)(
+	struct efi_simple_text_input_ex_protocol *this,
+	efi_bool_t extended_verification
+);
+
+struct efi_key_state {
+	u32 shift_state;
+	u8 toggle_state;
+};
+
+struct efi_key_data {
+	struct efi_input_key key;
+	struct efi_key_state state;
+};
+
+typedef efi_status_t (EFIAPI *efi_input_read_key_ex)(
+	struct efi_simple_text_input_ex_protocol *this,
+	struct efi_key_data *keydata
+);
+
+typedef efi_status_t (EFIAPI *efi_set_state)(
+	struct efi_simple_text_input_ex_protocol *this,
+	u8 *key_toggle_state
+);
+
+typedef efi_status_t (EFIAPI *efi_key_notify_function)(
+	struct efi_key_data *keydata
+);
+
+typedef efi_status_t (EFIAPI *efi_register_keystroke_notify)(
+	struct efi_simple_text_input_ex_protocol *this,
+	struct efi_key_data keydata,
+	efi_key_notify_function key_notification_function,
+	void **notify_handle
+);
+
+typedef efi_status_t (EFIAPI *efi_unregister_keystroke_notify)(
+	struct efi_simple_text_input_ex_protocol *this,
+	void *notification_handle
+);
+
+struct efi_simple_text_input_ex_protocol {
+	efi_input_reset_ex reset;
+	efi_input_read_key_ex read_key_stroke_ex;
+	void *wait_for_key_ex;
+	efi_set_state set_state;
+	efi_register_keystroke_notify register_key_notify;
+	efi_unregister_keystroke_notify unregister_key_notify;
+};
+
+#endif
-- 
2.23.0


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

  reply	other threads:[~2019-10-21  9:23 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-10-21  9:23 [PATCH 1/2] serial: efi-stdio: replace globals with handles in priv struct Ahmad Fatoum
2019-10-21  9:23 ` Ahmad Fatoum [this message]
2019-10-23  7:00 ` Sascha Hauer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20191021092341.27633-2-a.fatoum@pengutronix.de \
    --to=a.fatoum@pengutronix.de \
    --cc=barebox@lists.infradead.org \
    --cc=mol@pengutronix.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox