[U-Boot] [PATCH v3 0/13] efi_loader: EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL

Support Unicode letters received as UTF-8 from the serial console. Correct handling of the WaitForKey event. Update unit test for the EFI_SIMPLE_TEXT_INPUT__PROTOCOL.
Fix bugs for the EFI_SIMPLE_TEXT_INPUT__PROTOCOL. Implement the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL. Provide a unit test. Fix a U-Boot binary size problem.
v3: support modifiers for F1 - F4 ESC before a letter signifies the ALT modifier do not use introduce EFI dependency in charset.c v2: merge two patch series move reading of Unicode to charset.c drop support for German keyboard layout
Heinrich Schuchardt (13): efi_loader: support Unicode text input test/py: Unicode w/ EFI_SIMPLE_TEXT_INPUT_PROTOCOL efi_selftest: refactor text input test efi_loader: rework event handling for console efi_selftest: use WaitForKey to test text input test/py: rework test_efi_selftest_text_input() efi_loader: EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL efi_loader: support modifiers for F1 - F4 efi_selftest: test EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL test/py: test EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL efi_loader: implement key notify functions efi_selftest: test key notification functions efi_loader: unset CONFIG_EFI_UNICODE_CAPITALIZATION
configs/vf610twr_defconfig | 1 + configs/vf610twr_nand_defconfig | 1 + include/charset.h | 9 + include/efi_api.h | 56 ++ include/efi_selftest.h | 16 + lib/charset.c | 136 +++-- lib/efi_loader/efi_console.c | 540 +++++++++++++++++--- lib/efi_selftest/Makefile | 1 + lib/efi_selftest/efi_selftest_textinput.c | 136 +---- lib/efi_selftest/efi_selftest_textinputex.c | 198 +++++++ lib/efi_selftest/efi_selftest_util.c | 93 ++++ test/py/tests/test_efi_selftest.py | 101 +++- test/unicode_ut.c | 8 +- 13 files changed, 1068 insertions(+), 228 deletions(-) create mode 100644 lib/efi_selftest/efi_selftest_textinputex.c

Up to now the EFI_TEXT_INPUT_PROTOCOL only supported ASCII characters. With the patch it can consume UTF-8 from the console.
Currently only the serial console and the console can deliver UTF-8. Local consoles are restricted to ASCII.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- v2: drop support for German keyboard move reading of Unicode code to charset.c --- include/charset.h | 9 +++ lib/charset.c | 136 ++++++++++++++++++++++------------- lib/efi_loader/efi_console.c | 13 ++-- test/unicode_ut.c | 8 +-- 4 files changed, 108 insertions(+), 58 deletions(-)
diff --git a/include/charset.h b/include/charset.h index 686db5a1fe..a7de5f6948 100644 --- a/include/charset.h +++ b/include/charset.h @@ -8,11 +8,20 @@ #ifndef __CHARSET_H_ #define __CHARSET_H_
+#include <efi.h> #include <linux/kernel.h> #include <linux/types.h>
#define MAX_UTF8_PER_UTF16 3
+/** + * console_read_unicode() - read Unicode code point from console + * + * @code: code point + * Return: 0 = success + */ +int console_read_unicode(s32 *code); + /** * utf8_get() - get next UTF-8 code point from buffer * diff --git a/lib/charset.c b/lib/charset.c index 72c808ce64..1806b41cc3 100644 --- a/lib/charset.c +++ b/lib/charset.c @@ -5,6 +5,7 @@ * Copyright (c) 2017 Rob Clark */
+#include <common.h> #include <charset.h> #include <capitalization.h> #include <malloc.h> @@ -18,67 +19,106 @@ static struct capitalization_table capitalization_table[] = CP437_CAPITALIZATION_TABLE; #endif
-s32 utf8_get(const char **src) +/** + * get_code() - read Unicode code point from UTF-8 stream + * + * @read_u8: - stream reader + * @src: - string buffer passed to stream reader, optional + * Return: - Unicode code point + */ +static int get_code(u8 (*read_u8)(void *data), void *data) { - s32 code = 0; - unsigned char c; + s32 ch = 0;
- if (!src || !*src) - return -1; - if (!**src) + ch = read_u8(data); + if (!ch) return 0; - c = **src; - if (c >= 0x80) { - ++*src; - if (!**src) - return -1; - /* - * We do not expect a continuation byte (0x80 - 0xbf). - * 0x80 is coded as 0xc2 0x80, so we cannot have less then 0xc2 - * here. - * The highest code point is 0x10ffff which is coded as - * 0xf4 0x8f 0xbf 0xbf. So we cannot have a byte above 0xf4. - */ - if (c < 0xc2 || code > 0xf4) - return -1; - if (c >= 0xe0) { - if (c >= 0xf0) { + if (ch >= 0xc2 && ch <= 0xf4) { + int code = 0; + + if (ch >= 0xe0) { + if (ch >= 0xf0) { /* 0xf0 - 0xf4 */ - c &= 0x07; - code = c << 18; - c = **src; - ++*src; - if (!**src) - return -1; - if (c < 0x80 || c > 0xbf) - return -1; - c &= 0x3f; + ch &= 0x07; + code = ch << 18; + ch = read_u8(data); + if (ch < 0x80 || ch > 0xbf) + goto error; + ch &= 0x3f; } else { /* 0xe0 - 0xef */ - c &= 0x0f; + ch &= 0x0f; } - code += c << 12; + code += ch << 12; if ((code >= 0xD800 && code <= 0xDFFF) || code >= 0x110000) - return -1; - c = **src; - ++*src; - if (!**src) - return -1; - if (c < 0x80 || c > 0xbf) - return -1; + goto error; + ch = read_u8(data); + if (ch < 0x80 || ch > 0xbf) + goto error; } /* 0xc0 - 0xdf or continuation byte (0x80 - 0xbf) */ - c &= 0x3f; - code += c << 6; - c = **src; - if (c < 0x80 || c > 0xbf) - return -1; - c &= 0x3f; + ch &= 0x3f; + code += ch << 6; + ch = read_u8(data); + if (ch < 0x80 || ch > 0xbf) + goto error; + ch &= 0x3f; + ch += code; + } else if (ch >= 0x80) { + goto error; } - code += c; + return ch; +error: + return '?'; +} + +/** + * read_string() - read byte from character string + * + * @data: - pointer to string + * Return: - byte read + * + * The string pointer is incremented if it does not point to '\0'. + */ +static u8 read_string(void *data) + +{ + const char **src = (const char **)data; + u8 c; + + if (!src || !*src || !**src) + return 0; + c = (unsigned char)**src; ++*src; - return code; + return c; +} + +/** + * read_console() - read byte from console + * + * @src - not used, needed to match interface + * Return: - byte read + */ +static u8 read_console(void *data) +{ + return getc(); +} + +int console_read_unicode(s32 *code) +{ + if (!tstc()) + /* No input available */ + return 1; + + /* Read Unicode code */ + *code = get_code(read_console, NULL); + return 0; +} + +s32 utf8_get(const char **src) +{ + return get_code(read_string, src); }
int utf8_put(s32 code, char **dst) diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index 3ca6fe536c..6af083984c 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -449,23 +449,24 @@ static efi_status_t EFIAPI efi_cin_read_key_stroke( struct efi_simple_text_input_protocol *this, struct efi_input_key *key) { + efi_status_t ret; struct efi_input_key pressed_key = { .scan_code = 0, .unicode_char = 0, }; - char ch; + s32 ch;
EFI_ENTRY("%p, %p", this, key);
/* We don't do interrupts, so check for timers cooperatively */ efi_timer_check();
- if (!tstc()) { - /* No key pressed */ + ret = console_read_unicode(&ch); + if (ret) return EFI_EXIT(EFI_NOT_READY); - } - - ch = getc(); + /* We do not support multi-word codes */ + if (ch >= 0x10000) + ch = '?'; if (ch == cESC) { /* * Xterm Control Sequences diff --git a/test/unicode_ut.c b/test/unicode_ut.c index b94b4a651f..b115d18afd 100644 --- a/test/unicode_ut.c +++ b/test/unicode_ut.c @@ -178,7 +178,7 @@ static int ut_utf8_utf16_strlen(struct unit_test_state *uts)
/* illegal utf-8 sequences */ ut_asserteq(4, utf8_utf16_strlen(j1)); - ut_asserteq(5, utf8_utf16_strlen(j2)); + ut_asserteq(4, utf8_utf16_strlen(j2)); ut_asserteq(3, utf8_utf16_strlen(j3));
return 0; @@ -196,7 +196,7 @@ static int ut_utf8_utf16_strnlen(struct unit_test_state *uts)
/* illegal utf-8 sequences */ ut_asserteq(4, utf8_utf16_strnlen(j1, 16)); - ut_asserteq(5, utf8_utf16_strnlen(j2, 16)); + ut_asserteq(4, utf8_utf16_strnlen(j2, 16)); ut_asserteq(3, utf8_utf16_strnlen(j3, 16));
return 0; @@ -255,8 +255,8 @@ static int ut_utf8_utf16_strcpy(struct unit_test_state *uts)
pos = buf; utf8_utf16_strcpy(&pos, j2); - ut_asserteq(5, pos - buf); - ut_assert(!ut_u16_strcmp(buf, L"j2??l", SIZE_MAX)); + ut_asserteq(4, pos - buf); + ut_assert(!ut_u16_strcmp(buf, L"j2?l", SIZE_MAX));
pos = buf; utf8_utf16_strcpy(&pos, j3);

On 11.09.18 22:38, 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 console.
Currently only the serial console and the console can deliver UTF-8. Local consoles are restricted to ASCII.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de
v2: drop support for German keyboard move reading of Unicode code to charset.c
include/charset.h | 9 +++ lib/charset.c | 136 ++++++++++++++++++++++------------- lib/efi_loader/efi_console.c | 13 ++-- test/unicode_ut.c | 8 +-- 4 files changed, 108 insertions(+), 58 deletions(-)
diff --git a/include/charset.h b/include/charset.h index 686db5a1fe..a7de5f6948 100644 --- a/include/charset.h +++ b/include/charset.h @@ -8,11 +8,20 @@ #ifndef __CHARSET_H_ #define __CHARSET_H_
+#include <efi.h>
Yeah ... eh ... no :).
I assume this is just a leftover from the old version?
#include <linux/kernel.h> #include <linux/types.h>
#define MAX_UTF8_PER_UTF16 3
+/**
- console_read_unicode() - read Unicode code point from console
- @code: code point
Please specify this a bit clearer.
- Return: 0 = success
- */
+int console_read_unicode(s32 *code);
/**
- utf8_get() - get next UTF-8 code point from buffer
diff --git a/lib/charset.c b/lib/charset.c index 72c808ce64..1806b41cc3 100644 --- a/lib/charset.c +++ b/lib/charset.c @@ -5,6 +5,7 @@
- Copyright (c) 2017 Rob Clark
*/
+#include <common.h> #include <charset.h> #include <capitalization.h> #include <malloc.h> @@ -18,67 +19,106 @@ static struct capitalization_table capitalization_table[] = CP437_CAPITALIZATION_TABLE; #endif
-s32 utf8_get(const char **src) +/**
- get_code() - read Unicode code point from UTF-8 stream
- @read_u8: - stream reader
- @src: - string buffer passed to stream reader, optional
- Return: - Unicode code point
- */
+static int get_code(u8 (*read_u8)(void *data), void *data) {
- s32 code = 0;
- unsigned char c;
- s32 ch = 0;
- if (!src || !*src)
return -1;
- if (!**src)
- ch = read_u8(data);
- if (!ch) return 0;
- c = **src;
- if (c >= 0x80) {
++*src;
if (!**src)
return -1;
/*
* We do not expect a continuation byte (0x80 - 0xbf).
* 0x80 is coded as 0xc2 0x80, so we cannot have less then 0xc2
* here.
* The highest code point is 0x10ffff which is coded as
* 0xf4 0x8f 0xbf 0xbf. So we cannot have a byte above 0xf4.
*/
if (c < 0xc2 || code > 0xf4)
return -1;
if (c >= 0xe0) {
if (c >= 0xf0) {
- if (ch >= 0xc2 && ch <= 0xf4) {
int code = 0;
if (ch >= 0xe0) {
if (ch >= 0xf0) { /* 0xf0 - 0xf4 */
c &= 0x07;
code = c << 18;
c = **src;
++*src;
if (!**src)
return -1;
if (c < 0x80 || c > 0xbf)
return -1;
c &= 0x3f;
ch &= 0x07;
code = ch << 18;
ch = read_u8(data);
if (ch < 0x80 || ch > 0xbf)
goto error;
ch &= 0x3f; } else { /* 0xe0 - 0xef */
c &= 0x0f;
ch &= 0x0f; }
code += c << 12;
code += ch << 12; if ((code >= 0xD800 && code <= 0xDFFF) || code >= 0x110000)
return -1;
c = **src;
++*src;
if (!**src)
return -1;
if (c < 0x80 || c > 0xbf)
return -1;
goto error;
ch = read_u8(data);
if (ch < 0x80 || ch > 0xbf)
} /* 0xc0 - 0xdf or continuation byte (0x80 - 0xbf) */goto error;
c &= 0x3f;
code += c << 6;
c = **src;
if (c < 0x80 || c > 0xbf)
return -1;
c &= 0x3f;
ch &= 0x3f;
code += ch << 6;
ch = read_u8(data);
if (ch < 0x80 || ch > 0xbf)
goto error;
ch &= 0x3f;
ch += code;
- } else if (ch >= 0x80) {
}goto error;
- code += c;
- return ch;
+error:
- return '?';
+}
+/**
- read_string() - read byte from character string
- @data: - pointer to string
- Return: - byte read
- The string pointer is incremented if it does not point to '\0'.
- */
+static u8 read_string(void *data)
+{
- const char **src = (const char **)data;
- u8 c;
- if (!src || !*src || !**src)
return 0;
- c = (unsigned char)**src;
Please remove the cast. Btw, you could also write this as
return *(*src++);
++*src;
- return code;
- return c;
+}
+/**
- read_console() - read byte from console
- @src - not used, needed to match interface
- Return: - byte read
- */
+static u8 read_console(void *data) +{
- return getc();
+}
+int console_read_unicode(s32 *code) +{
- if (!tstc())
/* No input available */
return 1;
Please avoid multi-line indented code without braces.
- /* Read Unicode code */
- *code = get_code(read_console, NULL);
- return 0;
+}
+s32 utf8_get(const char **src) +{
- return get_code(read_string, src);
}
Alex

Up to now the EFI_TEXT_INPUT_PROTOCOL only supported ASCII characters. With the patch it can consume UTF-8 from the console.
Currently only the serial console and the console can deliver UTF-8. Local consoles are restricted to ASCII.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- v3,v4 remove EFI dependencies in charset.c use consistent naming of variables and functions v2: drop support for German keyboard move reading of Unicode code to charset.c --- include/charset.h | 9 +++ lib/charset.c | 136 ++++++++++++++++++++++------------- lib/efi_loader/efi_console.c | 13 ++-- test/unicode_ut.c | 8 +-- 4 files changed, 108 insertions(+), 58 deletions(-)
diff --git a/include/charset.h b/include/charset.h index 686db5a1fe..a7de5f6948 100644 --- a/include/charset.h +++ b/include/charset.h @@ -8,11 +8,20 @@ #ifndef __CHARSET_H_ #define __CHARSET_H_
+#include <efi.h> #include <linux/kernel.h> #include <linux/types.h>
#define MAX_UTF8_PER_UTF16 3
+/** + * console_read_unicode() - read Unicode code point from console + * + * @code: code point + * Return: 0 = success + */ +int console_read_unicode(s32 *code); + /** * utf8_get() - get next UTF-8 code point from buffer * diff --git a/lib/charset.c b/lib/charset.c index 72c808ce64..1806b41cc3 100644 --- a/lib/charset.c +++ b/lib/charset.c @@ -5,6 +5,7 @@ * Copyright (c) 2017 Rob Clark */
+#include <common.h> #include <charset.h> #include <capitalization.h> #include <malloc.h> @@ -18,67 +19,106 @@ static struct capitalization_table capitalization_table[] = CP437_CAPITALIZATION_TABLE; #endif
-s32 utf8_get(const char **src) +/** + * get_code() - read Unicode code point from UTF-8 stream + * + * @read_u8: - stream reader + * @src: - string buffer passed to stream reader, optional + * Return: - Unicode code point + */ +static int get_code(u8 (*read_u8)(void *data), void *data) { - s32 code = 0; - unsigned char c; + s32 ch = 0;
- if (!src || !*src) - return -1; - if (!**src) + ch = read_u8(data); + if (!ch) return 0; - c = **src; - if (c >= 0x80) { - ++*src; - if (!**src) - return -1; - /* - * We do not expect a continuation byte (0x80 - 0xbf). - * 0x80 is coded as 0xc2 0x80, so we cannot have less then 0xc2 - * here. - * The highest code point is 0x10ffff which is coded as - * 0xf4 0x8f 0xbf 0xbf. So we cannot have a byte above 0xf4. - */ - if (c < 0xc2 || code > 0xf4) - return -1; - if (c >= 0xe0) { - if (c >= 0xf0) { + if (ch >= 0xc2 && ch <= 0xf4) { + int code = 0; + + if (ch >= 0xe0) { + if (ch >= 0xf0) { /* 0xf0 - 0xf4 */ - c &= 0x07; - code = c << 18; - c = **src; - ++*src; - if (!**src) - return -1; - if (c < 0x80 || c > 0xbf) - return -1; - c &= 0x3f; + ch &= 0x07; + code = ch << 18; + ch = read_u8(data); + if (ch < 0x80 || ch > 0xbf) + goto error; + ch &= 0x3f; } else { /* 0xe0 - 0xef */ - c &= 0x0f; + ch &= 0x0f; } - code += c << 12; + code += ch << 12; if ((code >= 0xD800 && code <= 0xDFFF) || code >= 0x110000) - return -1; - c = **src; - ++*src; - if (!**src) - return -1; - if (c < 0x80 || c > 0xbf) - return -1; + goto error; + ch = read_u8(data); + if (ch < 0x80 || ch > 0xbf) + goto error; } /* 0xc0 - 0xdf or continuation byte (0x80 - 0xbf) */ - c &= 0x3f; - code += c << 6; - c = **src; - if (c < 0x80 || c > 0xbf) - return -1; - c &= 0x3f; + ch &= 0x3f; + code += ch << 6; + ch = read_u8(data); + if (ch < 0x80 || ch > 0xbf) + goto error; + ch &= 0x3f; + ch += code; + } else if (ch >= 0x80) { + goto error; } - code += c; + return ch; +error: + return '?'; +} + +/** + * read_string() - read byte from character string + * + * @data: - pointer to string + * Return: - byte read + * + * The string pointer is incremented if it does not point to '\0'. + */ +static u8 read_string(void *data) + +{ + const char **src = (const char **)data; + u8 c; + + if (!src || !*src || !**src) + return 0; + c = (unsigned char)**src; ++*src; - return code; + return c; +} + +/** + * read_console() - read byte from console + * + * @src - not used, needed to match interface + * Return: - byte read + */ +static u8 read_console(void *data) +{ + return getc(); +} + +int console_read_unicode(s32 *code) +{ + if (!tstc()) + /* No input available */ + return 1; + + /* Read Unicode code */ + *code = get_code(read_console, NULL); + return 0; +} + +s32 utf8_get(const char **src) +{ + return get_code(read_string, src); }
int utf8_put(s32 code, char **dst) diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index 3ca6fe536c..6af083984c 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -449,23 +449,24 @@ static efi_status_t EFIAPI efi_cin_read_key_stroke( struct efi_simple_text_input_protocol *this, struct efi_input_key *key) { + efi_status_t ret; struct efi_input_key pressed_key = { .scan_code = 0, .unicode_char = 0, }; - char ch; + s32 ch;
EFI_ENTRY("%p, %p", this, key);
/* We don't do interrupts, so check for timers cooperatively */ efi_timer_check();
- if (!tstc()) { - /* No key pressed */ + ret = console_read_unicode(&ch); + if (ret) return EFI_EXIT(EFI_NOT_READY); - } - - ch = getc(); + /* We do not support multi-word codes */ + if (ch >= 0x10000) + ch = '?'; if (ch == cESC) { /* * Xterm Control Sequences diff --git a/test/unicode_ut.c b/test/unicode_ut.c index b94b4a651f..b115d18afd 100644 --- a/test/unicode_ut.c +++ b/test/unicode_ut.c @@ -178,7 +178,7 @@ static int ut_utf8_utf16_strlen(struct unit_test_state *uts)
/* illegal utf-8 sequences */ ut_asserteq(4, utf8_utf16_strlen(j1)); - ut_asserteq(5, utf8_utf16_strlen(j2)); + ut_asserteq(4, utf8_utf16_strlen(j2)); ut_asserteq(3, utf8_utf16_strlen(j3));
return 0; @@ -196,7 +196,7 @@ static int ut_utf8_utf16_strnlen(struct unit_test_state *uts)
/* illegal utf-8 sequences */ ut_asserteq(4, utf8_utf16_strnlen(j1, 16)); - ut_asserteq(5, utf8_utf16_strnlen(j2, 16)); + ut_asserteq(4, utf8_utf16_strnlen(j2, 16)); ut_asserteq(3, utf8_utf16_strnlen(j3, 16));
return 0; @@ -255,8 +255,8 @@ static int ut_utf8_utf16_strcpy(struct unit_test_state *uts)
pos = buf; utf8_utf16_strcpy(&pos, j2); - ut_asserteq(5, pos - buf); - ut_assert(!ut_u16_strcmp(buf, L"j2??l", SIZE_MAX)); + ut_asserteq(4, pos - buf); + ut_assert(!ut_u16_strcmp(buf, L"j2?l", SIZE_MAX));
pos = buf; utf8_utf16_strcpy(&pos, j3);

Up to now the EFI_TEXT_INPUT_PROTOCOL only supported ASCII characters. With the patch it can consume UTF-8 from the console.
Currently only the serial console and the console can deliver UTF-8. Local consoles are restricted to ASCII.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- v3-5: remove dependency on EFI subsystem in charset.c consistent naming of variable and pointers v2: drop support for German keyboard move reading of Unicode code to charset.c --- include/charset.h | 8 ++ lib/charset.c | 137 +++++++++++++++++++++++------------ lib/efi_loader/efi_console.c | 13 ++-- test/unicode_ut.c | 8 +- 4 files changed, 108 insertions(+), 58 deletions(-)
diff --git a/include/charset.h b/include/charset.h index 686db5a1fe..4d45e246e5 100644 --- a/include/charset.h +++ b/include/charset.h @@ -13,6 +13,14 @@
#define MAX_UTF8_PER_UTF16 3
+/** + * console_read_unicode() - read Unicode code point from console + * + * @code: pointer to store Unicode code point + * Return: 0 = success + */ +int console_read_unicode(s32 *code); + /** * utf8_get() - get next UTF-8 code point from buffer * diff --git a/lib/charset.c b/lib/charset.c index 72c808ce64..0cede9b60b 100644 --- a/lib/charset.c +++ b/lib/charset.c @@ -5,6 +5,7 @@ * Copyright (c) 2017 Rob Clark */
+#include <common.h> #include <charset.h> #include <capitalization.h> #include <malloc.h> @@ -18,67 +19,107 @@ static struct capitalization_table capitalization_table[] = CP437_CAPITALIZATION_TABLE; #endif
-s32 utf8_get(const char **src) +/** + * get_code() - read Unicode code point from UTF-8 stream + * + * @read_u8: - stream reader + * @src: - string buffer passed to stream reader, optional + * Return: - Unicode code point + */ +static int get_code(u8 (*read_u8)(void *data), void *data) { - s32 code = 0; - unsigned char c; + s32 ch = 0;
- if (!src || !*src) - return -1; - if (!**src) + ch = read_u8(data); + if (!ch) return 0; - c = **src; - if (c >= 0x80) { - ++*src; - if (!**src) - return -1; - /* - * We do not expect a continuation byte (0x80 - 0xbf). - * 0x80 is coded as 0xc2 0x80, so we cannot have less then 0xc2 - * here. - * The highest code point is 0x10ffff which is coded as - * 0xf4 0x8f 0xbf 0xbf. So we cannot have a byte above 0xf4. - */ - if (c < 0xc2 || code > 0xf4) - return -1; - if (c >= 0xe0) { - if (c >= 0xf0) { + if (ch >= 0xc2 && ch <= 0xf4) { + int code = 0; + + if (ch >= 0xe0) { + if (ch >= 0xf0) { /* 0xf0 - 0xf4 */ - c &= 0x07; - code = c << 18; - c = **src; - ++*src; - if (!**src) - return -1; - if (c < 0x80 || c > 0xbf) - return -1; - c &= 0x3f; + ch &= 0x07; + code = ch << 18; + ch = read_u8(data); + if (ch < 0x80 || ch > 0xbf) + goto error; + ch &= 0x3f; } else { /* 0xe0 - 0xef */ - c &= 0x0f; + ch &= 0x0f; } - code += c << 12; + code += ch << 12; if ((code >= 0xD800 && code <= 0xDFFF) || code >= 0x110000) - return -1; - c = **src; - ++*src; - if (!**src) - return -1; - if (c < 0x80 || c > 0xbf) - return -1; + goto error; + ch = read_u8(data); + if (ch < 0x80 || ch > 0xbf) + goto error; } /* 0xc0 - 0xdf or continuation byte (0x80 - 0xbf) */ - c &= 0x3f; - code += c << 6; - c = **src; - if (c < 0x80 || c > 0xbf) - return -1; - c &= 0x3f; + ch &= 0x3f; + code += ch << 6; + ch = read_u8(data); + if (ch < 0x80 || ch > 0xbf) + goto error; + ch &= 0x3f; + ch += code; + } else if (ch >= 0x80) { + goto error; } - code += c; + return ch; +error: + return '?'; +} + +/** + * read_string() - read byte from character string + * + * @data: - pointer to string + * Return: - byte read + * + * The string pointer is incremented if it does not point to '\0'. + */ +static u8 read_string(void *data) + +{ + const char **src = (const char **)data; + u8 c; + + if (!src || !*src || !**src) + return 0; + c = **src; ++*src; - return code; + return c; +} + +/** + * read_console() - read byte from console + * + * @src - not used, needed to match interface + * Return: - byte read + */ +static u8 read_console(void *data) +{ + return getc(); +} + +int console_read_unicode(s32 *code) +{ + if (!tstc()) { + /* No input available */ + return 1; + } + + /* Read Unicode code */ + *code = get_code(read_console, NULL); + return 0; +} + +s32 utf8_get(const char **src) +{ + return get_code(read_string, src); }
int utf8_put(s32 code, char **dst) diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index 3ca6fe536c..6af083984c 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -449,23 +449,24 @@ static efi_status_t EFIAPI efi_cin_read_key_stroke( struct efi_simple_text_input_protocol *this, struct efi_input_key *key) { + efi_status_t ret; struct efi_input_key pressed_key = { .scan_code = 0, .unicode_char = 0, }; - char ch; + s32 ch;
EFI_ENTRY("%p, %p", this, key);
/* We don't do interrupts, so check for timers cooperatively */ efi_timer_check();
- if (!tstc()) { - /* No key pressed */ + ret = console_read_unicode(&ch); + if (ret) return EFI_EXIT(EFI_NOT_READY); - } - - ch = getc(); + /* We do not support multi-word codes */ + if (ch >= 0x10000) + ch = '?'; if (ch == cESC) { /* * Xterm Control Sequences diff --git a/test/unicode_ut.c b/test/unicode_ut.c index b94b4a651f..b115d18afd 100644 --- a/test/unicode_ut.c +++ b/test/unicode_ut.c @@ -178,7 +178,7 @@ static int ut_utf8_utf16_strlen(struct unit_test_state *uts)
/* illegal utf-8 sequences */ ut_asserteq(4, utf8_utf16_strlen(j1)); - ut_asserteq(5, utf8_utf16_strlen(j2)); + ut_asserteq(4, utf8_utf16_strlen(j2)); ut_asserteq(3, utf8_utf16_strlen(j3));
return 0; @@ -196,7 +196,7 @@ static int ut_utf8_utf16_strnlen(struct unit_test_state *uts)
/* illegal utf-8 sequences */ ut_asserteq(4, utf8_utf16_strnlen(j1, 16)); - ut_asserteq(5, utf8_utf16_strnlen(j2, 16)); + ut_asserteq(4, utf8_utf16_strnlen(j2, 16)); ut_asserteq(3, utf8_utf16_strnlen(j3, 16));
return 0; @@ -255,8 +255,8 @@ static int ut_utf8_utf16_strcpy(struct unit_test_state *uts)
pos = buf; utf8_utf16_strcpy(&pos, j2); - ut_asserteq(5, pos - buf); - ut_assert(!ut_u16_strcmp(buf, L"j2??l", SIZE_MAX)); + ut_asserteq(4, pos - buf); + ut_assert(!ut_u16_strcmp(buf, L"j2?l", SIZE_MAX));
pos = buf; utf8_utf16_strcpy(&pos, j3);

Test that the Euro sign is correctly retrieved from the console via the EFI_SIMPLE_TEXT_INPUT_PROTOCOL.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- v3 no change v2 no change --- test/py/tests/test_efi_selftest.py | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/test/py/tests/test_efi_selftest.py b/test/py/tests/test_efi_selftest.py index 994d2e2241..5d0dba6f4e 100644 --- a/test/py/tests/test_efi_selftest.py +++ b/test/py/tests/test_efi_selftest.py @@ -100,6 +100,13 @@ def test_efi_selftest_text_input(u_boot_console): if m != 0: raise Exception('UP failed in 'text input' test') u_boot_console.drain_console() + # Euro sign + u_boot_console.run_command(cmd='\xe2\x82\xac', wait_for_echo=False, + send_nl=False, wait_for_prompt=False) + m = u_boot_console.p.expect(['Unicode char 8364']) + if m != 0: + raise Exception('Euro sign failed in 'text input' test') + u_boot_console.drain_console() u_boot_console.run_command(cmd='x', wait_for_echo=False, send_nl=False, wait_for_prompt=False) m = u_boot_console.p.expect(['Summary: 0 failures', 'Press any key'])

Move reusable utility functions to efi_selftest_util.c.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- v03 no change v02 no change --- include/efi_selftest.h | 16 ++++ lib/efi_selftest/efi_selftest_textinput.c | 109 +--------------------- lib/efi_selftest/efi_selftest_util.c | 93 ++++++++++++++++++ 3 files changed, 111 insertions(+), 107 deletions(-)
diff --git a/include/efi_selftest.h b/include/efi_selftest.h index 59b3c080bd..56beac305e 100644 --- a/include/efi_selftest.h +++ b/include/efi_selftest.h @@ -76,6 +76,22 @@ void efi_st_exit_boot_services(void); void efi_st_printc(int color, const char *fmt, ...) __attribute__ ((format (__printf__, 2, 3)));
+/** + * efi_st_translate_char() - translate a unicode character to a string + * + * @code: unicode character + * Return: string + */ +u16 *efi_st_translate_char(u16 code); + +/** + * efi_st_translate_code() - translate a scan code to a human readable string + * + * @code: unicode character + * Return: string + */ +u16 *efi_st_translate_code(u16 code); + /* * Compare memory. * We cannot use lib/string.c due to different CFLAGS values. diff --git a/lib/efi_selftest/efi_selftest_textinput.c b/lib/efi_selftest/efi_selftest_textinput.c index 7aa84de89d..40b0a8b25c 100644 --- a/lib/efi_selftest/efi_selftest_textinput.c +++ b/lib/efi_selftest/efi_selftest_textinput.c @@ -14,113 +14,8 @@
#include <efi_selftest.h>
-struct translate { - u16 code; - u16 *text; -}; - static struct efi_boot_services *boottime;
-static struct translate control_characters[] = { - {0, L"Null"}, - {8, L"BS"}, - {9, L"TAB"}, - {10, L"LF"}, - {13, L"CR"}, - {0, NULL}, -}; - -static u16 ch[] = L"' '"; -static u16 unknown[] = L"unknown"; - -static struct translate scan_codes[] = { - {0x00, L"Null"}, - {0x01, L"Up"}, - {0x02, L"Down"}, - {0x03, L"Right"}, - {0x04, L"Left"}, - {0x05, L"Home"}, - {0x06, L"End"}, - {0x07, L"Insert"}, - {0x08, L"Delete"}, - {0x09, L"Page Up"}, - {0x0a, L"Page Down"}, - {0x0b, L"FN 1"}, - {0x0c, L"FN 2"}, - {0x0d, L"FN 3"}, - {0x0e, L"FN 4"}, - {0x0f, L"FN 5"}, - {0x10, L"FN 6"}, - {0x11, L"FN 7"}, - {0x12, L"FN 8"}, - {0x13, L"FN 9"}, - {0x14, L"FN 10"}, - {0x15, L"FN 11"}, - {0x16, L"FN 12"}, - {0x17, L"Escape"}, - {0x68, L"FN 13"}, - {0x69, L"FN 14"}, - {0x6a, L"FN 15"}, - {0x6b, L"FN 16"}, - {0x6c, L"FN 17"}, - {0x6d, L"FN 18"}, - {0x6e, L"FN 19"}, - {0x6f, L"FN 20"}, - {0x70, L"FN 21"}, - {0x71, L"FN 22"}, - {0x72, L"FN 23"}, - {0x73, L"FN 24"}, - {0x7f, L"Mute"}, - {0x80, L"Volume Up"}, - {0x81, L"Volume Down"}, - {0x100, L"Brightness Up"}, - {0x101, L"Brightness Down"}, - {0x102, L"Suspend"}, - {0x103, L"Hibernate"}, - {0x104, L"Toggle Display"}, - {0x105, L"Recovery"}, - {0x106, L"Reject"}, - {0x0, NULL}, -}; - -/* - * Translate a unicode character to a string. - * - * @code unicode character - * @return string - */ -static u16 *translate_char(u16 code) -{ - struct translate *tr; - - if (code >= ' ') { - ch[1] = code; - return ch; - } - for (tr = control_characters; tr->text; ++tr) { - if (tr->code == code) - return tr->text; - } - return unknown; -} - -/* - * Translate a scan code to a human readable string. - * - * @code unicode character - * @return string - */ -static u16 *translate_code(u16 code) -{ - struct translate *tr; - - for (tr = scan_codes; tr->text; ++tr) { - if (tr->code == code) - return tr->text; - } - return unknown; -} - /* * Setup unit test. * @@ -160,9 +55,9 @@ static int execute(void)
efi_st_printf("Unicode char %u (%ps), scan code %u (%ps)\n", (unsigned int)input_key.unicode_char, - translate_char(input_key.unicode_char), + efi_st_translate_char(input_key.unicode_char), (unsigned int)input_key.scan_code, - translate_code(input_key.scan_code)); + efi_st_translate_code(input_key.scan_code));
switch (input_key.unicode_char) { case 'x': diff --git a/lib/efi_selftest/efi_selftest_util.c b/lib/efi_selftest/efi_selftest_util.c index 87a04f898a..96a964c863 100644 --- a/lib/efi_selftest/efi_selftest_util.c +++ b/lib/efi_selftest/efi_selftest_util.c @@ -9,6 +9,99 @@
#include <efi_selftest.h>
+struct efi_st_translate { + u16 code; + u16 *text; +}; + +static struct efi_st_translate efi_st_control_characters[] = { + {0, L"Null"}, + {8, L"BS"}, + {9, L"TAB"}, + {10, L"LF"}, + {13, L"CR"}, + {0, NULL}, +}; + +static u16 efi_st_ch[] = L"' '"; +static u16 efi_st_unknown[] = L"unknown"; + +static struct efi_st_translate efi_st_scan_codes[] = { + {0x00, L"Null"}, + {0x01, L"Up"}, + {0x02, L"Down"}, + {0x03, L"Right"}, + {0x04, L"Left"}, + {0x05, L"Home"}, + {0x06, L"End"}, + {0x07, L"Insert"}, + {0x08, L"Delete"}, + {0x09, L"Page Up"}, + {0x0a, L"Page Down"}, + {0x0b, L"FN 1"}, + {0x0c, L"FN 2"}, + {0x0d, L"FN 3"}, + {0x0e, L"FN 4"}, + {0x0f, L"FN 5"}, + {0x10, L"FN 6"}, + {0x11, L"FN 7"}, + {0x12, L"FN 8"}, + {0x13, L"FN 9"}, + {0x14, L"FN 10"}, + {0x15, L"FN 11"}, + {0x16, L"FN 12"}, + {0x17, L"Escape"}, + {0x68, L"FN 13"}, + {0x69, L"FN 14"}, + {0x6a, L"FN 15"}, + {0x6b, L"FN 16"}, + {0x6c, L"FN 17"}, + {0x6d, L"FN 18"}, + {0x6e, L"FN 19"}, + {0x6f, L"FN 20"}, + {0x70, L"FN 21"}, + {0x71, L"FN 22"}, + {0x72, L"FN 23"}, + {0x73, L"FN 24"}, + {0x7f, L"Mute"}, + {0x80, L"Volume Up"}, + {0x81, L"Volume Down"}, + {0x100, L"Brightness Up"}, + {0x101, L"Brightness Down"}, + {0x102, L"Suspend"}, + {0x103, L"Hibernate"}, + {0x104, L"Toggle Display"}, + {0x105, L"Recovery"}, + {0x106, L"Reject"}, + {0x0, NULL}, +}; + +u16 *efi_st_translate_char(u16 code) +{ + struct efi_st_translate *tr; + + if (code >= ' ') { + efi_st_ch[1] = code; + return efi_st_ch; + } + for (tr = efi_st_control_characters; tr->text; ++tr) { + if (tr->code == code) + return tr->text; + } + return efi_st_unknown; +} + +u16 *efi_st_translate_code(u16 code) +{ + struct efi_st_translate *tr; + + for (tr = efi_st_scan_codes; tr->text; ++tr) { + if (tr->code == code) + return tr->text; + } + return efi_st_unknown; +} + int efi_st_memcmp(const void *buf1, const void *buf2, size_t length) { const u8 *pos1 = buf1;

Preread the next key in the console timer event.
The EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL requires to trigger registered key notification functions based on the prefetched key.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- v3 rebase patch v2 rebase patch --- lib/efi_loader/efi_console.c | 175 +++++++++++++++++++++++++++-------- 1 file changed, 137 insertions(+), 38 deletions(-)
diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index 6af083984c..e191e027a8 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -390,21 +390,12 @@ struct efi_simple_text_output_protocol efi_con_out = { .mode = (void*)&efi_con_mode, };
-static efi_status_t EFIAPI efi_cin_reset( - struct efi_simple_text_input_protocol *this, - bool extended_verification) -{ - EFI_ENTRY("%p, %d", this, extended_verification); - - /* Empty input buffer */ - while (tstc()) - getc(); +static bool key_available; +static struct efi_input_key next_key;
- return EFI_EXIT(EFI_SUCCESS); -} - -/* - * Analyze modifiers (shift, alt, ctrl) for function keys. +/** + * skip_modifiers() - analyze modifiers (shift, alt, ctrl) for function keys + * * This gets called when we have already parsed CSI. * * @modifiers: bitmask (shift, alt, ctrl) @@ -445,9 +436,13 @@ out: return ret; }
-static efi_status_t EFIAPI efi_cin_read_key_stroke( - struct efi_simple_text_input_protocol *this, - struct efi_input_key *key) +/** + * efi_cin_read_key() - read a key from the console input + * + * @key: - key received + * Return: - status code + */ +static efi_status_t efi_cin_read_key(struct efi_input_key *key) { efi_status_t ret; struct efi_input_key pressed_key = { @@ -456,14 +451,9 @@ static efi_status_t EFIAPI efi_cin_read_key_stroke( }; s32 ch;
- EFI_ENTRY("%p, %p", this, key); - - /* We don't do interrupts, so check for timers cooperatively */ - efi_timer_check(); - ret = console_read_unicode(&ch); if (ret) - return EFI_EXIT(EFI_NOT_READY); + return EFI_NOT_READY; /* We do not support multi-word codes */ if (ch >= 0x10000) ch = '?'; @@ -556,7 +546,109 @@ static efi_status_t EFIAPI efi_cin_read_key_stroke( pressed_key.unicode_char = ch; *key = pressed_key;
- return EFI_EXIT(EFI_SUCCESS); + return EFI_SUCCESS; +} + +/** + * efi_cin_check() - check if keyboard input is available + */ +static void efi_cin_check(void) +{ + efi_status_t ret; + + if (key_available) { + efi_signal_event(efi_con_in.wait_for_key, true); + return; + } + + if (tstc()) { + ret = efi_cin_read_key(&next_key); + if (ret == EFI_SUCCESS) { + key_available = true; + + /* Queue the wait for key event */ + efi_signal_event(efi_con_in.wait_for_key, true); + } + } +} + +/** + * efi_cin_reset() - drain the input buffer + * + * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL + * @extended_verification: allow for exhaustive verification + * Return: status code + * + * This function implements the Reset service of the + * EFI_SIMPLE_TEXT_INPUT_PROTOCOL. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + */ +static efi_status_t EFIAPI efi_cin_reset + (struct efi_simple_text_input_protocol *this, + bool extended_verification) +{ + efi_status_t ret = EFI_SUCCESS; + + EFI_ENTRY("%p, %d", this, extended_verification); + + /* Check parameters */ + if (!this) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + /* Empty input buffer */ + while (tstc()) + getc(); + key_available = false; +out: + return EFI_EXIT(ret); +} + +/** + * efi_cin_reset() - drain the input buffer + * + * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL + * @key: key read from console + * Return: status code + * + * This function implements the ReadKeyStroke service of the + * EFI_SIMPLE_TEXT_INPUT_PROTOCOL. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + */ +static efi_status_t EFIAPI efi_cin_read_key_stroke + (struct efi_simple_text_input_protocol *this, + struct efi_input_key *key) +{ + efi_status_t ret = EFI_SUCCESS; + + EFI_ENTRY("%p, %p", this, key); + + /* Check parameters */ + if (!this || !key) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + /* We don't do interrupts, so check for timers cooperatively */ + efi_timer_check(); + + /* Enable console input after ExitBootServices */ + efi_cin_check(); + + if (!key_available) { + ret = EFI_NOT_READY; + goto out; + } + *key = next_key; + key_available = false; + efi_con_in.wait_for_key->is_signaled = false; +out: + return EFI_EXIT(ret); }
struct efi_simple_text_input_protocol efi_con_in = { @@ -567,31 +659,38 @@ struct efi_simple_text_input_protocol efi_con_in = {
static struct efi_event *console_timer_event;
-static void EFIAPI efi_key_notify(struct efi_event *event, void *context) -{ -} - /* - * Notification function of the console timer event. + * efi_console_timer_notify() - notify the console timer event * - * event: console timer event - * context: not used + * @event: console timer event + * @context: not used */ static void EFIAPI efi_console_timer_notify(struct efi_event *event, void *context) { EFI_ENTRY("%p, %p", event, context); + efi_cin_check(); + EFI_EXIT(EFI_SUCCESS); +}
- /* Check if input is available */ - if (tstc()) { - /* Queue the wait for key event */ - efi_con_in.wait_for_key->is_signaled = true; - efi_signal_event(efi_con_in.wait_for_key, true); - } +/** + * efi_key_notify() - notify the wait for key event + * + * @event: wait for key event + * @context: not used + */ +static void EFIAPI efi_key_notify(struct efi_event *event, void *context) +{ + EFI_ENTRY("%p, %p", event, context); + efi_cin_check(); EFI_EXIT(EFI_SUCCESS); }
-/* This gets called from do_bootefi_exec(). */ +/** + * efi_console_register() - install the console protocols + * + * This function is called from do_bootefi_exec(). + */ int efi_console_register(void) { efi_status_t r;

We should test the WaitForKey event. Testing for EFI_NOT_READY can be done after resetting the console.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- v3 no change v2 no change --- lib/efi_selftest/efi_selftest_textinput.c | 27 ++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-)
diff --git a/lib/efi_selftest/efi_selftest_textinput.c b/lib/efi_selftest/efi_selftest_textinput.c index 40b0a8b25c..164fbffe6c 100644 --- a/lib/efi_selftest/efi_selftest_textinput.c +++ b/lib/efi_selftest/efi_selftest_textinput.c @@ -40,15 +40,36 @@ static int execute(void) { struct efi_input_key input_key = {0}; efi_status_t ret; + efi_uintn_t index; + + /* Drain the console input */ + ret = con_in->reset(con_in, true); + if (ret != EFI_SUCCESS) { + efi_st_error("Reset failed\n"); + return EFI_ST_FAILURE; + } + ret = con_in->read_key_stroke(con_in, &input_key); + if (ret != EFI_NOT_READY) { + efi_st_error("Empty buffer not reported\n"); + return EFI_ST_FAILURE; + }
efi_st_printf("Waiting for your input\n"); efi_st_printf("To terminate type 'x'\n");
for (;;) { /* Wait for next key */ - do { - ret = con_in->read_key_stroke(con_in, &input_key); - } while (ret == EFI_NOT_READY); + ret = boottime->wait_for_event(1, &con_in->wait_for_key, + &index); + if (ret != EFI_ST_SUCCESS) { + efi_st_error("WaitForEvent failed\n"); + return EFI_ST_FAILURE; + } + ret = con_in->read_key_stroke(con_in, &input_key); + if (ret != EFI_SUCCESS) { + efi_st_error("ReadKeyStroke failed\n"); + return EFI_ST_FAILURE; + }
/* Allow 5 minutes until time out */ boottime->set_watchdog_timer(300, 0, 0, NULL);

Use more precise regular expressions.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- v3 no change v2 no change --- test/py/tests/test_efi_selftest.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/test/py/tests/test_efi_selftest.py b/test/py/tests/test_efi_selftest.py index 5d0dba6f4e..3e70abdafc 100644 --- a/test/py/tests/test_efi_selftest.py +++ b/test/py/tests/test_efi_selftest.py @@ -68,42 +68,47 @@ def test_efi_selftest_text_input(u_boot_console): # EOT u_boot_console.run_command(cmd=chr(4), wait_for_echo=False, send_nl=False, wait_for_prompt=False) - m = u_boot_console.p.expect(['Unicode char 4']) + m = u_boot_console.p.expect( + ['Unicode char 4 (unknown), scan code 0 (Null)']) if m != 0: raise Exception('EOT failed in 'text input' test') u_boot_console.drain_console() # BS u_boot_console.run_command(cmd=chr(8), wait_for_echo=False, send_nl=False, wait_for_prompt=False) - m = u_boot_console.p.expect(['(BS)']) + m = u_boot_console.p.expect( + ['Unicode char 8 (BS), scan code 0 (Null)']) if m != 0: raise Exception('BS failed in 'text input' test') u_boot_console.drain_console() # TAB u_boot_console.run_command(cmd=chr(9), wait_for_echo=False, send_nl=False, wait_for_prompt=False) - m = u_boot_console.p.expect(['(TAB)']) + m = u_boot_console.p.expect( + ['Unicode char 9 (TAB), scan code 0 (Null)']) if m != 0: raise Exception('BS failed in 'text input' test') u_boot_console.drain_console() # a u_boot_console.run_command(cmd='a', wait_for_echo=False, send_nl=False, wait_for_prompt=False) - m = u_boot_console.p.expect(['('a')']) + m = u_boot_console.p.expect( + ['Unicode char 97 ('a'), scan code 0 (Null)']) if m != 0: raise Exception(''a' failed in 'text input' test') u_boot_console.drain_console() # UP escape sequence u_boot_console.run_command(cmd=chr(27) + '[A', wait_for_echo=False, send_nl=False, wait_for_prompt=False) - m = u_boot_console.p.expect(['(Up)']) + m = u_boot_console.p.expect( + ['Unicode char 0 (Null), scan code 1 (Up)']) if m != 0: raise Exception('UP failed in 'text input' test') u_boot_console.drain_console() # Euro sign u_boot_console.run_command(cmd='\xe2\x82\xac', wait_for_echo=False, send_nl=False, wait_for_prompt=False) - m = u_boot_console.p.expect(['Unicode char 8364']) + m = u_boot_console.p.expect(['Unicode char 8364 ('']) if m != 0: raise Exception('Euro sign failed in 'text input' test') u_boot_console.drain_console()

This patch implements the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
The implementation of notification functions is postponed to a later patch.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- v3: fix support for CTRL+l repace Unicode code points over 0xffff to '?' v2: rebase patch --- include/efi_api.h | 56 ++++++++ lib/efi_loader/efi_console.c | 253 ++++++++++++++++++++++++++++++++--- 2 files changed, 287 insertions(+), 22 deletions(-)
diff --git a/include/efi_api.h b/include/efi_api.h index ae840d1bb4..5004f520ff 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -588,11 +588,67 @@ struct efi_simple_text_output_protocol { struct simple_text_output_mode *mode; };
+#define EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID \ + EFI_GUID(0xdd9e7534, 0x7762, 0x4698, \ + 0x8c, 0x14, 0xf5, 0x85, 0x17, 0xa6, 0x25, 0xaa) + struct efi_input_key { u16 scan_code; s16 unicode_char; };
+#define EFI_SHIFT_STATE_INVALID 0x00000000 +#define EFI_RIGHT_SHIFT_PRESSED 0x00000001 +#define EFI_LEFT_SHIFT_PRESSED 0x00000002 +#define EFI_RIGHT_CONTROL_PRESSED 0x00000004 +#define EFI_LEFT_CONTROL_PRESSED 0x00000008 +#define EFI_RIGHT_ALT_PRESSED 0x00000010 +#define EFI_LEFT_ALT_PRESSED 0x00000020 +#define EFI_RIGHT_LOGO_PRESSED 0x00000040 +#define EFI_LEFT_LOGO_PRESSED 0x00000080 +#define EFI_MENU_KEY_PRESSED 0x00000100 +#define EFI_SYS_REQ_PRESSED 0x00000200 +#define EFI_SHIFT_STATE_VALID 0x80000000 + +#define EFI_TOGGLE_STATE_INVALID 0x00 +#define EFI_SCROLL_LOCK_ACTIVE 0x01 +#define EFI_NUM_LOCK_ACTIVE 0x02 +#define EFI_CAPS_LOCK_ACTIVE 0x04 +#define EFI_KEY_STATE_EXPOSED 0x40 +#define EFI_TOGGLE_STATE_VALID 0x80 + +struct efi_key_state { + u32 key_shift_state; + u8 key_toggle_state; +}; + +struct efi_key_data { + struct efi_input_key key; + struct efi_key_state key_state; +}; + +struct efi_simple_text_input_ex_protocol { + efi_status_t (EFIAPI *reset) ( + struct efi_simple_text_input_ex_protocol *this, + bool extended_verification); + efi_status_t (EFIAPI *read_key_stroke_ex) ( + struct efi_simple_text_input_ex_protocol *this, + struct efi_key_data *key_data); + struct efi_event *wait_for_key_ex; + efi_status_t (EFIAPI *set_state) ( + struct efi_simple_text_input_ex_protocol *this, + u8 key_toggle_state); + efi_status_t (EFIAPI *register_key_notify) ( + struct efi_simple_text_input_ex_protocol *this, + struct efi_key_data *key_data, + efi_status_t (EFIAPI *key_notify_function)( + struct efi_key_data *key_data), + void **notify_handle); + efi_status_t (EFIAPI *unregister_key_notify) ( + struct efi_simple_text_input_ex_protocol *this, + void *notification_handle); +}; + #define EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID \ EFI_GUID(0x387477c1, 0x69c7, 0x11d2, \ 0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b) diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index e191e027a8..d17f0a13f9 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -42,10 +42,12 @@ static struct cout_mode efi_cout_modes[] = { }, };
-const efi_guid_t efi_guid_text_output_protocol = - EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID; +const efi_guid_t efi_guid_text_input_ex_protocol = + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID; const efi_guid_t efi_guid_text_input_protocol = EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID; +const efi_guid_t efi_guid_text_output_protocol = + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID;
#define cESC '\x1b' #define ESC "\x1b" @@ -391,19 +393,19 @@ struct efi_simple_text_output_protocol efi_con_out = { };
static bool key_available; -static struct efi_input_key next_key; +static struct efi_key_data next_key;
/** - * skip_modifiers() - analyze modifiers (shift, alt, ctrl) for function keys + * analyze_modifiers() - analyze modifiers (shift, alt, ctrl) for function keys * * This gets called when we have already parsed CSI. * * @modifiers: bitmask (shift, alt, ctrl) * @return: the unmodified code */ -static char skip_modifiers(int *modifiers) +static int analyze_modifiers(struct efi_key_state *key_state) { - char c, mod = 0, ret = 0; + int c, mod = 0, ret = 0;
c = getc();
@@ -429,8 +431,17 @@ static char skip_modifiers(int *modifiers) out: if (mod) --mod; - if (modifiers) - *modifiers = mod; + key_state->key_shift_state = EFI_SHIFT_STATE_VALID; + if (mod) { + if (mod & 1) + key_state->key_shift_state |= EFI_LEFT_SHIFT_PRESSED; + if (mod & 2) + key_state->key_shift_state |= EFI_LEFT_ALT_PRESSED; + if (mod & 4) + key_state->key_shift_state |= EFI_LEFT_CONTROL_PRESSED; + if (mod & 8) + key_state->key_shift_state |= EFI_LEFT_LOGO_PRESSED; + } if (!ret) ret = c; return ret; @@ -442,7 +453,7 @@ out: * @key: - key received * Return: - status code */ -static efi_status_t efi_cin_read_key(struct efi_input_key *key) +static efi_status_t efi_cin_read_key(struct efi_key_data *key) { efi_status_t ret; struct efi_input_key pressed_key = { @@ -454,6 +465,10 @@ static efi_status_t efi_cin_read_key(struct efi_input_key *key) ret = console_read_unicode(&ch); if (ret) return EFI_NOT_READY; + + key->key_state.key_shift_state = EFI_SHIFT_STATE_INVALID; + key->key_state.key_toggle_state = EFI_TOGGLE_STATE_INVALID; + /* We do not support multi-word codes */ if (ch >= 0x10000) ch = '?'; @@ -490,7 +505,7 @@ static efi_status_t efi_cin_read_key(struct efi_input_key *key) pressed_key.scan_code = 5; break; case '1': - ch = skip_modifiers(NULL); + ch = analyze_modifiers(&key->key_state); switch (ch) { case '1'...'5': /* F1 - F5 */ pressed_key.scan_code = ch - '1' + 11; @@ -510,7 +525,7 @@ static efi_status_t efi_cin_read_key(struct efi_input_key *key) } break; case '2': - ch = skip_modifiers(NULL); + ch = analyze_modifiers(&key->key_state); switch (ch) { case '0'...'1': /* F9 - F10 */ pressed_key.scan_code = ch - '0' + 19; @@ -525,15 +540,15 @@ static efi_status_t efi_cin_read_key(struct efi_input_key *key) break; case '3': /* DEL */ pressed_key.scan_code = 8; - skip_modifiers(NULL); + analyze_modifiers(&key->key_state); break; case '5': /* PG UP */ pressed_key.scan_code = 9; - skip_modifiers(NULL); + analyze_modifiers(&key->key_state); break; case '6': /* PG DOWN */ pressed_key.scan_code = 10; - skip_modifiers(NULL); + analyze_modifiers(&key->key_state); break; } break; @@ -542,9 +557,28 @@ static efi_status_t efi_cin_read_key(struct efi_input_key *key) /* Backspace */ ch = 0x08; } - if (!pressed_key.scan_code) + if (pressed_key.scan_code) { + key->key_state.key_shift_state |= EFI_SHIFT_STATE_VALID; + } else { pressed_key.unicode_char = ch; - *key = pressed_key; + + /* + * Assume left control key for control characters typically + * entered using the control key. + */ + if (ch >= 0x01 && ch <= 0x1f) { + key->key_state.key_shift_state = + EFI_SHIFT_STATE_VALID; + switch (ch) { + case 0x01 ... 0x07: + case 0x0b ... 0x0c: + case 0x0e ... 0x1f: + key->key_state.key_shift_state |= + EFI_LEFT_CONTROL_PRESSED; + } + } + } + key->key = pressed_key;
return EFI_SUCCESS; } @@ -572,6 +606,170 @@ static void efi_cin_check(void) } }
+/** + * efi_cin_empty_buffer() - empty input buffer + */ +static void efi_cin_empty_buffer(void) +{ + while (tstc()) + getc(); + key_available = false; +} + +/** + * efi_cin_reset_ex() - reset console input + * + * @this: - the extended simple text input protocol + * @extended_verification: - extended verification + * + * This function implements the reset service of the + * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * Return: old value of the task priority level + */ +static efi_status_t EFIAPI efi_cin_reset_ex( + struct efi_simple_text_input_ex_protocol *this, + bool extended_verification) +{ + efi_status_t ret = EFI_SUCCESS; + + EFI_ENTRY("%p, %d", this, extended_verification); + + /* Check parameters */ + if (!this) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + efi_cin_empty_buffer(); +out: + return EFI_EXIT(ret); +} + +/** + * efi_cin_read_key_stroke_ex() - read key stroke + * + * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL + * @key_data: key read from console + * Return: status code + * + * This function implements the ReadKeyStrokeEx service of the + * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + */ +static efi_status_t EFIAPI efi_cin_read_key_stroke_ex( + struct efi_simple_text_input_ex_protocol *this, + struct efi_key_data *key_data) +{ + efi_status_t ret = EFI_SUCCESS; + + EFI_ENTRY("%p, %p", this, key_data); + + /* Check parameters */ + if (!this || !key_data) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + /* We don't do interrupts, so check for timers cooperatively */ + efi_timer_check(); + + /* Enable console input after ExitBootServices */ + efi_cin_check(); + + if (!key_available) { + ret = EFI_NOT_READY; + goto out; + } + *key_data = next_key; + key_available = false; + efi_con_in.wait_for_key->is_signaled = false; +out: + return EFI_EXIT(ret); +} + +/** + * efi_cin_set_state() - set toggle key state + * + * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL + * @key_toggle_state: key toggle state + * Return: status code + * + * This function implements the SetState service of the + * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + */ +static efi_status_t EFIAPI efi_cin_set_state( + struct efi_simple_text_input_ex_protocol *this, + u8 key_toggle_state) +{ + EFI_ENTRY("%p, %u", this, key_toggle_state); + /* + * U-Boot supports multiple console input sources like serial and + * net console for which a key toggle state cannot be set at all. + * + * According to the UEFI specification it is allowable to not implement + * this service. + */ + return EFI_EXIT(EFI_UNSUPPORTED); +} + +/** + * efi_cin_register_key_notify() - register key notification function + * + * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL + * @key_data: key to be notified + * @key_notify_function: function to be called if the key is pressed + * @notify_handle: handle for unregistering the notification + * Return: status code + * + * This function implements the SetState service of the + * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + */ +static efi_status_t EFIAPI efi_cin_register_key_notify( + struct efi_simple_text_input_ex_protocol *this, + struct efi_key_data *key_data, + efi_status_t (EFIAPI *key_notify_function)( + struct efi_key_data *key_data), + void **notify_handle) +{ + EFI_ENTRY("%p, %p, %p, %p", + this, key_data, key_notify_function, notify_handle); + return EFI_EXIT(EFI_OUT_OF_RESOURCES); +} + +/** + * efi_cin_unregister_key_notify() - unregister key notification function + * + * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL + * @notification_handle: handle received when registering + * Return: status code + * + * This function implements the SetState service of the + * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + */ +static efi_status_t EFIAPI efi_cin_unregister_key_notify( + struct efi_simple_text_input_ex_protocol *this, + void *notification_handle) +{ + EFI_ENTRY("%p, %p", this, notification_handle); + return EFI_EXIT(EFI_INVALID_PARAMETER); +} + + /** * efi_cin_reset() - drain the input buffer * @@ -599,16 +797,13 @@ static efi_status_t EFIAPI efi_cin_reset goto out; }
- /* Empty input buffer */ - while (tstc()) - getc(); - key_available = false; + efi_cin_empty_buffer(); out: return EFI_EXIT(ret); }
/** - * efi_cin_reset() - drain the input buffer + * efi_cin_read_key_stroke() - read key stroke * * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL * @key: key read from console @@ -644,13 +839,22 @@ static efi_status_t EFIAPI efi_cin_read_key_stroke ret = EFI_NOT_READY; goto out; } - *key = next_key; + *key = next_key.key; key_available = false; efi_con_in.wait_for_key->is_signaled = false; out: return EFI_EXIT(ret); }
+static struct efi_simple_text_input_ex_protocol efi_con_in_ex = { + .reset = efi_cin_reset_ex, + .read_key_stroke_ex = efi_cin_read_key_stroke_ex, + .wait_for_key_ex = NULL, + .set_state = efi_cin_set_state, + .register_key_notify = efi_cin_register_key_notify, + .unregister_key_notify = efi_cin_unregister_key_notify, +}; + struct efi_simple_text_input_protocol efi_con_in = { .reset = efi_cin_reset, .read_key_stroke = efi_cin_read_key_stroke, @@ -721,6 +925,10 @@ int efi_console_register(void) if (r != EFI_SUCCESS) goto out_of_memory; systab.con_in_handle = efi_console_input_obj->handle; + r = efi_add_protocol(efi_console_input_obj->handle, + &efi_guid_text_input_ex_protocol, &efi_con_in_ex); + if (r != EFI_SUCCESS) + goto out_of_memory;
/* Create console events */ r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK, efi_key_notify, @@ -729,6 +937,7 @@ int efi_console_register(void) printf("ERROR: Failed to register WaitForKey event\n"); return r; } + efi_con_in_ex.wait_for_key_ex = efi_con_in.wait_for_key; r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK, efi_console_timer_notify, NULL, NULL, &console_timer_event);

Support modifiers for F1 - F4. Add support for letters with ALT key.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- v3 new patch --- lib/efi_loader/efi_console.c | 64 ++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 25 deletions(-)
diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index d17f0a13f9..a6fd93d2c5 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -395,6 +395,29 @@ struct efi_simple_text_output_protocol efi_con_out = { static bool key_available; static struct efi_key_data next_key;
+/** + * set_shift_mask() - set shift mask + * + * @mod: Xterm shift mask + */ +void set_shift_mask(int mod, struct efi_key_state *key_state) +{ + key_state->key_shift_state = EFI_SHIFT_STATE_VALID; + if (mod) { + --mod; + if (mod & 1) + key_state->key_shift_state |= EFI_LEFT_SHIFT_PRESSED; + if (mod & 2) + key_state->key_shift_state |= EFI_LEFT_ALT_PRESSED; + if (mod & 4) + key_state->key_shift_state |= EFI_LEFT_CONTROL_PRESSED; + if (mod & 8) + key_state->key_shift_state |= EFI_LEFT_LOGO_PRESSED; + } else { + key_state->key_shift_state |= EFI_LEFT_LOGO_PRESSED; + } +} + /** * analyze_modifiers() - analyze modifiers (shift, alt, ctrl) for function keys * @@ -429,19 +452,7 @@ static int analyze_modifiers(struct efi_key_state *key_state) } } out: - if (mod) - --mod; - key_state->key_shift_state = EFI_SHIFT_STATE_VALID; - if (mod) { - if (mod & 1) - key_state->key_shift_state |= EFI_LEFT_SHIFT_PRESSED; - if (mod & 2) - key_state->key_shift_state |= EFI_LEFT_ALT_PRESSED; - if (mod & 4) - key_state->key_shift_state |= EFI_LEFT_CONTROL_PRESSED; - if (mod & 8) - key_state->key_shift_state |= EFI_LEFT_LOGO_PRESSED; - } + set_shift_mask(mod, key_state); if (!ret) ret = c; return ret; @@ -455,15 +466,13 @@ out: */ static efi_status_t efi_cin_read_key(struct efi_key_data *key) { - efi_status_t ret; struct efi_input_key pressed_key = { .scan_code = 0, .unicode_char = 0, }; s32 ch;
- ret = console_read_unicode(&ch); - if (ret) + if (console_read_unicode(&ch)) return EFI_NOT_READY;
key->key_state.key_shift_state = EFI_SHIFT_STATE_INVALID; @@ -472,7 +481,9 @@ static efi_status_t efi_cin_read_key(struct efi_key_data *key) /* We do not support multi-word codes */ if (ch >= 0x10000) ch = '?'; - if (ch == cESC) { + + switch (ch) { + case 0x1b: /* * Xterm Control Sequences * https://www.xfree86.org/4.8.0/ctlseqs.html @@ -484,14 +495,13 @@ static efi_status_t efi_cin_read_key(struct efi_key_data *key) break; case 'O': /* F1 - F4 */ ch = getc(); - /* skip modifiers */ - if (ch <= '9') + /* consider modifiers */ + if (ch < 'P') { + set_shift_mask(ch - '0', &key->key_state); ch = getc(); + } pressed_key.scan_code = ch - 'P' + 11; break; - case 'a'...'z': - ch = ch - 'a'; - break; case '[': ch = getc(); switch (ch) { @@ -550,10 +560,14 @@ static efi_status_t efi_cin_read_key(struct efi_key_data *key) pressed_key.scan_code = 10; analyze_modifiers(&key->key_state); break; - } + } /* [ */ break; + default: + /* ALT key */ + set_shift_mask(3, &key->key_state); } - } else if (ch == 0x7f) { + break; + case 0x7f: /* Backspace */ ch = 0x08; } @@ -567,7 +581,7 @@ static efi_status_t efi_cin_read_key(struct efi_key_data *key) * entered using the control key. */ if (ch >= 0x01 && ch <= 0x1f) { - key->key_state.key_shift_state = + key->key_state.key_shift_state |= EFI_SHIFT_STATE_VALID; switch (ch) { case 0x01 ... 0x07:

Provide a unit test for the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- v3 no change v2 no change --- lib/efi_selftest/Makefile | 1 + lib/efi_selftest/efi_selftest_textinputex.c | 139 ++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 lib/efi_selftest/efi_selftest_textinputex.c
diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index 80bc5d8a64..2f55d9d66f 100644 --- a/lib/efi_selftest/Makefile +++ b/lib/efi_selftest/Makefile @@ -29,6 +29,7 @@ efi_selftest_manageprotocols.o \ efi_selftest_rtc.o \ efi_selftest_snp.o \ efi_selftest_textinput.o \ +efi_selftest_textinputex.o \ efi_selftest_textoutput.o \ efi_selftest_tpl.o \ efi_selftest_unicode_collation.o \ diff --git a/lib/efi_selftest/efi_selftest_textinputex.c b/lib/efi_selftest/efi_selftest_textinputex.c new file mode 100644 index 0000000000..935bf065c4 --- /dev/null +++ b/lib/efi_selftest/efi_selftest_textinputex.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * efi_selftest_textinput + * + * Copyright (c) 2018 Heinrich Schuchardt xypron.glpk@gmx.de + * + * Provides a unit test for the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL. + * The unicode character and the scan code are printed for text + * input. To run the test: + * + * setenv efi_selftest extended text input + * bootefi selftest + */ + +#include <efi_selftest.h> + +static const efi_guid_t text_input_ex_protocol_guid = + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID; + +static struct efi_simple_text_input_ex_protocol *con_in_ex; + +static struct efi_boot_services *boottime; + +/* + * Setup unit test. + * + * @handle: handle of the loaded image + * @systable: system table + * @return: EFI_ST_SUCCESS for success + */ +static int setup(const efi_handle_t handle, + const struct efi_system_table *systable) +{ + efi_status_t ret; + + boottime = systable->boottime; + + ret = boottime->locate_protocol(&text_input_ex_protocol_guid, NULL, + (void **)&con_in_ex); + if (ret != EFI_SUCCESS) { + con_in_ex = NULL; + efi_st_error + ("Extended text input protocol is not available.\n"); + return EFI_ST_FAILURE; + } + + return EFI_ST_SUCCESS; +} + +/* + * Execute unit test. + * + * @return: EFI_ST_SUCCESS for success + */ +static int execute(void) +{ + struct efi_key_data input_key = {0,}; + efi_status_t ret; + efi_uintn_t index; + + if (!con_in_ex) { + efi_st_printf("Setup failed\n"); + return EFI_ST_FAILURE; + } + + /* Drain the console input */ + ret = con_in_ex->reset(con_in_ex, true); + if (ret != EFI_SUCCESS) { + efi_st_error("Reset failed\n"); + return EFI_ST_FAILURE; + } + ret = con_in_ex->read_key_stroke_ex(con_in_ex, &input_key); + if (ret != EFI_NOT_READY) { + efi_st_error("Empty buffer not reported\n"); + return EFI_ST_FAILURE; + } + + efi_st_printf("Waiting for your input\n"); + efi_st_printf("To terminate type 'x'\n"); + + for (;;) { + /* Wait for next key */ + ret = boottime->wait_for_event(1, &con_in_ex->wait_for_key_ex, + &index); + if (ret != EFI_ST_SUCCESS) { + efi_st_error("WaitForEvent failed\n"); + return EFI_ST_FAILURE; + } + ret = con_in_ex->read_key_stroke_ex(con_in_ex, &input_key); + if (ret != EFI_SUCCESS) { + efi_st_error("ReadKeyStroke failed\n"); + return EFI_ST_FAILURE; + } + + /* Allow 5 minutes until time out */ + boottime->set_watchdog_timer(300, 0, 0, NULL); + + efi_st_printf("Unicode char %u (%ps), scan code %u (", + (unsigned int)input_key.key.unicode_char, + efi_st_translate_char(input_key.key.unicode_char), + (unsigned int)input_key.key.scan_code); + if (input_key.key_state.key_shift_state & + EFI_SHIFT_STATE_VALID) { + if (input_key.key_state.key_shift_state & + (EFI_LEFT_SHIFT_PRESSED | EFI_RIGHT_SHIFT_PRESSED)) + efi_st_printf("SHIFT+"); + if (input_key.key_state.key_shift_state & + (EFI_LEFT_ALT_PRESSED | EFI_RIGHT_ALT_PRESSED)) + efi_st_printf("ALT+"); + if (input_key.key_state.key_shift_state & + (EFI_LEFT_CONTROL_PRESSED | + EFI_RIGHT_CONTROL_PRESSED)) + efi_st_printf("CTRL+"); + if (input_key.key_state.key_shift_state & + (EFI_LEFT_LOGO_PRESSED | EFI_RIGHT_LOGO_PRESSED)) + efi_st_printf("META+"); + if (input_key.key_state.key_shift_state == + EFI_SHIFT_STATE_VALID) + efi_st_printf("+"); + } + + efi_st_printf("%ps)\n", + efi_st_translate_code(input_key.key.scan_code)); + + switch (input_key.key.unicode_char) { + case 'x': + case 'X': + return EFI_ST_SUCCESS; + } + } +} + +EFI_UNIT_TEST(textinputex) = { + .name = "extended text input", + .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, + .setup = setup, + .execute = execute, + .on_request = true, +};

Add a unit test for the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- v3 no change v2 no change --- test/py/tests/test_efi_selftest.py | 79 ++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+)
diff --git a/test/py/tests/test_efi_selftest.py b/test/py/tests/test_efi_selftest.py index 3e70abdafc..f84aa4d706 100644 --- a/test/py/tests/test_efi_selftest.py +++ b/test/py/tests/test_efi_selftest.py @@ -118,3 +118,82 @@ def test_efi_selftest_text_input(u_boot_console): if m != 0: raise Exception('Failures occurred during the EFI selftest') u_boot_console.restart_uboot(); + +@pytest.mark.buildconfigspec('cmd_bootefi_selftest') +def test_efi_selftest_text_input_ex(u_boot_console): + """Test the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL + + :param u_boot_console: U-Boot console + + This function calls the extended text input EFI selftest. + """ + u_boot_console.run_command(cmd='setenv efi_selftest extended text input') + output = u_boot_console.run_command(cmd='bootefi selftest', + wait_for_prompt=False) + m = u_boot_console.p.expect(['To terminate type 'x'']) + if m != 0: + raise Exception('No prompt for 'text input' test') + u_boot_console.drain_console() + u_boot_console.p.timeout = 500 + # EOT + u_boot_console.run_command(cmd=chr(4), wait_for_echo=False, + send_nl=False, wait_for_prompt=False) + m = u_boot_console.p.expect( + ['Unicode char 4 (unknown), scan code 0 (CTRL+Null)']) + if m != 0: + raise Exception('EOT failed in 'text input' test') + u_boot_console.drain_console() + # BS + u_boot_console.run_command(cmd=chr(8), wait_for_echo=False, + send_nl=False, wait_for_prompt=False) + m = u_boot_console.p.expect( + ['Unicode char 8 (BS), scan code 0 (+Null)']) + if m != 0: + raise Exception('BS failed in 'text input' test') + u_boot_console.drain_console() + # TAB + u_boot_console.run_command(cmd=chr(9), wait_for_echo=False, + send_nl=False, wait_for_prompt=False) + m = u_boot_console.p.expect( + ['Unicode char 9 (TAB), scan code 0 (+Null)']) + if m != 0: + raise Exception('TAB failed in 'text input' test') + u_boot_console.drain_console() + # a + u_boot_console.run_command(cmd='a', wait_for_echo=False, send_nl=False, + wait_for_prompt=False) + m = u_boot_console.p.expect( + ['Unicode char 97 ('a'), scan code 0 (Null)']) + if m != 0: + raise Exception(''a' failed in 'text input' test') + u_boot_console.drain_console() + # UP escape sequence + u_boot_console.run_command(cmd=chr(27) + '[A', wait_for_echo=False, + send_nl=False, wait_for_prompt=False) + m = u_boot_console.p.expect( + ['Unicode char 0 (Null), scan code 1 (+Up)']) + if m != 0: + raise Exception('UP failed in 'text input' test') + u_boot_console.drain_console() + # Euro sign + u_boot_console.run_command(cmd='\xe2\x82\xac', wait_for_echo=False, + send_nl=False, wait_for_prompt=False) + m = u_boot_console.p.expect(['Unicode char 8364 ('']) + if m != 0: + raise Exception('Euro sign failed in 'text input' test') + u_boot_console.drain_console() + # SHIFT+ALT+FN 5 + u_boot_console.run_command(cmd='\x1b\x5b\x31\x35\x3b\x34\x7e', + wait_for_echo=False, send_nl=False, + wait_for_prompt=False) + m = u_boot_console.p.expect( + ['Unicode char 0 (Null), scan code 15 (SHIFT+ALT+FN 5)']) + if m != 0: + raise Exception('SHIFT+ALT+FN 5 failed in 'text input' test') + u_boot_console.drain_console() + u_boot_console.run_command(cmd='x', wait_for_echo=False, send_nl=False, + wait_for_prompt=False) + m = u_boot_console.p.expect(['Summary: 0 failures', 'Press any key']) + if m != 0: + raise Exception('Failures occurred during the EFI selftest') + u_boot_console.restart_uboot();

Implement registering and unregistreing key notify functions in the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- v3 no change v2 rebae patch --- lib/efi_loader/efi_console.c | 101 +++++++++++++++++++++++++++++++++-- 1 file changed, 98 insertions(+), 3 deletions(-)
diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index a6fd93d2c5..73f7ecf919 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -392,8 +392,23 @@ struct efi_simple_text_output_protocol efi_con_out = { .mode = (void*)&efi_con_mode, };
+/** + * struct efi_cin_notify_function - registered console input notify function + * + * @link: link to list + * @data: key to notify + * @function: function to call + */ +struct efi_cin_notify_function { + struct list_head link; + struct efi_key_data key; + efi_status_t (EFIAPI *function) + (struct efi_key_data *key_data); +}; + static bool key_available; static struct efi_key_data next_key; +static LIST_HEAD(cin_notify_functions);
/** * set_shift_mask() - set shift mask @@ -597,6 +612,34 @@ static efi_status_t efi_cin_read_key(struct efi_key_data *key) return EFI_SUCCESS; }
+/** + * efi_cin_notify() - notify registered functions + */ +static void efi_cin_notify(void) +{ + struct efi_cin_notify_function *item; + + list_for_each_entry(item, &cin_notify_functions, link) { + bool match = true; + + /* We do not support toggle states */ + if (item->key.key.unicode_char || item->key.key.scan_code) { + if (item->key.key.unicode_char != + next_key.key.unicode_char || + item->key.key.scan_code != next_key.key.scan_code) + match = false; + } + if (item->key.key_state.key_shift_state && + item->key.key_state.key_shift_state != + next_key.key_state.key_shift_state) + match = false; + + if (match) + /* We don't bother about the return code */ + EFI_CALL(item->function(&next_key)); + } +} + /** * efi_cin_check() - check if keyboard input is available */ @@ -614,8 +657,12 @@ static void efi_cin_check(void) if (ret == EFI_SUCCESS) { key_available = true;
+ /* Notify registered functions */ + efi_cin_notify(); + /* Queue the wait for key event */ - efi_signal_event(efi_con_in.wait_for_key, true); + if (key_available) + efi_signal_event(efi_con_in.wait_for_key, true); } } } @@ -757,9 +804,35 @@ static efi_status_t EFIAPI efi_cin_register_key_notify( struct efi_key_data *key_data), void **notify_handle) { + efi_status_t ret = EFI_SUCCESS; + struct efi_cin_notify_function *notify_function; + EFI_ENTRY("%p, %p, %p, %p", this, key_data, key_notify_function, notify_handle); - return EFI_EXIT(EFI_OUT_OF_RESOURCES); + + /* Check parameters */ + if (!this || !key_data || !key_notify_function || !notify_handle) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + EFI_PRINT("u+%04x, sc %04x, sh %08x, tg %02x\n", + key_data->key.unicode_char, + key_data->key.scan_code, + key_data->key_state.key_shift_state, + key_data->key_state.key_toggle_state); + + notify_function = calloc(1, sizeof(struct efi_cin_notify_function)); + if (!notify_function) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + notify_function->key = *key_data; + notify_function->function = key_notify_function; + list_add_tail(¬ify_function->link, &cin_notify_functions); + *notify_handle = notify_function; +out: + return EFI_EXIT(ret); }
/** @@ -779,8 +852,30 @@ static efi_status_t EFIAPI efi_cin_unregister_key_notify( struct efi_simple_text_input_ex_protocol *this, void *notification_handle) { + efi_status_t ret = EFI_INVALID_PARAMETER; + struct efi_cin_notify_function *item, *notify_function = + notification_handle; + EFI_ENTRY("%p, %p", this, notification_handle); - return EFI_EXIT(EFI_INVALID_PARAMETER); + + /* Check parameters */ + if (!this || !notification_handle) + goto out; + + list_for_each_entry(item, &cin_notify_functions, link) { + if (item == notify_function) { + ret = EFI_SUCCESS; + break; + } + } + if (ret != EFI_SUCCESS) + goto out; + + /* Remove the notify function */ + list_del(¬ify_function->link); + free(notify_function); +out: + return EFI_EXIT(ret); }

Use a key notification function to leave the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL test.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- v3 no change v2 no change --- lib/efi_selftest/efi_selftest_textinputex.c | 73 +++++++++++++++++++-- test/py/tests/test_efi_selftest.py | 4 +- 2 files changed, 68 insertions(+), 9 deletions(-)
diff --git a/lib/efi_selftest/efi_selftest_textinputex.c b/lib/efi_selftest/efi_selftest_textinputex.c index 935bf065c4..d20d8ad89d 100644 --- a/lib/efi_selftest/efi_selftest_textinputex.c +++ b/lib/efi_selftest/efi_selftest_textinputex.c @@ -21,6 +21,25 @@ static struct efi_simple_text_input_ex_protocol *con_in_ex;
static struct efi_boot_services *boottime;
+static void *efi_key_notify_handle; +static bool efi_running; + +/** + * efi_key_notify_function() - key notification function + * + * This function is called when the registered key is hit. + * + * @key_data: next key + * Return: status code + */ +static efi_status_t EFIAPI efi_key_notify_function + (struct efi_key_data *key_data) +{ + efi_running = false; + + return EFI_SUCCESS; +} + /* * Setup unit test. * @@ -32,6 +51,17 @@ static int setup(const efi_handle_t handle, const struct efi_system_table *systable) { efi_status_t ret; + struct efi_key_data key_data = { + .key = { + .scan_code = 0, + .unicode_char = 0x18 + }, + .key_state = { + .key_shift_state = EFI_SHIFT_STATE_VALID | + EFI_LEFT_CONTROL_PRESSED, + .key_toggle_state = EFI_TOGGLE_STATE_INVALID, + }, + };
boottime = systable->boottime;
@@ -44,9 +74,41 @@ static int setup(const efi_handle_t handle, return EFI_ST_FAILURE; }
+ ret = con_in_ex->register_key_notify(con_in_ex, &key_data, + efi_key_notify_function, + &efi_key_notify_handle); + if (ret != EFI_SUCCESS) { + efi_key_notify_handle = NULL; + efi_st_error + ("Notify function could not be registered.\n"); + return EFI_ST_FAILURE; + } + efi_running = true; + return EFI_ST_SUCCESS; }
+/* + * Tear down unit test. + * + * Unregister notify function. + * + * @return: EFI_ST_SUCCESS for success + */ +static int teardown(void) +{ + efi_status_t ret; + + ret = con_in_ex->unregister_key_notify + (con_in_ex, efi_key_notify_handle); + if (ret != EFI_SUCCESS) { + efi_st_error + ("Notify function could not be registered.\n"); + return EFI_ST_FAILURE; + } + + return EFI_ST_SUCCESS; +} /* * Execute unit test. * @@ -76,9 +138,9 @@ static int execute(void) }
efi_st_printf("Waiting for your input\n"); - efi_st_printf("To terminate type 'x'\n"); + efi_st_printf("To terminate type 'CTRL+x'\n");
- for (;;) { + while (efi_running) { /* Wait for next key */ ret = boottime->wait_for_event(1, &con_in_ex->wait_for_key_ex, &index); @@ -122,12 +184,8 @@ static int execute(void) efi_st_printf("%ps)\n", efi_st_translate_code(input_key.key.scan_code));
- switch (input_key.key.unicode_char) { - case 'x': - case 'X': - return EFI_ST_SUCCESS; - } } + return EFI_ST_SUCCESS; }
EFI_UNIT_TEST(textinputex) = { @@ -135,5 +193,6 @@ EFI_UNIT_TEST(textinputex) = { .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, .setup = setup, .execute = execute, + .teardown = teardown, .on_request = true, }; diff --git a/test/py/tests/test_efi_selftest.py b/test/py/tests/test_efi_selftest.py index f84aa4d706..e0833ffe22 100644 --- a/test/py/tests/test_efi_selftest.py +++ b/test/py/tests/test_efi_selftest.py @@ -130,7 +130,7 @@ def test_efi_selftest_text_input_ex(u_boot_console): u_boot_console.run_command(cmd='setenv efi_selftest extended text input') output = u_boot_console.run_command(cmd='bootefi selftest', wait_for_prompt=False) - m = u_boot_console.p.expect(['To terminate type 'x'']) + m = u_boot_console.p.expect(['To terminate type 'CTRL+x'']) if m != 0: raise Exception('No prompt for 'text input' test') u_boot_console.drain_console() @@ -191,7 +191,7 @@ def test_efi_selftest_text_input_ex(u_boot_console): if m != 0: raise Exception('SHIFT+ALT+FN 5 failed in 'text input' test') u_boot_console.drain_console() - u_boot_console.run_command(cmd='x', wait_for_echo=False, send_nl=False, + u_boot_console.run_command(cmd=chr(24), wait_for_echo=False, send_nl=False, wait_for_prompt=False) m = u_boot_console.p.expect(['Summary: 0 failures', 'Press any key']) if m != 0:

Unset CONFIG_EFI_UNICODE_CAPITALIZATION on boards with tough size restrictions.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- v3 no change v2 no change --- configs/vf610twr_defconfig | 1 + configs/vf610twr_nand_defconfig | 1 + 2 files changed, 2 insertions(+)
diff --git a/configs/vf610twr_defconfig b/configs/vf610twr_defconfig index 59066a36a9..3f38c8813b 100644 --- a/configs/vf610twr_defconfig +++ b/configs/vf610twr_defconfig @@ -39,3 +39,4 @@ CONFIG_PHY_MICREL=y CONFIG_MII=y CONFIG_DM_SERIAL=y CONFIG_FSL_LPUART=y +# CONFIG_EFI_UNICODE_CAPITALIZATION is not set diff --git a/configs/vf610twr_nand_defconfig b/configs/vf610twr_nand_defconfig index 5b269fdaf6..d6e318f58c 100644 --- a/configs/vf610twr_nand_defconfig +++ b/configs/vf610twr_nand_defconfig @@ -39,3 +39,4 @@ CONFIG_PHY_MICREL=y CONFIG_MII=y CONFIG_DM_SERIAL=y CONFIG_FSL_LPUART=y +# CONFIG_EFI_UNICODE_CAPITALIZATION is not set
participants (2)
-
Alexander Graf
-
Heinrich Schuchardt