mx35: adjust default environment for flea3 board
[oweals/u-boot.git] / lib / efi_loader / efi_console.c
1 /*
2  *  EFI application console interface
3  *
4  *  Copyright (c) 2016 Alexander Graf
5  *
6  *  SPDX-License-Identifier:     GPL-2.0+
7  */
8
9 #include <common.h>
10 #include <efi_loader.h>
11
12 /* If we can't determine the console size, default to 80x24 */
13 static int console_columns = 80;
14 static int console_rows = 24;
15 static bool console_size_queried;
16
17 const efi_guid_t efi_guid_console_control = CONSOLE_CONTROL_GUID;
18
19 #define cESC '\x1b'
20 #define ESC "\x1b"
21
22 static efi_status_t EFIAPI efi_cin_get_mode(
23                         struct efi_console_control_protocol *this,
24                         int *mode, char *uga_exists, char *std_in_locked)
25 {
26         EFI_ENTRY("%p, %p, %p, %p", this, mode, uga_exists, std_in_locked);
27
28         if (mode)
29                 *mode = EFI_CONSOLE_MODE_TEXT;
30         if (uga_exists)
31                 *uga_exists = 0;
32         if (std_in_locked)
33                 *std_in_locked = 0;
34
35         return EFI_EXIT(EFI_SUCCESS);
36 }
37
38 static efi_status_t EFIAPI efi_cin_set_mode(
39                         struct efi_console_control_protocol *this, int mode)
40 {
41         EFI_ENTRY("%p, %d", this, mode);
42         return EFI_EXIT(EFI_UNSUPPORTED);
43 }
44
45 static efi_status_t EFIAPI efi_cin_lock_std_in(
46                         struct efi_console_control_protocol *this,
47                         uint16_t *password)
48 {
49         EFI_ENTRY("%p, %p", this, password);
50         return EFI_EXIT(EFI_UNSUPPORTED);
51 }
52
53 const struct efi_console_control_protocol efi_console_control = {
54         .get_mode = efi_cin_get_mode,
55         .set_mode = efi_cin_set_mode,
56         .lock_std_in = efi_cin_lock_std_in,
57 };
58
59 static struct simple_text_output_mode efi_con_mode = {
60         .max_mode = 0,
61         .mode = 0,
62         .attribute = 0,
63         .cursor_column = 0,
64         .cursor_row = 0,
65         .cursor_visible = 1,
66 };
67
68 static int term_read_reply(int *n, int maxnum, char end_char)
69 {
70         char c;
71         int i = 0;
72
73         c = getc();
74         if (c != cESC)
75                 return -1;
76         c = getc();
77         if (c != '[')
78                 return -1;
79
80         n[0] = 0;
81         while (1) {
82                 c = getc();
83                 if (c == ';') {
84                         i++;
85                         if (i >= maxnum)
86                                 return -1;
87                         n[i] = 0;
88                         continue;
89                 } else if (c == end_char) {
90                         break;
91                 } else if (c > '9' || c < '0') {
92                         return -1;
93                 }
94
95                 /* Read one more decimal position */
96                 n[i] *= 10;
97                 n[i] += c - '0';
98         }
99
100         return 0;
101 }
102
103 static efi_status_t EFIAPI efi_cout_reset(
104                         struct efi_simple_text_output_protocol *this,
105                         char extended_verification)
106 {
107         EFI_ENTRY("%p, %d", this, extended_verification);
108         return EFI_EXIT(EFI_UNSUPPORTED);
109 }
110
111 static void print_unicode_in_utf8(u16 c)
112 {
113         char utf8[4] = { 0 };
114         char *b = utf8;
115
116         if (c < 0x80) {
117                 *(b++) = c;
118         } else if (c < 0x800) {
119                 *(b++) = 192 + c / 64;
120                 *(b++) = 128 + c % 64;
121         } else {
122                 *(b++) = 224 + c / 4096;
123                 *(b++) = 128 + c / 64 % 64;
124                 *(b++) = 128 + c % 64;
125         }
126
127         puts(utf8);
128 }
129
130 static efi_status_t EFIAPI efi_cout_output_string(
131                         struct efi_simple_text_output_protocol *this,
132                         const unsigned short *string)
133 {
134         u16 ch;
135
136         EFI_ENTRY("%p, %p", this, string);
137         for (;(ch = *string); string++) {
138                 print_unicode_in_utf8(ch);
139                 efi_con_mode.cursor_column++;
140                 if (ch == '\n') {
141                         efi_con_mode.cursor_column = 1;
142                         efi_con_mode.cursor_row++;
143                 } else if (efi_con_mode.cursor_column > console_columns) {
144                         efi_con_mode.cursor_column = 1;
145                         efi_con_mode.cursor_row++;
146                 }
147                 if (efi_con_mode.cursor_row > console_rows) {
148                         efi_con_mode.cursor_row = console_rows;
149                 }
150         }
151
152         return EFI_EXIT(EFI_SUCCESS);
153 }
154
155 static efi_status_t EFIAPI efi_cout_test_string(
156                         struct efi_simple_text_output_protocol *this,
157                         const unsigned short *string)
158 {
159         EFI_ENTRY("%p, %p", this, string);
160         return EFI_EXIT(EFI_SUCCESS);
161 }
162
163 static efi_status_t EFIAPI efi_cout_query_mode(
164                         struct efi_simple_text_output_protocol *this,
165                         unsigned long mode_number, unsigned long *columns,
166                         unsigned long *rows)
167 {
168         EFI_ENTRY("%p, %ld, %p, %p", this, mode_number, columns, rows);
169
170         if (!console_size_queried) {
171                 /* Ask the terminal about its size */
172                 int n[3];
173                 u64 timeout;
174
175                 console_size_queried = true;
176
177                 /* Empty input buffer */
178                 while (tstc())
179                         getc();
180
181                 printf(ESC"[18t");
182
183                 /* Check if we have a terminal that understands */
184                 timeout = timer_get_us() + 1000000;
185                 while (!tstc())
186                         if (timer_get_us() > timeout)
187                                 goto out;
188
189                 /* Read {depth,rows,cols} */
190                 if (term_read_reply(n, 3, 't')) {
191                         goto out;
192                 }
193
194                 console_columns = n[2];
195                 console_rows = n[1];
196         }
197
198 out:
199         if (columns)
200                 *columns = console_columns;
201         if (rows)
202                 *rows = console_rows;
203
204         return EFI_EXIT(EFI_SUCCESS);
205 }
206
207 static efi_status_t EFIAPI efi_cout_set_mode(
208                         struct efi_simple_text_output_protocol *this,
209                         unsigned long mode_number)
210 {
211         EFI_ENTRY("%p, %ld", this, mode_number);
212
213         /* We only support text output for now */
214         if (mode_number == EFI_CONSOLE_MODE_TEXT)
215                 return EFI_EXIT(EFI_SUCCESS);
216
217         return EFI_EXIT(EFI_UNSUPPORTED);
218 }
219
220 static efi_status_t EFIAPI efi_cout_set_attribute(
221                         struct efi_simple_text_output_protocol *this,
222                         unsigned long attribute)
223 {
224         EFI_ENTRY("%p, %lx", this, attribute);
225
226         /* Just ignore attributes (colors) for now */
227         return EFI_EXIT(EFI_UNSUPPORTED);
228 }
229
230 static efi_status_t EFIAPI efi_cout_clear_screen(
231                         struct efi_simple_text_output_protocol *this)
232 {
233         EFI_ENTRY("%p", this);
234
235         printf(ESC"[2J");
236
237         return EFI_EXIT(EFI_SUCCESS);
238 }
239
240 static efi_status_t EFIAPI efi_cout_set_cursor_position(
241                         struct efi_simple_text_output_protocol *this,
242                         unsigned long column, unsigned long row)
243 {
244         EFI_ENTRY("%p, %ld, %ld", this, column, row);
245
246         printf(ESC"[%d;%df", (int)row, (int)column);
247         efi_con_mode.cursor_column = column;
248         efi_con_mode.cursor_row = row;
249
250         return EFI_EXIT(EFI_SUCCESS);
251 }
252
253 static efi_status_t EFIAPI efi_cout_enable_cursor(
254                         struct efi_simple_text_output_protocol *this,
255                         bool enable)
256 {
257         EFI_ENTRY("%p, %d", this, enable);
258
259         printf(ESC"[?25%c", enable ? 'h' : 'l');
260
261         return EFI_EXIT(EFI_SUCCESS);
262 }
263
264 const struct efi_simple_text_output_protocol efi_con_out = {
265         .reset = efi_cout_reset,
266         .output_string = efi_cout_output_string,
267         .test_string = efi_cout_test_string,
268         .query_mode = efi_cout_query_mode,
269         .set_mode = efi_cout_set_mode,
270         .set_attribute = efi_cout_set_attribute,
271         .clear_screen = efi_cout_clear_screen,
272         .set_cursor_position = efi_cout_set_cursor_position,
273         .enable_cursor = efi_cout_enable_cursor,
274         .mode = (void*)&efi_con_mode,
275 };
276
277 static efi_status_t EFIAPI efi_cin_reset(
278                         struct efi_simple_input_interface *this,
279                         bool extended_verification)
280 {
281         EFI_ENTRY("%p, %d", this, extended_verification);
282         return EFI_EXIT(EFI_UNSUPPORTED);
283 }
284
285 static efi_status_t EFIAPI efi_cin_read_key_stroke(
286                         struct efi_simple_input_interface *this,
287                         struct efi_input_key *key)
288 {
289         struct efi_input_key pressed_key = {
290                 .scan_code = 0,
291                 .unicode_char = 0,
292         };
293         char ch;
294
295         EFI_ENTRY("%p, %p", this, key);
296
297         /* We don't do interrupts, so check for timers cooperatively */
298         efi_timer_check();
299
300         if (!tstc()) {
301                 /* No key pressed */
302                 return EFI_EXIT(EFI_NOT_READY);
303         }
304
305         ch = getc();
306         if (ch == cESC) {
307                 /* Escape Sequence */
308                 ch = getc();
309                 switch (ch) {
310                 case cESC: /* ESC */
311                         pressed_key.scan_code = 23;
312                         break;
313                 case 'O': /* F1 - F4 */
314                         pressed_key.scan_code = getc() - 'P' + 11;
315                         break;
316                 case 'a'...'z':
317                         ch = ch - 'a';
318                         break;
319                 case '[':
320                         ch = getc();
321                         switch (ch) {
322                         case 'A'...'D': /* up, down right, left */
323                                 pressed_key.scan_code = ch - 'A' + 1;
324                                 break;
325                         case 'F': /* End */
326                                 pressed_key.scan_code = 6;
327                                 break;
328                         case 'H': /* Home */
329                                 pressed_key.scan_code = 5;
330                                 break;
331                         case '1': /* F5 - F8 */
332                                 pressed_key.scan_code = getc() - '0' + 11;
333                                 getc();
334                                 break;
335                         case '2': /* F9 - F12 */
336                                 pressed_key.scan_code = getc() - '0' + 19;
337                                 getc();
338                                 break;
339                         case '3': /* DEL */
340                                 pressed_key.scan_code = 8;
341                                 getc();
342                                 break;
343                         }
344                         break;
345                 }
346         } else if (ch == 0x7f) {
347                 /* Backspace */
348                 ch = 0x08;
349         }
350         pressed_key.unicode_char = ch;
351         *key = pressed_key;
352
353         return EFI_EXIT(EFI_SUCCESS);
354 }
355
356 const struct efi_simple_input_interface efi_con_in = {
357         .reset = efi_cin_reset,
358         .read_key_stroke = efi_cin_read_key_stroke,
359         .wait_for_key = NULL,
360 };