]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/efi/libefi/efi_console.c
loader: no-TERM_EMU is broken now
[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                 int n;
140
141                 n = 8 - ((conout->Mode->CursorColumn + 8) % 8);
142                 for (i = 0; i < n; i++)
143                         efi_cons_rawputchar(' ');
144         } else {
145 #ifndef TERM_EMU
146                 if (c == '\n')
147                         efi_cons_efiputchar('\r');
148                 efi_cons_efiputchar(c);
149 #else
150                 switch (c) {
151                 case '\r':
152                         curx = 0;
153                         efi_cons_efiputchar('\r');
154                         return;
155                 case '\n':
156                         efi_cons_efiputchar('\n');
157                         efi_cons_efiputchar('\r');
158                         cury++;
159                         if (cury >= y)
160                                 cury--;
161                         curx = 0;
162                         return;
163                 case '\b':
164                         if (curx > 0) {
165                                 efi_cons_efiputchar('\b');
166                                 curx--;
167                         }
168                         return;
169                 default:
170                         efi_cons_efiputchar(c);
171                         curx++;
172                         if (curx > x-1) {
173                                 curx = 0;
174                                 cury++;
175                         }
176                         if (cury > y-1) {
177                                 curx = 0;
178                                 cury--;
179                         }
180                 }
181 #endif
182         }
183 }
184
185 #ifdef TERM_EMU
186 /* Gracefully exit ESC-sequence processing in case of misunderstanding. */
187 static void
188 bail_out(int c)
189 {
190         char buf[16], *ch;
191         int i;
192
193         if (esc) {
194                 efi_cons_rawputchar('\033');
195                 if (esc != '\033')
196                         efi_cons_rawputchar(esc);
197                 for (i = 0; i <= argc; ++i) {
198                         sprintf(buf, "%d", args[i]);
199                         ch = buf;
200                         while (*ch)
201                                 efi_cons_rawputchar(*ch++);
202                 }
203         }
204         efi_cons_rawputchar(c);
205         end_term();
206 }
207
208 /* Clear display from current position to end of screen. */
209 static void
210 CD(void) {
211         int i;
212         UINTN x, y;
213
214         get_pos(&curx, &cury);
215         if (curx == 0 && cury == 0) {
216                 conout->ClearScreen(conout);
217                 end_term();
218                 return;
219         }
220
221         conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
222         CL(0);  /* clear current line from cursor to end */
223         for (i = cury + 1; i < y-1; i++) {
224                 curs_move(NULL, NULL, 0, i);
225                 CL(0);
226         }
227         curs_move(NULL, NULL, curx, cury);
228         end_term();
229 }
230
231 /*
232  * Absolute cursor move to args[0] rows and args[1] columns
233  * (the coordinates are 1-based).
234  */
235 static void
236 CM(void)
237 {
238         if (args[0] > 0)
239                 args[0]--;
240         if (args[1] > 0)
241                 args[1]--;
242         curs_move(&curx, &cury, args[1], args[0]);
243         end_term();
244 }
245
246 /* Home cursor (left top corner), also called from mode command. */
247 void
248 HO(void)
249 {
250         argc = 1;
251         args[0] = args[1] = 1;
252         CM();
253 }
254
255 /* Clear line from current position to end of line */
256 static void
257 CL(int direction)
258 {
259         int i, len;
260         UINTN x, y;
261         CHAR16 *line;
262
263         conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
264         switch (direction) {
265         case 0:         /* from cursor to end */
266                 len = x - curx + 1;
267                 break;
268         case 1:         /* from beginning to cursor */
269                 len = curx;
270                 break;
271         case 2:         /* entire line */
272                 len = x;
273                 break;
274         default:        /* NOTREACHED */
275                 __unreachable();
276         }
277
278         if (cury == y - 1)
279                 len--;
280
281         line = malloc(len * sizeof (CHAR16));
282         if (line == NULL) {
283                 printf("out of memory\n");
284                 return;
285         }
286         for (i = 0; i < len; i++)
287                 line[i] = ' ';
288         line[len-1] = 0;
289
290         if (direction != 0)
291                 curs_move(NULL, NULL, 0, cury);
292
293         conout->OutputString(conout, line);
294         /* restore cursor position */
295         curs_move(NULL, NULL, curx, cury);
296         free(line);
297         end_term();
298 }
299
300 static void
301 get_arg(int c)
302 {
303         if (argc < 0)
304                 argc = 0;
305         args[argc] *= 10;
306         args[argc] += c - '0';
307 }
308
309 /* Emulate basic capabilities of cons25 terminal */
310 static void
311 efi_term_emu(int c)
312 {
313         static int ansi_col[] = {
314                 0, 4, 2, 6, 1, 5, 3, 7
315         };
316         int t, i;
317
318         switch (esc) {
319         case 0:
320                 switch (c) {
321                 case '\033':
322                         esc = c;
323                         break;
324                 default:
325                         efi_cons_rawputchar(c);
326                         break;
327                 }
328                 break;
329         case '\033':
330                 switch (c) {
331                 case '[':
332                         esc = c;
333                         args[0] = 0;
334                         argc = -1;
335                         break;
336                 default:
337                         bail_out(c);
338                         break;
339                 }
340                 break;
341         case '[':
342                 switch (c) {
343                 case ';':
344                         if (argc < 0)
345                                 argc = 0;
346                         else if (argc + 1 >= MAXARGS)
347                                 bail_out(c);
348                         else
349                                 args[++argc] = 0;
350                         break;
351                 case 'H':               /* ho = \E[H */
352                         if (argc < 0)
353                                 HO();
354                         else if (argc == 1)
355                                 CM();
356                         else
357                                 bail_out(c);
358                         break;
359                 case 'J':               /* cd = \E[J */
360                         if (argc < 0)
361                                 CD();
362                         else
363                                 bail_out(c);
364                         break;
365                 case 'm':
366                         if (argc < 0) {
367                                 fg_c = DEFAULT_FGCOLOR;
368                                 bg_c = DEFAULT_BGCOLOR;
369                         }
370                         for (i = 0; i <= argc; ++i) {
371                                 switch (args[i]) {
372                                 case 0:         /* back to normal */
373                                         fg_c = DEFAULT_FGCOLOR;
374                                         bg_c = DEFAULT_BGCOLOR;
375                                         break;
376                                 case 1:         /* bold */
377                                         fg_c |= 0x8;
378                                         break;
379                                 case 4:         /* underline */
380                                 case 5:         /* blink */
381                                         bg_c |= 0x8;
382                                         break;
383                                 case 7:         /* reverse */
384                                         t = fg_c;
385                                         fg_c = bg_c;
386                                         bg_c = t;
387                                         break;
388                                 case 22:        /* normal intensity */
389                                         fg_c &= ~0x8;
390                                         break;
391                                 case 24:        /* not underline */
392                                 case 25:        /* not blinking */
393                                         bg_c &= ~0x8;
394                                         break;
395                                 case 30: case 31: case 32: case 33:
396                                 case 34: case 35: case 36: case 37:
397                                         fg_c = ansi_col[args[i] - 30];
398                                         break;
399                                 case 39:        /* normal */
400                                         fg_c = DEFAULT_FGCOLOR;
401                                         break;
402                                 case 40: case 41: case 42: case 43:
403                                 case 44: case 45: case 46: case 47:
404                                         bg_c = ansi_col[args[i] - 40];
405                                         break;
406                                 case 49:        /* normal */
407                                         bg_c = DEFAULT_BGCOLOR;
408                                         break;
409                                 }
410                         }
411                         conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c));
412                         end_term();
413                         break;
414                 default:
415                         if (isdigit(c))
416                                 get_arg(c);
417                         else
418                                 bail_out(c);
419                         break;
420                 }
421                 break;
422         default:
423                 bail_out(c);
424                 break;
425         }
426 }
427 #else
428 void
429 HO(void)
430 {
431 }
432 #endif
433
434 void
435 efi_cons_putchar(int c)
436 {
437 #ifdef TERM_EMU
438         efi_term_emu(c);
439 #else
440         efi_cons_rawputchar(c);
441 #endif
442 }
443
444 static int
445 keybuf_getchar(void)
446 {
447         int i, c = 0;
448
449         for (i = 0; i < KEYBUFSZ; i++) {
450                 if (keybuf[i] != 0) {
451                         c = keybuf[i];
452                         keybuf[i] = 0;
453                         break;
454                 }
455         }
456
457         return (c);
458 }
459
460 static bool
461 keybuf_ischar(void)
462 {
463         int i;
464
465         for (i = 0; i < KEYBUFSZ; i++) {
466                 if (keybuf[i] != 0)
467                         return (true);
468         }
469         return (false);
470 }
471
472 /*
473  * We are not reading input before keybuf is empty, so we are safe
474  * just to fill keybuf from the beginning.
475  */
476 static void
477 keybuf_inschar(EFI_INPUT_KEY *key)
478 {
479
480         switch (key->ScanCode) {
481         case 0x1: /* UP */
482                 keybuf[0] = 0x1b;       /* esc */
483                 keybuf[1] = '[';
484                 keybuf[2] = 'A';
485                 break;
486         case 0x2: /* DOWN */
487                 keybuf[0] = 0x1b;       /* esc */
488                 keybuf[1] = '[';
489                 keybuf[2] = 'B';
490                 break;
491         case 0x3: /* RIGHT */
492                 keybuf[0] = 0x1b;       /* esc */
493                 keybuf[1] = '[';
494                 keybuf[2] = 'C';
495                 break;
496         case 0x4: /* LEFT */
497                 keybuf[0] = 0x1b;       /* esc */
498                 keybuf[1] = '[';
499                 keybuf[2] = 'D';
500                 break;
501         case 0x17:
502                 keybuf[0] = 0x1b;       /* esc */
503                 break;
504         default:
505                 keybuf[0] = key->UnicodeChar;
506                 break;
507         }
508 }
509
510 static bool
511 efi_readkey(void)
512 {
513         EFI_STATUS status;
514         EFI_INPUT_KEY key;
515
516         status = conin->ReadKeyStroke(conin, &key);
517         if (status == EFI_SUCCESS) {
518                 keybuf_inschar(&key);
519                 return (true);
520         }
521         return (false);
522 }
523
524 int
525 efi_cons_getchar(void)
526 {
527         int c;
528
529         if ((c = keybuf_getchar()) != 0)
530                 return (c);
531
532         key_pending = 0;
533
534         if (efi_readkey())
535                 return (keybuf_getchar());
536
537         return (-1);
538 }
539
540 int
541 efi_cons_poll(void)
542 {
543
544         if (keybuf_ischar() || key_pending)
545                 return (1);
546
547         /*
548          * Some EFI implementation (u-boot for example) do not support
549          * WaitForKey().
550          * CheckEvent() can clear the signaled state.
551          */
552         if (conin->WaitForKey == NULL)
553                 key_pending = efi_readkey();
554         else
555                 key_pending = BS->CheckEvent(conin->WaitForKey) == EFI_SUCCESS;
556
557         return (key_pending);
558 }
559
560 /* Plain direct access to EFI OutputString(). */
561 void
562 efi_cons_efiputchar(int c)
563 {
564         CHAR16 buf[2];
565
566         /*
567          * translate box chars to unicode
568          */
569         switch (c) {
570         /* single frame */
571         case 0xb3: buf[0] = BOXDRAW_VERTICAL; break;
572         case 0xbf: buf[0] = BOXDRAW_DOWN_LEFT; break;
573         case 0xc0: buf[0] = BOXDRAW_UP_RIGHT; break;
574         case 0xc4: buf[0] = BOXDRAW_HORIZONTAL; break;
575         case 0xda: buf[0] = BOXDRAW_DOWN_RIGHT; break;
576         case 0xd9: buf[0] = BOXDRAW_UP_LEFT; break;
577
578         /* double frame */
579         case 0xba: buf[0] = BOXDRAW_DOUBLE_VERTICAL; break;
580         case 0xbb: buf[0] = BOXDRAW_DOUBLE_DOWN_LEFT; break;
581         case 0xbc: buf[0] = BOXDRAW_DOUBLE_UP_LEFT; break;
582         case 0xc8: buf[0] = BOXDRAW_DOUBLE_UP_RIGHT; break;
583         case 0xc9: buf[0] = BOXDRAW_DOUBLE_DOWN_RIGHT; break;
584         case 0xcd: buf[0] = BOXDRAW_DOUBLE_HORIZONTAL; break;
585
586         default:
587                 buf[0] = c;
588         }
589         buf[1] = 0;     /* terminate string */
590
591         conout->OutputString(conout, buf);
592 }