All of lore.kernel.org
 help / color / mirror / Atom feed
* [U-Boot] [PATCH v3 0/13] efi_loader: EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL
@ 2018-09-11 20:38 Heinrich Schuchardt
  2018-09-11 20:38 ` [U-Boot] [PATCH v3 01/13] efi_loader: support Unicode text input Heinrich Schuchardt
                   ` (12 more replies)
  0 siblings, 13 replies; 17+ messages in thread
From: Heinrich Schuchardt @ 2018-09-11 20:38 UTC (permalink / raw)
  To: u-boot

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

-- 
2.18.0

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [U-Boot] [PATCH v3 01/13] efi_loader: support Unicode text input
  2018-09-11 20:38 [U-Boot] [PATCH v3 0/13] efi_loader: EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL Heinrich Schuchardt
@ 2018-09-11 20:38 ` Heinrich Schuchardt
  2018-09-11 21:37   ` Alexander Graf
  2018-09-11 20:38 ` [U-Boot] [PATCH v3 02/13] test/py: Unicode w/ EFI_SIMPLE_TEXT_INPUT_PROTOCOL Heinrich Schuchardt
                   ` (11 subsequent siblings)
  12 siblings, 1 reply; 17+ messages in thread
From: Heinrich Schuchardt @ 2018-09-11 20:38 UTC (permalink / raw)
  To: u-boot

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);
-- 
2.18.0

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [U-Boot] [PATCH v3 02/13] test/py: Unicode w/ EFI_SIMPLE_TEXT_INPUT_PROTOCOL
  2018-09-11 20:38 [U-Boot] [PATCH v3 0/13] efi_loader: EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL Heinrich Schuchardt
  2018-09-11 20:38 ` [U-Boot] [PATCH v3 01/13] efi_loader: support Unicode text input Heinrich Schuchardt
@ 2018-09-11 20:38 ` Heinrich Schuchardt
  2018-09-11 20:38 ` [U-Boot] [PATCH v3 03/13] efi_selftest: refactor text input test Heinrich Schuchardt
                   ` (10 subsequent siblings)
  12 siblings, 0 replies; 17+ messages in thread
From: Heinrich Schuchardt @ 2018-09-11 20:38 UTC (permalink / raw)
  To: u-boot

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'])
-- 
2.18.0

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [U-Boot] [PATCH v3 03/13] efi_selftest: refactor text input test
  2018-09-11 20:38 [U-Boot] [PATCH v3 0/13] efi_loader: EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL Heinrich Schuchardt
  2018-09-11 20:38 ` [U-Boot] [PATCH v3 01/13] efi_loader: support Unicode text input Heinrich Schuchardt
  2018-09-11 20:38 ` [U-Boot] [PATCH v3 02/13] test/py: Unicode w/ EFI_SIMPLE_TEXT_INPUT_PROTOCOL Heinrich Schuchardt
@ 2018-09-11 20:38 ` Heinrich Schuchardt
  2018-09-11 20:38 ` [U-Boot] [PATCH v3 04/13] efi_loader: rework event handling for console Heinrich Schuchardt
                   ` (9 subsequent siblings)
  12 siblings, 0 replies; 17+ messages in thread
From: Heinrich Schuchardt @ 2018-09-11 20:38 UTC (permalink / raw)
  To: u-boot

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;
-- 
2.18.0

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [U-Boot] [PATCH v3 04/13] efi_loader: rework event handling for console
  2018-09-11 20:38 [U-Boot] [PATCH v3 0/13] efi_loader: EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL Heinrich Schuchardt
                   ` (2 preceding siblings ...)
  2018-09-11 20:38 ` [U-Boot] [PATCH v3 03/13] efi_selftest: refactor text input test Heinrich Schuchardt
@ 2018-09-11 20:38 ` Heinrich Schuchardt
  2018-09-11 20:38 ` [U-Boot] [PATCH v3 05/13] efi_selftest: use WaitForKey to test text input Heinrich Schuchardt
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 17+ messages in thread
From: Heinrich Schuchardt @ 2018-09-11 20:38 UTC (permalink / raw)
  To: u-boot

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;
-- 
2.18.0

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [U-Boot] [PATCH v3 05/13] efi_selftest: use WaitForKey to test text input
  2018-09-11 20:38 [U-Boot] [PATCH v3 0/13] efi_loader: EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL Heinrich Schuchardt
                   ` (3 preceding siblings ...)
  2018-09-11 20:38 ` [U-Boot] [PATCH v3 04/13] efi_loader: rework event handling for console Heinrich Schuchardt
@ 2018-09-11 20:38 ` Heinrich Schuchardt
  2018-09-11 20:38 ` [U-Boot] [PATCH v3 06/13] test/py: rework test_efi_selftest_text_input() Heinrich Schuchardt
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 17+ messages in thread
From: Heinrich Schuchardt @ 2018-09-11 20:38 UTC (permalink / raw)
  To: u-boot

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);
-- 
2.18.0

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [U-Boot] [PATCH v3 06/13] test/py: rework test_efi_selftest_text_input()
  2018-09-11 20:38 [U-Boot] [PATCH v3 0/13] efi_loader: EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL Heinrich Schuchardt
                   ` (4 preceding siblings ...)
  2018-09-11 20:38 ` [U-Boot] [PATCH v3 05/13] efi_selftest: use WaitForKey to test text input Heinrich Schuchardt
@ 2018-09-11 20:38 ` Heinrich Schuchardt
  2018-09-11 20:38 ` [U-Boot] [PATCH v3 07/13] efi_loader: EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL Heinrich Schuchardt
                   ` (6 subsequent siblings)
  12 siblings, 0 replies; 17+ messages in thread
From: Heinrich Schuchardt @ 2018-09-11 20:38 UTC (permalink / raw)
  To: u-boot

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()
-- 
2.18.0

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [U-Boot] [PATCH v3 07/13] efi_loader: EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL
  2018-09-11 20:38 [U-Boot] [PATCH v3 0/13] efi_loader: EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL Heinrich Schuchardt
                   ` (5 preceding siblings ...)
  2018-09-11 20:38 ` [U-Boot] [PATCH v3 06/13] test/py: rework test_efi_selftest_text_input() Heinrich Schuchardt
@ 2018-09-11 20:38 ` Heinrich Schuchardt
  2018-09-11 20:38 ` [U-Boot] [PATCH v3 08/13] efi_loader: support modifiers for F1 - F4 Heinrich Schuchardt
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 17+ messages in thread
From: Heinrich Schuchardt @ 2018-09-11 20:38 UTC (permalink / raw)
  To: u-boot

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);
-- 
2.18.0

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [U-Boot] [PATCH v3 08/13] efi_loader: support modifiers for F1 - F4
  2018-09-11 20:38 [U-Boot] [PATCH v3 0/13] efi_loader: EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL Heinrich Schuchardt
                   ` (6 preceding siblings ...)
  2018-09-11 20:38 ` [U-Boot] [PATCH v3 07/13] efi_loader: EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL Heinrich Schuchardt
@ 2018-09-11 20:38 ` Heinrich Schuchardt
  2018-09-11 20:38 ` [U-Boot] [PATCH v3 09/13] efi_selftest: test EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL Heinrich Schuchardt
                   ` (4 subsequent siblings)
  12 siblings, 0 replies; 17+ messages in thread
From: Heinrich Schuchardt @ 2018-09-11 20:38 UTC (permalink / raw)
  To: u-boot

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:
-- 
2.18.0

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [U-Boot] [PATCH v3 09/13] efi_selftest: test EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL
  2018-09-11 20:38 [U-Boot] [PATCH v3 0/13] efi_loader: EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL Heinrich Schuchardt
                   ` (7 preceding siblings ...)
  2018-09-11 20:38 ` [U-Boot] [PATCH v3 08/13] efi_loader: support modifiers for F1 - F4 Heinrich Schuchardt
@ 2018-09-11 20:38 ` Heinrich Schuchardt
  2018-09-11 20:38 ` [U-Boot] [PATCH v3 10/13] test/py: " Heinrich Schuchardt
                   ` (3 subsequent siblings)
  12 siblings, 0 replies; 17+ messages in thread
From: Heinrich Schuchardt @ 2018-09-11 20:38 UTC (permalink / raw)
  To: u-boot

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,
+};
-- 
2.18.0

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [U-Boot] [PATCH v3 10/13] test/py: test EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL
  2018-09-11 20:38 [U-Boot] [PATCH v3 0/13] efi_loader: EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL Heinrich Schuchardt
                   ` (8 preceding siblings ...)
  2018-09-11 20:38 ` [U-Boot] [PATCH v3 09/13] efi_selftest: test EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL Heinrich Schuchardt
@ 2018-09-11 20:38 ` Heinrich Schuchardt
  2018-09-11 20:38 ` [U-Boot] [PATCH v3 11/13] efi_loader: implement key notify functions Heinrich Schuchardt
                   ` (2 subsequent siblings)
  12 siblings, 0 replies; 17+ messages in thread
From: Heinrich Schuchardt @ 2018-09-11 20:38 UTC (permalink / raw)
  To: u-boot

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();
+
+ at 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();
-- 
2.18.0

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [U-Boot] [PATCH v3 11/13] efi_loader: implement key notify functions
  2018-09-11 20:38 [U-Boot] [PATCH v3 0/13] efi_loader: EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL Heinrich Schuchardt
                   ` (9 preceding siblings ...)
  2018-09-11 20:38 ` [U-Boot] [PATCH v3 10/13] test/py: " Heinrich Schuchardt
@ 2018-09-11 20:38 ` Heinrich Schuchardt
  2018-09-11 20:38 ` [U-Boot] [PATCH v3 12/13] efi_selftest: test key notification functions Heinrich Schuchardt
  2018-09-11 20:38 ` [U-Boot] [PATCH v3 13/13] efi_loader: unset CONFIG_EFI_UNICODE_CAPITALIZATION Heinrich Schuchardt
  12 siblings, 0 replies; 17+ messages in thread
From: Heinrich Schuchardt @ 2018-09-11 20:38 UTC (permalink / raw)
  To: u-boot

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(&notify_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(&notify_function->link);
+	free(notify_function);
+out:
+	return EFI_EXIT(ret);
 }
 
 
-- 
2.18.0

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [U-Boot] [PATCH v3 12/13] efi_selftest: test key notification functions
  2018-09-11 20:38 [U-Boot] [PATCH v3 0/13] efi_loader: EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL Heinrich Schuchardt
                   ` (10 preceding siblings ...)
  2018-09-11 20:38 ` [U-Boot] [PATCH v3 11/13] efi_loader: implement key notify functions Heinrich Schuchardt
@ 2018-09-11 20:38 ` Heinrich Schuchardt
  2018-09-11 20:38 ` [U-Boot] [PATCH v3 13/13] efi_loader: unset CONFIG_EFI_UNICODE_CAPITALIZATION Heinrich Schuchardt
  12 siblings, 0 replies; 17+ messages in thread
From: Heinrich Schuchardt @ 2018-09-11 20:38 UTC (permalink / raw)
  To: u-boot

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:
-- 
2.18.0

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [U-Boot] [PATCH v3 13/13] efi_loader: unset CONFIG_EFI_UNICODE_CAPITALIZATION
  2018-09-11 20:38 [U-Boot] [PATCH v3 0/13] efi_loader: EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL Heinrich Schuchardt
                   ` (11 preceding siblings ...)
  2018-09-11 20:38 ` [U-Boot] [PATCH v3 12/13] efi_selftest: test key notification functions Heinrich Schuchardt
@ 2018-09-11 20:38 ` Heinrich Schuchardt
  12 siblings, 0 replies; 17+ messages in thread
From: Heinrich Schuchardt @ 2018-09-11 20:38 UTC (permalink / raw)
  To: u-boot

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
-- 
2.18.0

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [U-Boot] [PATCH v3 01/13] efi_loader: support Unicode text input
  2018-09-11 20:38 ` [U-Boot] [PATCH v3 01/13] efi_loader: support Unicode text input Heinrich Schuchardt
@ 2018-09-11 21:37   ` Alexander Graf
  2018-09-11 21:51     ` [U-Boot] [PATCH v4 " Heinrich Schuchardt
  2018-09-11 22:05     ` [U-Boot] [PATCH v5 1/13] " Heinrich Schuchardt
  0 siblings, 2 replies; 17+ messages in thread
From: Alexander Graf @ 2018-09-11 21:37 UTC (permalink / raw)
  To: u-boot



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)
> +				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;

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

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [U-Boot] [PATCH v4 01/13] efi_loader: support Unicode text input
  2018-09-11 21:37   ` Alexander Graf
@ 2018-09-11 21:51     ` Heinrich Schuchardt
  2018-09-11 22:05     ` [U-Boot] [PATCH v5 1/13] " Heinrich Schuchardt
  1 sibling, 0 replies; 17+ messages in thread
From: Heinrich Schuchardt @ 2018-09-11 21:51 UTC (permalink / raw)
  To: u-boot

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);
-- 
2.18.0

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [U-Boot] [PATCH v5 1/13] efi_loader: support Unicode text input
  2018-09-11 21:37   ` Alexander Graf
  2018-09-11 21:51     ` [U-Boot] [PATCH v4 " Heinrich Schuchardt
@ 2018-09-11 22:05     ` Heinrich Schuchardt
  1 sibling, 0 replies; 17+ messages in thread
From: Heinrich Schuchardt @ 2018-09-11 22:05 UTC (permalink / raw)
  To: u-boot

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);
-- 
2.18.0

^ permalink raw reply related	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2018-09-11 22:05 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-09-11 20:38 [U-Boot] [PATCH v3 0/13] efi_loader: EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL Heinrich Schuchardt
2018-09-11 20:38 ` [U-Boot] [PATCH v3 01/13] efi_loader: support Unicode text input Heinrich Schuchardt
2018-09-11 21:37   ` Alexander Graf
2018-09-11 21:51     ` [U-Boot] [PATCH v4 " Heinrich Schuchardt
2018-09-11 22:05     ` [U-Boot] [PATCH v5 1/13] " Heinrich Schuchardt
2018-09-11 20:38 ` [U-Boot] [PATCH v3 02/13] test/py: Unicode w/ EFI_SIMPLE_TEXT_INPUT_PROTOCOL Heinrich Schuchardt
2018-09-11 20:38 ` [U-Boot] [PATCH v3 03/13] efi_selftest: refactor text input test Heinrich Schuchardt
2018-09-11 20:38 ` [U-Boot] [PATCH v3 04/13] efi_loader: rework event handling for console Heinrich Schuchardt
2018-09-11 20:38 ` [U-Boot] [PATCH v3 05/13] efi_selftest: use WaitForKey to test text input Heinrich Schuchardt
2018-09-11 20:38 ` [U-Boot] [PATCH v3 06/13] test/py: rework test_efi_selftest_text_input() Heinrich Schuchardt
2018-09-11 20:38 ` [U-Boot] [PATCH v3 07/13] efi_loader: EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL Heinrich Schuchardt
2018-09-11 20:38 ` [U-Boot] [PATCH v3 08/13] efi_loader: support modifiers for F1 - F4 Heinrich Schuchardt
2018-09-11 20:38 ` [U-Boot] [PATCH v3 09/13] efi_selftest: test EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL Heinrich Schuchardt
2018-09-11 20:38 ` [U-Boot] [PATCH v3 10/13] test/py: " Heinrich Schuchardt
2018-09-11 20:38 ` [U-Boot] [PATCH v3 11/13] efi_loader: implement key notify functions Heinrich Schuchardt
2018-09-11 20:38 ` [U-Boot] [PATCH v3 12/13] efi_selftest: test key notification functions Heinrich Schuchardt
2018-09-11 20:38 ` [U-Boot] [PATCH v3 13/13] efi_loader: unset CONFIG_EFI_UNICODE_CAPITALIZATION Heinrich Schuchardt

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.