2 * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
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.
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.
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.
19 FILE_LICENCE ( GPL2_OR_LATER
);
23 #include <gpxe/efi/efi.h>
24 #include <gpxe/ansiesc.h>
27 #define ATTR_BOLD 0x08
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
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
49 #define ATTR_DEFAULT ATTR_FCOL_WHITE
51 /** Current character attribute */
52 static unsigned int efi_attr
= ATTR_DEFAULT
;
55 * Handle ANSI CUP (cursor position)
57 * @v count Parameter count
58 * @v params[0] Row (1 is top)
59 * @v params[1] Column (1 is left)
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 );
71 conout
->SetCursorPosition ( conout
, cx
, cy
);
75 * Handle ANSI ED (erase in page)
77 * @v count Parameter count
78 * @v params[0] Region to erase
80 static void efi_handle_ed ( unsigned int count __unused
,
81 int params
[] __unused
) {
82 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*conout
= efi_systab
->ConOut
;
84 /* We assume that we always clear the whole screen */
85 assert ( params
[0] == ANSIESC_ED_ALL
);
87 conout
->ClearScreen ( conout
);
91 * Handle ANSI SGR (set graphics rendition)
93 * @v count Parameter count
94 * @v params List of graphic rendition aspects
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 */
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 */
113 for ( i
= 0 ; i
< count
; i
++ ) {
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 ];
130 conout
->SetAttribute ( conout
, efi_attr
);
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
},
141 /** EFI console ANSI escape sequence context */
142 static struct ansiesc_context efi_ansiesc_ctx
= {
143 .handlers
= efi_ansiesc_handlers
,
147 * Print a character to EFI console
149 * @v character Character to be printed
151 static void efi_putchar ( int character
) {
152 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*conout
= efi_systab
->ConOut
;
153 wchar_t wstr
[] = { character
, 0 };
155 /* Intercept ANSI escape sequences */
156 character
= ansiesc_process ( &efi_ansiesc_ctx
, character
);
160 conout
->OutputString ( conout
, wstr
);
164 * Pointer to current ANSI output sequence
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").
171 static const char *ansi_input
= "";
173 /** Mapping from EFI scan codes to ANSI escape sequences */
174 static const char *ansi_sequences
[] = {
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.
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.
199 * Get ANSI escape sequence corresponding to EFI scancode
201 * @v scancode EFI scancode
202 * @ret ansi_seq ANSI escape sequence, if any, otherwise NULL
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
];
213 * Get character from EFI console
215 * @ret character Character read from console
217 static int efi_getchar ( void ) {
218 EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*conin
= efi_systab
->ConIn
;
219 const char *ansi_seq
;
223 /* If we are mid-sequence, pass out the next byte */
225 return *(ansi_input
++);
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
) );
233 DBG2 ( "EFI read key stroke with unicode %04x scancode %04x\n",
234 key
.UnicodeChar
, key
.ScanCode
);
236 /* If key has a Unicode representation, return it */
237 if ( key
.UnicodeChar
)
238 return key
.UnicodeChar
;
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
;
251 * Check for character ready to read from EFI console
253 * @ret True Character available to read
254 * @ret False No character available to read
256 static int efi_iskey ( void ) {
257 EFI_BOOT_SERVICES
*bs
= efi_systab
->BootServices
;
258 EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*conin
= efi_systab
->ConIn
;
261 /* If we are mid-sequence, we are always ready */
265 /* Check to see if the WaitForKey event has fired */
266 if ( ( efirc
= bs
->CheckEvent ( conin
->WaitForKey
) ) == 0 )
272 struct console_driver efi_console __console_driver
= {
273 .putchar
= efi_putchar
,
274 .getchar
= efi_getchar
,