]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/efi/libefi/efi_console.c
MFC r341101, r341231, r341276, r341329, r341433, r341780, r342054-r342055,
[FreeBSD/FreeBSD.git] / stand / efi / libefi / efi_console.c
1 /*-
2  * Copyright (c) 2000 Doug Rabson
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <efi.h>
31 #include <efilib.h>
32
33 #include "bootstrap.h"
34
35 static SIMPLE_TEXT_OUTPUT_INTERFACE     *conout;
36 static SIMPLE_INPUT_INTERFACE           *conin;
37
38 #ifdef TERM_EMU
39 #define DEFAULT_FGCOLOR EFI_LIGHTGRAY
40 #define DEFAULT_BGCOLOR EFI_BLACK
41
42 #define MAXARGS 8
43 static int args[MAXARGS], argc;
44 static int fg_c, bg_c, curx, cury;
45 static int esc;
46
47 void get_pos(int *x, int *y);
48 void curs_move(int *_x, int *_y, int x, int y);
49 static void CL(int);
50 void HO(void);
51 void end_term(void);
52 #endif
53
54 #define KEYBUFSZ 10
55 static unsigned keybuf[KEYBUFSZ];       /* keybuf for extended codes */
56 static int key_pending;
57
58 static void efi_cons_probe(struct console *);
59 static int efi_cons_init(int);
60 void efi_cons_putchar(int);
61 int efi_cons_getchar(void);
62 void efi_cons_efiputchar(int);
63 int efi_cons_poll(void);
64
65 struct console efi_console = {
66         "efi",
67         "EFI console",
68         C_WIDEOUT,
69         efi_cons_probe,
70         efi_cons_init,
71         efi_cons_putchar,
72         efi_cons_getchar,
73         efi_cons_poll
74 };
75
76 #ifdef TERM_EMU
77
78 /* Get cursor position. */
79 void
80 get_pos(int *x, int *y)
81 {
82         *x = conout->Mode->CursorColumn;
83         *y = conout->Mode->CursorRow;
84 }
85
86 /* Move cursor to x rows and y cols (0-based). */
87 void
88 curs_move(int *_x, int *_y, int x, int y)
89 {
90         conout->SetCursorPosition(conout, x, y);
91         if (_x != NULL)
92                 *_x = conout->Mode->CursorColumn;
93         if (_y != NULL)
94                 *_y = conout->Mode->CursorRow;
95 }
96
97 /* Clear internal state of the terminal emulation code. */
98 void
99 end_term(void)
100 {
101         esc = 0;
102         argc = -1;
103 }
104
105 #endif
106
107 static void
108 efi_cons_probe(struct console *cp)
109 {
110         conout = ST->ConOut;
111         conin = ST->ConIn;
112         cp->c_flags |= C_PRESENTIN | C_PRESENTOUT;
113 }
114
115 static int
116 efi_cons_init(int arg)
117 {
118 #ifdef TERM_EMU
119         conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR,
120             DEFAULT_BGCOLOR));
121         end_term();
122         get_pos(&curx, &cury);
123         curs_move(&curx, &cury, curx, cury);
124         fg_c = DEFAULT_FGCOLOR;
125         bg_c = DEFAULT_BGCOLOR;
126 #endif
127         conout->EnableCursor(conout, TRUE);
128         return 0;
129 }
130
131 static void
132 efi_cons_rawputchar(int c)
133 {
134         int i;
135         UINTN x, y;
136         conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
137
138         if (c == '\t')
139                 /* XXX lame tab expansion */
140                 for (i = 0; i < 8; i++)
141                         efi_cons_rawputchar(' ');
142         else {
143 #ifndef TERM_EMU
144                 if (c == '\n')
145                         efi_cons_efiputchar('\r');
146                 efi_cons_efiputchar(c);
147 #else
148                 switch (c) {
149                 case '\r':
150                         curx = 0;
151                         efi_cons_efiputchar('\r');
152                         return;
153                 case '\n':
154                         efi_cons_efiputchar('\n');
155                         efi_cons_efiputchar('\r');
156                         cury++;
157                         if (cury >= y)
158                                 cury--;
159                         curx = 0;
160                         return;
161                 case '\b':
162                         if (curx > 0) {
163                                 efi_cons_efiputchar('\b');
164                                 curx--;
165                         }
166                         return;
167                 default:
168                         efi_cons_efiputchar(c);
169                         curx++;
170                         if (curx > x-1) {
171                                 curx = 0;
172                                 cury++;
173                         }
174                         if (cury > y-1) {
175                                 curx = 0;
176                                 cury--;
177                         }
178                 }
179 #endif
180         }
181 }
182
183 #ifdef TERM_EMU
184 /* Gracefully exit ESC-sequence processing in case of misunderstanding. */
185 static void
186 bail_out(int c)
187 {
188         char buf[16], *ch;
189         int i;
190
191         if (esc) {
192                 efi_cons_rawputchar('\033');
193                 if (esc != '\033')
194                         efi_cons_rawputchar(esc);
195                 for (i = 0; i <= argc; ++i) {
196                         sprintf(buf, "%d", args[i]);
197                         ch = buf;
198                         while (*ch)
199                                 efi_cons_rawputchar(*ch++);
200                 }
201         }
202         efi_cons_rawputchar(c);
203         end_term();
204 }
205
206 /* Clear display from current position to end of screen. */
207 static void
208 CD(void) {
209         int i;
210         UINTN x, y;
211
212         get_pos(&curx, &cury);
213         if (curx == 0 && cury == 0) {
214                 conout->ClearScreen(conout);
215                 end_term();
216                 return;
217         }
218
219         conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
220         CL(0);  /* clear current line from cursor to end */
221         for (i = cury + 1; i < y-1; i++) {
222                 curs_move(NULL, NULL, 0, i);
223                 CL(0);
224         }
225         curs_move(NULL, NULL, curx, cury);
226         end_term();
227 }
228
229 /*
230  * Absolute cursor move to args[0] rows and args[1] columns
231  * (the coordinates are 1-based).
232  */
233 static void
234 CM(void)
235 {
236         if (args[0] > 0)
237                 args[0]--;
238         if (args[1] > 0)
239                 args[1]--;
240         curs_move(&curx, &cury, args[1], args[0]);
241         end_term();
242 }
243
244 /* Home cursor (left top corner), also called from mode command. */
245 void
246 HO(void)
247 {
248         argc = 1;
249         args[0] = args[1] = 1;
250         CM();
251 }
252
253 /* Clear line from current position to end of line */
254 static void
255 CL(int direction)
256 {
257         int i, len;
258         UINTN x, y;
259         CHAR16 *line;
260
261         conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
262         switch (direction) {
263         case 0:         /* from cursor to end */
264                 len = x - curx + 1;
265                 break;
266         case 1:         /* from beginning to cursor */
267                 len = curx;
268                 break;
269         case 2:         /* entire line */
270                 len = x;
271                 break;
272         default:        /* NOTREACHED */
273                 __unreachable();
274         }
275
276         if (cury == y - 1)
277                 len--;
278
279         line = malloc(len * sizeof (CHAR16));
280         if (line == NULL) {
281                 printf("out of memory\n");
282                 return;
283         }
284         for (i = 0; i < len; i++)
285                 line[i] = ' ';
286         line[len-1] = 0;
287
288         if (direction != 0)
289                 curs_move(NULL, NULL, 0, cury);
290
291         conout->OutputString(conout, line);
292         /* restore cursor position */
293         curs_move(NULL, NULL, curx, cury);
294         free(line);
295         end_term();
296 }
297
298 static void
299 get_arg(int c)
300 {
301         if (argc < 0)
302                 argc = 0;
303         args[argc] *= 10;
304         args[argc] += c - '0';
305 }
306
307 /* Emulate basic capabilities of cons25 terminal */
308 static void
309 efi_term_emu(int c)
310 {
311         static int ansi_col[] = {
312                 0, 4, 2, 6, 1, 5, 3, 7
313         };
314         int t, i;
315
316         switch (esc) {
317         case 0:
318                 switch (c) {
319                 case '\033':
320                         esc = c;
321                         break;
322                 default:
323                         efi_cons_rawputchar(c);
324                         break;
325                 }
326                 break;
327         case '\033':
328                 switch (c) {
329                 case '[':
330                         esc = c;
331                         args[0] = 0;
332                         argc = -1;
333                         break;
334                 default:
335                         bail_out(c);
336                         break;
337                 }
338                 break;
339         case '[':
340                 switch (c) {
341                 case ';':
342                         if (argc < 0)
343                                 argc = 0;
344                         else if (argc + 1 >= MAXARGS)
345                                 bail_out(c);
346                         else
347                                 args[++argc] = 0;
348                         break;
349                 case 'H':               /* ho = \E[H */
350                         if (argc < 0)
351                                 HO();
352                         else if (argc == 1)
353                                 CM();
354                         else
355                                 bail_out(c);
356                         break;
357                 case 'J':               /* cd = \E[J */
358                         if (argc < 0)
359                                 CD();
360                         else
361                                 bail_out(c);
362                         break;
363                 case 'm':
364                         if (argc < 0) {
365                                 fg_c = DEFAULT_FGCOLOR;
366                                 bg_c = DEFAULT_BGCOLOR;
367                         }
368                         for (i = 0; i <= argc; ++i) {
369                                 switch (args[i]) {
370                                 case 0:         /* back to normal */
371                                         fg_c = DEFAULT_FGCOLOR;
372                                         bg_c = DEFAULT_BGCOLOR;
373                                         break;
374                                 case 1:         /* bold */
375                                         fg_c |= 0x8;
376                                         break;
377                                 case 4:         /* underline */
378                                 case 5:         /* blink */
379                                         bg_c |= 0x8;
380                                         break;
381                                 case 7:         /* reverse */
382                                         t = fg_c;
383                                         fg_c = bg_c;
384                                         bg_c = t;
385                                         break;
386                                 case 22:        /* normal intensity */
387                                         fg_c &= ~0x8;
388                                         break;
389                                 case 24:        /* not underline */
390                                 case 25:        /* not blinking */
391                                         bg_c &= ~0x8;
392                                         break;
393                                 case 30: case 31: case 32: case 33:
394                                 case 34: case 35: case 36: case 37:
395                                         fg_c = ansi_col[args[i] - 30];
396                                         break;
397                                 case 39:        /* normal */
398                                         fg_c = DEFAULT_FGCOLOR;
399                                         break;
400                                 case 40: case 41: case 42: case 43:
401                                 case 44: case 45: case 46: case 47:
402                                         bg_c = ansi_col[args[i] - 40];
403                                         break;
404                                 case 49:        /* normal */
405                                         bg_c = DEFAULT_BGCOLOR;
406                                         break;
407                                 }
408                         }
409                         conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c));
410                         end_term();
411                         break;
412                 default:
413                         if (isdigit(c))
414                                 get_arg(c);
415                         else
416                                 bail_out(c);
417                         break;
418                 }
419                 break;
420         default:
421                 bail_out(c);
422                 break;
423         }
424 }
425 #else
426 void
427 HO(void)
428 {
429 }
430 #endif
431
432 void
433 efi_cons_putchar(int c)
434 {
435 #ifdef TERM_EMU
436         efi_term_emu(c);
437 #else
438         efi_cons_rawputchar(c);
439 #endif
440 }
441
442 static int
443 keybuf_getchar(void)
444 {
445         int i, c = 0;
446
447         for (i = 0; i < KEYBUFSZ; i++) {
448                 if (keybuf[i] != 0) {
449                         c = keybuf[i];
450                         keybuf[i] = 0;
451                         break;
452                 }
453         }
454
455         return (c);
456 }
457
458 static bool
459 keybuf_ischar(void)
460 {
461         int i;
462
463         for (i = 0; i < KEYBUFSZ; i++) {
464                 if (keybuf[i] != 0)
465                         return (true);
466         }
467         return (false);
468 }
469
470 /*
471  * We are not reading input before keybuf is empty, so we are safe
472  * just to fill keybuf from the beginning.
473  */
474 static void
475 keybuf_inschar(EFI_INPUT_KEY *key)
476 {
477
478         switch (key->ScanCode) {
479         case 0x1: /* UP */
480                 keybuf[0] = 0x1b;       /* esc */
481                 keybuf[1] = '[';
482                 keybuf[2] = 'A';
483                 break;
484         case 0x2: /* DOWN */
485                 keybuf[0] = 0x1b;       /* esc */
486                 keybuf[1] = '[';
487                 keybuf[2] = 'B';
488                 break;
489         case 0x3: /* RIGHT */
490                 keybuf[0] = 0x1b;       /* esc */
491                 keybuf[1] = '[';
492                 keybuf[2] = 'C';
493                 break;
494         case 0x4: /* LEFT */
495                 keybuf[0] = 0x1b;       /* esc */
496                 keybuf[1] = '[';
497                 keybuf[2] = 'D';
498                 break;
499         case 0x17:
500                 keybuf[0] = 0x1b;       /* esc */
501                 break;
502         default:
503                 keybuf[0] = key->UnicodeChar;
504                 break;
505         }
506 }
507
508 static bool
509 efi_readkey(void)
510 {
511         EFI_STATUS status;
512         EFI_INPUT_KEY key;
513
514         status = conin->ReadKeyStroke(conin, &key);
515         if (status == EFI_SUCCESS) {
516                 keybuf_inschar(&key);
517                 return (true);
518         }
519         return (false);
520 }
521
522 int
523 efi_cons_getchar(void)
524 {
525         int c;
526
527         if ((c = keybuf_getchar()) != 0)
528                 return (c);
529
530         key_pending = 0;
531
532         if (efi_readkey())
533                 return (keybuf_getchar());
534
535         return (-1);
536 }
537
538 int
539 efi_cons_poll(void)
540 {
541
542         if (keybuf_ischar() || key_pending)
543                 return (1);
544
545         /*
546          * Some EFI implementation (u-boot for example) do not support
547          * WaitForKey().
548          * CheckEvent() can clear the signaled state.
549          */
550         if (conin->WaitForKey == NULL)
551                 key_pending = efi_readkey();
552         else
553                 key_pending = BS->CheckEvent(conin->WaitForKey) == EFI_SUCCESS;
554
555         return (key_pending);
556 }
557
558 /* Plain direct access to EFI OutputString(). */
559 void
560 efi_cons_efiputchar(int c)
561 {
562         CHAR16 buf[2];
563
564         /*
565          * translate box chars to unicode
566          */
567         switch (c) {
568         /* single frame */
569         case 0xb3: buf[0] = BOXDRAW_VERTICAL; break;
570         case 0xbf: buf[0] = BOXDRAW_DOWN_LEFT; break;
571         case 0xc0: buf[0] = BOXDRAW_UP_RIGHT; break;
572         case 0xc4: buf[0] = BOXDRAW_HORIZONTAL; break;
573         case 0xda: buf[0] = BOXDRAW_DOWN_RIGHT; break;
574         case 0xd9: buf[0] = BOXDRAW_UP_LEFT; break;
575
576         /* double frame */
577         case 0xba: buf[0] = BOXDRAW_DOUBLE_VERTICAL; break;
578         case 0xbb: buf[0] = BOXDRAW_DOUBLE_DOWN_LEFT; break;
579         case 0xbc: buf[0] = BOXDRAW_DOUBLE_UP_LEFT; break;
580         case 0xc8: buf[0] = BOXDRAW_DOUBLE_UP_RIGHT; break;
581         case 0xc9: buf[0] = BOXDRAW_DOUBLE_DOWN_RIGHT; break;
582         case 0xcd: buf[0] = BOXDRAW_DOUBLE_HORIZONTAL; break;
583
584         default:
585                 buf[0] = c;
586         }
587         buf[1] = 0;     /* terminate string */
588
589         conout->OutputString(conout, buf);
590 }