diff options
author | Ahmad Fatoum <a.fatoum@pengutronix.de> | 2019-10-21 11:23:41 +0200 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2019-10-23 09:00:23 +0200 |
commit | 438f80e986586ddb1288caac4b9e1b0e6498e0cc (patch) | |
tree | 746ea64b4e02d0c30cf4ba40125a372d63e998b4 /drivers/serial/efi-stdio.c | |
parent | a3b0c7f17290d86e261bd35211e8f322ac97e749 (diff) | |
download | barebox-438f80e986586ddb1288caac4b9e1b0e6498e0cc.tar.gz barebox-438f80e986586ddb1288caac4b9e1b0e6498e0cc.tar.xz |
serial: efi-stdio: Add simple_text_input_ex_protocol backend
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>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers/serial/efi-stdio.c')
-rw-r--r-- | drivers/serial/efi-stdio.c | 75 |
1 files changed, 65 insertions, 10 deletions
diff --git a/drivers/serial/efi-stdio.c b/drivers/serial/efi-stdio.c index a8cc967b32..9e825181e6 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); |