1 /*
2 * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19 FILE_LICENCE ( GPL2_OR_LATER );
20
21 #include <stddef.h>
22 #include <assert.h>
23 #include <gpxe/efi/efi.h>
24 #include <gpxe/ansiesc.h>
25 #include <console.h>
26
27 #define ATTR_BOLD 0x08
28
29 #define ATTR_FCOL_MASK 0x07
30 #define ATTR_FCOL_BLACK 0x00
31 #define ATTR_FCOL_BLUE 0x01
32 #define ATTR_FCOL_GREEN 0x02
33 #define ATTR_FCOL_CYAN 0x03
34 #define ATTR_FCOL_RED 0x04
35 #define ATTR_FCOL_MAGENTA 0x05
36 #define ATTR_FCOL_YELLOW 0x06
37 #define ATTR_FCOL_WHITE 0x07
38
39 #define ATTR_BCOL_MASK 0x70
40 #define ATTR_BCOL_BLACK 0x00
41 #define ATTR_BCOL_BLUE 0x10
42 #define ATTR_BCOL_GREEN 0x20
43 #define ATTR_BCOL_CYAN 0x30
44 #define ATTR_BCOL_RED 0x40
45 #define ATTR_BCOL_MAGENTA 0x50
46 #define ATTR_BCOL_YELLOW 0x60
47 #define ATTR_BCOL_WHITE 0x70
48
49 #define ATTR_DEFAULT ATTR_FCOL_WHITE
50
51 /** Current character attribute */
52 static unsigned int efi_attr = ATTR_DEFAULT;
53
54 /**
55 * Handle ANSI CUP (cursor position)
56 *
57 * @v count Parameter count
58 * @v params[0] Row (1 is top)
59 * @v params[1] Column (1 is left)
60 */
efi_handle_cup(unsigned int count __unused,int params[])61 static void efi_handle_cup ( unsigned int count __unused, int params[] ) {
62 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *conout = efi_systab->ConOut;
63 int cx = ( params[1] - 1 );
64 int cy = ( params[0] - 1 );
65
66 if ( cx < 0 )
67 cx = 0;
68 if ( cy < 0 )
69 cy = 0;
70
71 conout->SetCursorPosition ( conout, cx, cy );
72 }
73
74 /**
75 * Handle ANSI ED (erase in page)
76 *
77 * @v count Parameter count
78 * @v params[0] Region to erase
79 */
efi_handle_ed(unsigned int count __unused,int params[]__unused)80 static void efi_handle_ed ( unsigned int count __unused,
81 int params[] __unused ) {
82 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *conout = efi_systab->ConOut;
83
84 /* We assume that we always clear the whole screen */
85 assert ( params[0] == ANSIESC_ED_ALL );
86
87 conout->ClearScreen ( conout );
88 }
89
90 /**
91 * Handle ANSI SGR (set graphics rendition)
92 *
93 * @v count Parameter count
94 * @v params List of graphic rendition aspects
95 */
efi_handle_sgr(unsigned int count,int params[])96 static void efi_handle_sgr ( unsigned int count, int params[] ) {
97 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *conout = efi_systab->ConOut;
98 static const uint8_t efi_attr_fcols[10] = {
99 ATTR_FCOL_BLACK, ATTR_FCOL_RED, ATTR_FCOL_GREEN,
100 ATTR_FCOL_YELLOW, ATTR_FCOL_BLUE, ATTR_FCOL_MAGENTA,
101 ATTR_FCOL_CYAN, ATTR_FCOL_WHITE,
102 ATTR_FCOL_WHITE, ATTR_FCOL_WHITE /* defaults */
103 };
104 static const uint8_t efi_attr_bcols[10] = {
105 ATTR_BCOL_BLACK, ATTR_BCOL_RED, ATTR_BCOL_GREEN,
106 ATTR_BCOL_YELLOW, ATTR_BCOL_BLUE, ATTR_BCOL_MAGENTA,
107 ATTR_BCOL_CYAN, ATTR_BCOL_WHITE,
108 ATTR_BCOL_BLACK, ATTR_BCOL_BLACK /* defaults */
109 };
110 unsigned int i;
111 int aspect;
112
113 for ( i = 0 ; i < count ; i++ ) {
114 aspect = params[i];
115 if ( aspect == 0 ) {
116 efi_attr = ATTR_DEFAULT;
117 } else if ( aspect == 1 ) {
118 efi_attr |= ATTR_BOLD;
119 } else if ( aspect == 22 ) {
120 efi_attr &= ~ATTR_BOLD;
121 } else if ( ( aspect >= 30 ) && ( aspect <= 39 ) ) {
122 efi_attr &= ~ATTR_FCOL_MASK;
123 efi_attr |= efi_attr_fcols[ aspect - 30 ];
124 } else if ( ( aspect >= 40 ) && ( aspect <= 49 ) ) {
125 efi_attr &= ~ATTR_BCOL_MASK;
126 efi_attr |= efi_attr_bcols[ aspect - 40 ];
127 }
128 }
129
130 conout->SetAttribute ( conout, efi_attr );
131 }
132
133 /** EFI console ANSI escape sequence handlers */
134 static struct ansiesc_handler efi_ansiesc_handlers[] = {
135 { ANSIESC_CUP, efi_handle_cup },
136 { ANSIESC_ED, efi_handle_ed },
137 { ANSIESC_SGR, efi_handle_sgr },
138 { 0, NULL }
139 };
140
141 /** EFI console ANSI escape sequence context */
142 static struct ansiesc_context efi_ansiesc_ctx = {
143 .handlers = efi_ansiesc_handlers,
144 };
145
146 /**
147 * Print a character to EFI console
148 *
149 * @v character Character to be printed
150 */
efi_putchar(int character)151 static void efi_putchar ( int character ) {
152 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *conout = efi_systab->ConOut;
153 wchar_t wstr[] = { character, 0 };
154
155 /* Intercept ANSI escape sequences */
156 character = ansiesc_process ( &efi_ansiesc_ctx, character );
157 if ( character < 0 )
158 return;
159
160 conout->OutputString ( conout, wstr );
161 }
162
163 /**
164 * Pointer to current ANSI output sequence
165 *
166 * While we are in the middle of returning an ANSI sequence for a
167 * special key, this will point to the next character to return. When
168 * not in the middle of such a sequence, this will point to a NUL
169 * (note: not "will be NULL").
170 */
171 static const char *ansi_input = "";
172
173 /** Mapping from EFI scan codes to ANSI escape sequences */
174 static const char *ansi_sequences[] = {
175 [SCAN_UP] = "[A",
176 [SCAN_DOWN] = "[B",
177 [SCAN_RIGHT] = "[C",
178 [SCAN_LEFT] = "[D",
179 [SCAN_HOME] = "[H",
180 [SCAN_END] = "[F",
181 [SCAN_INSERT] = "[2~",
182 /* EFI translates an incoming backspace via the serial console
183 * into a SCAN_DELETE. There's not much we can do about this.
184 */
185 [SCAN_DELETE] = "[3~",
186 [SCAN_PAGE_UP] = "[5~",
187 [SCAN_PAGE_DOWN] = "[6~",
188 /* EFI translates some (but not all) incoming escape sequences
189 * via the serial console into equivalent scancodes. When it
190 * doesn't recognise a sequence, it helpfully(!) translates
191 * the initial ESC and passes the remainder through verbatim.
192 * Treating SCAN_ESC as equivalent to an empty escape sequence
193 * works around this bug.
194 */
195 [SCAN_ESC] = "",
196 };
197
198 /**
199 * Get ANSI escape sequence corresponding to EFI scancode
200 *
201 * @v scancode EFI scancode
202 * @ret ansi_seq ANSI escape sequence, if any, otherwise NULL
203 */
scancode_to_ansi_seq(unsigned int scancode)204 static const char * scancode_to_ansi_seq ( unsigned int scancode ) {
205 if ( scancode < ( sizeof ( ansi_sequences ) /
206 sizeof ( ansi_sequences[0] ) ) ) {
207 return ansi_sequences[scancode];
208 }
209 return NULL;
210 }
211
212 /**
213 * Get character from EFI console
214 *
215 * @ret character Character read from console
216 */
efi_getchar(void)217 static int efi_getchar ( void ) {
218 EFI_SIMPLE_TEXT_INPUT_PROTOCOL *conin = efi_systab->ConIn;
219 const char *ansi_seq;
220 EFI_INPUT_KEY key;
221 EFI_STATUS efirc;
222
223 /* If we are mid-sequence, pass out the next byte */
224 if ( *ansi_input )
225 return *(ansi_input++);
226
227 /* Read key from real EFI console */
228 if ( ( efirc = conin->ReadKeyStroke ( conin, &key ) ) != 0 ) {
229 DBG ( "EFI could not read keystroke: %s\n",
230 efi_strerror ( efirc ) );
231 return 0;
232 }
233 DBG2 ( "EFI read key stroke with unicode %04x scancode %04x\n",
234 key.UnicodeChar, key.ScanCode );
235
236 /* If key has a Unicode representation, return it */
237 if ( key.UnicodeChar )
238 return key.UnicodeChar;
239
240 /* Otherwise, check for a special key that we know about */
241 if ( ( ansi_seq = scancode_to_ansi_seq ( key.ScanCode ) ) ) {
242 /* Start of escape sequence: return ESC (0x1b) */
243 ansi_input = ansi_seq;
244 return 0x1b;
245 }
246
247 return 0;
248 }
249
250 /**
251 * Check for character ready to read from EFI console
252 *
253 * @ret True Character available to read
254 * @ret False No character available to read
255 */
efi_iskey(void)256 static int efi_iskey ( void ) {
257 EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
258 EFI_SIMPLE_TEXT_INPUT_PROTOCOL *conin = efi_systab->ConIn;
259 EFI_STATUS efirc;
260
261 /* If we are mid-sequence, we are always ready */
262 if ( *ansi_input )
263 return 1;
264
265 /* Check to see if the WaitForKey event has fired */
266 if ( ( efirc = bs->CheckEvent ( conin->WaitForKey ) ) == 0 )
267 return 1;
268
269 return 0;
270 }
271
272 struct console_driver efi_console __console_driver = {
273 .putchar = efi_putchar,
274 .getchar = efi_getchar,
275 .iskey = efi_iskey,
276 };
277