xref: /openbmc/u-boot/lib/efi_loader/efi_console.c (revision 116a3a1ae085753e04b62c37a0f11f80cab9d730)
1f739fcd8STom Rini // SPDX-License-Identifier: GPL-2.0+
2c1311ad4SAlexander Graf /*
3c1311ad4SAlexander Graf  *  EFI application console interface
4c1311ad4SAlexander Graf  *
5c1311ad4SAlexander Graf  *  Copyright (c) 2016 Alexander Graf
6c1311ad4SAlexander Graf  */
7c1311ad4SAlexander Graf 
8c1311ad4SAlexander Graf #include <common.h>
978178bb0SRob Clark #include <charset.h>
10a18c5a83SRob Clark #include <dm/device.h>
11c1311ad4SAlexander Graf #include <efi_loader.h>
12a18c5a83SRob Clark #include <stdio_dev.h>
13a18c5a83SRob Clark #include <video_console.h>
14c1311ad4SAlexander Graf 
155be8b0a3SEmmanuel Vadot #define EFI_COUT_MODE_2 2
165be8b0a3SEmmanuel Vadot #define EFI_MAX_COUT_MODE 3
175be8b0a3SEmmanuel Vadot 
185be8b0a3SEmmanuel Vadot struct cout_mode {
195be8b0a3SEmmanuel Vadot 	unsigned long columns;
205be8b0a3SEmmanuel Vadot 	unsigned long rows;
215be8b0a3SEmmanuel Vadot 	int present;
225be8b0a3SEmmanuel Vadot };
235be8b0a3SEmmanuel Vadot 
245be8b0a3SEmmanuel Vadot static struct cout_mode efi_cout_modes[] = {
255be8b0a3SEmmanuel Vadot 	/* EFI Mode 0 is 80x25 and always present */
265be8b0a3SEmmanuel Vadot 	{
275be8b0a3SEmmanuel Vadot 		.columns = 80,
285be8b0a3SEmmanuel Vadot 		.rows = 25,
295be8b0a3SEmmanuel Vadot 		.present = 1,
305be8b0a3SEmmanuel Vadot 	},
315be8b0a3SEmmanuel Vadot 	/* EFI Mode 1 is always 80x50 */
325be8b0a3SEmmanuel Vadot 	{
335be8b0a3SEmmanuel Vadot 		.columns = 80,
345be8b0a3SEmmanuel Vadot 		.rows = 50,
355be8b0a3SEmmanuel Vadot 		.present = 0,
365be8b0a3SEmmanuel Vadot 	},
375be8b0a3SEmmanuel Vadot 	/* Value are unknown until we query the console */
385be8b0a3SEmmanuel Vadot 	{
395be8b0a3SEmmanuel Vadot 		.columns = 0,
405be8b0a3SEmmanuel Vadot 		.rows = 0,
415be8b0a3SEmmanuel Vadot 		.present = 0,
425be8b0a3SEmmanuel Vadot 	},
435be8b0a3SEmmanuel Vadot };
445be8b0a3SEmmanuel Vadot 
45110c6280SHeinrich Schuchardt const efi_guid_t efi_guid_text_input_ex_protocol =
46110c6280SHeinrich Schuchardt 			EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
47ebb4dd5bSHeinrich Schuchardt const efi_guid_t efi_guid_text_input_protocol =
48ebb4dd5bSHeinrich Schuchardt 			EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID;
49110c6280SHeinrich Schuchardt const efi_guid_t efi_guid_text_output_protocol =
50110c6280SHeinrich Schuchardt 			EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID;
51c1311ad4SAlexander Graf 
52c1311ad4SAlexander Graf #define cESC '\x1b'
53c1311ad4SAlexander Graf #define ESC "\x1b"
54c1311ad4SAlexander Graf 
555be8b0a3SEmmanuel Vadot /* Default to mode 0 */
56c1311ad4SAlexander Graf static struct simple_text_output_mode efi_con_mode = {
575be8b0a3SEmmanuel Vadot 	.max_mode = 1,
58c1311ad4SAlexander Graf 	.mode = 0,
59c1311ad4SAlexander Graf 	.attribute = 0,
60c1311ad4SAlexander Graf 	.cursor_column = 0,
61c1311ad4SAlexander Graf 	.cursor_row = 0,
62c1311ad4SAlexander Graf 	.cursor_visible = 1,
63c1311ad4SAlexander Graf };
64c1311ad4SAlexander Graf 
term_get_char(s32 * c)65*dd1a1ec2SMatthias Brugger static int term_get_char(s32 *c)
66*dd1a1ec2SMatthias Brugger {
67*dd1a1ec2SMatthias Brugger 	u64 timeout;
68*dd1a1ec2SMatthias Brugger 
69*dd1a1ec2SMatthias Brugger 	/* Wait up to 100 ms for a character */
70*dd1a1ec2SMatthias Brugger 	timeout = timer_get_us() + 100000;
71*dd1a1ec2SMatthias Brugger 
72*dd1a1ec2SMatthias Brugger 	while (!tstc())
73*dd1a1ec2SMatthias Brugger 		if (timer_get_us() > timeout)
74*dd1a1ec2SMatthias Brugger 			return 1;
75*dd1a1ec2SMatthias Brugger 
76*dd1a1ec2SMatthias Brugger 	*c = getc();
77*dd1a1ec2SMatthias Brugger 	return 0;
78*dd1a1ec2SMatthias Brugger }
79*dd1a1ec2SMatthias Brugger 
8062217295SHeinrich Schuchardt /*
8162217295SHeinrich Schuchardt  * Receive and parse a reply from the terminal.
8262217295SHeinrich Schuchardt  *
8362217295SHeinrich Schuchardt  * @n:		array of return values
8462217295SHeinrich Schuchardt  * @num:	number of return values expected
8562217295SHeinrich Schuchardt  * @end_char:	character indicating end of terminal message
8662217295SHeinrich Schuchardt  * @return:	non-zero indicates error
8762217295SHeinrich Schuchardt  */
term_read_reply(int * n,int num,char end_char)8862217295SHeinrich Schuchardt static int term_read_reply(int *n, int num, char end_char)
89c1311ad4SAlexander Graf {
90*dd1a1ec2SMatthias Brugger 	s32 c;
91c1311ad4SAlexander Graf 	int i = 0;
92c1311ad4SAlexander Graf 
93*dd1a1ec2SMatthias Brugger 	if (term_get_char(&c) || c != cESC)
94c1311ad4SAlexander Graf 		return -1;
95*dd1a1ec2SMatthias Brugger 
96*dd1a1ec2SMatthias Brugger 	if (term_get_char(&c) || c != '[')
97c1311ad4SAlexander Graf 		return -1;
98c1311ad4SAlexander Graf 
99c1311ad4SAlexander Graf 	n[0] = 0;
100c1311ad4SAlexander Graf 	while (1) {
101*dd1a1ec2SMatthias Brugger 		if (!term_get_char(&c)) {
102c1311ad4SAlexander Graf 			if (c == ';') {
103c1311ad4SAlexander Graf 				i++;
10462217295SHeinrich Schuchardt 				if (i >= num)
105c1311ad4SAlexander Graf 					return -1;
106c1311ad4SAlexander Graf 				n[i] = 0;
107c1311ad4SAlexander Graf 				continue;
108c1311ad4SAlexander Graf 			} else if (c == end_char) {
109c1311ad4SAlexander Graf 				break;
110c1311ad4SAlexander Graf 			} else if (c > '9' || c < '0') {
111c1311ad4SAlexander Graf 				return -1;
112c1311ad4SAlexander Graf 			}
113c1311ad4SAlexander Graf 
114c1311ad4SAlexander Graf 			/* Read one more decimal position */
115c1311ad4SAlexander Graf 			n[i] *= 10;
116c1311ad4SAlexander Graf 			n[i] += c - '0';
117*dd1a1ec2SMatthias Brugger 		} else {
118*dd1a1ec2SMatthias Brugger 			return -1;
119*dd1a1ec2SMatthias Brugger 		}
120c1311ad4SAlexander Graf 	}
12162217295SHeinrich Schuchardt 	if (i != num - 1)
12262217295SHeinrich Schuchardt 		return -1;
123c1311ad4SAlexander Graf 
124c1311ad4SAlexander Graf 	return 0;
125c1311ad4SAlexander Graf }
126c1311ad4SAlexander Graf 
efi_cout_output_string(struct efi_simple_text_output_protocol * this,const efi_string_t string)127c1311ad4SAlexander Graf static efi_status_t EFIAPI efi_cout_output_string(
128c1311ad4SAlexander Graf 			struct efi_simple_text_output_protocol *this,
1293a45bc7fSRob Clark 			const efi_string_t string)
130c1311ad4SAlexander Graf {
1313a45bc7fSRob Clark 	struct simple_text_output_mode *con = &efi_con_mode;
1323a45bc7fSRob Clark 	struct cout_mode *mode = &efi_cout_modes[con->mode];
133ba7bd5c2SHeinrich Schuchardt 	char *buf, *pos;
134ba7bd5c2SHeinrich Schuchardt 	u16 *p;
135ba7bd5c2SHeinrich Schuchardt 	efi_status_t ret = EFI_SUCCESS;
136c1311ad4SAlexander Graf 
137c1311ad4SAlexander Graf 	EFI_ENTRY("%p, %p", this, string);
1383a45bc7fSRob Clark 
139ba7bd5c2SHeinrich Schuchardt 	buf = malloc(utf16_utf8_strlen(string) + 1);
140ba7bd5c2SHeinrich Schuchardt 	if (!buf) {
141ba7bd5c2SHeinrich Schuchardt 		ret = EFI_OUT_OF_RESOURCES;
142ba7bd5c2SHeinrich Schuchardt 		goto out;
143ba7bd5c2SHeinrich Schuchardt 	}
144ba7bd5c2SHeinrich Schuchardt 	pos = buf;
145ba7bd5c2SHeinrich Schuchardt 	utf16_utf8_strcpy(&pos, string);
1463a45bc7fSRob Clark 	fputs(stdout, buf);
147ba7bd5c2SHeinrich Schuchardt 	free(buf);
1483a45bc7fSRob Clark 
1497ca7c3c0SHeinrich Schuchardt 	/*
1507ca7c3c0SHeinrich Schuchardt 	 * Update the cursor position.
1517ca7c3c0SHeinrich Schuchardt 	 *
1527ca7c3c0SHeinrich Schuchardt 	 * The UEFI spec provides advance rules for U+0000, U+0008, U+000A,
1537ca7c3c0SHeinrich Schuchardt 	 * and U000D. All other characters, including control characters
15414d103bbSHeinrich Schuchardt 	 * U+0007 (BEL) and U+0009 (TAB), have to increase the column by one.
1557ca7c3c0SHeinrich Schuchardt 	 */
1567ca7c3c0SHeinrich Schuchardt 	for (p = string; *p; ++p) {
1573a45bc7fSRob Clark 		switch (*p) {
1587ca7c3c0SHeinrich Schuchardt 		case '\b':	/* U+0008, backspace */
1597ca7c3c0SHeinrich Schuchardt 			con->cursor_column = max(0, con->cursor_column - 1);
1603a45bc7fSRob Clark 			break;
1617ca7c3c0SHeinrich Schuchardt 		case '\n':	/* U+000A, newline */
1623a45bc7fSRob Clark 			con->cursor_column = 0;
1633a45bc7fSRob Clark 			con->cursor_row++;
1643a45bc7fSRob Clark 			break;
1657ca7c3c0SHeinrich Schuchardt 		case '\r':	/* U+000D, carriage-return */
1667ca7c3c0SHeinrich Schuchardt 			con->cursor_column = 0;
1673a45bc7fSRob Clark 			break;
1687ca7c3c0SHeinrich Schuchardt 		case 0xd800 ... 0xdbff:
1697ca7c3c0SHeinrich Schuchardt 			/*
1707ca7c3c0SHeinrich Schuchardt 			 * Ignore high surrogates, we do not want to count a
1717ca7c3c0SHeinrich Schuchardt 			 * Unicode character twice.
1727ca7c3c0SHeinrich Schuchardt 			 */
1733a45bc7fSRob Clark 			break;
1743a45bc7fSRob Clark 		default:
1753a45bc7fSRob Clark 			con->cursor_column++;
1763a45bc7fSRob Clark 			break;
177c1311ad4SAlexander Graf 		}
1783a45bc7fSRob Clark 		if (con->cursor_column >= mode->columns) {
1793a45bc7fSRob Clark 			con->cursor_column = 0;
1803a45bc7fSRob Clark 			con->cursor_row++;
1813a45bc7fSRob Clark 		}
1823a45bc7fSRob Clark 		con->cursor_row = min(con->cursor_row, (s32)mode->rows - 1);
183c1311ad4SAlexander Graf 	}
184c1311ad4SAlexander Graf 
185ba7bd5c2SHeinrich Schuchardt out:
186ba7bd5c2SHeinrich Schuchardt 	return EFI_EXIT(ret);
187c1311ad4SAlexander Graf }
188c1311ad4SAlexander Graf 
efi_cout_test_string(struct efi_simple_text_output_protocol * this,const efi_string_t string)189c1311ad4SAlexander Graf static efi_status_t EFIAPI efi_cout_test_string(
190c1311ad4SAlexander Graf 			struct efi_simple_text_output_protocol *this,
1913a45bc7fSRob Clark 			const efi_string_t string)
192c1311ad4SAlexander Graf {
193c1311ad4SAlexander Graf 	EFI_ENTRY("%p, %p", this, string);
194c1311ad4SAlexander Graf 	return EFI_EXIT(EFI_SUCCESS);
195c1311ad4SAlexander Graf }
196c1311ad4SAlexander Graf 
cout_mode_matches(struct cout_mode * mode,int rows,int cols)1975be8b0a3SEmmanuel Vadot static bool cout_mode_matches(struct cout_mode *mode, int rows, int cols)
1985be8b0a3SEmmanuel Vadot {
1995be8b0a3SEmmanuel Vadot 	if (!mode->present)
2005be8b0a3SEmmanuel Vadot 		return false;
2015be8b0a3SEmmanuel Vadot 
2025be8b0a3SEmmanuel Vadot 	return (mode->rows == rows) && (mode->columns == cols);
2035be8b0a3SEmmanuel Vadot }
2045be8b0a3SEmmanuel Vadot 
2056bb591f7SHeinrich Schuchardt /**
2066bb591f7SHeinrich Schuchardt  * query_console_serial() - query console size
2076bb591f7SHeinrich Schuchardt  *
2086bb591f7SHeinrich Schuchardt  * @rows	pointer to return number of rows
2096bb591f7SHeinrich Schuchardt  * @columns	pointer to return number of columns
2106bb591f7SHeinrich Schuchardt  * Returns	0 on success
2116bb591f7SHeinrich Schuchardt  */
query_console_serial(int * rows,int * cols)21271cc25c3SRob Clark static int query_console_serial(int *rows, int *cols)
213c1311ad4SAlexander Graf {
2146bb591f7SHeinrich Schuchardt 	int ret = 0;
2156bb591f7SHeinrich Schuchardt 	int n[2];
216c1311ad4SAlexander Graf 
217c1311ad4SAlexander Graf 	/* Empty input buffer */
218c1311ad4SAlexander Graf 	while (tstc())
219c1311ad4SAlexander Graf 		getc();
220c1311ad4SAlexander Graf 
2216bb591f7SHeinrich Schuchardt 	/*
2226bb591f7SHeinrich Schuchardt 	 * Not all terminals understand CSI [18t for querying the console size.
2236bb591f7SHeinrich Schuchardt 	 * We should adhere to escape sequences documented in the console_codes
2246bb591f7SHeinrich Schuchardt 	 * man page and the ECMA-48 standard.
2256bb591f7SHeinrich Schuchardt 	 *
2266bb591f7SHeinrich Schuchardt 	 * So here we follow a different approach. We position the cursor to the
2276bb591f7SHeinrich Schuchardt 	 * bottom right and query its position. Before leaving the function we
2286bb591f7SHeinrich Schuchardt 	 * restore the original cursor position.
2296bb591f7SHeinrich Schuchardt 	 */
2306bb591f7SHeinrich Schuchardt 	printf(ESC "7"		/* Save cursor position */
2316bb591f7SHeinrich Schuchardt 	       ESC "[r"		/* Set scrolling region to full window */
2326bb591f7SHeinrich Schuchardt 	       ESC "[999;999H"	/* Move to bottom right corner */
2336bb591f7SHeinrich Schuchardt 	       ESC "[6n");	/* Query cursor position */
234c1311ad4SAlexander Graf 
2356bb591f7SHeinrich Schuchardt 	/* Read {rows,cols} */
2366bb591f7SHeinrich Schuchardt 	if (term_read_reply(n, 2, 'R')) {
2376bb591f7SHeinrich Schuchardt 		ret = 1;
2386bb591f7SHeinrich Schuchardt 		goto out;
2396bb591f7SHeinrich Schuchardt 	}
24071cc25c3SRob Clark 
2416bb591f7SHeinrich Schuchardt 	*cols = n[1];
2426bb591f7SHeinrich Schuchardt 	*rows = n[0];
2436bb591f7SHeinrich Schuchardt out:
2446bb591f7SHeinrich Schuchardt 	printf(ESC "8");	/* Restore cursor position */
2456bb591f7SHeinrich Schuchardt 	return ret;
246c1311ad4SAlexander Graf }
247c1311ad4SAlexander Graf 
248a4aa7befSHeinrich Schuchardt /*
249a4aa7befSHeinrich Schuchardt  * Update the mode table.
250a4aa7befSHeinrich Schuchardt  *
251a4aa7befSHeinrich Schuchardt  * By default the only mode available is 80x25. If the console has at least 50
252a4aa7befSHeinrich Schuchardt  * lines, enable mode 80x50. If we can query the console size and it is neither
253a4aa7befSHeinrich Schuchardt  * 80x25 nor 80x50, set it as an additional mode.
254a4aa7befSHeinrich Schuchardt  */
query_console_size(void)255a4aa7befSHeinrich Schuchardt static void query_console_size(void)
25671cc25c3SRob Clark {
257a18c5a83SRob Clark 	const char *stdout_name = env_get("stdout");
25880483b2aSAlexander Graf 	int rows = 25, cols = 80;
25971cc25c3SRob Clark 
260a18c5a83SRob Clark 	if (stdout_name && !strcmp(stdout_name, "vidconsole") &&
261a18c5a83SRob Clark 	    IS_ENABLED(CONFIG_DM_VIDEO)) {
262a18c5a83SRob Clark 		struct stdio_dev *stdout_dev =
263a18c5a83SRob Clark 			stdio_get_by_name("vidconsole");
264a18c5a83SRob Clark 		struct udevice *dev = stdout_dev->priv;
265a18c5a83SRob Clark 		struct vidconsole_priv *priv =
266a18c5a83SRob Clark 			dev_get_uclass_priv(dev);
267a18c5a83SRob Clark 		rows = priv->rows;
268a18c5a83SRob Clark 		cols = priv->cols;
269a18c5a83SRob Clark 	} else if (query_console_serial(&rows, &cols)) {
270a4aa7befSHeinrich Schuchardt 		return;
271a18c5a83SRob Clark 	}
2725be8b0a3SEmmanuel Vadot 
2735be8b0a3SEmmanuel Vadot 	/* Test if we can have Mode 1 */
2745be8b0a3SEmmanuel Vadot 	if (cols >= 80 && rows >= 50) {
2755be8b0a3SEmmanuel Vadot 		efi_cout_modes[1].present = 1;
2765be8b0a3SEmmanuel Vadot 		efi_con_mode.max_mode = 2;
277c1311ad4SAlexander Graf 	}
278c1311ad4SAlexander Graf 
2795be8b0a3SEmmanuel Vadot 	/*
2805be8b0a3SEmmanuel Vadot 	 * Install our mode as mode 2 if it is different
2815be8b0a3SEmmanuel Vadot 	 * than mode 0 or 1 and set it as the currently selected mode
2825be8b0a3SEmmanuel Vadot 	 */
2835be8b0a3SEmmanuel Vadot 	if (!cout_mode_matches(&efi_cout_modes[0], rows, cols) &&
2845be8b0a3SEmmanuel Vadot 	    !cout_mode_matches(&efi_cout_modes[1], rows, cols)) {
2855be8b0a3SEmmanuel Vadot 		efi_cout_modes[EFI_COUT_MODE_2].columns = cols;
2865be8b0a3SEmmanuel Vadot 		efi_cout_modes[EFI_COUT_MODE_2].rows = rows;
2875be8b0a3SEmmanuel Vadot 		efi_cout_modes[EFI_COUT_MODE_2].present = 1;
2885be8b0a3SEmmanuel Vadot 		efi_con_mode.max_mode = EFI_MAX_COUT_MODE;
2895be8b0a3SEmmanuel Vadot 		efi_con_mode.mode = EFI_COUT_MODE_2;
2905be8b0a3SEmmanuel Vadot 	}
2915be8b0a3SEmmanuel Vadot }
2925be8b0a3SEmmanuel Vadot 
efi_cout_query_mode(struct efi_simple_text_output_protocol * this,unsigned long mode_number,unsigned long * columns,unsigned long * rows)293a4aa7befSHeinrich Schuchardt static efi_status_t EFIAPI efi_cout_query_mode(
294a4aa7befSHeinrich Schuchardt 			struct efi_simple_text_output_protocol *this,
295a4aa7befSHeinrich Schuchardt 			unsigned long mode_number, unsigned long *columns,
296a4aa7befSHeinrich Schuchardt 			unsigned long *rows)
297a4aa7befSHeinrich Schuchardt {
298a4aa7befSHeinrich Schuchardt 	EFI_ENTRY("%p, %ld, %p, %p", this, mode_number, columns, rows);
299a4aa7befSHeinrich Schuchardt 
3005be8b0a3SEmmanuel Vadot 	if (mode_number >= efi_con_mode.max_mode)
3015be8b0a3SEmmanuel Vadot 		return EFI_EXIT(EFI_UNSUPPORTED);
3025be8b0a3SEmmanuel Vadot 
3035be8b0a3SEmmanuel Vadot 	if (efi_cout_modes[mode_number].present != 1)
3045be8b0a3SEmmanuel Vadot 		return EFI_EXIT(EFI_UNSUPPORTED);
3055be8b0a3SEmmanuel Vadot 
306c1311ad4SAlexander Graf 	if (columns)
3075be8b0a3SEmmanuel Vadot 		*columns = efi_cout_modes[mode_number].columns;
308c1311ad4SAlexander Graf 	if (rows)
3095be8b0a3SEmmanuel Vadot 		*rows = efi_cout_modes[mode_number].rows;
310c1311ad4SAlexander Graf 
311c1311ad4SAlexander Graf 	return EFI_EXIT(EFI_SUCCESS);
312c1311ad4SAlexander Graf }
313c1311ad4SAlexander Graf 
efi_cout_set_mode(struct efi_simple_text_output_protocol * this,unsigned long mode_number)314c1311ad4SAlexander Graf static efi_status_t EFIAPI efi_cout_set_mode(
315c1311ad4SAlexander Graf 			struct efi_simple_text_output_protocol *this,
316c1311ad4SAlexander Graf 			unsigned long mode_number)
317c1311ad4SAlexander Graf {
318c1311ad4SAlexander Graf 	EFI_ENTRY("%p, %ld", this, mode_number);
319c1311ad4SAlexander Graf 
320c1311ad4SAlexander Graf 
3215be8b0a3SEmmanuel Vadot 	if (mode_number > efi_con_mode.max_mode)
322c1311ad4SAlexander Graf 		return EFI_EXIT(EFI_UNSUPPORTED);
3235be8b0a3SEmmanuel Vadot 
3245be8b0a3SEmmanuel Vadot 	efi_con_mode.mode = mode_number;
3255be8b0a3SEmmanuel Vadot 	efi_con_mode.cursor_column = 0;
3265be8b0a3SEmmanuel Vadot 	efi_con_mode.cursor_row = 0;
3275be8b0a3SEmmanuel Vadot 
3285be8b0a3SEmmanuel Vadot 	return EFI_EXIT(EFI_SUCCESS);
329c1311ad4SAlexander Graf }
330c1311ad4SAlexander Graf 
3312d5dc2a5SRob Clark static const struct {
3322d5dc2a5SRob Clark 	unsigned int fg;
3332d5dc2a5SRob Clark 	unsigned int bg;
3342d5dc2a5SRob Clark } color[] = {
3352d5dc2a5SRob Clark 	{ 30, 40 },     /* 0: black */
3362d5dc2a5SRob Clark 	{ 34, 44 },     /* 1: blue */
3372d5dc2a5SRob Clark 	{ 32, 42 },     /* 2: green */
3382d5dc2a5SRob Clark 	{ 36, 46 },     /* 3: cyan */
3392d5dc2a5SRob Clark 	{ 31, 41 },     /* 4: red */
3402d5dc2a5SRob Clark 	{ 35, 45 },     /* 5: magenta */
34114d103bbSHeinrich Schuchardt 	{ 33, 43 },     /* 6: brown, map to yellow as EDK2 does*/
34214d103bbSHeinrich Schuchardt 	{ 37, 47 },     /* 7: light gray, map to white */
3432d5dc2a5SRob Clark };
3442d5dc2a5SRob Clark 
3452d5dc2a5SRob Clark /* See EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.SetAttribute(). */
efi_cout_set_attribute(struct efi_simple_text_output_protocol * this,unsigned long attribute)346c1311ad4SAlexander Graf static efi_status_t EFIAPI efi_cout_set_attribute(
347c1311ad4SAlexander Graf 			struct efi_simple_text_output_protocol *this,
348c1311ad4SAlexander Graf 			unsigned long attribute)
349c1311ad4SAlexander Graf {
3502d5dc2a5SRob Clark 	unsigned int bold = EFI_ATTR_BOLD(attribute);
3512d5dc2a5SRob Clark 	unsigned int fg = EFI_ATTR_FG(attribute);
3522d5dc2a5SRob Clark 	unsigned int bg = EFI_ATTR_BG(attribute);
3532d5dc2a5SRob Clark 
354c1311ad4SAlexander Graf 	EFI_ENTRY("%p, %lx", this, attribute);
355c1311ad4SAlexander Graf 
3562d5dc2a5SRob Clark 	if (attribute)
3572d5dc2a5SRob Clark 		printf(ESC"[%u;%u;%um", bold, color[fg].fg, color[bg].bg);
3582d5dc2a5SRob Clark 	else
3592d5dc2a5SRob Clark 		printf(ESC"[0;37;40m");
3602d5dc2a5SRob Clark 
3612d5dc2a5SRob Clark 	return EFI_EXIT(EFI_SUCCESS);
362c1311ad4SAlexander Graf }
363c1311ad4SAlexander Graf 
efi_cout_clear_screen(struct efi_simple_text_output_protocol * this)364c1311ad4SAlexander Graf static efi_status_t EFIAPI efi_cout_clear_screen(
365c1311ad4SAlexander Graf 			struct efi_simple_text_output_protocol *this)
366c1311ad4SAlexander Graf {
367c1311ad4SAlexander Graf 	EFI_ENTRY("%p", this);
368c1311ad4SAlexander Graf 
369c1311ad4SAlexander Graf 	printf(ESC"[2J");
370e67ff94dSHeinrich Schuchardt 	efi_con_mode.cursor_column = 0;
371e67ff94dSHeinrich Schuchardt 	efi_con_mode.cursor_row = 0;
372c1311ad4SAlexander Graf 
373c1311ad4SAlexander Graf 	return EFI_EXIT(EFI_SUCCESS);
374c1311ad4SAlexander Graf }
375c1311ad4SAlexander Graf 
efi_cout_reset(struct efi_simple_text_output_protocol * this,char extended_verification)3769d12daffSHeinrich Schuchardt static efi_status_t EFIAPI efi_cout_reset(
3779d12daffSHeinrich Schuchardt 			struct efi_simple_text_output_protocol *this,
3789d12daffSHeinrich Schuchardt 			char extended_verification)
3799d12daffSHeinrich Schuchardt {
3809d12daffSHeinrich Schuchardt 	EFI_ENTRY("%p, %d", this, extended_verification);
3819d12daffSHeinrich Schuchardt 
3829d12daffSHeinrich Schuchardt 	/* Clear screen */
3839d12daffSHeinrich Schuchardt 	EFI_CALL(efi_cout_clear_screen(this));
3849d12daffSHeinrich Schuchardt 	/* Set default colors */
3859d12daffSHeinrich Schuchardt 	printf(ESC "[0;37;40m");
3869d12daffSHeinrich Schuchardt 
3879d12daffSHeinrich Schuchardt 	return EFI_EXIT(EFI_SUCCESS);
3889d12daffSHeinrich Schuchardt }
3899d12daffSHeinrich Schuchardt 
efi_cout_set_cursor_position(struct efi_simple_text_output_protocol * this,unsigned long column,unsigned long row)390c1311ad4SAlexander Graf static efi_status_t EFIAPI efi_cout_set_cursor_position(
391c1311ad4SAlexander Graf 			struct efi_simple_text_output_protocol *this,
392c1311ad4SAlexander Graf 			unsigned long column, unsigned long row)
393c1311ad4SAlexander Graf {
394aaace4b0SHeinrich Schuchardt 	efi_status_t ret = EFI_SUCCESS;
395aaace4b0SHeinrich Schuchardt 	struct simple_text_output_mode *con = &efi_con_mode;
396aaace4b0SHeinrich Schuchardt 	struct cout_mode *mode = &efi_cout_modes[con->mode];
397aaace4b0SHeinrich Schuchardt 
398c1311ad4SAlexander Graf 	EFI_ENTRY("%p, %ld, %ld", this, column, row);
399c1311ad4SAlexander Graf 
400aaace4b0SHeinrich Schuchardt 	/* Check parameters */
401aaace4b0SHeinrich Schuchardt 	if (!this) {
402aaace4b0SHeinrich Schuchardt 		ret = EFI_INVALID_PARAMETER;
403aaace4b0SHeinrich Schuchardt 		goto out;
404aaace4b0SHeinrich Schuchardt 	}
405aaace4b0SHeinrich Schuchardt 	if (row >= mode->rows || column >= mode->columns) {
406aaace4b0SHeinrich Schuchardt 		ret = EFI_UNSUPPORTED;
407aaace4b0SHeinrich Schuchardt 		goto out;
408aaace4b0SHeinrich Schuchardt 	}
409aaace4b0SHeinrich Schuchardt 
410aaace4b0SHeinrich Schuchardt 	/*
411aaace4b0SHeinrich Schuchardt 	 * Set cursor position by sending CSI H.
412aaace4b0SHeinrich Schuchardt 	 * EFI origin is [0, 0], terminal origin is [1, 1].
413aaace4b0SHeinrich Schuchardt 	 */
414aaace4b0SHeinrich Schuchardt 	printf(ESC "[%d;%dH", (int)row + 1, (int)column + 1);
415c1311ad4SAlexander Graf 	efi_con_mode.cursor_column = column;
416c1311ad4SAlexander Graf 	efi_con_mode.cursor_row = row;
417aaace4b0SHeinrich Schuchardt out:
418aaace4b0SHeinrich Schuchardt 	return EFI_EXIT(ret);
419c1311ad4SAlexander Graf }
420c1311ad4SAlexander Graf 
efi_cout_enable_cursor(struct efi_simple_text_output_protocol * this,bool enable)421c1311ad4SAlexander Graf static efi_status_t EFIAPI efi_cout_enable_cursor(
422c1311ad4SAlexander Graf 			struct efi_simple_text_output_protocol *this,
423c1311ad4SAlexander Graf 			bool enable)
424c1311ad4SAlexander Graf {
425c1311ad4SAlexander Graf 	EFI_ENTRY("%p, %d", this, enable);
426c1311ad4SAlexander Graf 
427c1311ad4SAlexander Graf 	printf(ESC"[?25%c", enable ? 'h' : 'l');
428c1311ad4SAlexander Graf 
429c1311ad4SAlexander Graf 	return EFI_EXIT(EFI_SUCCESS);
430c1311ad4SAlexander Graf }
431c1311ad4SAlexander Graf 
432ebb4dd5bSHeinrich Schuchardt struct efi_simple_text_output_protocol efi_con_out = {
433c1311ad4SAlexander Graf 	.reset = efi_cout_reset,
434c1311ad4SAlexander Graf 	.output_string = efi_cout_output_string,
435c1311ad4SAlexander Graf 	.test_string = efi_cout_test_string,
436c1311ad4SAlexander Graf 	.query_mode = efi_cout_query_mode,
437c1311ad4SAlexander Graf 	.set_mode = efi_cout_set_mode,
438c1311ad4SAlexander Graf 	.set_attribute = efi_cout_set_attribute,
439c1311ad4SAlexander Graf 	.clear_screen = efi_cout_clear_screen,
440c1311ad4SAlexander Graf 	.set_cursor_position = efi_cout_set_cursor_position,
441c1311ad4SAlexander Graf 	.enable_cursor = efi_cout_enable_cursor,
442c1311ad4SAlexander Graf 	.mode = (void*)&efi_con_mode,
443c1311ad4SAlexander Graf };
444c1311ad4SAlexander Graf 
4454fdcf066SHeinrich Schuchardt /**
4464fdcf066SHeinrich Schuchardt  * struct efi_cin_notify_function - registered console input notify function
4474fdcf066SHeinrich Schuchardt  *
4484fdcf066SHeinrich Schuchardt  * @link:	link to list
4494fdcf066SHeinrich Schuchardt  * @data:	key to notify
4504fdcf066SHeinrich Schuchardt  * @function:	function to call
4514fdcf066SHeinrich Schuchardt  */
4524fdcf066SHeinrich Schuchardt struct efi_cin_notify_function {
4534fdcf066SHeinrich Schuchardt 	struct list_head link;
4544fdcf066SHeinrich Schuchardt 	struct efi_key_data key;
4554fdcf066SHeinrich Schuchardt 	efi_status_t (EFIAPI *function)
4564fdcf066SHeinrich Schuchardt 		(struct efi_key_data *key_data);
4574fdcf066SHeinrich Schuchardt };
4584fdcf066SHeinrich Schuchardt 
4590dfd13a4SHeinrich Schuchardt static bool key_available;
460110c6280SHeinrich Schuchardt static struct efi_key_data next_key;
4614fdcf066SHeinrich Schuchardt static LIST_HEAD(cin_notify_functions);
4624f187897SHeinrich Schuchardt 
4630dfd13a4SHeinrich Schuchardt /**
46455fbdf99SHeinrich Schuchardt  * set_shift_mask() - set shift mask
46555fbdf99SHeinrich Schuchardt  *
46655fbdf99SHeinrich Schuchardt  * @mod:	Xterm shift mask
46755fbdf99SHeinrich Schuchardt  */
set_shift_mask(int mod,struct efi_key_state * key_state)46855fbdf99SHeinrich Schuchardt void set_shift_mask(int mod, struct efi_key_state *key_state)
46955fbdf99SHeinrich Schuchardt {
47055fbdf99SHeinrich Schuchardt 	key_state->key_shift_state = EFI_SHIFT_STATE_VALID;
47155fbdf99SHeinrich Schuchardt 	if (mod) {
47255fbdf99SHeinrich Schuchardt 		--mod;
47355fbdf99SHeinrich Schuchardt 		if (mod & 1)
47455fbdf99SHeinrich Schuchardt 			key_state->key_shift_state |= EFI_LEFT_SHIFT_PRESSED;
47555fbdf99SHeinrich Schuchardt 		if (mod & 2)
47655fbdf99SHeinrich Schuchardt 			key_state->key_shift_state |= EFI_LEFT_ALT_PRESSED;
47755fbdf99SHeinrich Schuchardt 		if (mod & 4)
47855fbdf99SHeinrich Schuchardt 			key_state->key_shift_state |= EFI_LEFT_CONTROL_PRESSED;
47955fbdf99SHeinrich Schuchardt 		if (mod & 8)
48055fbdf99SHeinrich Schuchardt 			key_state->key_shift_state |= EFI_LEFT_LOGO_PRESSED;
48155fbdf99SHeinrich Schuchardt 	} else {
48255fbdf99SHeinrich Schuchardt 		key_state->key_shift_state |= EFI_LEFT_LOGO_PRESSED;
48355fbdf99SHeinrich Schuchardt 	}
48455fbdf99SHeinrich Schuchardt }
48555fbdf99SHeinrich Schuchardt 
48655fbdf99SHeinrich Schuchardt /**
487110c6280SHeinrich Schuchardt  * analyze_modifiers() - analyze modifiers (shift, alt, ctrl) for function keys
4880dfd13a4SHeinrich Schuchardt  *
4890fb4169eSHeinrich Schuchardt  * This gets called when we have already parsed CSI.
4900fb4169eSHeinrich Schuchardt  *
4910fb4169eSHeinrich Schuchardt  * @modifiers:  bit mask (shift, alt, ctrl)
4920fb4169eSHeinrich Schuchardt  * @return:	the unmodified code
4930fb4169eSHeinrich Schuchardt  */
analyze_modifiers(struct efi_key_state * key_state)494110c6280SHeinrich Schuchardt static int analyze_modifiers(struct efi_key_state *key_state)
4950fb4169eSHeinrich Schuchardt {
496110c6280SHeinrich Schuchardt 	int c, mod = 0, ret = 0;
4970fb4169eSHeinrich Schuchardt 
4980fb4169eSHeinrich Schuchardt 	c = getc();
4990fb4169eSHeinrich Schuchardt 
5000fb4169eSHeinrich Schuchardt 	if (c != ';') {
5010fb4169eSHeinrich Schuchardt 		ret = c;
5020fb4169eSHeinrich Schuchardt 		if (c == '~')
5030fb4169eSHeinrich Schuchardt 			goto out;
5040fb4169eSHeinrich Schuchardt 		c = getc();
5050fb4169eSHeinrich Schuchardt 	}
5060fb4169eSHeinrich Schuchardt 	for (;;) {
5070fb4169eSHeinrich Schuchardt 		switch (c) {
5080fb4169eSHeinrich Schuchardt 		case '0'...'9':
5090fb4169eSHeinrich Schuchardt 			mod *= 10;
5100fb4169eSHeinrich Schuchardt 			mod += c - '0';
5110fb4169eSHeinrich Schuchardt 		/* fall through */
5120fb4169eSHeinrich Schuchardt 		case ';':
5130fb4169eSHeinrich Schuchardt 			c = getc();
5140fb4169eSHeinrich Schuchardt 			break;
5150fb4169eSHeinrich Schuchardt 		default:
5160fb4169eSHeinrich Schuchardt 			goto out;
5170fb4169eSHeinrich Schuchardt 		}
5180fb4169eSHeinrich Schuchardt 	}
5190fb4169eSHeinrich Schuchardt out:
52055fbdf99SHeinrich Schuchardt 	set_shift_mask(mod, key_state);
5210fb4169eSHeinrich Schuchardt 	if (!ret)
5220fb4169eSHeinrich Schuchardt 		ret = c;
5230fb4169eSHeinrich Schuchardt 	return ret;
5240fb4169eSHeinrich Schuchardt }
5250fb4169eSHeinrich Schuchardt 
5260dfd13a4SHeinrich Schuchardt /**
5270dfd13a4SHeinrich Schuchardt  * efi_cin_read_key() - read a key from the console input
5280dfd13a4SHeinrich Schuchardt  *
5290dfd13a4SHeinrich Schuchardt  * @key:	- key received
5300dfd13a4SHeinrich Schuchardt  * Return:	- status code
5310dfd13a4SHeinrich Schuchardt  */
efi_cin_read_key(struct efi_key_data * key)532110c6280SHeinrich Schuchardt static efi_status_t efi_cin_read_key(struct efi_key_data *key)
533c1311ad4SAlexander Graf {
534c1311ad4SAlexander Graf 	struct efi_input_key pressed_key = {
535c1311ad4SAlexander Graf 		.scan_code = 0,
536c1311ad4SAlexander Graf 		.unicode_char = 0,
537c1311ad4SAlexander Graf 	};
53835cbb796SHeinrich Schuchardt 	s32 ch;
539c1311ad4SAlexander Graf 
54055fbdf99SHeinrich Schuchardt 	if (console_read_unicode(&ch))
5410dfd13a4SHeinrich Schuchardt 		return EFI_NOT_READY;
542110c6280SHeinrich Schuchardt 
543110c6280SHeinrich Schuchardt 	key->key_state.key_shift_state = EFI_SHIFT_STATE_INVALID;
544110c6280SHeinrich Schuchardt 	key->key_state.key_toggle_state = EFI_TOGGLE_STATE_INVALID;
545110c6280SHeinrich Schuchardt 
54635cbb796SHeinrich Schuchardt 	/* We do not support multi-word codes */
54735cbb796SHeinrich Schuchardt 	if (ch >= 0x10000)
54835cbb796SHeinrich Schuchardt 		ch = '?';
54955fbdf99SHeinrich Schuchardt 
55055fbdf99SHeinrich Schuchardt 	switch (ch) {
55155fbdf99SHeinrich Schuchardt 	case 0x1b:
5520fb4169eSHeinrich Schuchardt 		/*
5530fb4169eSHeinrich Schuchardt 		 * Xterm Control Sequences
5540fb4169eSHeinrich Schuchardt 		 * https://www.xfree86.org/4.8.0/ctlseqs.html
5550fb4169eSHeinrich Schuchardt 		 */
556c1311ad4SAlexander Graf 		ch = getc();
557c1311ad4SAlexander Graf 		switch (ch) {
558c1311ad4SAlexander Graf 		case cESC: /* ESC */
559c1311ad4SAlexander Graf 			pressed_key.scan_code = 23;
560c1311ad4SAlexander Graf 			break;
561c1311ad4SAlexander Graf 		case 'O': /* F1 - F4 */
5620fb4169eSHeinrich Schuchardt 			ch = getc();
56355fbdf99SHeinrich Schuchardt 			/* consider modifiers */
56455fbdf99SHeinrich Schuchardt 			if (ch < 'P') {
56555fbdf99SHeinrich Schuchardt 				set_shift_mask(ch - '0', &key->key_state);
5660fb4169eSHeinrich Schuchardt 				ch = getc();
56755fbdf99SHeinrich Schuchardt 			}
5680fb4169eSHeinrich Schuchardt 			pressed_key.scan_code = ch - 'P' + 11;
569c1311ad4SAlexander Graf 			break;
570c1311ad4SAlexander Graf 		case '[':
571c1311ad4SAlexander Graf 			ch = getc();
572c1311ad4SAlexander Graf 			switch (ch) {
573c1311ad4SAlexander Graf 			case 'A'...'D': /* up, down right, left */
574c1311ad4SAlexander Graf 				pressed_key.scan_code = ch - 'A' + 1;
575c1311ad4SAlexander Graf 				break;
576c1311ad4SAlexander Graf 			case 'F': /* End */
577c1311ad4SAlexander Graf 				pressed_key.scan_code = 6;
578c1311ad4SAlexander Graf 				break;
579c1311ad4SAlexander Graf 			case 'H': /* Home */
580c1311ad4SAlexander Graf 				pressed_key.scan_code = 5;
581c1311ad4SAlexander Graf 				break;
5820fb4169eSHeinrich Schuchardt 			case '1':
583110c6280SHeinrich Schuchardt 				ch = analyze_modifiers(&key->key_state);
5840fb4169eSHeinrich Schuchardt 				switch (ch) {
5850fb4169eSHeinrich Schuchardt 				case '1'...'5': /* F1 - F5 */
5860fb4169eSHeinrich Schuchardt 					pressed_key.scan_code = ch - '1' + 11;
587c1311ad4SAlexander Graf 					break;
5880fb4169eSHeinrich Schuchardt 				case '7'...'9': /* F6 - F8 */
5890fb4169eSHeinrich Schuchardt 					pressed_key.scan_code = ch - '7' + 16;
5900fb4169eSHeinrich Schuchardt 					break;
5910fb4169eSHeinrich Schuchardt 				case 'A'...'D': /* up, down right, left */
5920fb4169eSHeinrich Schuchardt 					pressed_key.scan_code = ch - 'A' + 1;
5930fb4169eSHeinrich Schuchardt 					break;
5940fb4169eSHeinrich Schuchardt 				case 'F':
5950fb4169eSHeinrich Schuchardt 					pressed_key.scan_code = 6; /* End */
5960fb4169eSHeinrich Schuchardt 					break;
5970fb4169eSHeinrich Schuchardt 				case 'H':
5980fb4169eSHeinrich Schuchardt 					pressed_key.scan_code = 5; /* Home */
5990fb4169eSHeinrich Schuchardt 					break;
6000fb4169eSHeinrich Schuchardt 				}
6010fb4169eSHeinrich Schuchardt 				break;
6020fb4169eSHeinrich Schuchardt 			case '2':
603110c6280SHeinrich Schuchardt 				ch = analyze_modifiers(&key->key_state);
6040fb4169eSHeinrich Schuchardt 				switch (ch) {
6050fb4169eSHeinrich Schuchardt 				case '0'...'1': /* F9 - F10 */
6060fb4169eSHeinrich Schuchardt 					pressed_key.scan_code = ch - '0' + 19;
6070fb4169eSHeinrich Schuchardt 					break;
6080fb4169eSHeinrich Schuchardt 				case '3'...'4': /* F11 - F12 */
6090fb4169eSHeinrich Schuchardt 					pressed_key.scan_code = ch - '3' + 21;
6100fb4169eSHeinrich Schuchardt 					break;
6110fb4169eSHeinrich Schuchardt 				case '~': /* INS */
6120fb4169eSHeinrich Schuchardt 					pressed_key.scan_code = 7;
6130fb4169eSHeinrich Schuchardt 					break;
6140fb4169eSHeinrich Schuchardt 				}
615c1311ad4SAlexander Graf 				break;
616c1311ad4SAlexander Graf 			case '3': /* DEL */
617c1311ad4SAlexander Graf 				pressed_key.scan_code = 8;
618110c6280SHeinrich Schuchardt 				analyze_modifiers(&key->key_state);
6190fb4169eSHeinrich Schuchardt 				break;
6200fb4169eSHeinrich Schuchardt 			case '5': /* PG UP */
6210fb4169eSHeinrich Schuchardt 				pressed_key.scan_code = 9;
622110c6280SHeinrich Schuchardt 				analyze_modifiers(&key->key_state);
6230fb4169eSHeinrich Schuchardt 				break;
6240fb4169eSHeinrich Schuchardt 			case '6': /* PG DOWN */
6250fb4169eSHeinrich Schuchardt 				pressed_key.scan_code = 10;
626110c6280SHeinrich Schuchardt 				analyze_modifiers(&key->key_state);
627c1311ad4SAlexander Graf 				break;
62855fbdf99SHeinrich Schuchardt 			} /* [ */
62955fbdf99SHeinrich Schuchardt 			break;
63055fbdf99SHeinrich Schuchardt 		default:
63155fbdf99SHeinrich Schuchardt 			/* ALT key */
63255fbdf99SHeinrich Schuchardt 			set_shift_mask(3, &key->key_state);
633c1311ad4SAlexander Graf 		}
634c1311ad4SAlexander Graf 		break;
63555fbdf99SHeinrich Schuchardt 	case 0x7f:
636c1311ad4SAlexander Graf 		/* Backspace */
637c1311ad4SAlexander Graf 		ch = 0x08;
638c1311ad4SAlexander Graf 	}
639110c6280SHeinrich Schuchardt 	if (pressed_key.scan_code) {
640110c6280SHeinrich Schuchardt 		key->key_state.key_shift_state |= EFI_SHIFT_STATE_VALID;
641110c6280SHeinrich Schuchardt 	} else {
642c1311ad4SAlexander Graf 		pressed_key.unicode_char = ch;
643110c6280SHeinrich Schuchardt 
644110c6280SHeinrich Schuchardt 		/*
645110c6280SHeinrich Schuchardt 		 * Assume left control key for control characters typically
646110c6280SHeinrich Schuchardt 		 * entered using the control key.
647110c6280SHeinrich Schuchardt 		 */
648110c6280SHeinrich Schuchardt 		if (ch >= 0x01 && ch <= 0x1f) {
64955fbdf99SHeinrich Schuchardt 			key->key_state.key_shift_state |=
650110c6280SHeinrich Schuchardt 					EFI_SHIFT_STATE_VALID;
651110c6280SHeinrich Schuchardt 			switch (ch) {
652110c6280SHeinrich Schuchardt 			case 0x01 ... 0x07:
653110c6280SHeinrich Schuchardt 			case 0x0b ... 0x0c:
654110c6280SHeinrich Schuchardt 			case 0x0e ... 0x1f:
655110c6280SHeinrich Schuchardt 				key->key_state.key_shift_state |=
656110c6280SHeinrich Schuchardt 						EFI_LEFT_CONTROL_PRESSED;
657110c6280SHeinrich Schuchardt 			}
658110c6280SHeinrich Schuchardt 		}
659110c6280SHeinrich Schuchardt 	}
660110c6280SHeinrich Schuchardt 	key->key = pressed_key;
661c1311ad4SAlexander Graf 
6620dfd13a4SHeinrich Schuchardt 	return EFI_SUCCESS;
6630dfd13a4SHeinrich Schuchardt }
6640dfd13a4SHeinrich Schuchardt 
6650dfd13a4SHeinrich Schuchardt /**
6664fdcf066SHeinrich Schuchardt  * efi_cin_notify() - notify registered functions
6674fdcf066SHeinrich Schuchardt  */
efi_cin_notify(void)6684fdcf066SHeinrich Schuchardt static void efi_cin_notify(void)
6694fdcf066SHeinrich Schuchardt {
6704fdcf066SHeinrich Schuchardt 	struct efi_cin_notify_function *item;
6714fdcf066SHeinrich Schuchardt 
6724fdcf066SHeinrich Schuchardt 	list_for_each_entry(item, &cin_notify_functions, link) {
6734fdcf066SHeinrich Schuchardt 		bool match = true;
6744fdcf066SHeinrich Schuchardt 
6754fdcf066SHeinrich Schuchardt 		/* We do not support toggle states */
6764fdcf066SHeinrich Schuchardt 		if (item->key.key.unicode_char || item->key.key.scan_code) {
6774fdcf066SHeinrich Schuchardt 			if (item->key.key.unicode_char !=
6784fdcf066SHeinrich Schuchardt 			    next_key.key.unicode_char ||
6794fdcf066SHeinrich Schuchardt 			    item->key.key.scan_code != next_key.key.scan_code)
6804fdcf066SHeinrich Schuchardt 				match = false;
6814fdcf066SHeinrich Schuchardt 		}
6824fdcf066SHeinrich Schuchardt 		if (item->key.key_state.key_shift_state &&
6834fdcf066SHeinrich Schuchardt 		    item->key.key_state.key_shift_state !=
6844fdcf066SHeinrich Schuchardt 		    next_key.key_state.key_shift_state)
6854fdcf066SHeinrich Schuchardt 			match = false;
6864fdcf066SHeinrich Schuchardt 
6874fdcf066SHeinrich Schuchardt 		if (match)
6884fdcf066SHeinrich Schuchardt 			/* We don't bother about the return code */
6894fdcf066SHeinrich Schuchardt 			EFI_CALL(item->function(&next_key));
6904fdcf066SHeinrich Schuchardt 	}
6914fdcf066SHeinrich Schuchardt }
6924fdcf066SHeinrich Schuchardt 
6934fdcf066SHeinrich Schuchardt /**
6940dfd13a4SHeinrich Schuchardt  * efi_cin_check() - check if keyboard input is available
6950dfd13a4SHeinrich Schuchardt  */
efi_cin_check(void)6960dfd13a4SHeinrich Schuchardt static void efi_cin_check(void)
6970dfd13a4SHeinrich Schuchardt {
6980dfd13a4SHeinrich Schuchardt 	efi_status_t ret;
6990dfd13a4SHeinrich Schuchardt 
7000dfd13a4SHeinrich Schuchardt 	if (key_available) {
7010dfd13a4SHeinrich Schuchardt 		efi_signal_event(efi_con_in.wait_for_key, true);
7020dfd13a4SHeinrich Schuchardt 		return;
7030dfd13a4SHeinrich Schuchardt 	}
7040dfd13a4SHeinrich Schuchardt 
7050dfd13a4SHeinrich Schuchardt 	if (tstc()) {
7060dfd13a4SHeinrich Schuchardt 		ret = efi_cin_read_key(&next_key);
7070dfd13a4SHeinrich Schuchardt 		if (ret == EFI_SUCCESS) {
7080dfd13a4SHeinrich Schuchardt 			key_available = true;
7090dfd13a4SHeinrich Schuchardt 
7104fdcf066SHeinrich Schuchardt 			/* Notify registered functions */
7114fdcf066SHeinrich Schuchardt 			efi_cin_notify();
7124fdcf066SHeinrich Schuchardt 
7130dfd13a4SHeinrich Schuchardt 			/* Queue the wait for key event */
7144fdcf066SHeinrich Schuchardt 			if (key_available)
7150dfd13a4SHeinrich Schuchardt 				efi_signal_event(efi_con_in.wait_for_key, true);
7160dfd13a4SHeinrich Schuchardt 		}
7170dfd13a4SHeinrich Schuchardt 	}
7180dfd13a4SHeinrich Schuchardt }
7190dfd13a4SHeinrich Schuchardt 
7200dfd13a4SHeinrich Schuchardt /**
721110c6280SHeinrich Schuchardt  * efi_cin_empty_buffer() - empty input buffer
722110c6280SHeinrich Schuchardt  */
efi_cin_empty_buffer(void)723110c6280SHeinrich Schuchardt static void efi_cin_empty_buffer(void)
724110c6280SHeinrich Schuchardt {
725110c6280SHeinrich Schuchardt 	while (tstc())
726110c6280SHeinrich Schuchardt 		getc();
727110c6280SHeinrich Schuchardt 	key_available = false;
728110c6280SHeinrich Schuchardt }
729110c6280SHeinrich Schuchardt 
730110c6280SHeinrich Schuchardt /**
731110c6280SHeinrich Schuchardt  * efi_cin_reset_ex() - reset console input
732110c6280SHeinrich Schuchardt  *
733110c6280SHeinrich Schuchardt  * @this:			- the extended simple text input protocol
734110c6280SHeinrich Schuchardt  * @extended_verification:	- extended verification
735110c6280SHeinrich Schuchardt  *
736110c6280SHeinrich Schuchardt  * This function implements the reset service of the
737110c6280SHeinrich Schuchardt  * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
738110c6280SHeinrich Schuchardt  *
739110c6280SHeinrich Schuchardt  * See the Unified Extensible Firmware Interface (UEFI) specification for
740110c6280SHeinrich Schuchardt  * details.
741110c6280SHeinrich Schuchardt  *
742110c6280SHeinrich Schuchardt  * Return: old value of the task priority level
743110c6280SHeinrich Schuchardt  */
efi_cin_reset_ex(struct efi_simple_text_input_ex_protocol * this,bool extended_verification)744110c6280SHeinrich Schuchardt static efi_status_t EFIAPI efi_cin_reset_ex(
745110c6280SHeinrich Schuchardt 		struct efi_simple_text_input_ex_protocol *this,
746110c6280SHeinrich Schuchardt 		bool extended_verification)
747110c6280SHeinrich Schuchardt {
748110c6280SHeinrich Schuchardt 	efi_status_t ret = EFI_SUCCESS;
749110c6280SHeinrich Schuchardt 
750110c6280SHeinrich Schuchardt 	EFI_ENTRY("%p, %d", this, extended_verification);
751110c6280SHeinrich Schuchardt 
752110c6280SHeinrich Schuchardt 	/* Check parameters */
753110c6280SHeinrich Schuchardt 	if (!this) {
754110c6280SHeinrich Schuchardt 		ret = EFI_INVALID_PARAMETER;
755110c6280SHeinrich Schuchardt 		goto out;
756110c6280SHeinrich Schuchardt 	}
757110c6280SHeinrich Schuchardt 
758110c6280SHeinrich Schuchardt 	efi_cin_empty_buffer();
759110c6280SHeinrich Schuchardt out:
760110c6280SHeinrich Schuchardt 	return EFI_EXIT(ret);
761110c6280SHeinrich Schuchardt }
762110c6280SHeinrich Schuchardt 
763110c6280SHeinrich Schuchardt /**
764110c6280SHeinrich Schuchardt  * efi_cin_read_key_stroke_ex() - read key stroke
765110c6280SHeinrich Schuchardt  *
766110c6280SHeinrich Schuchardt  * @this:	instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
767110c6280SHeinrich Schuchardt  * @key_data:	key read from console
768110c6280SHeinrich Schuchardt  * Return:	status code
769110c6280SHeinrich Schuchardt  *
770110c6280SHeinrich Schuchardt  * This function implements the ReadKeyStrokeEx service of the
771110c6280SHeinrich Schuchardt  * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
772110c6280SHeinrich Schuchardt  *
773110c6280SHeinrich Schuchardt  * See the Unified Extensible Firmware Interface (UEFI) specification for
774110c6280SHeinrich Schuchardt  * details.
775110c6280SHeinrich Schuchardt  */
efi_cin_read_key_stroke_ex(struct efi_simple_text_input_ex_protocol * this,struct efi_key_data * key_data)776110c6280SHeinrich Schuchardt static efi_status_t EFIAPI efi_cin_read_key_stroke_ex(
777110c6280SHeinrich Schuchardt 		struct efi_simple_text_input_ex_protocol *this,
778110c6280SHeinrich Schuchardt 		struct efi_key_data *key_data)
779110c6280SHeinrich Schuchardt {
780110c6280SHeinrich Schuchardt 	efi_status_t ret = EFI_SUCCESS;
781110c6280SHeinrich Schuchardt 
782110c6280SHeinrich Schuchardt 	EFI_ENTRY("%p, %p", this, key_data);
783110c6280SHeinrich Schuchardt 
784110c6280SHeinrich Schuchardt 	/* Check parameters */
785110c6280SHeinrich Schuchardt 	if (!this || !key_data) {
786110c6280SHeinrich Schuchardt 		ret = EFI_INVALID_PARAMETER;
787110c6280SHeinrich Schuchardt 		goto out;
788110c6280SHeinrich Schuchardt 	}
789110c6280SHeinrich Schuchardt 
790110c6280SHeinrich Schuchardt 	/* We don't do interrupts, so check for timers cooperatively */
791110c6280SHeinrich Schuchardt 	efi_timer_check();
792110c6280SHeinrich Schuchardt 
793110c6280SHeinrich Schuchardt 	/* Enable console input after ExitBootServices */
794110c6280SHeinrich Schuchardt 	efi_cin_check();
795110c6280SHeinrich Schuchardt 
796110c6280SHeinrich Schuchardt 	if (!key_available) {
797110c6280SHeinrich Schuchardt 		ret = EFI_NOT_READY;
798110c6280SHeinrich Schuchardt 		goto out;
799110c6280SHeinrich Schuchardt 	}
800110c6280SHeinrich Schuchardt 	*key_data = next_key;
801110c6280SHeinrich Schuchardt 	key_available = false;
802110c6280SHeinrich Schuchardt 	efi_con_in.wait_for_key->is_signaled = false;
803110c6280SHeinrich Schuchardt out:
804110c6280SHeinrich Schuchardt 	return EFI_EXIT(ret);
805110c6280SHeinrich Schuchardt }
806110c6280SHeinrich Schuchardt 
807110c6280SHeinrich Schuchardt /**
808110c6280SHeinrich Schuchardt  * efi_cin_set_state() - set toggle key state
809110c6280SHeinrich Schuchardt  *
810110c6280SHeinrich Schuchardt  * @this:		instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
811110c6280SHeinrich Schuchardt  * @key_toggle_state:	key toggle state
812110c6280SHeinrich Schuchardt  * Return:		status code
813110c6280SHeinrich Schuchardt  *
814110c6280SHeinrich Schuchardt  * This function implements the SetState service of the
815110c6280SHeinrich Schuchardt  * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
816110c6280SHeinrich Schuchardt  *
817110c6280SHeinrich Schuchardt  * See the Unified Extensible Firmware Interface (UEFI) specification for
818110c6280SHeinrich Schuchardt  * details.
819110c6280SHeinrich Schuchardt  */
efi_cin_set_state(struct efi_simple_text_input_ex_protocol * this,u8 key_toggle_state)820110c6280SHeinrich Schuchardt static efi_status_t EFIAPI efi_cin_set_state(
821110c6280SHeinrich Schuchardt 		struct efi_simple_text_input_ex_protocol *this,
822110c6280SHeinrich Schuchardt 		u8 key_toggle_state)
823110c6280SHeinrich Schuchardt {
824110c6280SHeinrich Schuchardt 	EFI_ENTRY("%p, %u", this, key_toggle_state);
825110c6280SHeinrich Schuchardt 	/*
826110c6280SHeinrich Schuchardt 	 * U-Boot supports multiple console input sources like serial and
827110c6280SHeinrich Schuchardt 	 * net console for which a key toggle state cannot be set at all.
828110c6280SHeinrich Schuchardt 	 *
829110c6280SHeinrich Schuchardt 	 * According to the UEFI specification it is allowable to not implement
830110c6280SHeinrich Schuchardt 	 * this service.
831110c6280SHeinrich Schuchardt 	 */
832110c6280SHeinrich Schuchardt 	return EFI_EXIT(EFI_UNSUPPORTED);
833110c6280SHeinrich Schuchardt }
834110c6280SHeinrich Schuchardt 
835110c6280SHeinrich Schuchardt /**
836110c6280SHeinrich Schuchardt  * efi_cin_register_key_notify() - register key notification function
837110c6280SHeinrich Schuchardt  *
838110c6280SHeinrich Schuchardt  * @this:			instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
839110c6280SHeinrich Schuchardt  * @key_data:			key to be notified
840110c6280SHeinrich Schuchardt  * @key_notify_function:	function to be called if the key is pressed
841110c6280SHeinrich Schuchardt  * @notify_handle:		handle for unregistering the notification
842110c6280SHeinrich Schuchardt  * Return:			status code
843110c6280SHeinrich Schuchardt  *
844110c6280SHeinrich Schuchardt  * This function implements the SetState service of the
845110c6280SHeinrich Schuchardt  * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
846110c6280SHeinrich Schuchardt  *
847110c6280SHeinrich Schuchardt  * See the Unified Extensible Firmware Interface (UEFI) specification for
848110c6280SHeinrich Schuchardt  * details.
849110c6280SHeinrich Schuchardt  */
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)850110c6280SHeinrich Schuchardt static efi_status_t EFIAPI efi_cin_register_key_notify(
851110c6280SHeinrich Schuchardt 		struct efi_simple_text_input_ex_protocol *this,
852110c6280SHeinrich Schuchardt 		struct efi_key_data *key_data,
853110c6280SHeinrich Schuchardt 		efi_status_t (EFIAPI *key_notify_function)(
854110c6280SHeinrich Schuchardt 			struct efi_key_data *key_data),
855110c6280SHeinrich Schuchardt 		void **notify_handle)
856110c6280SHeinrich Schuchardt {
8574fdcf066SHeinrich Schuchardt 	efi_status_t ret = EFI_SUCCESS;
8584fdcf066SHeinrich Schuchardt 	struct efi_cin_notify_function *notify_function;
8594fdcf066SHeinrich Schuchardt 
860110c6280SHeinrich Schuchardt 	EFI_ENTRY("%p, %p, %p, %p",
861110c6280SHeinrich Schuchardt 		  this, key_data, key_notify_function, notify_handle);
8624fdcf066SHeinrich Schuchardt 
8634fdcf066SHeinrich Schuchardt 	/* Check parameters */
8644fdcf066SHeinrich Schuchardt 	if (!this || !key_data || !key_notify_function || !notify_handle) {
8654fdcf066SHeinrich Schuchardt 		ret = EFI_INVALID_PARAMETER;
8664fdcf066SHeinrich Schuchardt 		goto out;
8674fdcf066SHeinrich Schuchardt 	}
8684fdcf066SHeinrich Schuchardt 
8694fdcf066SHeinrich Schuchardt 	EFI_PRINT("u+%04x, sc %04x, sh %08x, tg %02x\n",
8704fdcf066SHeinrich Schuchardt 		  key_data->key.unicode_char,
8714fdcf066SHeinrich Schuchardt 	       key_data->key.scan_code,
8724fdcf066SHeinrich Schuchardt 	       key_data->key_state.key_shift_state,
8734fdcf066SHeinrich Schuchardt 	       key_data->key_state.key_toggle_state);
8744fdcf066SHeinrich Schuchardt 
8754fdcf066SHeinrich Schuchardt 	notify_function = calloc(1, sizeof(struct efi_cin_notify_function));
8764fdcf066SHeinrich Schuchardt 	if (!notify_function) {
8774fdcf066SHeinrich Schuchardt 		ret = EFI_OUT_OF_RESOURCES;
8784fdcf066SHeinrich Schuchardt 		goto out;
8794fdcf066SHeinrich Schuchardt 	}
8804fdcf066SHeinrich Schuchardt 	notify_function->key = *key_data;
8814fdcf066SHeinrich Schuchardt 	notify_function->function = key_notify_function;
8824fdcf066SHeinrich Schuchardt 	list_add_tail(&notify_function->link, &cin_notify_functions);
8834fdcf066SHeinrich Schuchardt 	*notify_handle = notify_function;
8844fdcf066SHeinrich Schuchardt out:
8854fdcf066SHeinrich Schuchardt 	return EFI_EXIT(ret);
886110c6280SHeinrich Schuchardt }
887110c6280SHeinrich Schuchardt 
888110c6280SHeinrich Schuchardt /**
889110c6280SHeinrich Schuchardt  * efi_cin_unregister_key_notify() - unregister key notification function
890110c6280SHeinrich Schuchardt  *
891110c6280SHeinrich Schuchardt  * @this:			instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
892110c6280SHeinrich Schuchardt  * @notification_handle:	handle received when registering
893110c6280SHeinrich Schuchardt  * Return:			status code
894110c6280SHeinrich Schuchardt  *
895110c6280SHeinrich Schuchardt  * This function implements the SetState service of the
896110c6280SHeinrich Schuchardt  * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
897110c6280SHeinrich Schuchardt  *
898110c6280SHeinrich Schuchardt  * See the Unified Extensible Firmware Interface (UEFI) specification for
899110c6280SHeinrich Schuchardt  * details.
900110c6280SHeinrich Schuchardt  */
efi_cin_unregister_key_notify(struct efi_simple_text_input_ex_protocol * this,void * notification_handle)901110c6280SHeinrich Schuchardt static efi_status_t EFIAPI efi_cin_unregister_key_notify(
902110c6280SHeinrich Schuchardt 		struct efi_simple_text_input_ex_protocol *this,
903110c6280SHeinrich Schuchardt 		void *notification_handle)
904110c6280SHeinrich Schuchardt {
9054fdcf066SHeinrich Schuchardt 	efi_status_t ret = EFI_INVALID_PARAMETER;
9064fdcf066SHeinrich Schuchardt 	struct efi_cin_notify_function *item, *notify_function =
9074fdcf066SHeinrich Schuchardt 			notification_handle;
9084fdcf066SHeinrich Schuchardt 
909110c6280SHeinrich Schuchardt 	EFI_ENTRY("%p, %p", this, notification_handle);
9104fdcf066SHeinrich Schuchardt 
9114fdcf066SHeinrich Schuchardt 	/* Check parameters */
9124fdcf066SHeinrich Schuchardt 	if (!this || !notification_handle)
9134fdcf066SHeinrich Schuchardt 		goto out;
9144fdcf066SHeinrich Schuchardt 
9154fdcf066SHeinrich Schuchardt 	list_for_each_entry(item, &cin_notify_functions, link) {
9164fdcf066SHeinrich Schuchardt 		if (item == notify_function) {
9174fdcf066SHeinrich Schuchardt 			ret = EFI_SUCCESS;
9184fdcf066SHeinrich Schuchardt 			break;
9194fdcf066SHeinrich Schuchardt 		}
9204fdcf066SHeinrich Schuchardt 	}
9214fdcf066SHeinrich Schuchardt 	if (ret != EFI_SUCCESS)
9224fdcf066SHeinrich Schuchardt 		goto out;
9234fdcf066SHeinrich Schuchardt 
9244fdcf066SHeinrich Schuchardt 	/* Remove the notify function */
9254fdcf066SHeinrich Schuchardt 	list_del(&notify_function->link);
9264fdcf066SHeinrich Schuchardt 	free(notify_function);
9274fdcf066SHeinrich Schuchardt out:
9284fdcf066SHeinrich Schuchardt 	return EFI_EXIT(ret);
929110c6280SHeinrich Schuchardt }
930110c6280SHeinrich Schuchardt 
931110c6280SHeinrich Schuchardt 
932110c6280SHeinrich Schuchardt /**
9330dfd13a4SHeinrich Schuchardt  * efi_cin_reset() - drain the input buffer
9340dfd13a4SHeinrich Schuchardt  *
9350dfd13a4SHeinrich Schuchardt  * @this:			instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
9360dfd13a4SHeinrich Schuchardt  * @extended_verification:	allow for exhaustive verification
9370dfd13a4SHeinrich Schuchardt  * Return:			status code
9380dfd13a4SHeinrich Schuchardt  *
9390dfd13a4SHeinrich Schuchardt  * This function implements the Reset service of the
9400dfd13a4SHeinrich Schuchardt  * EFI_SIMPLE_TEXT_INPUT_PROTOCOL.
9410dfd13a4SHeinrich Schuchardt  *
9420dfd13a4SHeinrich Schuchardt  * See the Unified Extensible Firmware Interface (UEFI) specification for
9430dfd13a4SHeinrich Schuchardt  * details.
9440dfd13a4SHeinrich Schuchardt  */
efi_cin_reset(struct efi_simple_text_input_protocol * this,bool extended_verification)9450dfd13a4SHeinrich Schuchardt static efi_status_t EFIAPI efi_cin_reset
9460dfd13a4SHeinrich Schuchardt 			(struct efi_simple_text_input_protocol *this,
9470dfd13a4SHeinrich Schuchardt 			 bool extended_verification)
9480dfd13a4SHeinrich Schuchardt {
9490dfd13a4SHeinrich Schuchardt 	efi_status_t ret = EFI_SUCCESS;
9500dfd13a4SHeinrich Schuchardt 
9510dfd13a4SHeinrich Schuchardt 	EFI_ENTRY("%p, %d", this, extended_verification);
9520dfd13a4SHeinrich Schuchardt 
9530dfd13a4SHeinrich Schuchardt 	/* Check parameters */
9540dfd13a4SHeinrich Schuchardt 	if (!this) {
9550dfd13a4SHeinrich Schuchardt 		ret = EFI_INVALID_PARAMETER;
9560dfd13a4SHeinrich Schuchardt 		goto out;
9570dfd13a4SHeinrich Schuchardt 	}
9580dfd13a4SHeinrich Schuchardt 
959110c6280SHeinrich Schuchardt 	efi_cin_empty_buffer();
9600dfd13a4SHeinrich Schuchardt out:
9610dfd13a4SHeinrich Schuchardt 	return EFI_EXIT(ret);
9620dfd13a4SHeinrich Schuchardt }
9630dfd13a4SHeinrich Schuchardt 
9640dfd13a4SHeinrich Schuchardt /**
965110c6280SHeinrich Schuchardt  * efi_cin_read_key_stroke() - read key stroke
9660dfd13a4SHeinrich Schuchardt  *
9670dfd13a4SHeinrich Schuchardt  * @this:	instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
9680dfd13a4SHeinrich Schuchardt  * @key:	key read from console
9690dfd13a4SHeinrich Schuchardt  * Return:	status code
9700dfd13a4SHeinrich Schuchardt  *
9710dfd13a4SHeinrich Schuchardt  * This function implements the ReadKeyStroke service of the
9720dfd13a4SHeinrich Schuchardt  * EFI_SIMPLE_TEXT_INPUT_PROTOCOL.
9730dfd13a4SHeinrich Schuchardt  *
9740dfd13a4SHeinrich Schuchardt  * See the Unified Extensible Firmware Interface (UEFI) specification for
9750dfd13a4SHeinrich Schuchardt  * details.
9760dfd13a4SHeinrich Schuchardt  */
efi_cin_read_key_stroke(struct efi_simple_text_input_protocol * this,struct efi_input_key * key)9770dfd13a4SHeinrich Schuchardt static efi_status_t EFIAPI efi_cin_read_key_stroke
9780dfd13a4SHeinrich Schuchardt 			(struct efi_simple_text_input_protocol *this,
9790dfd13a4SHeinrich Schuchardt 			 struct efi_input_key *key)
9800dfd13a4SHeinrich Schuchardt {
9810dfd13a4SHeinrich Schuchardt 	efi_status_t ret = EFI_SUCCESS;
9820dfd13a4SHeinrich Schuchardt 
9830dfd13a4SHeinrich Schuchardt 	EFI_ENTRY("%p, %p", this, key);
9840dfd13a4SHeinrich Schuchardt 
9850dfd13a4SHeinrich Schuchardt 	/* Check parameters */
9860dfd13a4SHeinrich Schuchardt 	if (!this || !key) {
9870dfd13a4SHeinrich Schuchardt 		ret = EFI_INVALID_PARAMETER;
9880dfd13a4SHeinrich Schuchardt 		goto out;
9890dfd13a4SHeinrich Schuchardt 	}
9900dfd13a4SHeinrich Schuchardt 
9910dfd13a4SHeinrich Schuchardt 	/* We don't do interrupts, so check for timers cooperatively */
9920dfd13a4SHeinrich Schuchardt 	efi_timer_check();
9930dfd13a4SHeinrich Schuchardt 
9940dfd13a4SHeinrich Schuchardt 	/* Enable console input after ExitBootServices */
9950dfd13a4SHeinrich Schuchardt 	efi_cin_check();
9960dfd13a4SHeinrich Schuchardt 
9970dfd13a4SHeinrich Schuchardt 	if (!key_available) {
9980dfd13a4SHeinrich Schuchardt 		ret = EFI_NOT_READY;
9990dfd13a4SHeinrich Schuchardt 		goto out;
10000dfd13a4SHeinrich Schuchardt 	}
1001110c6280SHeinrich Schuchardt 	*key = next_key.key;
10020dfd13a4SHeinrich Schuchardt 	key_available = false;
10030dfd13a4SHeinrich Schuchardt 	efi_con_in.wait_for_key->is_signaled = false;
10040dfd13a4SHeinrich Schuchardt out:
10050dfd13a4SHeinrich Schuchardt 	return EFI_EXIT(ret);
1006c1311ad4SAlexander Graf }
1007c1311ad4SAlexander Graf 
1008110c6280SHeinrich Schuchardt static struct efi_simple_text_input_ex_protocol efi_con_in_ex = {
1009110c6280SHeinrich Schuchardt 	.reset = efi_cin_reset_ex,
1010110c6280SHeinrich Schuchardt 	.read_key_stroke_ex = efi_cin_read_key_stroke_ex,
1011110c6280SHeinrich Schuchardt 	.wait_for_key_ex = NULL,
1012110c6280SHeinrich Schuchardt 	.set_state = efi_cin_set_state,
1013110c6280SHeinrich Schuchardt 	.register_key_notify = efi_cin_register_key_notify,
1014110c6280SHeinrich Schuchardt 	.unregister_key_notify = efi_cin_unregister_key_notify,
1015110c6280SHeinrich Schuchardt };
1016110c6280SHeinrich Schuchardt 
10173e603ec7SHeinrich Schuchardt struct efi_simple_text_input_protocol efi_con_in = {
1018c1311ad4SAlexander Graf 	.reset = efi_cin_reset,
1019c1311ad4SAlexander Graf 	.read_key_stroke = efi_cin_read_key_stroke,
1020c1311ad4SAlexander Graf 	.wait_for_key = NULL,
1021c1311ad4SAlexander Graf };
102291be9a77Sxypron.glpk@gmx.de 
102391be9a77Sxypron.glpk@gmx.de static struct efi_event *console_timer_event;
102491be9a77Sxypron.glpk@gmx.de 
10259bc9664dSHeinrich Schuchardt /*
10260dfd13a4SHeinrich Schuchardt  * efi_console_timer_notify() - notify the console timer event
10279bc9664dSHeinrich Schuchardt  *
10280dfd13a4SHeinrich Schuchardt  * @event:	console timer event
10290dfd13a4SHeinrich Schuchardt  * @context:	not used
10309bc9664dSHeinrich Schuchardt  */
efi_console_timer_notify(struct efi_event * event,void * context)1031ff925938Sxypron.glpk@gmx.de static void EFIAPI efi_console_timer_notify(struct efi_event *event,
1032ff925938Sxypron.glpk@gmx.de 					    void *context)
103391be9a77Sxypron.glpk@gmx.de {
103491be9a77Sxypron.glpk@gmx.de 	EFI_ENTRY("%p, %p", event, context);
10350dfd13a4SHeinrich Schuchardt 	efi_cin_check();
103691be9a77Sxypron.glpk@gmx.de 	EFI_EXIT(EFI_SUCCESS);
103791be9a77Sxypron.glpk@gmx.de }
103891be9a77Sxypron.glpk@gmx.de 
10390dfd13a4SHeinrich Schuchardt /**
10400dfd13a4SHeinrich Schuchardt  * efi_key_notify() - notify the wait for key event
10410dfd13a4SHeinrich Schuchardt  *
10420dfd13a4SHeinrich Schuchardt  * @event:	wait for key event
10430dfd13a4SHeinrich Schuchardt  * @context:	not used
10440dfd13a4SHeinrich Schuchardt  */
efi_key_notify(struct efi_event * event,void * context)10450dfd13a4SHeinrich Schuchardt static void EFIAPI efi_key_notify(struct efi_event *event, void *context)
10460dfd13a4SHeinrich Schuchardt {
10470dfd13a4SHeinrich Schuchardt 	EFI_ENTRY("%p, %p", event, context);
10480dfd13a4SHeinrich Schuchardt 	efi_cin_check();
10490dfd13a4SHeinrich Schuchardt 	EFI_EXIT(EFI_SUCCESS);
10500dfd13a4SHeinrich Schuchardt }
10510dfd13a4SHeinrich Schuchardt 
10520dfd13a4SHeinrich Schuchardt /**
10530dfd13a4SHeinrich Schuchardt  * efi_console_register() - install the console protocols
10540dfd13a4SHeinrich Schuchardt  *
10550dfd13a4SHeinrich Schuchardt  * This function is called from do_bootefi_exec().
10566f566c23SHeinrich Schuchardt  *
10576f566c23SHeinrich Schuchardt  * Return:	status code
10580dfd13a4SHeinrich Schuchardt  */
efi_console_register(void)10596f566c23SHeinrich Schuchardt efi_status_t efi_console_register(void)
106091be9a77Sxypron.glpk@gmx.de {
106191be9a77Sxypron.glpk@gmx.de 	efi_status_t r;
1062fae0118eSHeinrich Schuchardt 	efi_handle_t console_output_handle;
1063fae0118eSHeinrich Schuchardt 	efi_handle_t console_input_handle;
1064a17e62ccSRob Clark 
1065a4aa7befSHeinrich Schuchardt 	/* Set up mode information */
1066a4aa7befSHeinrich Schuchardt 	query_console_size();
1067a4aa7befSHeinrich Schuchardt 
1068ebb4dd5bSHeinrich Schuchardt 	/* Create handles */
1069fae0118eSHeinrich Schuchardt 	r = efi_create_handle(&console_output_handle);
1070ebb4dd5bSHeinrich Schuchardt 	if (r != EFI_SUCCESS)
1071ebb4dd5bSHeinrich Schuchardt 		goto out_of_memory;
107240e3e757SAlexander Graf 
1073fae0118eSHeinrich Schuchardt 	r = efi_add_protocol(console_output_handle,
1074ebb4dd5bSHeinrich Schuchardt 			     &efi_guid_text_output_protocol, &efi_con_out);
1075ebb4dd5bSHeinrich Schuchardt 	if (r != EFI_SUCCESS)
1076ebb4dd5bSHeinrich Schuchardt 		goto out_of_memory;
1077fae0118eSHeinrich Schuchardt 	systab.con_out_handle = console_output_handle;
1078fae0118eSHeinrich Schuchardt 	systab.stderr_handle = console_output_handle;
107940e3e757SAlexander Graf 
1080fae0118eSHeinrich Schuchardt 	r = efi_create_handle(&console_input_handle);
1081ebb4dd5bSHeinrich Schuchardt 	if (r != EFI_SUCCESS)
1082ebb4dd5bSHeinrich Schuchardt 		goto out_of_memory;
108340e3e757SAlexander Graf 
1084fae0118eSHeinrich Schuchardt 	r = efi_add_protocol(console_input_handle,
1085ebb4dd5bSHeinrich Schuchardt 			     &efi_guid_text_input_protocol, &efi_con_in);
1086ebb4dd5bSHeinrich Schuchardt 	if (r != EFI_SUCCESS)
1087ebb4dd5bSHeinrich Schuchardt 		goto out_of_memory;
1088fae0118eSHeinrich Schuchardt 	systab.con_in_handle = console_input_handle;
1089fae0118eSHeinrich Schuchardt 	r = efi_add_protocol(console_input_handle,
1090110c6280SHeinrich Schuchardt 			     &efi_guid_text_input_ex_protocol, &efi_con_in_ex);
1091110c6280SHeinrich Schuchardt 	if (r != EFI_SUCCESS)
1092110c6280SHeinrich Schuchardt 		goto out_of_memory;
1093a17e62ccSRob Clark 
1094ebb4dd5bSHeinrich Schuchardt 	/* Create console events */
1095b095f3c8SHeinrich Schuchardt 	r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK, efi_key_notify,
1096b095f3c8SHeinrich Schuchardt 			     NULL, NULL, &efi_con_in.wait_for_key);
109791be9a77Sxypron.glpk@gmx.de 	if (r != EFI_SUCCESS) {
109891be9a77Sxypron.glpk@gmx.de 		printf("ERROR: Failed to register WaitForKey event\n");
109991be9a77Sxypron.glpk@gmx.de 		return r;
110091be9a77Sxypron.glpk@gmx.de 	}
1101110c6280SHeinrich Schuchardt 	efi_con_in_ex.wait_for_key_ex = efi_con_in.wait_for_key;
110291be9a77Sxypron.glpk@gmx.de 	r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
1103b095f3c8SHeinrich Schuchardt 			     efi_console_timer_notify, NULL, NULL,
110491be9a77Sxypron.glpk@gmx.de 			     &console_timer_event);
110591be9a77Sxypron.glpk@gmx.de 	if (r != EFI_SUCCESS) {
110691be9a77Sxypron.glpk@gmx.de 		printf("ERROR: Failed to register console event\n");
110791be9a77Sxypron.glpk@gmx.de 		return r;
110891be9a77Sxypron.glpk@gmx.de 	}
110991be9a77Sxypron.glpk@gmx.de 	/* 5000 ns cycle is sufficient for 2 MBaud */
111091be9a77Sxypron.glpk@gmx.de 	r = efi_set_timer(console_timer_event, EFI_TIMER_PERIODIC, 50);
111191be9a77Sxypron.glpk@gmx.de 	if (r != EFI_SUCCESS)
111291be9a77Sxypron.glpk@gmx.de 		printf("ERROR: Failed to set console timer\n");
111391be9a77Sxypron.glpk@gmx.de 	return r;
1114ebb4dd5bSHeinrich Schuchardt out_of_memory:
111514d103bbSHeinrich Schuchardt 	printf("ERROR: Out of memory\n");
1116ebb4dd5bSHeinrich Schuchardt 	return r;
111791be9a77Sxypron.glpk@gmx.de }
1118