From mboxrd@z Thu Jan 1 00:00:00 1970 From: Alexander Graf Date: Mon, 10 Sep 2018 12:05:35 +0200 Subject: [U-Boot] [PATCH 2/6] efi_loader: support Unicode text input In-Reply-To: <20180909055704.1994-3-xypron.glpk@gmx.de> References: <20180909055704.1994-1-xypron.glpk@gmx.de> <20180909055704.1994-3-xypron.glpk@gmx.de> Message-ID: List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: u-boot@lists.denx.de On 09/09/2018 07:57 AM, Heinrich Schuchardt wrote: > Up to now the EFI_TEXT_INPUT_PROTOCOL only supported ASCII characters. > > With the patch it can consume UTF-8 from the serial console or > codepage 437 special characters from the local keyboard. > > Signed-off-by: Heinrich Schuchardt > --- > lib/efi_loader/efi_console.c | 80 ++++++++++++++++++++++++++++++++++-- > 1 file changed, 76 insertions(+), 4 deletions(-) > > diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c > index 3ca6fe536c..8c45290b2e 100644 > --- a/lib/efi_loader/efi_console.c > +++ b/lib/efi_loader/efi_console.c > @@ -15,12 +15,18 @@ > #define EFI_COUT_MODE_2 2 > #define EFI_MAX_COUT_MODE 3 > > +/* Keyboard layouts */ > +#define KBD_US 0 /* default US layout */ > +#define KBD_GER 1 /* German layout */ > + > struct cout_mode { > unsigned long columns; > unsigned long rows; > int present; > }; > > +static int keymap = KBD_US; > + > static struct cout_mode efi_cout_modes[] = { > /* EFI Mode 0 is 80x25 and always present */ > { > @@ -390,6 +396,19 @@ struct efi_simple_text_output_protocol efi_con_out = { > .mode = (void*)&efi_con_mode, > }; > > +static void efi_set_keymap(void) > +{ > + char *penv; > + > + /* Init keyboard device (default US layout) */ > + keymap = KBD_US; > + penv = env_get("keymap"); > + if (penv) { > + if (strncmp(penv, "de", 3) == 0) > + keymap = KBD_GER; I'm not terribly happy with this. It's very i8042 specific. Currently U-Boot only implements the keymap variable there. Couldn't we add an extended getc() that reads a full 32bit unicode value from the target device? That way we could add an interim layer that everyone - not just efi_loader - can use to extract keycodes coherently from any input. > + } > +} > + > static efi_status_t EFIAPI efi_cin_reset( > struct efi_simple_text_input_protocol *this, > bool extended_verification) > @@ -453,17 +472,16 @@ static efi_status_t EFIAPI efi_cin_read_key_stroke( > .scan_code = 0, > .unicode_char = 0, > }; > - char ch; > + int ch; > > EFI_ENTRY("%p, %p", this, key); > > /* We don't do interrupts, so check for timers cooperatively */ > efi_timer_check(); > > - if (!tstc()) { > + if (!tstc()) > /* No key pressed */ > - return EFI_EXIT(EFI_NOT_READY); > - } > + goto error; > > ch = getc(); > if (ch == cESC) { > @@ -550,12 +568,63 @@ static efi_status_t EFIAPI efi_cin_read_key_stroke( > } else if (ch == 0x7f) { > /* Backspace */ > ch = 0x08; > + } else if (keymap == KBD_US && ch >= 0xc2 && ch <= 0xf4) { > + /* > + * Unicode > + * > + * We assume here that the serial console is using UTF-8. > + * This of cause depends on the terminal settings. > + */ > + int code = 0; > + > + if (ch >= 0xe0) { > + if (ch >= 0xf0) { > + /* 0xf0 - 0xf4 */ > + ch &= 0x07; > + code = ch << 18; > + ch = getc(); > + if (ch < 0x80 || ch > 0xbf) > + goto error; > + ch &= 0x3f; > + } else { > + /* 0xe0 - 0xef */ > + ch &= 0x0f; > + } > + code += ch << 12; > + if ((code >= 0xD800 && code <= 0xDFFF) || > + code >= 0x110000) > + goto error; > + ch = getc(); > + if (ch < 0x80 || ch > 0xbf) > + goto error; > + } > + /* 0xc0 - 0xdf or continuation byte (0x80 - 0xbf) */ > + ch &= 0x3f; > + code += ch << 6; > + ch = getc(); > + if (ch < 0x80 || ch > 0xbf) > + goto error; > + ch &= 0x3f; > + ch += code; All of the logic above for example really shouldn't live inside of efi_loader. It belongs somewhere more generic. Alex > + } else if (keymap != KBD_US && ch >= 0x80 && ch <= 0xff) { > + /* > + * Code page 437 special characters > + * > + * The keyboard drivers emit code page 437 characters. Support > + * for German language special characters can be enabled via > + * environment variable 'keymap' in the i8042 driver. > + */ > + ch = codepage_437[ch - 0x80]; > + } else if (ch >= 0x80) { > + goto error; > } > if (!pressed_key.scan_code) > pressed_key.unicode_char = ch; > *key = pressed_key; > > return EFI_EXIT(EFI_SUCCESS); > +error: > + return EFI_EXIT(EFI_NOT_READY); > } > > struct efi_simple_text_input_protocol efi_con_in = { > @@ -597,6 +666,9 @@ int efi_console_register(void) > struct efi_object *efi_console_output_obj; > struct efi_object *efi_console_input_obj; > > + /* Set keymap */ > + efi_set_keymap(); > + > /* Set up mode information */ > query_console_size(); >