summaryrefslogtreecommitdiffstats
path: root/drivers/serial
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2019-11-07 14:26:41 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2019-11-07 14:26:41 +0100
commita2de36d8ce58e815dcfea081414b42e0956d5637 (patch)
treea05fdff8080ca8c460dbed399dad0a7ef567d939 /drivers/serial
parent760bc83263877de1c49598ad6bdc6aecaf772d07 (diff)
parent6ab809a3d5b6ddec6e0dd12096fd0ce6bab66006 (diff)
downloadbarebox-a2de36d8ce58e815dcfea081414b42e0956d5637.tar.gz
Merge branch 'for-next/efi'
Diffstat (limited to 'drivers/serial')
-rw-r--r--drivers/serial/efi-stdio.c78
-rw-r--r--drivers/serial/efi-stdio.h58
2 files changed, 125 insertions, 11 deletions
diff --git a/drivers/serial/efi-stdio.c b/drivers/serial/efi-stdio.c
index 2ca89fa..9e82518 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,35 +108,72 @@ 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(efi_sys_table->con_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)
{
uint16_t str[2] = {};
- struct efi_simple_text_output_protocol *con_out = efi_sys_table->con_out;
+ struct efi_console_priv *priv = to_efi(cdev);
+ struct efi_simple_text_output_protocol *con_out = priv->out;
str[0] = c;
@@ -331,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));
@@ -340,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 0000000..1fa417c
--- /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