blob: a55e4b33eced265b12a699ea5a078f1aba578414 [file] [log] [blame]
Tom Rinif739fcd2018-05-07 17:02:21 -04001// SPDX-License-Identifier: GPL-2.0+
Alexander Grafc1311ad2016-03-04 01:10:00 +01002/*
3 * EFI application console interface
4 *
5 * Copyright (c) 2016 Alexander Graf
Alexander Grafc1311ad2016-03-04 01:10:00 +01006 */
7
8#include <common.h>
Rob Clark78178bb2017-09-09 06:47:40 -04009#include <charset.h>
Rob Clarka18c5a82017-09-13 18:05:43 -040010#include <dm/device.h>
Alexander Grafc1311ad2016-03-04 01:10:00 +010011#include <efi_loader.h>
Simon Glass7b51b572019-08-01 09:46:52 -060012#include <env.h>
Rob Clarka18c5a82017-09-13 18:05:43 -040013#include <stdio_dev.h>
14#include <video_console.h>
Alexander Grafc1311ad2016-03-04 01:10:00 +010015
Emmanuel Vadot5be8b0a2016-11-08 06:03:29 +010016#define EFI_COUT_MODE_2 2
17#define EFI_MAX_COUT_MODE 3
18
19struct cout_mode {
20 unsigned long columns;
21 unsigned long rows;
22 int present;
23};
24
25static struct cout_mode efi_cout_modes[] = {
26 /* EFI Mode 0 is 80x25 and always present */
27 {
28 .columns = 80,
29 .rows = 25,
30 .present = 1,
31 },
32 /* EFI Mode 1 is always 80x50 */
33 {
34 .columns = 80,
35 .rows = 50,
36 .present = 0,
37 },
38 /* Value are unknown until we query the console */
39 {
40 .columns = 0,
41 .rows = 0,
42 .present = 0,
43 },
44};
45
Heinrich Schuchardt110c6282018-09-11 22:38:08 +020046const efi_guid_t efi_guid_text_input_ex_protocol =
47 EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
Heinrich Schuchardtebb4dd52017-10-26 19:25:59 +020048const efi_guid_t efi_guid_text_input_protocol =
49 EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID;
Heinrich Schuchardt110c6282018-09-11 22:38:08 +020050const efi_guid_t efi_guid_text_output_protocol =
51 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID;
Alexander Grafc1311ad2016-03-04 01:10:00 +010052
53#define cESC '\x1b'
54#define ESC "\x1b"
55
Emmanuel Vadot5be8b0a2016-11-08 06:03:29 +010056/* Default to mode 0 */
Alexander Grafc1311ad2016-03-04 01:10:00 +010057static struct simple_text_output_mode efi_con_mode = {
Emmanuel Vadot5be8b0a2016-11-08 06:03:29 +010058 .max_mode = 1,
Alexander Grafc1311ad2016-03-04 01:10:00 +010059 .mode = 0,
60 .attribute = 0,
61 .cursor_column = 0,
62 .cursor_row = 0,
63 .cursor_visible = 1,
64};
65
Matthias Bruggerdd1a1ec2019-03-05 12:50:18 +010066static int term_get_char(s32 *c)
67{
68 u64 timeout;
69
70 /* Wait up to 100 ms for a character */
71 timeout = timer_get_us() + 100000;
72
73 while (!tstc())
74 if (timer_get_us() > timeout)
75 return 1;
76
77 *c = getc();
78 return 0;
79}
80
Heinrich Schuchardt62217292018-05-16 18:17:38 +020081/*
82 * Receive and parse a reply from the terminal.
83 *
84 * @n: array of return values
85 * @num: number of return values expected
86 * @end_char: character indicating end of terminal message
87 * @return: non-zero indicates error
88 */
89static int term_read_reply(int *n, int num, char end_char)
Alexander Grafc1311ad2016-03-04 01:10:00 +010090{
Matthias Bruggerdd1a1ec2019-03-05 12:50:18 +010091 s32 c;
Alexander Grafc1311ad2016-03-04 01:10:00 +010092 int i = 0;
93
Matthias Bruggerdd1a1ec2019-03-05 12:50:18 +010094 if (term_get_char(&c) || c != cESC)
Alexander Grafc1311ad2016-03-04 01:10:00 +010095 return -1;
Matthias Bruggerdd1a1ec2019-03-05 12:50:18 +010096
97 if (term_get_char(&c) || c != '[')
Alexander Grafc1311ad2016-03-04 01:10:00 +010098 return -1;
99
100 n[0] = 0;
101 while (1) {
Matthias Bruggerdd1a1ec2019-03-05 12:50:18 +0100102 if (!term_get_char(&c)) {
103 if (c == ';') {
104 i++;
105 if (i >= num)
106 return -1;
107 n[i] = 0;
108 continue;
109 } else if (c == end_char) {
110 break;
111 } else if (c > '9' || c < '0') {
Alexander Grafc1311ad2016-03-04 01:10:00 +0100112 return -1;
Matthias Bruggerdd1a1ec2019-03-05 12:50:18 +0100113 }
114
115 /* Read one more decimal position */
116 n[i] *= 10;
117 n[i] += c - '0';
118 } else {
Alexander Grafc1311ad2016-03-04 01:10:00 +0100119 return -1;
120 }
Alexander Grafc1311ad2016-03-04 01:10:00 +0100121 }
Heinrich Schuchardt62217292018-05-16 18:17:38 +0200122 if (i != num - 1)
123 return -1;
Alexander Grafc1311ad2016-03-04 01:10:00 +0100124
125 return 0;
126}
127
Alexander Grafc1311ad2016-03-04 01:10:00 +0100128static efi_status_t EFIAPI efi_cout_output_string(
129 struct efi_simple_text_output_protocol *this,
Rob Clark3a45bc72017-09-13 18:05:44 -0400130 const efi_string_t string)
Alexander Grafc1311ad2016-03-04 01:10:00 +0100131{
Rob Clark3a45bc72017-09-13 18:05:44 -0400132 struct simple_text_output_mode *con = &efi_con_mode;
133 struct cout_mode *mode = &efi_cout_modes[con->mode];
Heinrich Schuchardtba7bd5c2018-08-31 21:31:32 +0200134 char *buf, *pos;
135 u16 *p;
136 efi_status_t ret = EFI_SUCCESS;
Alexander Grafc1311ad2016-03-04 01:10:00 +0100137
138 EFI_ENTRY("%p, %p", this, string);
Rob Clark3a45bc72017-09-13 18:05:44 -0400139
Heinrich Schuchardtb31ca6b2019-05-18 18:11:54 +0200140 if (!this || !string) {
141 ret = EFI_INVALID_PARAMETER;
142 goto out;
143 }
144
Heinrich Schuchardtba7bd5c2018-08-31 21:31:32 +0200145 buf = malloc(utf16_utf8_strlen(string) + 1);
146 if (!buf) {
147 ret = EFI_OUT_OF_RESOURCES;
148 goto out;
149 }
150 pos = buf;
151 utf16_utf8_strcpy(&pos, string);
Rob Clark3a45bc72017-09-13 18:05:44 -0400152 fputs(stdout, buf);
Heinrich Schuchardtba7bd5c2018-08-31 21:31:32 +0200153 free(buf);
Rob Clark3a45bc72017-09-13 18:05:44 -0400154
Heinrich Schuchardt7ca7c3c2018-04-29 16:24:25 +0200155 /*
156 * Update the cursor position.
157 *
158 * The UEFI spec provides advance rules for U+0000, U+0008, U+000A,
Heinrich Schuchardt97ea0692019-09-04 21:13:45 +0200159 * and U000D. All other control characters are ignored. Any non-control
160 * character increase the column by one.
Heinrich Schuchardt7ca7c3c2018-04-29 16:24:25 +0200161 */
162 for (p = string; *p; ++p) {
Rob Clark3a45bc72017-09-13 18:05:44 -0400163 switch (*p) {
Heinrich Schuchardt7ca7c3c2018-04-29 16:24:25 +0200164 case '\b': /* U+0008, backspace */
Heinrich Schuchardt97ea0692019-09-04 21:13:45 +0200165 if (con->cursor_column)
166 con->cursor_column--;
Rob Clark3a45bc72017-09-13 18:05:44 -0400167 break;
Heinrich Schuchardt7ca7c3c2018-04-29 16:24:25 +0200168 case '\n': /* U+000A, newline */
Rob Clark3a45bc72017-09-13 18:05:44 -0400169 con->cursor_column = 0;
170 con->cursor_row++;
171 break;
Heinrich Schuchardt7ca7c3c2018-04-29 16:24:25 +0200172 case '\r': /* U+000D, carriage-return */
173 con->cursor_column = 0;
Rob Clark3a45bc72017-09-13 18:05:44 -0400174 break;
Heinrich Schuchardt7ca7c3c2018-04-29 16:24:25 +0200175 case 0xd800 ... 0xdbff:
176 /*
177 * Ignore high surrogates, we do not want to count a
178 * Unicode character twice.
179 */
Rob Clark3a45bc72017-09-13 18:05:44 -0400180 break;
181 default:
Heinrich Schuchardt97ea0692019-09-04 21:13:45 +0200182 /* Exclude control codes */
183 if (*p > 0x1f)
184 con->cursor_column++;
Rob Clark3a45bc72017-09-13 18:05:44 -0400185 break;
Alexander Grafc1311ad2016-03-04 01:10:00 +0100186 }
Rob Clark3a45bc72017-09-13 18:05:44 -0400187 if (con->cursor_column >= mode->columns) {
188 con->cursor_column = 0;
189 con->cursor_row++;
190 }
Heinrich Schuchardt97ea0692019-09-04 21:13:45 +0200191 /*
192 * When we exceed the row count the terminal will scroll up one
193 * line. We have to adjust the cursor position.
194 */
195 if (con->cursor_row >= mode->rows && con->cursor_row)
196 con->cursor_row--;
Alexander Grafc1311ad2016-03-04 01:10:00 +0100197 }
198
Heinrich Schuchardtba7bd5c2018-08-31 21:31:32 +0200199out:
200 return EFI_EXIT(ret);
Alexander Grafc1311ad2016-03-04 01:10:00 +0100201}
202
203static efi_status_t EFIAPI efi_cout_test_string(
204 struct efi_simple_text_output_protocol *this,
Rob Clark3a45bc72017-09-13 18:05:44 -0400205 const efi_string_t string)
Alexander Grafc1311ad2016-03-04 01:10:00 +0100206{
207 EFI_ENTRY("%p, %p", this, string);
208 return EFI_EXIT(EFI_SUCCESS);
209}
210
Emmanuel Vadot5be8b0a2016-11-08 06:03:29 +0100211static bool cout_mode_matches(struct cout_mode *mode, int rows, int cols)
212{
213 if (!mode->present)
214 return false;
215
216 return (mode->rows == rows) && (mode->columns == cols);
217}
218
Heinrich Schuchardt6bb591f2018-09-15 23:52:07 +0200219/**
220 * query_console_serial() - query console size
221 *
Heinrich Schuchardtfe1a81c2019-09-05 20:37:13 +0200222 * @rows: pointer to return number of rows
223 * @cols: pointer to return number of columns
224 * Returns: 0 on success
Heinrich Schuchardt6bb591f2018-09-15 23:52:07 +0200225 */
Rob Clark71cc25c2017-09-13 18:05:42 -0400226static int query_console_serial(int *rows, int *cols)
227{
Heinrich Schuchardt6bb591f2018-09-15 23:52:07 +0200228 int ret = 0;
229 int n[2];
Rob Clark71cc25c2017-09-13 18:05:42 -0400230
231 /* Empty input buffer */
232 while (tstc())
233 getc();
234
Heinrich Schuchardt6bb591f2018-09-15 23:52:07 +0200235 /*
236 * Not all terminals understand CSI [18t for querying the console size.
237 * We should adhere to escape sequences documented in the console_codes
Heinrich Schuchardte1fec152018-10-18 21:51:38 +0200238 * man page and the ECMA-48 standard.
Heinrich Schuchardt6bb591f2018-09-15 23:52:07 +0200239 *
240 * So here we follow a different approach. We position the cursor to the
241 * bottom right and query its position. Before leaving the function we
242 * restore the original cursor position.
243 */
244 printf(ESC "7" /* Save cursor position */
245 ESC "[r" /* Set scrolling region to full window */
246 ESC "[999;999H" /* Move to bottom right corner */
247 ESC "[6n"); /* Query cursor position */
Rob Clark71cc25c2017-09-13 18:05:42 -0400248
Heinrich Schuchardt6bb591f2018-09-15 23:52:07 +0200249 /* Read {rows,cols} */
250 if (term_read_reply(n, 2, 'R')) {
251 ret = 1;
252 goto out;
253 }
Rob Clark71cc25c2017-09-13 18:05:42 -0400254
Heinrich Schuchardt6bb591f2018-09-15 23:52:07 +0200255 *cols = n[1];
256 *rows = n[0];
257out:
258 printf(ESC "8"); /* Restore cursor position */
259 return ret;
Rob Clark71cc25c2017-09-13 18:05:42 -0400260}
261
Heinrich Schuchardta4aa7be2018-04-29 20:02:46 +0200262/*
263 * Update the mode table.
264 *
265 * By default the only mode available is 80x25. If the console has at least 50
266 * lines, enable mode 80x50. If we can query the console size and it is neither
267 * 80x25 nor 80x50, set it as an additional mode.
268 */
269static void query_console_size(void)
270{
271 const char *stdout_name = env_get("stdout");
Alexander Graf80483b22018-06-03 15:51:17 +0200272 int rows = 25, cols = 80;
Heinrich Schuchardta4aa7be2018-04-29 20:02:46 +0200273
274 if (stdout_name && !strcmp(stdout_name, "vidconsole") &&
275 IS_ENABLED(CONFIG_DM_VIDEO)) {
276 struct stdio_dev *stdout_dev =
277 stdio_get_by_name("vidconsole");
278 struct udevice *dev = stdout_dev->priv;
279 struct vidconsole_priv *priv =
280 dev_get_uclass_priv(dev);
281 rows = priv->rows;
282 cols = priv->cols;
283 } else if (query_console_serial(&rows, &cols)) {
284 return;
285 }
286
287 /* Test if we can have Mode 1 */
288 if (cols >= 80 && rows >= 50) {
289 efi_cout_modes[1].present = 1;
290 efi_con_mode.max_mode = 2;
291 }
292
293 /*
294 * Install our mode as mode 2 if it is different
295 * than mode 0 or 1 and set it as the currently selected mode
296 */
297 if (!cout_mode_matches(&efi_cout_modes[0], rows, cols) &&
298 !cout_mode_matches(&efi_cout_modes[1], rows, cols)) {
299 efi_cout_modes[EFI_COUT_MODE_2].columns = cols;
300 efi_cout_modes[EFI_COUT_MODE_2].rows = rows;
301 efi_cout_modes[EFI_COUT_MODE_2].present = 1;
302 efi_con_mode.max_mode = EFI_MAX_COUT_MODE;
303 efi_con_mode.mode = EFI_COUT_MODE_2;
304 }
305}
306
Alexander Grafc1311ad2016-03-04 01:10:00 +0100307static efi_status_t EFIAPI efi_cout_query_mode(
308 struct efi_simple_text_output_protocol *this,
309 unsigned long mode_number, unsigned long *columns,
310 unsigned long *rows)
311{
312 EFI_ENTRY("%p, %ld, %p, %p", this, mode_number, columns, rows);
313
Emmanuel Vadot5be8b0a2016-11-08 06:03:29 +0100314 if (mode_number >= efi_con_mode.max_mode)
315 return EFI_EXIT(EFI_UNSUPPORTED);
316
317 if (efi_cout_modes[mode_number].present != 1)
318 return EFI_EXIT(EFI_UNSUPPORTED);
319
Alexander Grafc1311ad2016-03-04 01:10:00 +0100320 if (columns)
Emmanuel Vadot5be8b0a2016-11-08 06:03:29 +0100321 *columns = efi_cout_modes[mode_number].columns;
Alexander Grafc1311ad2016-03-04 01:10:00 +0100322 if (rows)
Emmanuel Vadot5be8b0a2016-11-08 06:03:29 +0100323 *rows = efi_cout_modes[mode_number].rows;
Alexander Grafc1311ad2016-03-04 01:10:00 +0100324
325 return EFI_EXIT(EFI_SUCCESS);
326}
327
Rob Clark2d5dc2a2017-10-10 08:23:01 -0400328static const struct {
329 unsigned int fg;
330 unsigned int bg;
331} color[] = {
332 { 30, 40 }, /* 0: black */
333 { 34, 44 }, /* 1: blue */
334 { 32, 42 }, /* 2: green */
335 { 36, 46 }, /* 3: cyan */
336 { 31, 41 }, /* 4: red */
337 { 35, 45 }, /* 5: magenta */
Heinrich Schuchardt14d103b2018-09-08 19:57:24 +0200338 { 33, 43 }, /* 6: brown, map to yellow as EDK2 does*/
339 { 37, 47 }, /* 7: light gray, map to white */
Rob Clark2d5dc2a2017-10-10 08:23:01 -0400340};
341
342/* See EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.SetAttribute(). */
Alexander Grafc1311ad2016-03-04 01:10:00 +0100343static efi_status_t EFIAPI efi_cout_set_attribute(
344 struct efi_simple_text_output_protocol *this,
345 unsigned long attribute)
346{
Rob Clark2d5dc2a2017-10-10 08:23:01 -0400347 unsigned int bold = EFI_ATTR_BOLD(attribute);
348 unsigned int fg = EFI_ATTR_FG(attribute);
349 unsigned int bg = EFI_ATTR_BG(attribute);
350
Alexander Grafc1311ad2016-03-04 01:10:00 +0100351 EFI_ENTRY("%p, %lx", this, attribute);
352
Heinrich Schuchardt3950f0f2019-06-14 07:16:57 +0200353 efi_con_mode.attribute = attribute;
Rob Clark2d5dc2a2017-10-10 08:23:01 -0400354 if (attribute)
355 printf(ESC"[%u;%u;%um", bold, color[fg].fg, color[bg].bg);
356 else
357 printf(ESC"[0;37;40m");
358
359 return EFI_EXIT(EFI_SUCCESS);
Alexander Grafc1311ad2016-03-04 01:10:00 +0100360}
361
362static efi_status_t EFIAPI efi_cout_clear_screen(
363 struct efi_simple_text_output_protocol *this)
364{
365 EFI_ENTRY("%p", this);
366
367 printf(ESC"[2J");
Heinrich Schuchardte67ff942018-07-05 08:18:00 +0200368 efi_con_mode.cursor_column = 0;
369 efi_con_mode.cursor_row = 0;
Alexander Grafc1311ad2016-03-04 01:10:00 +0100370
371 return EFI_EXIT(EFI_SUCCESS);
372}
373
Heinrich Schuchardt2ad238f2019-06-14 07:20:51 +0200374static efi_status_t EFIAPI efi_cout_set_mode(
375 struct efi_simple_text_output_protocol *this,
376 unsigned long mode_number)
377{
378 EFI_ENTRY("%p, %ld", this, mode_number);
379
380 if (mode_number >= efi_con_mode.max_mode)
381 return EFI_EXIT(EFI_UNSUPPORTED);
Heinrich Schuchardt03446982019-09-04 22:46:13 +0200382
383 if (!efi_cout_modes[mode_number].present)
384 return EFI_EXIT(EFI_UNSUPPORTED);
385
Heinrich Schuchardt2ad238f2019-06-14 07:20:51 +0200386 efi_con_mode.mode = mode_number;
387 EFI_CALL(efi_cout_clear_screen(this));
388
389 return EFI_EXIT(EFI_SUCCESS);
390}
391
Heinrich Schuchardt9d12daf2018-07-05 19:58:07 +0200392static efi_status_t EFIAPI efi_cout_reset(
393 struct efi_simple_text_output_protocol *this,
394 char extended_verification)
395{
396 EFI_ENTRY("%p, %d", this, extended_verification);
397
398 /* Clear screen */
399 EFI_CALL(efi_cout_clear_screen(this));
400 /* Set default colors */
Heinrich Schuchardt3950f0f2019-06-14 07:16:57 +0200401 efi_con_mode.attribute = 0x07;
Heinrich Schuchardt9d12daf2018-07-05 19:58:07 +0200402 printf(ESC "[0;37;40m");
403
404 return EFI_EXIT(EFI_SUCCESS);
405}
406
Alexander Grafc1311ad2016-03-04 01:10:00 +0100407static efi_status_t EFIAPI efi_cout_set_cursor_position(
408 struct efi_simple_text_output_protocol *this,
409 unsigned long column, unsigned long row)
410{
Heinrich Schuchardtaaace4b2018-09-14 18:49:26 +0200411 efi_status_t ret = EFI_SUCCESS;
412 struct simple_text_output_mode *con = &efi_con_mode;
413 struct cout_mode *mode = &efi_cout_modes[con->mode];
414
Alexander Grafc1311ad2016-03-04 01:10:00 +0100415 EFI_ENTRY("%p, %ld, %ld", this, column, row);
416
Heinrich Schuchardtaaace4b2018-09-14 18:49:26 +0200417 /* Check parameters */
418 if (!this) {
419 ret = EFI_INVALID_PARAMETER;
420 goto out;
421 }
422 if (row >= mode->rows || column >= mode->columns) {
423 ret = EFI_UNSUPPORTED;
424 goto out;
425 }
426
427 /*
428 * Set cursor position by sending CSI H.
429 * EFI origin is [0, 0], terminal origin is [1, 1].
430 */
431 printf(ESC "[%d;%dH", (int)row + 1, (int)column + 1);
Alexander Grafc1311ad2016-03-04 01:10:00 +0100432 efi_con_mode.cursor_column = column;
433 efi_con_mode.cursor_row = row;
Heinrich Schuchardtaaace4b2018-09-14 18:49:26 +0200434out:
435 return EFI_EXIT(ret);
Alexander Grafc1311ad2016-03-04 01:10:00 +0100436}
437
438static efi_status_t EFIAPI efi_cout_enable_cursor(
439 struct efi_simple_text_output_protocol *this,
440 bool enable)
441{
442 EFI_ENTRY("%p, %d", this, enable);
443
444 printf(ESC"[?25%c", enable ? 'h' : 'l');
Heinrich Schuchardt25e6fb52019-06-02 22:54:28 +0200445 efi_con_mode.cursor_visible = !!enable;
Alexander Grafc1311ad2016-03-04 01:10:00 +0100446
447 return EFI_EXIT(EFI_SUCCESS);
448}
449
Heinrich Schuchardtebb4dd52017-10-26 19:25:59 +0200450struct efi_simple_text_output_protocol efi_con_out = {
Alexander Grafc1311ad2016-03-04 01:10:00 +0100451 .reset = efi_cout_reset,
452 .output_string = efi_cout_output_string,
453 .test_string = efi_cout_test_string,
454 .query_mode = efi_cout_query_mode,
455 .set_mode = efi_cout_set_mode,
456 .set_attribute = efi_cout_set_attribute,
457 .clear_screen = efi_cout_clear_screen,
458 .set_cursor_position = efi_cout_set_cursor_position,
459 .enable_cursor = efi_cout_enable_cursor,
460 .mode = (void*)&efi_con_mode,
461};
462
Heinrich Schuchardt4fdcf062018-09-11 22:38:12 +0200463/**
464 * struct efi_cin_notify_function - registered console input notify function
465 *
466 * @link: link to list
Heinrich Schuchardtfe1a81c2019-09-05 20:37:13 +0200467 * @key: key to notify
Heinrich Schuchardt4fdcf062018-09-11 22:38:12 +0200468 * @function: function to call
469 */
470struct efi_cin_notify_function {
471 struct list_head link;
472 struct efi_key_data key;
473 efi_status_t (EFIAPI *function)
474 (struct efi_key_data *key_data);
475};
476
Heinrich Schuchardt0dfd13a2018-09-11 22:38:05 +0200477static bool key_available;
Heinrich Schuchardt110c6282018-09-11 22:38:08 +0200478static struct efi_key_data next_key;
Heinrich Schuchardt4fdcf062018-09-11 22:38:12 +0200479static LIST_HEAD(cin_notify_functions);
Heinrich Schuchardt4f187892018-07-05 08:17:59 +0200480
Heinrich Schuchardt0dfd13a2018-09-11 22:38:05 +0200481/**
Heinrich Schuchardt55fbdf92018-09-11 22:38:09 +0200482 * set_shift_mask() - set shift mask
483 *
484 * @mod: Xterm shift mask
Heinrich Schuchardtfe1a81c2019-09-05 20:37:13 +0200485 * @key_state: receives the state of the shift, alt, control, and logo keys
Heinrich Schuchardt55fbdf92018-09-11 22:38:09 +0200486 */
487void set_shift_mask(int mod, struct efi_key_state *key_state)
488{
489 key_state->key_shift_state = EFI_SHIFT_STATE_VALID;
490 if (mod) {
491 --mod;
492 if (mod & 1)
493 key_state->key_shift_state |= EFI_LEFT_SHIFT_PRESSED;
494 if (mod & 2)
495 key_state->key_shift_state |= EFI_LEFT_ALT_PRESSED;
496 if (mod & 4)
497 key_state->key_shift_state |= EFI_LEFT_CONTROL_PRESSED;
Heinrich Schuchardt3b435c12019-06-16 22:33:20 +0200498 if (!mod || (mod & 8))
Heinrich Schuchardt55fbdf92018-09-11 22:38:09 +0200499 key_state->key_shift_state |= EFI_LEFT_LOGO_PRESSED;
Heinrich Schuchardt55fbdf92018-09-11 22:38:09 +0200500 }
501}
502
503/**
Heinrich Schuchardt110c6282018-09-11 22:38:08 +0200504 * analyze_modifiers() - analyze modifiers (shift, alt, ctrl) for function keys
Heinrich Schuchardt0dfd13a2018-09-11 22:38:05 +0200505 *
Heinrich Schuchardt0fb41692018-02-19 18:53:29 +0100506 * This gets called when we have already parsed CSI.
507 *
Heinrich Schuchardtfe1a81c2019-09-05 20:37:13 +0200508 * @key_state: receives the state of the shift, alt, control, and logo keys
Heinrich Schuchardt0fb41692018-02-19 18:53:29 +0100509 * @return: the unmodified code
510 */
Heinrich Schuchardt110c6282018-09-11 22:38:08 +0200511static int analyze_modifiers(struct efi_key_state *key_state)
Heinrich Schuchardt0fb41692018-02-19 18:53:29 +0100512{
Heinrich Schuchardt110c6282018-09-11 22:38:08 +0200513 int c, mod = 0, ret = 0;
Heinrich Schuchardt0fb41692018-02-19 18:53:29 +0100514
515 c = getc();
516
517 if (c != ';') {
518 ret = c;
519 if (c == '~')
520 goto out;
521 c = getc();
522 }
523 for (;;) {
524 switch (c) {
525 case '0'...'9':
526 mod *= 10;
527 mod += c - '0';
528 /* fall through */
529 case ';':
530 c = getc();
531 break;
532 default:
533 goto out;
534 }
535 }
536out:
Heinrich Schuchardt55fbdf92018-09-11 22:38:09 +0200537 set_shift_mask(mod, key_state);
Heinrich Schuchardt0fb41692018-02-19 18:53:29 +0100538 if (!ret)
539 ret = c;
540 return ret;
541}
542
Heinrich Schuchardt0dfd13a2018-09-11 22:38:05 +0200543/**
544 * efi_cin_read_key() - read a key from the console input
545 *
546 * @key: - key received
547 * Return: - status code
548 */
Heinrich Schuchardt110c6282018-09-11 22:38:08 +0200549static efi_status_t efi_cin_read_key(struct efi_key_data *key)
Alexander Grafc1311ad2016-03-04 01:10:00 +0100550{
551 struct efi_input_key pressed_key = {
552 .scan_code = 0,
553 .unicode_char = 0,
554 };
Heinrich Schuchardt35cbb792018-09-12 00:05:32 +0200555 s32 ch;
Alexander Grafc1311ad2016-03-04 01:10:00 +0100556
Heinrich Schuchardt55fbdf92018-09-11 22:38:09 +0200557 if (console_read_unicode(&ch))
Heinrich Schuchardt0dfd13a2018-09-11 22:38:05 +0200558 return EFI_NOT_READY;
Heinrich Schuchardt110c6282018-09-11 22:38:08 +0200559
560 key->key_state.key_shift_state = EFI_SHIFT_STATE_INVALID;
561 key->key_state.key_toggle_state = EFI_TOGGLE_STATE_INVALID;
562
Heinrich Schuchardt35cbb792018-09-12 00:05:32 +0200563 /* We do not support multi-word codes */
564 if (ch >= 0x10000)
565 ch = '?';
Heinrich Schuchardt55fbdf92018-09-11 22:38:09 +0200566
567 switch (ch) {
568 case 0x1b:
Heinrich Schuchardt0fb41692018-02-19 18:53:29 +0100569 /*
570 * Xterm Control Sequences
571 * https://www.xfree86.org/4.8.0/ctlseqs.html
572 */
Alexander Grafc1311ad2016-03-04 01:10:00 +0100573 ch = getc();
574 switch (ch) {
575 case cESC: /* ESC */
576 pressed_key.scan_code = 23;
577 break;
Heinrich Schuchardt3b2b2de2019-06-16 21:41:13 +0200578 case 'O': /* F1 - F4, End */
Heinrich Schuchardt0fb41692018-02-19 18:53:29 +0100579 ch = getc();
Heinrich Schuchardt55fbdf92018-09-11 22:38:09 +0200580 /* consider modifiers */
Heinrich Schuchardt3b2b2de2019-06-16 21:41:13 +0200581 if (ch == 'F') { /* End */
582 pressed_key.scan_code = 6;
583 break;
584 } else if (ch < 'P') {
Heinrich Schuchardt55fbdf92018-09-11 22:38:09 +0200585 set_shift_mask(ch - '0', &key->key_state);
Heinrich Schuchardt0fb41692018-02-19 18:53:29 +0100586 ch = getc();
Heinrich Schuchardt55fbdf92018-09-11 22:38:09 +0200587 }
Heinrich Schuchardt0fb41692018-02-19 18:53:29 +0100588 pressed_key.scan_code = ch - 'P' + 11;
Alexander Grafc1311ad2016-03-04 01:10:00 +0100589 break;
Alexander Grafc1311ad2016-03-04 01:10:00 +0100590 case '[':
591 ch = getc();
592 switch (ch) {
593 case 'A'...'D': /* up, down right, left */
594 pressed_key.scan_code = ch - 'A' + 1;
595 break;
596 case 'F': /* End */
597 pressed_key.scan_code = 6;
598 break;
599 case 'H': /* Home */
600 pressed_key.scan_code = 5;
601 break;
Heinrich Schuchardt0fb41692018-02-19 18:53:29 +0100602 case '1':
Heinrich Schuchardt110c6282018-09-11 22:38:08 +0200603 ch = analyze_modifiers(&key->key_state);
Heinrich Schuchardt0fb41692018-02-19 18:53:29 +0100604 switch (ch) {
605 case '1'...'5': /* F1 - F5 */
606 pressed_key.scan_code = ch - '1' + 11;
607 break;
Heinrich Schuchardt3b2b2de2019-06-16 21:41:13 +0200608 case '6'...'9': /* F5 - F8 */
609 pressed_key.scan_code = ch - '6' + 15;
Heinrich Schuchardt0fb41692018-02-19 18:53:29 +0100610 break;
611 case 'A'...'D': /* up, down right, left */
612 pressed_key.scan_code = ch - 'A' + 1;
613 break;
Heinrich Schuchardt3b2b2de2019-06-16 21:41:13 +0200614 case 'F': /* End */
615 pressed_key.scan_code = 6;
Heinrich Schuchardt0fb41692018-02-19 18:53:29 +0100616 break;
Heinrich Schuchardt3b2b2de2019-06-16 21:41:13 +0200617 case 'H': /* Home */
618 pressed_key.scan_code = 5;
619 break;
620 case '~': /* Home */
621 pressed_key.scan_code = 5;
Heinrich Schuchardt0fb41692018-02-19 18:53:29 +0100622 break;
623 }
Alexander Grafc1311ad2016-03-04 01:10:00 +0100624 break;
Heinrich Schuchardt0fb41692018-02-19 18:53:29 +0100625 case '2':
Heinrich Schuchardt110c6282018-09-11 22:38:08 +0200626 ch = analyze_modifiers(&key->key_state);
Heinrich Schuchardt0fb41692018-02-19 18:53:29 +0100627 switch (ch) {
628 case '0'...'1': /* F9 - F10 */
629 pressed_key.scan_code = ch - '0' + 19;
630 break;
631 case '3'...'4': /* F11 - F12 */
632 pressed_key.scan_code = ch - '3' + 21;
633 break;
634 case '~': /* INS */
635 pressed_key.scan_code = 7;
636 break;
637 }
Alexander Grafc1311ad2016-03-04 01:10:00 +0100638 break;
639 case '3': /* DEL */
640 pressed_key.scan_code = 8;
Heinrich Schuchardt110c6282018-09-11 22:38:08 +0200641 analyze_modifiers(&key->key_state);
Heinrich Schuchardt0fb41692018-02-19 18:53:29 +0100642 break;
643 case '5': /* PG UP */
644 pressed_key.scan_code = 9;
Heinrich Schuchardt110c6282018-09-11 22:38:08 +0200645 analyze_modifiers(&key->key_state);
Heinrich Schuchardt0fb41692018-02-19 18:53:29 +0100646 break;
647 case '6': /* PG DOWN */
648 pressed_key.scan_code = 10;
Heinrich Schuchardt110c6282018-09-11 22:38:08 +0200649 analyze_modifiers(&key->key_state);
Alexander Grafc1311ad2016-03-04 01:10:00 +0100650 break;
Heinrich Schuchardt55fbdf92018-09-11 22:38:09 +0200651 } /* [ */
Alexander Grafc1311ad2016-03-04 01:10:00 +0100652 break;
Heinrich Schuchardt55fbdf92018-09-11 22:38:09 +0200653 default:
654 /* ALT key */
655 set_shift_mask(3, &key->key_state);
Alexander Grafc1311ad2016-03-04 01:10:00 +0100656 }
Heinrich Schuchardt55fbdf92018-09-11 22:38:09 +0200657 break;
658 case 0x7f:
Alexander Grafc1311ad2016-03-04 01:10:00 +0100659 /* Backspace */
660 ch = 0x08;
661 }
Heinrich Schuchardt110c6282018-09-11 22:38:08 +0200662 if (pressed_key.scan_code) {
663 key->key_state.key_shift_state |= EFI_SHIFT_STATE_VALID;
664 } else {
Heinrich Schuchardt0fb41692018-02-19 18:53:29 +0100665 pressed_key.unicode_char = ch;
Heinrich Schuchardt110c6282018-09-11 22:38:08 +0200666
667 /*
668 * Assume left control key for control characters typically
669 * entered using the control key.
670 */
671 if (ch >= 0x01 && ch <= 0x1f) {
Heinrich Schuchardt55fbdf92018-09-11 22:38:09 +0200672 key->key_state.key_shift_state |=
Heinrich Schuchardt110c6282018-09-11 22:38:08 +0200673 EFI_SHIFT_STATE_VALID;
674 switch (ch) {
675 case 0x01 ... 0x07:
676 case 0x0b ... 0x0c:
677 case 0x0e ... 0x1f:
678 key->key_state.key_shift_state |=
679 EFI_LEFT_CONTROL_PRESSED;
680 }
681 }
682 }
683 key->key = pressed_key;
Alexander Grafc1311ad2016-03-04 01:10:00 +0100684
Heinrich Schuchardt0dfd13a2018-09-11 22:38:05 +0200685 return EFI_SUCCESS;
686}
687
688/**
Heinrich Schuchardt4fdcf062018-09-11 22:38:12 +0200689 * efi_cin_notify() - notify registered functions
690 */
691static void efi_cin_notify(void)
692{
693 struct efi_cin_notify_function *item;
694
695 list_for_each_entry(item, &cin_notify_functions, link) {
696 bool match = true;
697
698 /* We do not support toggle states */
699 if (item->key.key.unicode_char || item->key.key.scan_code) {
700 if (item->key.key.unicode_char !=
701 next_key.key.unicode_char ||
702 item->key.key.scan_code != next_key.key.scan_code)
703 match = false;
704 }
705 if (item->key.key_state.key_shift_state &&
706 item->key.key_state.key_shift_state !=
707 next_key.key_state.key_shift_state)
708 match = false;
709
710 if (match)
711 /* We don't bother about the return code */
712 EFI_CALL(item->function(&next_key));
713 }
714}
715
716/**
Heinrich Schuchardt0dfd13a2018-09-11 22:38:05 +0200717 * efi_cin_check() - check if keyboard input is available
718 */
719static void efi_cin_check(void)
720{
721 efi_status_t ret;
722
723 if (key_available) {
Heinrich Schuchardt7eaa9002019-06-07 06:47:01 +0200724 efi_signal_event(efi_con_in.wait_for_key);
Heinrich Schuchardt0dfd13a2018-09-11 22:38:05 +0200725 return;
726 }
727
728 if (tstc()) {
729 ret = efi_cin_read_key(&next_key);
730 if (ret == EFI_SUCCESS) {
731 key_available = true;
732
Heinrich Schuchardt4fdcf062018-09-11 22:38:12 +0200733 /* Notify registered functions */
734 efi_cin_notify();
735
Heinrich Schuchardt0dfd13a2018-09-11 22:38:05 +0200736 /* Queue the wait for key event */
Heinrich Schuchardt4fdcf062018-09-11 22:38:12 +0200737 if (key_available)
Heinrich Schuchardt7eaa9002019-06-07 06:47:01 +0200738 efi_signal_event(efi_con_in.wait_for_key);
Heinrich Schuchardt0dfd13a2018-09-11 22:38:05 +0200739 }
740 }
741}
742
743/**
Heinrich Schuchardt110c6282018-09-11 22:38:08 +0200744 * efi_cin_empty_buffer() - empty input buffer
745 */
746static void efi_cin_empty_buffer(void)
747{
748 while (tstc())
749 getc();
750 key_available = false;
751}
752
753/**
754 * efi_cin_reset_ex() - reset console input
755 *
756 * @this: - the extended simple text input protocol
757 * @extended_verification: - extended verification
758 *
759 * This function implements the reset service of the
760 * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
761 *
762 * See the Unified Extensible Firmware Interface (UEFI) specification for
763 * details.
764 *
765 * Return: old value of the task priority level
766 */
767static efi_status_t EFIAPI efi_cin_reset_ex(
768 struct efi_simple_text_input_ex_protocol *this,
769 bool extended_verification)
770{
771 efi_status_t ret = EFI_SUCCESS;
772
773 EFI_ENTRY("%p, %d", this, extended_verification);
774
775 /* Check parameters */
776 if (!this) {
777 ret = EFI_INVALID_PARAMETER;
778 goto out;
779 }
780
781 efi_cin_empty_buffer();
782out:
783 return EFI_EXIT(ret);
784}
785
786/**
787 * efi_cin_read_key_stroke_ex() - read key stroke
788 *
789 * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
790 * @key_data: key read from console
791 * Return: status code
792 *
793 * This function implements the ReadKeyStrokeEx service of the
794 * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
795 *
796 * See the Unified Extensible Firmware Interface (UEFI) specification for
797 * details.
798 */
799static efi_status_t EFIAPI efi_cin_read_key_stroke_ex(
800 struct efi_simple_text_input_ex_protocol *this,
801 struct efi_key_data *key_data)
802{
803 efi_status_t ret = EFI_SUCCESS;
804
805 EFI_ENTRY("%p, %p", this, key_data);
806
807 /* Check parameters */
808 if (!this || !key_data) {
809 ret = EFI_INVALID_PARAMETER;
810 goto out;
811 }
812
813 /* We don't do interrupts, so check for timers cooperatively */
814 efi_timer_check();
815
816 /* Enable console input after ExitBootServices */
817 efi_cin_check();
818
819 if (!key_available) {
820 ret = EFI_NOT_READY;
821 goto out;
822 }
Heinrich Schuchardtbfc2dd52019-04-06 20:59:24 +0200823 /*
824 * CTRL+A - CTRL+Z have to be signaled as a - z.
825 * SHIFT+CTRL+A - SHIFT+CTRL+Z have to be signaled as A - Z.
826 */
827 switch (next_key.key.unicode_char) {
828 case 0x01 ... 0x07:
829 case 0x0b ... 0x0c:
830 case 0x0e ... 0x1a:
831 if (!(next_key.key_state.key_toggle_state &
832 EFI_CAPS_LOCK_ACTIVE) ^
833 !(next_key.key_state.key_shift_state &
834 (EFI_LEFT_SHIFT_PRESSED | EFI_RIGHT_SHIFT_PRESSED)))
835 next_key.key.unicode_char += 0x40;
836 else
837 next_key.key.unicode_char += 0x60;
838 }
Heinrich Schuchardt110c6282018-09-11 22:38:08 +0200839 *key_data = next_key;
840 key_available = false;
841 efi_con_in.wait_for_key->is_signaled = false;
Heinrich Schuchardtbfc2dd52019-04-06 20:59:24 +0200842
Heinrich Schuchardt110c6282018-09-11 22:38:08 +0200843out:
844 return EFI_EXIT(ret);
845}
846
847/**
848 * efi_cin_set_state() - set toggle key state
849 *
850 * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
Heinrich Schuchardtacee9652019-05-18 17:07:52 +0200851 * @key_toggle_state: pointer to key toggle state
Heinrich Schuchardt110c6282018-09-11 22:38:08 +0200852 * Return: status code
853 *
854 * This function implements the SetState service of the
855 * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
856 *
857 * See the Unified Extensible Firmware Interface (UEFI) specification for
858 * details.
859 */
860static efi_status_t EFIAPI efi_cin_set_state(
861 struct efi_simple_text_input_ex_protocol *this,
Heinrich Schuchardtacee9652019-05-18 17:07:52 +0200862 u8 *key_toggle_state)
Heinrich Schuchardt110c6282018-09-11 22:38:08 +0200863{
Heinrich Schuchardtacee9652019-05-18 17:07:52 +0200864 EFI_ENTRY("%p, %p", this, key_toggle_state);
Heinrich Schuchardt110c6282018-09-11 22:38:08 +0200865 /*
866 * U-Boot supports multiple console input sources like serial and
867 * net console for which a key toggle state cannot be set at all.
868 *
869 * According to the UEFI specification it is allowable to not implement
870 * this service.
871 */
872 return EFI_EXIT(EFI_UNSUPPORTED);
873}
874
875/**
876 * efi_cin_register_key_notify() - register key notification function
877 *
878 * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
879 * @key_data: key to be notified
880 * @key_notify_function: function to be called if the key is pressed
881 * @notify_handle: handle for unregistering the notification
882 * Return: status code
883 *
884 * This function implements the SetState service of the
885 * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
886 *
887 * See the Unified Extensible Firmware Interface (UEFI) specification for
888 * details.
889 */
890static efi_status_t EFIAPI efi_cin_register_key_notify(
891 struct efi_simple_text_input_ex_protocol *this,
892 struct efi_key_data *key_data,
893 efi_status_t (EFIAPI *key_notify_function)(
894 struct efi_key_data *key_data),
895 void **notify_handle)
896{
Heinrich Schuchardt4fdcf062018-09-11 22:38:12 +0200897 efi_status_t ret = EFI_SUCCESS;
898 struct efi_cin_notify_function *notify_function;
899
Heinrich Schuchardt110c6282018-09-11 22:38:08 +0200900 EFI_ENTRY("%p, %p, %p, %p",
901 this, key_data, key_notify_function, notify_handle);
Heinrich Schuchardt4fdcf062018-09-11 22:38:12 +0200902
903 /* Check parameters */
904 if (!this || !key_data || !key_notify_function || !notify_handle) {
905 ret = EFI_INVALID_PARAMETER;
906 goto out;
907 }
908
909 EFI_PRINT("u+%04x, sc %04x, sh %08x, tg %02x\n",
910 key_data->key.unicode_char,
911 key_data->key.scan_code,
912 key_data->key_state.key_shift_state,
913 key_data->key_state.key_toggle_state);
914
915 notify_function = calloc(1, sizeof(struct efi_cin_notify_function));
916 if (!notify_function) {
917 ret = EFI_OUT_OF_RESOURCES;
918 goto out;
919 }
920 notify_function->key = *key_data;
921 notify_function->function = key_notify_function;
922 list_add_tail(&notify_function->link, &cin_notify_functions);
923 *notify_handle = notify_function;
924out:
925 return EFI_EXIT(ret);
Heinrich Schuchardt110c6282018-09-11 22:38:08 +0200926}
927
928/**
929 * efi_cin_unregister_key_notify() - unregister key notification function
930 *
931 * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
932 * @notification_handle: handle received when registering
933 * Return: status code
934 *
935 * This function implements the SetState service of the
936 * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
937 *
938 * See the Unified Extensible Firmware Interface (UEFI) specification for
939 * details.
940 */
941static efi_status_t EFIAPI efi_cin_unregister_key_notify(
942 struct efi_simple_text_input_ex_protocol *this,
943 void *notification_handle)
944{
Heinrich Schuchardt4fdcf062018-09-11 22:38:12 +0200945 efi_status_t ret = EFI_INVALID_PARAMETER;
946 struct efi_cin_notify_function *item, *notify_function =
947 notification_handle;
948
Heinrich Schuchardt110c6282018-09-11 22:38:08 +0200949 EFI_ENTRY("%p, %p", this, notification_handle);
Heinrich Schuchardt4fdcf062018-09-11 22:38:12 +0200950
951 /* Check parameters */
952 if (!this || !notification_handle)
953 goto out;
954
955 list_for_each_entry(item, &cin_notify_functions, link) {
956 if (item == notify_function) {
957 ret = EFI_SUCCESS;
958 break;
959 }
960 }
961 if (ret != EFI_SUCCESS)
962 goto out;
963
964 /* Remove the notify function */
965 list_del(&notify_function->link);
966 free(notify_function);
967out:
968 return EFI_EXIT(ret);
Heinrich Schuchardt110c6282018-09-11 22:38:08 +0200969}
970
971
972/**
Heinrich Schuchardt0dfd13a2018-09-11 22:38:05 +0200973 * efi_cin_reset() - drain the input buffer
974 *
975 * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
976 * @extended_verification: allow for exhaustive verification
977 * Return: status code
978 *
979 * This function implements the Reset service of the
980 * EFI_SIMPLE_TEXT_INPUT_PROTOCOL.
981 *
982 * See the Unified Extensible Firmware Interface (UEFI) specification for
983 * details.
984 */
985static efi_status_t EFIAPI efi_cin_reset
986 (struct efi_simple_text_input_protocol *this,
987 bool extended_verification)
988{
989 efi_status_t ret = EFI_SUCCESS;
990
991 EFI_ENTRY("%p, %d", this, extended_verification);
992
993 /* Check parameters */
994 if (!this) {
995 ret = EFI_INVALID_PARAMETER;
996 goto out;
997 }
998
Heinrich Schuchardt110c6282018-09-11 22:38:08 +0200999 efi_cin_empty_buffer();
Heinrich Schuchardt0dfd13a2018-09-11 22:38:05 +02001000out:
1001 return EFI_EXIT(ret);
1002}
1003
1004/**
Heinrich Schuchardt110c6282018-09-11 22:38:08 +02001005 * efi_cin_read_key_stroke() - read key stroke
Heinrich Schuchardt0dfd13a2018-09-11 22:38:05 +02001006 *
1007 * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
1008 * @key: key read from console
1009 * Return: status code
1010 *
1011 * This function implements the ReadKeyStroke service of the
1012 * EFI_SIMPLE_TEXT_INPUT_PROTOCOL.
1013 *
1014 * See the Unified Extensible Firmware Interface (UEFI) specification for
1015 * details.
1016 */
1017static efi_status_t EFIAPI efi_cin_read_key_stroke
1018 (struct efi_simple_text_input_protocol *this,
1019 struct efi_input_key *key)
1020{
1021 efi_status_t ret = EFI_SUCCESS;
1022
1023 EFI_ENTRY("%p, %p", this, key);
1024
1025 /* Check parameters */
1026 if (!this || !key) {
1027 ret = EFI_INVALID_PARAMETER;
1028 goto out;
1029 }
1030
1031 /* We don't do interrupts, so check for timers cooperatively */
1032 efi_timer_check();
1033
1034 /* Enable console input after ExitBootServices */
1035 efi_cin_check();
1036
1037 if (!key_available) {
1038 ret = EFI_NOT_READY;
1039 goto out;
1040 }
Heinrich Schuchardt110c6282018-09-11 22:38:08 +02001041 *key = next_key.key;
Heinrich Schuchardt0dfd13a2018-09-11 22:38:05 +02001042 key_available = false;
1043 efi_con_in.wait_for_key->is_signaled = false;
1044out:
1045 return EFI_EXIT(ret);
Alexander Grafc1311ad2016-03-04 01:10:00 +01001046}
1047
Heinrich Schuchardt110c6282018-09-11 22:38:08 +02001048static struct efi_simple_text_input_ex_protocol efi_con_in_ex = {
1049 .reset = efi_cin_reset_ex,
1050 .read_key_stroke_ex = efi_cin_read_key_stroke_ex,
1051 .wait_for_key_ex = NULL,
1052 .set_state = efi_cin_set_state,
1053 .register_key_notify = efi_cin_register_key_notify,
1054 .unregister_key_notify = efi_cin_unregister_key_notify,
1055};
1056
Heinrich Schuchardt3e603ec2018-09-08 10:20:10 +02001057struct efi_simple_text_input_protocol efi_con_in = {
Alexander Grafc1311ad2016-03-04 01:10:00 +01001058 .reset = efi_cin_reset,
1059 .read_key_stroke = efi_cin_read_key_stroke,
1060 .wait_for_key = NULL,
1061};
xypron.glpk@gmx.de91be9a72017-07-18 20:17:22 +02001062
1063static struct efi_event *console_timer_event;
1064
Heinrich Schuchardt9bc96642018-01-19 20:24:51 +01001065/*
Heinrich Schuchardt0dfd13a2018-09-11 22:38:05 +02001066 * efi_console_timer_notify() - notify the console timer event
Heinrich Schuchardt9bc96642018-01-19 20:24:51 +01001067 *
Heinrich Schuchardt0dfd13a2018-09-11 22:38:05 +02001068 * @event: console timer event
1069 * @context: not used
Heinrich Schuchardt9bc96642018-01-19 20:24:51 +01001070 */
xypron.glpk@gmx.deff925932017-07-20 05:26:07 +02001071static void EFIAPI efi_console_timer_notify(struct efi_event *event,
1072 void *context)
xypron.glpk@gmx.de91be9a72017-07-18 20:17:22 +02001073{
1074 EFI_ENTRY("%p, %p", event, context);
Heinrich Schuchardt0dfd13a2018-09-11 22:38:05 +02001075 efi_cin_check();
xypron.glpk@gmx.de91be9a72017-07-18 20:17:22 +02001076 EFI_EXIT(EFI_SUCCESS);
1077}
1078
Heinrich Schuchardt0dfd13a2018-09-11 22:38:05 +02001079/**
1080 * efi_key_notify() - notify the wait for key event
1081 *
1082 * @event: wait for key event
1083 * @context: not used
1084 */
1085static void EFIAPI efi_key_notify(struct efi_event *event, void *context)
1086{
1087 EFI_ENTRY("%p, %p", event, context);
1088 efi_cin_check();
1089 EFI_EXIT(EFI_SUCCESS);
1090}
1091
1092/**
1093 * efi_console_register() - install the console protocols
1094 *
1095 * This function is called from do_bootefi_exec().
Heinrich Schuchardt6f566c22018-10-02 06:08:26 +02001096 *
1097 * Return: status code
Heinrich Schuchardt0dfd13a2018-09-11 22:38:05 +02001098 */
Heinrich Schuchardt6f566c22018-10-02 06:08:26 +02001099efi_status_t efi_console_register(void)
xypron.glpk@gmx.de91be9a72017-07-18 20:17:22 +02001100{
1101 efi_status_t r;
Heinrich Schuchardtfae01182018-09-26 05:27:55 +02001102 efi_handle_t console_output_handle;
1103 efi_handle_t console_input_handle;
Rob Clarka17e62c2017-07-24 10:39:01 -04001104
Heinrich Schuchardta4aa7be2018-04-29 20:02:46 +02001105 /* Set up mode information */
1106 query_console_size();
1107
Heinrich Schuchardtebb4dd52017-10-26 19:25:59 +02001108 /* Create handles */
Heinrich Schuchardtfae01182018-09-26 05:27:55 +02001109 r = efi_create_handle(&console_output_handle);
Heinrich Schuchardtebb4dd52017-10-26 19:25:59 +02001110 if (r != EFI_SUCCESS)
1111 goto out_of_memory;
Alexander Graf40e3e752018-09-04 14:59:11 +02001112
Heinrich Schuchardtfae01182018-09-26 05:27:55 +02001113 r = efi_add_protocol(console_output_handle,
Heinrich Schuchardtebb4dd52017-10-26 19:25:59 +02001114 &efi_guid_text_output_protocol, &efi_con_out);
1115 if (r != EFI_SUCCESS)
1116 goto out_of_memory;
Heinrich Schuchardtfae01182018-09-26 05:27:55 +02001117 systab.con_out_handle = console_output_handle;
1118 systab.stderr_handle = console_output_handle;
Alexander Graf40e3e752018-09-04 14:59:11 +02001119
Heinrich Schuchardtfae01182018-09-26 05:27:55 +02001120 r = efi_create_handle(&console_input_handle);
Heinrich Schuchardtebb4dd52017-10-26 19:25:59 +02001121 if (r != EFI_SUCCESS)
1122 goto out_of_memory;
Alexander Graf40e3e752018-09-04 14:59:11 +02001123
Heinrich Schuchardtfae01182018-09-26 05:27:55 +02001124 r = efi_add_protocol(console_input_handle,
Heinrich Schuchardtebb4dd52017-10-26 19:25:59 +02001125 &efi_guid_text_input_protocol, &efi_con_in);
1126 if (r != EFI_SUCCESS)
1127 goto out_of_memory;
Heinrich Schuchardtfae01182018-09-26 05:27:55 +02001128 systab.con_in_handle = console_input_handle;
1129 r = efi_add_protocol(console_input_handle,
Heinrich Schuchardt110c6282018-09-11 22:38:08 +02001130 &efi_guid_text_input_ex_protocol, &efi_con_in_ex);
1131 if (r != EFI_SUCCESS)
1132 goto out_of_memory;
Rob Clarka17e62c2017-07-24 10:39:01 -04001133
Heinrich Schuchardtebb4dd52017-10-26 19:25:59 +02001134 /* Create console events */
Heinrich Schuchardtb095f3c2018-02-18 15:17:52 +01001135 r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK, efi_key_notify,
1136 NULL, NULL, &efi_con_in.wait_for_key);
xypron.glpk@gmx.de91be9a72017-07-18 20:17:22 +02001137 if (r != EFI_SUCCESS) {
1138 printf("ERROR: Failed to register WaitForKey event\n");
1139 return r;
1140 }
Heinrich Schuchardt110c6282018-09-11 22:38:08 +02001141 efi_con_in_ex.wait_for_key_ex = efi_con_in.wait_for_key;
xypron.glpk@gmx.de91be9a72017-07-18 20:17:22 +02001142 r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
Heinrich Schuchardtb095f3c2018-02-18 15:17:52 +01001143 efi_console_timer_notify, NULL, NULL,
xypron.glpk@gmx.de91be9a72017-07-18 20:17:22 +02001144 &console_timer_event);
1145 if (r != EFI_SUCCESS) {
1146 printf("ERROR: Failed to register console event\n");
1147 return r;
1148 }
1149 /* 5000 ns cycle is sufficient for 2 MBaud */
1150 r = efi_set_timer(console_timer_event, EFI_TIMER_PERIODIC, 50);
1151 if (r != EFI_SUCCESS)
1152 printf("ERROR: Failed to set console timer\n");
1153 return r;
Heinrich Schuchardtebb4dd52017-10-26 19:25:59 +02001154out_of_memory:
Heinrich Schuchardt14d103b2018-09-08 19:57:24 +02001155 printf("ERROR: Out of memory\n");
Heinrich Schuchardtebb4dd52017-10-26 19:25:59 +02001156 return r;
xypron.glpk@gmx.de91be9a72017-07-18 20:17:22 +02001157}