1 /* 2 * Copyright (C) 2008 Michael Brown <mbrown (at) 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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