]>
Commit | Line | Data |
---|---|---|
f739fcd8 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
c1311ad4 AG |
2 | /* |
3 | * EFI application console interface | |
4 | * | |
5 | * Copyright (c) 2016 Alexander Graf | |
c1311ad4 AG |
6 | */ |
7 | ||
8 | #include <common.h> | |
78178bb0 | 9 | #include <charset.h> |
a18c5a83 | 10 | #include <dm/device.h> |
c1311ad4 | 11 | #include <efi_loader.h> |
a18c5a83 RC |
12 | #include <stdio_dev.h> |
13 | #include <video_console.h> | |
c1311ad4 | 14 | |
5be8b0a3 EV |
15 | #define EFI_COUT_MODE_2 2 |
16 | #define EFI_MAX_COUT_MODE 3 | |
17 | ||
18 | struct cout_mode { | |
19 | unsigned long columns; | |
20 | unsigned long rows; | |
21 | int present; | |
22 | }; | |
23 | ||
24 | static struct cout_mode efi_cout_modes[] = { | |
25 | /* EFI Mode 0 is 80x25 and always present */ | |
26 | { | |
27 | .columns = 80, | |
28 | .rows = 25, | |
29 | .present = 1, | |
30 | }, | |
31 | /* EFI Mode 1 is always 80x50 */ | |
32 | { | |
33 | .columns = 80, | |
34 | .rows = 50, | |
35 | .present = 0, | |
36 | }, | |
37 | /* Value are unknown until we query the console */ | |
38 | { | |
39 | .columns = 0, | |
40 | .rows = 0, | |
41 | .present = 0, | |
42 | }, | |
43 | }; | |
44 | ||
ebb4dd5b HS |
45 | const efi_guid_t efi_guid_text_output_protocol = |
46 | EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID; | |
47 | const efi_guid_t efi_guid_text_input_protocol = | |
48 | EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID; | |
c1311ad4 AG |
49 | |
50 | #define cESC '\x1b' | |
51 | #define ESC "\x1b" | |
52 | ||
5be8b0a3 | 53 | /* Default to mode 0 */ |
c1311ad4 | 54 | static struct simple_text_output_mode efi_con_mode = { |
5be8b0a3 | 55 | .max_mode = 1, |
c1311ad4 AG |
56 | .mode = 0, |
57 | .attribute = 0, | |
58 | .cursor_column = 0, | |
59 | .cursor_row = 0, | |
60 | .cursor_visible = 1, | |
61 | }; | |
62 | ||
62217295 HS |
63 | /* |
64 | * Receive and parse a reply from the terminal. | |
65 | * | |
66 | * @n: array of return values | |
67 | * @num: number of return values expected | |
68 | * @end_char: character indicating end of terminal message | |
69 | * @return: non-zero indicates error | |
70 | */ | |
71 | static int term_read_reply(int *n, int num, char end_char) | |
c1311ad4 AG |
72 | { |
73 | char c; | |
74 | int i = 0; | |
75 | ||
76 | c = getc(); | |
77 | if (c != cESC) | |
78 | return -1; | |
79 | c = getc(); | |
80 | if (c != '[') | |
81 | return -1; | |
82 | ||
83 | n[0] = 0; | |
84 | while (1) { | |
85 | c = getc(); | |
86 | if (c == ';') { | |
87 | i++; | |
62217295 | 88 | if (i >= num) |
c1311ad4 AG |
89 | return -1; |
90 | n[i] = 0; | |
91 | continue; | |
92 | } else if (c == end_char) { | |
93 | break; | |
94 | } else if (c > '9' || c < '0') { | |
95 | return -1; | |
96 | } | |
97 | ||
98 | /* Read one more decimal position */ | |
99 | n[i] *= 10; | |
100 | n[i] += c - '0'; | |
101 | } | |
62217295 HS |
102 | if (i != num - 1) |
103 | return -1; | |
c1311ad4 AG |
104 | |
105 | return 0; | |
106 | } | |
107 | ||
c1311ad4 AG |
108 | static efi_status_t EFIAPI efi_cout_output_string( |
109 | struct efi_simple_text_output_protocol *this, | |
3a45bc7f | 110 | const efi_string_t string) |
c1311ad4 | 111 | { |
3a45bc7f RC |
112 | struct simple_text_output_mode *con = &efi_con_mode; |
113 | struct cout_mode *mode = &efi_cout_modes[con->mode]; | |
ba7bd5c2 | 114 | char *buf, *pos; |
7ca7c3c0 | 115 | u16 *p; |
ba7bd5c2 | 116 | efi_status_t ret = EFI_SUCCESS; |
3a45bc7f | 117 | |
ba7bd5c2 | 118 | EFI_ENTRY("%p, %p", this, string); |
3a45bc7f | 119 | |
ba7bd5c2 HS |
120 | buf = malloc(utf16_utf8_strlen(string) + 1); |
121 | if (!buf) { | |
122 | ret = EFI_OUT_OF_RESOURCES; | |
123 | goto out; | |
124 | } | |
125 | pos = buf; | |
126 | utf16_utf8_strcpy(&pos, string); | |
3a45bc7f | 127 | fputs(stdout, buf); |
ba7bd5c2 | 128 | free(buf); |
3a45bc7f | 129 | |
7ca7c3c0 HS |
130 | /* |
131 | * Update the cursor position. | |
132 | * | |
133 | * The UEFI spec provides advance rules for U+0000, U+0008, U+000A, | |
134 | * and U000D. All other characters, including control characters | |
135 | * U+0007 (bel) and U+0009 (tab), have to increase the column by one. | |
136 | */ | |
137 | for (p = string; *p; ++p) { | |
3a45bc7f | 138 | switch (*p) { |
7ca7c3c0 HS |
139 | case '\b': /* U+0008, backspace */ |
140 | con->cursor_column = max(0, con->cursor_column - 1); | |
3a45bc7f | 141 | break; |
7ca7c3c0 | 142 | case '\n': /* U+000A, newline */ |
3a45bc7f RC |
143 | con->cursor_column = 0; |
144 | con->cursor_row++; | |
145 | break; | |
7ca7c3c0 HS |
146 | case '\r': /* U+000D, carriage-return */ |
147 | con->cursor_column = 0; | |
3a45bc7f | 148 | break; |
7ca7c3c0 HS |
149 | case 0xd800 ... 0xdbff: |
150 | /* | |
151 | * Ignore high surrogates, we do not want to count a | |
152 | * Unicode character twice. | |
153 | */ | |
3a45bc7f RC |
154 | break; |
155 | default: | |
156 | con->cursor_column++; | |
157 | break; | |
158 | } | |
159 | if (con->cursor_column >= mode->columns) { | |
160 | con->cursor_column = 0; | |
161 | con->cursor_row++; | |
c1311ad4 | 162 | } |
3a45bc7f | 163 | con->cursor_row = min(con->cursor_row, (s32)mode->rows - 1); |
c1311ad4 AG |
164 | } |
165 | ||
ba7bd5c2 HS |
166 | out: |
167 | return EFI_EXIT(ret); | |
c1311ad4 AG |
168 | } |
169 | ||
170 | static efi_status_t EFIAPI efi_cout_test_string( | |
171 | struct efi_simple_text_output_protocol *this, | |
3a45bc7f | 172 | const efi_string_t string) |
c1311ad4 AG |
173 | { |
174 | EFI_ENTRY("%p, %p", this, string); | |
175 | return EFI_EXIT(EFI_SUCCESS); | |
176 | } | |
177 | ||
5be8b0a3 EV |
178 | static bool cout_mode_matches(struct cout_mode *mode, int rows, int cols) |
179 | { | |
180 | if (!mode->present) | |
181 | return false; | |
182 | ||
183 | return (mode->rows == rows) && (mode->columns == cols); | |
184 | } | |
185 | ||
71cc25c3 RC |
186 | static int query_console_serial(int *rows, int *cols) |
187 | { | |
188 | /* Ask the terminal about its size */ | |
189 | int n[3]; | |
190 | u64 timeout; | |
191 | ||
192 | /* Empty input buffer */ | |
193 | while (tstc()) | |
194 | getc(); | |
195 | ||
196 | printf(ESC"[18t"); | |
197 | ||
198 | /* Check if we have a terminal that understands */ | |
199 | timeout = timer_get_us() + 1000000; | |
200 | while (!tstc()) | |
201 | if (timer_get_us() > timeout) | |
202 | return -1; | |
203 | ||
204 | /* Read {depth,rows,cols} */ | |
205 | if (term_read_reply(n, 3, 't')) | |
206 | return -1; | |
207 | ||
208 | *cols = n[2]; | |
209 | *rows = n[1]; | |
210 | ||
211 | return 0; | |
212 | } | |
213 | ||
a4aa7bef HS |
214 | /* |
215 | * Update the mode table. | |
216 | * | |
217 | * By default the only mode available is 80x25. If the console has at least 50 | |
218 | * lines, enable mode 80x50. If we can query the console size and it is neither | |
219 | * 80x25 nor 80x50, set it as an additional mode. | |
220 | */ | |
221 | static void query_console_size(void) | |
222 | { | |
223 | const char *stdout_name = env_get("stdout"); | |
80483b2a | 224 | int rows = 25, cols = 80; |
a4aa7bef HS |
225 | |
226 | if (stdout_name && !strcmp(stdout_name, "vidconsole") && | |
227 | IS_ENABLED(CONFIG_DM_VIDEO)) { | |
228 | struct stdio_dev *stdout_dev = | |
229 | stdio_get_by_name("vidconsole"); | |
230 | struct udevice *dev = stdout_dev->priv; | |
231 | struct vidconsole_priv *priv = | |
232 | dev_get_uclass_priv(dev); | |
233 | rows = priv->rows; | |
234 | cols = priv->cols; | |
235 | } else if (query_console_serial(&rows, &cols)) { | |
236 | return; | |
237 | } | |
238 | ||
239 | /* Test if we can have Mode 1 */ | |
240 | if (cols >= 80 && rows >= 50) { | |
241 | efi_cout_modes[1].present = 1; | |
242 | efi_con_mode.max_mode = 2; | |
243 | } | |
244 | ||
245 | /* | |
246 | * Install our mode as mode 2 if it is different | |
247 | * than mode 0 or 1 and set it as the currently selected mode | |
248 | */ | |
249 | if (!cout_mode_matches(&efi_cout_modes[0], rows, cols) && | |
250 | !cout_mode_matches(&efi_cout_modes[1], rows, cols)) { | |
251 | efi_cout_modes[EFI_COUT_MODE_2].columns = cols; | |
252 | efi_cout_modes[EFI_COUT_MODE_2].rows = rows; | |
253 | efi_cout_modes[EFI_COUT_MODE_2].present = 1; | |
254 | efi_con_mode.max_mode = EFI_MAX_COUT_MODE; | |
255 | efi_con_mode.mode = EFI_COUT_MODE_2; | |
256 | } | |
257 | } | |
258 | ||
c1311ad4 AG |
259 | static efi_status_t EFIAPI efi_cout_query_mode( |
260 | struct efi_simple_text_output_protocol *this, | |
261 | unsigned long mode_number, unsigned long *columns, | |
262 | unsigned long *rows) | |
263 | { | |
264 | EFI_ENTRY("%p, %ld, %p, %p", this, mode_number, columns, rows); | |
265 | ||
5be8b0a3 EV |
266 | if (mode_number >= efi_con_mode.max_mode) |
267 | return EFI_EXIT(EFI_UNSUPPORTED); | |
268 | ||
269 | if (efi_cout_modes[mode_number].present != 1) | |
270 | return EFI_EXIT(EFI_UNSUPPORTED); | |
271 | ||
c1311ad4 | 272 | if (columns) |
5be8b0a3 | 273 | *columns = efi_cout_modes[mode_number].columns; |
c1311ad4 | 274 | if (rows) |
5be8b0a3 | 275 | *rows = efi_cout_modes[mode_number].rows; |
c1311ad4 AG |
276 | |
277 | return EFI_EXIT(EFI_SUCCESS); | |
278 | } | |
279 | ||
280 | static efi_status_t EFIAPI efi_cout_set_mode( | |
281 | struct efi_simple_text_output_protocol *this, | |
282 | unsigned long mode_number) | |
283 | { | |
284 | EFI_ENTRY("%p, %ld", this, mode_number); | |
285 | ||
c1311ad4 | 286 | |
5be8b0a3 EV |
287 | if (mode_number > efi_con_mode.max_mode) |
288 | return EFI_EXIT(EFI_UNSUPPORTED); | |
289 | ||
290 | efi_con_mode.mode = mode_number; | |
291 | efi_con_mode.cursor_column = 0; | |
292 | efi_con_mode.cursor_row = 0; | |
293 | ||
294 | return EFI_EXIT(EFI_SUCCESS); | |
c1311ad4 AG |
295 | } |
296 | ||
2d5dc2a5 RC |
297 | static const struct { |
298 | unsigned int fg; | |
299 | unsigned int bg; | |
300 | } color[] = { | |
301 | { 30, 40 }, /* 0: black */ | |
302 | { 34, 44 }, /* 1: blue */ | |
303 | { 32, 42 }, /* 2: green */ | |
304 | { 36, 46 }, /* 3: cyan */ | |
305 | { 31, 41 }, /* 4: red */ | |
306 | { 35, 45 }, /* 5: magenta */ | |
307 | { 33, 43 }, /* 6: brown, map to yellow as edk2 does*/ | |
308 | { 37, 47 }, /* 7: light grey, map to white */ | |
309 | }; | |
310 | ||
311 | /* See EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.SetAttribute(). */ | |
c1311ad4 AG |
312 | static efi_status_t EFIAPI efi_cout_set_attribute( |
313 | struct efi_simple_text_output_protocol *this, | |
314 | unsigned long attribute) | |
315 | { | |
2d5dc2a5 RC |
316 | unsigned int bold = EFI_ATTR_BOLD(attribute); |
317 | unsigned int fg = EFI_ATTR_FG(attribute); | |
318 | unsigned int bg = EFI_ATTR_BG(attribute); | |
319 | ||
c1311ad4 AG |
320 | EFI_ENTRY("%p, %lx", this, attribute); |
321 | ||
2d5dc2a5 RC |
322 | if (attribute) |
323 | printf(ESC"[%u;%u;%um", bold, color[fg].fg, color[bg].bg); | |
324 | else | |
325 | printf(ESC"[0;37;40m"); | |
326 | ||
327 | return EFI_EXIT(EFI_SUCCESS); | |
c1311ad4 AG |
328 | } |
329 | ||
330 | static efi_status_t EFIAPI efi_cout_clear_screen( | |
331 | struct efi_simple_text_output_protocol *this) | |
332 | { | |
333 | EFI_ENTRY("%p", this); | |
334 | ||
335 | printf(ESC"[2J"); | |
e67ff94d HS |
336 | efi_con_mode.cursor_column = 0; |
337 | efi_con_mode.cursor_row = 0; | |
c1311ad4 AG |
338 | |
339 | return EFI_EXIT(EFI_SUCCESS); | |
340 | } | |
341 | ||
9d12daff HS |
342 | static efi_status_t EFIAPI efi_cout_reset( |
343 | struct efi_simple_text_output_protocol *this, | |
344 | char extended_verification) | |
345 | { | |
346 | EFI_ENTRY("%p, %d", this, extended_verification); | |
347 | ||
348 | /* Clear screen */ | |
349 | EFI_CALL(efi_cout_clear_screen(this)); | |
350 | /* Set default colors */ | |
351 | printf(ESC "[0;37;40m"); | |
352 | ||
353 | return EFI_EXIT(EFI_SUCCESS); | |
354 | } | |
355 | ||
c1311ad4 AG |
356 | static efi_status_t EFIAPI efi_cout_set_cursor_position( |
357 | struct efi_simple_text_output_protocol *this, | |
358 | unsigned long column, unsigned long row) | |
359 | { | |
360 | EFI_ENTRY("%p, %ld, %ld", this, column, row); | |
361 | ||
362 | printf(ESC"[%d;%df", (int)row, (int)column); | |
363 | efi_con_mode.cursor_column = column; | |
364 | efi_con_mode.cursor_row = row; | |
365 | ||
366 | return EFI_EXIT(EFI_SUCCESS); | |
367 | } | |
368 | ||
369 | static efi_status_t EFIAPI efi_cout_enable_cursor( | |
370 | struct efi_simple_text_output_protocol *this, | |
371 | bool enable) | |
372 | { | |
373 | EFI_ENTRY("%p, %d", this, enable); | |
374 | ||
375 | printf(ESC"[?25%c", enable ? 'h' : 'l'); | |
376 | ||
377 | return EFI_EXIT(EFI_SUCCESS); | |
378 | } | |
379 | ||
ebb4dd5b | 380 | struct efi_simple_text_output_protocol efi_con_out = { |
c1311ad4 AG |
381 | .reset = efi_cout_reset, |
382 | .output_string = efi_cout_output_string, | |
383 | .test_string = efi_cout_test_string, | |
384 | .query_mode = efi_cout_query_mode, | |
385 | .set_mode = efi_cout_set_mode, | |
386 | .set_attribute = efi_cout_set_attribute, | |
387 | .clear_screen = efi_cout_clear_screen, | |
388 | .set_cursor_position = efi_cout_set_cursor_position, | |
389 | .enable_cursor = efi_cout_enable_cursor, | |
390 | .mode = (void*)&efi_con_mode, | |
391 | }; | |
392 | ||
393 | static efi_status_t EFIAPI efi_cin_reset( | |
3e603ec7 | 394 | struct efi_simple_text_input_protocol *this, |
c1311ad4 AG |
395 | bool extended_verification) |
396 | { | |
397 | EFI_ENTRY("%p, %d", this, extended_verification); | |
4f187897 HS |
398 | |
399 | /* Empty input buffer */ | |
400 | while (tstc()) | |
401 | getc(); | |
402 | ||
403 | return EFI_EXIT(EFI_SUCCESS); | |
c1311ad4 AG |
404 | } |
405 | ||
0fb4169e HS |
406 | /* |
407 | * Analyze modifiers (shift, alt, ctrl) for function keys. | |
408 | * This gets called when we have already parsed CSI. | |
409 | * | |
410 | * @modifiers: bitmask (shift, alt, ctrl) | |
411 | * @return: the unmodified code | |
412 | */ | |
413 | static char skip_modifiers(int *modifiers) | |
414 | { | |
415 | char c, mod = 0, ret = 0; | |
416 | ||
417 | c = getc(); | |
418 | ||
419 | if (c != ';') { | |
420 | ret = c; | |
421 | if (c == '~') | |
422 | goto out; | |
423 | c = getc(); | |
424 | } | |
425 | for (;;) { | |
426 | switch (c) { | |
427 | case '0'...'9': | |
428 | mod *= 10; | |
429 | mod += c - '0'; | |
430 | /* fall through */ | |
431 | case ';': | |
432 | c = getc(); | |
433 | break; | |
434 | default: | |
435 | goto out; | |
436 | } | |
437 | } | |
438 | out: | |
439 | if (mod) | |
440 | --mod; | |
441 | if (modifiers) | |
442 | *modifiers = mod; | |
443 | if (!ret) | |
444 | ret = c; | |
445 | return ret; | |
446 | } | |
447 | ||
c1311ad4 | 448 | static efi_status_t EFIAPI efi_cin_read_key_stroke( |
3e603ec7 | 449 | struct efi_simple_text_input_protocol *this, |
c1311ad4 AG |
450 | struct efi_input_key *key) |
451 | { | |
452 | struct efi_input_key pressed_key = { | |
453 | .scan_code = 0, | |
454 | .unicode_char = 0, | |
455 | }; | |
456 | char ch; | |
457 | ||
458 | EFI_ENTRY("%p, %p", this, key); | |
459 | ||
460 | /* We don't do interrupts, so check for timers cooperatively */ | |
461 | efi_timer_check(); | |
462 | ||
463 | if (!tstc()) { | |
464 | /* No key pressed */ | |
465 | return EFI_EXIT(EFI_NOT_READY); | |
466 | } | |
467 | ||
468 | ch = getc(); | |
469 | if (ch == cESC) { | |
0fb4169e HS |
470 | /* |
471 | * Xterm Control Sequences | |
472 | * https://www.xfree86.org/4.8.0/ctlseqs.html | |
473 | */ | |
c1311ad4 AG |
474 | ch = getc(); |
475 | switch (ch) { | |
476 | case cESC: /* ESC */ | |
477 | pressed_key.scan_code = 23; | |
478 | break; | |
479 | case 'O': /* F1 - F4 */ | |
0fb4169e HS |
480 | ch = getc(); |
481 | /* skip modifiers */ | |
482 | if (ch <= '9') | |
483 | ch = getc(); | |
484 | pressed_key.scan_code = ch - 'P' + 11; | |
c1311ad4 AG |
485 | break; |
486 | case 'a'...'z': | |
487 | ch = ch - 'a'; | |
488 | break; | |
489 | case '[': | |
490 | ch = getc(); | |
491 | switch (ch) { | |
492 | case 'A'...'D': /* up, down right, left */ | |
493 | pressed_key.scan_code = ch - 'A' + 1; | |
494 | break; | |
495 | case 'F': /* End */ | |
496 | pressed_key.scan_code = 6; | |
497 | break; | |
498 | case 'H': /* Home */ | |
499 | pressed_key.scan_code = 5; | |
500 | break; | |
0fb4169e HS |
501 | case '1': |
502 | ch = skip_modifiers(NULL); | |
503 | switch (ch) { | |
504 | case '1'...'5': /* F1 - F5 */ | |
505 | pressed_key.scan_code = ch - '1' + 11; | |
506 | break; | |
507 | case '7'...'9': /* F6 - F8 */ | |
508 | pressed_key.scan_code = ch - '7' + 16; | |
509 | break; | |
510 | case 'A'...'D': /* up, down right, left */ | |
511 | pressed_key.scan_code = ch - 'A' + 1; | |
512 | break; | |
513 | case 'F': | |
514 | pressed_key.scan_code = 6; /* End */ | |
515 | break; | |
516 | case 'H': | |
517 | pressed_key.scan_code = 5; /* Home */ | |
518 | break; | |
519 | } | |
c1311ad4 | 520 | break; |
0fb4169e HS |
521 | case '2': |
522 | ch = skip_modifiers(NULL); | |
523 | switch (ch) { | |
524 | case '0'...'1': /* F9 - F10 */ | |
525 | pressed_key.scan_code = ch - '0' + 19; | |
526 | break; | |
527 | case '3'...'4': /* F11 - F12 */ | |
528 | pressed_key.scan_code = ch - '3' + 21; | |
529 | break; | |
530 | case '~': /* INS */ | |
531 | pressed_key.scan_code = 7; | |
532 | break; | |
533 | } | |
c1311ad4 AG |
534 | break; |
535 | case '3': /* DEL */ | |
536 | pressed_key.scan_code = 8; | |
0fb4169e HS |
537 | skip_modifiers(NULL); |
538 | break; | |
539 | case '5': /* PG UP */ | |
540 | pressed_key.scan_code = 9; | |
541 | skip_modifiers(NULL); | |
542 | break; | |
543 | case '6': /* PG DOWN */ | |
544 | pressed_key.scan_code = 10; | |
545 | skip_modifiers(NULL); | |
c1311ad4 AG |
546 | break; |
547 | } | |
548 | break; | |
549 | } | |
550 | } else if (ch == 0x7f) { | |
551 | /* Backspace */ | |
552 | ch = 0x08; | |
553 | } | |
0fb4169e HS |
554 | if (!pressed_key.scan_code) |
555 | pressed_key.unicode_char = ch; | |
c1311ad4 AG |
556 | *key = pressed_key; |
557 | ||
558 | return EFI_EXIT(EFI_SUCCESS); | |
559 | } | |
560 | ||
3e603ec7 | 561 | struct efi_simple_text_input_protocol efi_con_in = { |
c1311ad4 AG |
562 | .reset = efi_cin_reset, |
563 | .read_key_stroke = efi_cin_read_key_stroke, | |
564 | .wait_for_key = NULL, | |
565 | }; | |
91be9a77 HS |
566 | |
567 | static struct efi_event *console_timer_event; | |
568 | ||
ff925938 | 569 | static void EFIAPI efi_key_notify(struct efi_event *event, void *context) |
91be9a77 HS |
570 | { |
571 | } | |
572 | ||
9bc9664d HS |
573 | /* |
574 | * Notification function of the console timer event. | |
575 | * | |
576 | * event: console timer event | |
577 | * context: not used | |
578 | */ | |
ff925938 HS |
579 | static void EFIAPI efi_console_timer_notify(struct efi_event *event, |
580 | void *context) | |
91be9a77 HS |
581 | { |
582 | EFI_ENTRY("%p, %p", event, context); | |
9bc9664d HS |
583 | |
584 | /* Check if input is available */ | |
ca62a4f5 | 585 | if (tstc()) { |
9bc9664d | 586 | /* Queue the wait for key event */ |
e190e897 | 587 | efi_con_in.wait_for_key->is_signaled = true; |
9bc9664d HS |
588 | efi_signal_event(efi_con_in.wait_for_key, true); |
589 | } | |
91be9a77 HS |
590 | EFI_EXIT(EFI_SUCCESS); |
591 | } | |
592 | ||
593 | /* This gets called from do_bootefi_exec(). */ | |
594 | int efi_console_register(void) | |
595 | { | |
596 | efi_status_t r; | |
ebb4dd5b HS |
597 | struct efi_object *efi_console_output_obj; |
598 | struct efi_object *efi_console_input_obj; | |
a17e62cc | 599 | |
a4aa7bef HS |
600 | /* Set up mode information */ |
601 | query_console_size(); | |
602 | ||
ebb4dd5b | 603 | /* Create handles */ |
2074f700 | 604 | r = efi_create_handle((efi_handle_t *)&efi_console_output_obj); |
ebb4dd5b HS |
605 | if (r != EFI_SUCCESS) |
606 | goto out_of_memory; | |
40e3e757 | 607 | |
ebb4dd5b HS |
608 | r = efi_add_protocol(efi_console_output_obj->handle, |
609 | &efi_guid_text_output_protocol, &efi_con_out); | |
610 | if (r != EFI_SUCCESS) | |
611 | goto out_of_memory; | |
40e3e757 AG |
612 | systab.con_out_handle = efi_console_output_obj->handle; |
613 | systab.stderr_handle = efi_console_output_obj->handle; | |
614 | ||
2074f700 | 615 | r = efi_create_handle((efi_handle_t *)&efi_console_input_obj); |
ebb4dd5b HS |
616 | if (r != EFI_SUCCESS) |
617 | goto out_of_memory; | |
40e3e757 | 618 | |
ebb4dd5b HS |
619 | r = efi_add_protocol(efi_console_input_obj->handle, |
620 | &efi_guid_text_input_protocol, &efi_con_in); | |
621 | if (r != EFI_SUCCESS) | |
622 | goto out_of_memory; | |
40e3e757 | 623 | systab.con_in_handle = efi_console_input_obj->handle; |
a17e62cc | 624 | |
ebb4dd5b | 625 | /* Create console events */ |
b095f3c8 HS |
626 | r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK, efi_key_notify, |
627 | NULL, NULL, &efi_con_in.wait_for_key); | |
91be9a77 HS |
628 | if (r != EFI_SUCCESS) { |
629 | printf("ERROR: Failed to register WaitForKey event\n"); | |
630 | return r; | |
631 | } | |
632 | r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK, | |
b095f3c8 | 633 | efi_console_timer_notify, NULL, NULL, |
91be9a77 HS |
634 | &console_timer_event); |
635 | if (r != EFI_SUCCESS) { | |
636 | printf("ERROR: Failed to register console event\n"); | |
637 | return r; | |
638 | } | |
639 | /* 5000 ns cycle is sufficient for 2 MBaud */ | |
640 | r = efi_set_timer(console_timer_event, EFI_TIMER_PERIODIC, 50); | |
641 | if (r != EFI_SUCCESS) | |
642 | printf("ERROR: Failed to set console timer\n"); | |
643 | return r; | |
ebb4dd5b HS |
644 | out_of_memory: |
645 | printf("ERROR: Out of meemory\n"); | |
646 | return r; | |
91be9a77 | 647 | } |