]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/efi/libefi/efi_console.c
MFC r355347:
[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 EFI_GUID simple_input_ex_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
36 static SIMPLE_TEXT_OUTPUT_INTERFACE     *conout;
37 static SIMPLE_INPUT_INTERFACE           *conin;
38 static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *coninex;
39
40 #ifdef TERM_EMU
41 #define DEFAULT_FGCOLOR EFI_LIGHTGRAY
42 #define DEFAULT_BGCOLOR EFI_BLACK
43
44 #define MAXARGS 8
45 static int args[MAXARGS], argc;
46 static int fg_c, bg_c, curx, cury;
47 static int esc;
48
49 void get_pos(int *x, int *y);
50 void curs_move(int *_x, int *_y, int x, int y);
51 static void CL(int);
52 void HO(void);
53 void end_term(void);
54 #endif
55
56 #define KEYBUFSZ 10
57 static unsigned keybuf[KEYBUFSZ];       /* keybuf for extended codes */
58 static int key_pending;
59
60 static void efi_cons_probe(struct console *);
61 static int efi_cons_init(int);
62 void efi_cons_putchar(int);
63 int efi_cons_getchar(void);
64 void efi_cons_efiputchar(int);
65 int efi_cons_poll(void);
66
67 struct console efi_console = {
68         "efi",
69         "EFI console",
70         C_WIDEOUT,
71         efi_cons_probe,
72         efi_cons_init,
73         efi_cons_putchar,
74         efi_cons_getchar,
75         efi_cons_poll
76 };
77
78 #ifdef TERM_EMU
79
80 /* Get cursor position. */
81 void
82 get_pos(int *x, int *y)
83 {
84         *x = conout->Mode->CursorColumn;
85         *y = conout->Mode->CursorRow;
86 }
87
88 /* Move cursor to x rows and y cols (0-based). */
89 void
90 curs_move(int *_x, int *_y, int x, int y)
91 {
92         conout->SetCursorPosition(conout, x, y);
93         if (_x != NULL)
94                 *_x = conout->Mode->CursorColumn;
95         if (_y != NULL)
96                 *_y = conout->Mode->CursorRow;
97 }
98
99 /* Clear internal state of the terminal emulation code. */
100 void
101 end_term(void)
102 {
103         esc = 0;
104         argc = -1;
105 }
106
107 #endif
108
109 static void
110 efi_cons_probe(struct console *cp)
111 {
112         conout = ST->ConOut;
113         conin = ST->ConIn;
114         cp->c_flags |= C_PRESENTIN | C_PRESENTOUT;
115 }
116
117 static int
118 efi_cons_init(int arg)
119 {
120         EFI_STATUS status;
121
122 #ifdef TERM_EMU
123         conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR,
124             DEFAULT_BGCOLOR));
125         end_term();
126         get_pos(&curx, &cury);
127         curs_move(&curx, &cury, curx, cury);
128         fg_c = DEFAULT_FGCOLOR;
129         bg_c = DEFAULT_BGCOLOR;
130 #endif
131         conout->EnableCursor(conout, TRUE);
132         status = BS->OpenProtocol(ST->ConsoleInHandle, &simple_input_ex_guid,
133             (void **)&coninex, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
134         if (status != EFI_SUCCESS)
135                 coninex = NULL;
136         return (0);
137 }
138
139 static void
140 efi_cons_rawputchar(int c)
141 {
142         int i;
143         UINTN x, y;
144         conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
145
146         if (c == '\t') {
147                 int n;
148
149                 n = 8 - ((conout->Mode->CursorColumn + 8) % 8);
150                 for (i = 0; i < n; i++)
151                         efi_cons_rawputchar(' ');
152         } else {
153 #ifndef TERM_EMU
154                 if (c == '\n')
155                         efi_cons_efiputchar('\r');
156                 efi_cons_efiputchar(c);
157 #else
158                 switch (c) {
159                 case '\r':
160                         curx = 0;
161                         efi_cons_efiputchar('\r');
162                         return;
163                 case '\n':
164                         efi_cons_efiputchar('\n');
165                         efi_cons_efiputchar('\r');
166                         cury++;
167                         if (cury >= y)
168                                 cury--;
169                         curx = 0;
170                         return;
171                 case '\b':
172                         if (curx > 0) {
173                                 efi_cons_efiputchar('\b');
174                                 curx--;
175                         }
176                         return;
177                 default:
178                         efi_cons_efiputchar(c);
179                         curx++;
180                         if (curx > x-1) {
181                                 curx = 0;
182                                 cury++;
183                         }
184                         if (cury > y-1) {
185                                 curx = 0;
186                                 cury--;
187                         }
188                 }
189 #endif
190         }
191 }
192
193 #ifdef TERM_EMU
194 /* Gracefully exit ESC-sequence processing in case of misunderstanding. */
195 static void
196 bail_out(int c)
197 {
198         char buf[16], *ch;
199         int i;
200
201         if (esc) {
202                 efi_cons_rawputchar('\033');
203                 if (esc != '\033')
204                         efi_cons_rawputchar(esc);
205                 for (i = 0; i <= argc; ++i) {
206                         sprintf(buf, "%d", args[i]);
207                         ch = buf;
208                         while (*ch)
209                                 efi_cons_rawputchar(*ch++);
210                 }
211         }
212         efi_cons_rawputchar(c);
213         end_term();
214 }
215
216 /* Clear display from current position to end of screen. */
217 static void
218 CD(void) {
219         int i;
220         UINTN x, y;
221
222         get_pos(&curx, &cury);
223         if (curx == 0 && cury == 0) {
224                 conout->ClearScreen(conout);
225                 end_term();
226                 return;
227         }
228
229         conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
230         CL(0);  /* clear current line from cursor to end */
231         for (i = cury + 1; i < y-1; i++) {
232                 curs_move(NULL, NULL, 0, i);
233                 CL(0);
234         }
235         curs_move(NULL, NULL, curx, cury);
236         end_term();
237 }
238
239 /*
240  * Absolute cursor move to args[0] rows and args[1] columns
241  * (the coordinates are 1-based).
242  */
243 static void
244 CM(void)
245 {
246         if (args[0] > 0)
247                 args[0]--;
248         if (args[1] > 0)
249                 args[1]--;
250         curs_move(&curx, &cury, args[1], args[0]);
251         end_term();
252 }
253
254 /* Home cursor (left top corner), also called from mode command. */
255 void
256 HO(void)
257 {
258         argc = 1;
259         args[0] = args[1] = 1;
260         CM();
261 }
262
263 /* Clear line from current position to end of line */
264 static void
265 CL(int direction)
266 {
267         int i, len;
268         UINTN x, y;
269         CHAR16 *line;
270
271         conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
272         switch (direction) {
273         case 0:         /* from cursor to end */
274                 len = x - curx + 1;
275                 break;
276         case 1:         /* from beginning to cursor */
277                 len = curx;
278                 break;
279         case 2:         /* entire line */
280                 len = x;
281                 break;
282         default:        /* NOTREACHED */
283                 __unreachable();
284         }
285
286         if (cury == y - 1)
287                 len--;
288
289         line = malloc(len * sizeof (CHAR16));
290         if (line == NULL) {
291                 printf("out of memory\n");
292                 return;
293         }
294         for (i = 0; i < len; i++)
295                 line[i] = ' ';
296         line[len-1] = 0;
297
298         if (direction != 0)
299                 curs_move(NULL, NULL, 0, cury);
300
301         conout->OutputString(conout, line);
302         /* restore cursor position */
303         curs_move(NULL, NULL, curx, cury);
304         free(line);
305         end_term();
306 }
307
308 static void
309 get_arg(int c)
310 {
311         if (argc < 0)
312                 argc = 0;
313         args[argc] *= 10;
314         args[argc] += c - '0';
315 }
316
317 /* Emulate basic capabilities of cons25 terminal */
318 static void
319 efi_term_emu(int c)
320 {
321         static int ansi_col[] = {
322                 0, 4, 2, 6, 1, 5, 3, 7
323         };
324         int t, i;
325
326         switch (esc) {
327         case 0:
328                 switch (c) {
329                 case '\033':
330                         esc = c;
331                         break;
332                 default:
333                         efi_cons_rawputchar(c);
334                         break;
335                 }
336                 break;
337         case '\033':
338                 switch (c) {
339                 case '[':
340                         esc = c;
341                         args[0] = 0;
342                         argc = -1;
343                         break;
344                 default:
345                         bail_out(c);
346                         break;
347                 }
348                 break;
349         case '[':
350                 switch (c) {
351                 case ';':
352                         if (argc < 0)
353                                 argc = 0;
354                         else if (argc + 1 >= MAXARGS)
355                                 bail_out(c);
356                         else
357                                 args[++argc] = 0;
358                         break;
359                 case 'H':               /* ho = \E[H */
360                         if (argc < 0)
361                                 HO();
362                         else if (argc == 1)
363                                 CM();
364                         else
365                                 bail_out(c);
366                         break;
367                 case 'J':               /* cd = \E[J */
368                         if (argc < 0)
369                                 CD();
370                         else
371                                 bail_out(c);
372                         break;
373                 case 'm':
374                         if (argc < 0) {
375                                 fg_c = DEFAULT_FGCOLOR;
376                                 bg_c = DEFAULT_BGCOLOR;
377                         }
378                         for (i = 0; i <= argc; ++i) {
379                                 switch (args[i]) {
380                                 case 0:         /* back to normal */
381                                         fg_c = DEFAULT_FGCOLOR;
382                                         bg_c = DEFAULT_BGCOLOR;
383                                         break;
384                                 case 1:         /* bold */
385                                         fg_c |= 0x8;
386                                         break;
387                                 case 4:         /* underline */
388                                 case 5:         /* blink */
389                                         bg_c |= 0x8;
390                                         break;
391                                 case 7:         /* reverse */
392                                         t = fg_c;
393                                         fg_c = bg_c;
394                                         bg_c = t;
395                                         break;
396                                 case 22:        /* normal intensity */
397                                         fg_c &= ~0x8;
398                                         break;
399                                 case 24:        /* not underline */
400                                 case 25:        /* not blinking */
401                                         bg_c &= ~0x8;
402                                         break;
403                                 case 30: case 31: case 32: case 33:
404                                 case 34: case 35: case 36: case 37:
405                                         fg_c = ansi_col[args[i] - 30];
406                                         break;
407                                 case 39:        /* normal */
408                                         fg_c = DEFAULT_FGCOLOR;
409                                         break;
410                                 case 40: case 41: case 42: case 43:
411                                 case 44: case 45: case 46: case 47:
412                                         bg_c = ansi_col[args[i] - 40];
413                                         break;
414                                 case 49:        /* normal */
415                                         bg_c = DEFAULT_BGCOLOR;
416                                         break;
417                                 }
418                         }
419                         conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c));
420                         end_term();
421                         break;
422                 default:
423                         if (isdigit(c))
424                                 get_arg(c);
425                         else
426                                 bail_out(c);
427                         break;
428                 }
429                 break;
430         default:
431                 bail_out(c);
432                 break;
433         }
434 }
435 #else
436 void
437 HO(void)
438 {
439 }
440 #endif
441
442 void
443 efi_cons_putchar(int c)
444 {
445 #ifdef TERM_EMU
446         efi_term_emu(c);
447 #else
448         efi_cons_rawputchar(c);
449 #endif
450 }
451
452 static int
453 keybuf_getchar(void)
454 {
455         int i, c = 0;
456
457         for (i = 0; i < KEYBUFSZ; i++) {
458                 if (keybuf[i] != 0) {
459                         c = keybuf[i];
460                         keybuf[i] = 0;
461                         break;
462                 }
463         }
464
465         return (c);
466 }
467
468 static bool
469 keybuf_ischar(void)
470 {
471         int i;
472
473         for (i = 0; i < KEYBUFSZ; i++) {
474                 if (keybuf[i] != 0)
475                         return (true);
476         }
477         return (false);
478 }
479
480 /*
481  * We are not reading input before keybuf is empty, so we are safe
482  * just to fill keybuf from the beginning.
483  */
484 static void
485 keybuf_inschar(EFI_INPUT_KEY *key)
486 {
487
488         switch (key->ScanCode) {
489         case SCAN_UP: /* UP */
490                 keybuf[0] = 0x1b;       /* esc */
491                 keybuf[1] = '[';
492                 keybuf[2] = 'A';
493                 break;
494         case SCAN_DOWN: /* DOWN */
495                 keybuf[0] = 0x1b;       /* esc */
496                 keybuf[1] = '[';
497                 keybuf[2] = 'B';
498                 break;
499         case SCAN_RIGHT: /* RIGHT */
500                 keybuf[0] = 0x1b;       /* esc */
501                 keybuf[1] = '[';
502                 keybuf[2] = 'C';
503                 break;
504         case SCAN_LEFT: /* LEFT */
505                 keybuf[0] = 0x1b;       /* esc */
506                 keybuf[1] = '[';
507                 keybuf[2] = 'D';
508                 break;
509         case SCAN_DELETE:
510                 keybuf[0] = CHAR_BACKSPACE;
511                 break;
512         case SCAN_ESC:
513                 keybuf[0] = 0x1b;       /* esc */
514                 break;
515         default:
516                 keybuf[0] = key->UnicodeChar;
517                 break;
518         }
519 }
520
521 static bool
522 efi_readkey(void)
523 {
524         EFI_STATUS status;
525         EFI_INPUT_KEY key;
526
527         status = conin->ReadKeyStroke(conin, &key);
528         if (status == EFI_SUCCESS) {
529                 keybuf_inschar(&key);
530                 return (true);
531         }
532         return (false);
533 }
534
535 static bool
536 efi_readkey_ex(void)
537 {
538         EFI_STATUS status;
539         EFI_INPUT_KEY *kp;
540         EFI_KEY_DATA  key_data;
541         uint32_t kss;
542
543         status = coninex->ReadKeyStrokeEx(coninex, &key_data);
544         if (status == EFI_SUCCESS) {
545                 kss = key_data.KeyState.KeyShiftState;
546                 kp = &key_data.Key;
547                 if (kss & EFI_SHIFT_STATE_VALID) {
548
549                         /*
550                          * quick mapping to control chars, replace with
551                          * map lookup later.
552                          */
553                         if (kss & EFI_RIGHT_CONTROL_PRESSED ||
554                             kss & EFI_LEFT_CONTROL_PRESSED) {
555                                 if (kp->UnicodeChar >= 'a' &&
556                                     kp->UnicodeChar <= 'z') {
557                                         kp->UnicodeChar -= 'a';
558                                         kp->UnicodeChar++;
559                                 }
560                         }
561                         if (kp->ScanCode == 0 && kp->UnicodeChar == 0)
562                                 return (false);
563                         keybuf_inschar(kp);
564                         return (true);
565                 }
566         }
567         return (false);
568 }
569
570 int
571 efi_cons_getchar(void)
572 {
573         int c;
574
575         if ((c = keybuf_getchar()) != 0)
576                 return (c);
577
578         key_pending = 0;
579
580         if (coninex == NULL) {
581                 if (efi_readkey())
582                         return (keybuf_getchar());
583         } else {
584                 if (efi_readkey_ex())
585                         return (keybuf_getchar());
586         }
587
588         return (-1);
589 }
590
591 int
592 efi_cons_poll(void)
593 {
594         EFI_STATUS status;
595
596         if (keybuf_ischar() || key_pending)
597                 return (1);
598
599         /*
600          * Some EFI implementation (u-boot for example) do not support
601          * WaitForKey().
602          * CheckEvent() can clear the signaled state.
603          */
604         if (coninex != NULL) {
605                 if (coninex->WaitForKeyEx == NULL) {
606                         key_pending = efi_readkey_ex();
607                 } else {
608                         status = BS->CheckEvent(coninex->WaitForKeyEx);
609                         key_pending = status == EFI_SUCCESS;
610                 }
611         } else {
612                 if (conin->WaitForKey == NULL) {
613                         key_pending = efi_readkey();
614                 } else {
615                         status = BS->CheckEvent(conin->WaitForKey);
616                         key_pending = status == EFI_SUCCESS;
617                 }
618         }
619
620         return (key_pending);
621 }
622
623 /* Plain direct access to EFI OutputString(). */
624 void
625 efi_cons_efiputchar(int c)
626 {
627         CHAR16 buf[2];
628
629         /*
630          * translate box chars to unicode
631          */
632         switch (c) {
633         /* single frame */
634         case 0xb3: buf[0] = BOXDRAW_VERTICAL; break;
635         case 0xbf: buf[0] = BOXDRAW_DOWN_LEFT; break;
636         case 0xc0: buf[0] = BOXDRAW_UP_RIGHT; break;
637         case 0xc4: buf[0] = BOXDRAW_HORIZONTAL; break;
638         case 0xda: buf[0] = BOXDRAW_DOWN_RIGHT; break;
639         case 0xd9: buf[0] = BOXDRAW_UP_LEFT; break;
640
641         /* double frame */
642         case 0xba: buf[0] = BOXDRAW_DOUBLE_VERTICAL; break;
643         case 0xbb: buf[0] = BOXDRAW_DOUBLE_DOWN_LEFT; break;
644         case 0xbc: buf[0] = BOXDRAW_DOUBLE_UP_LEFT; break;
645         case 0xc8: buf[0] = BOXDRAW_DOUBLE_UP_RIGHT; break;
646         case 0xc9: buf[0] = BOXDRAW_DOUBLE_DOWN_RIGHT; break;
647         case 0xcd: buf[0] = BOXDRAW_DOUBLE_HORIZONTAL; break;
648
649         default:
650                 buf[0] = c;
651         }
652         buf[1] = 0;     /* terminate string */
653
654         conout->OutputString(conout, buf);
655 }