efi_console.c revision 293233
177943Sdfr/*-
277943Sdfr * Copyright (c) 2000 Doug Rabson
377943Sdfr * All rights reserved.
477943Sdfr *
577943Sdfr * Redistribution and use in source and binary forms, with or without
677943Sdfr * modification, are permitted provided that the following conditions
777943Sdfr * are met:
877943Sdfr * 1. Redistributions of source code must retain the above copyright
977943Sdfr *    notice, this list of conditions and the following disclaimer.
1077943Sdfr * 2. Redistributions in binary form must reproduce the above copyright
1177943Sdfr *    notice, this list of conditions and the following disclaimer in the
1277943Sdfr *    documentation and/or other materials provided with the distribution.
1377943Sdfr *
1477943Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1577943Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1677943Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1777943Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1877943Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1977943Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2077943Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2177943Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2277943Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2377943Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2477943Sdfr * SUCH DAMAGE.
2577943Sdfr */
2677943Sdfr
27113038Sobrien#include <sys/cdefs.h>
28113038Sobrien__FBSDID("$FreeBSD: head/sys/boot/efi/libefi/efi_console.c 293233 2016-01-06 15:38:39Z emaste $");
2978327Sobrien
3077943Sdfr#include <efi.h>
3177943Sdfr#include <efilib.h>
3277943Sdfr
3377943Sdfr#include "bootstrap.h"
3477943Sdfr
3577943Sdfrstatic SIMPLE_TEXT_OUTPUT_INTERFACE	*conout;
3677943Sdfrstatic SIMPLE_INPUT_INTERFACE		*conin;
3777943Sdfr
38293233Semaste#ifdef TERM_EMU
39293233Semaste#define	DEFAULT_FGCOLOR	EFI_LIGHTGRAY
40293233Semaste#define	DEFAULT_BGCOLOR	EFI_BLACK
41293233Semaste
42293233Semaste#define	MAXARGS	8
43293233Semastestatic int args[MAXARGS], argc;
44293233Semastestatic int fg_c, bg_c, curx, cury;
45293233Semastestatic int esc;
46293233Semaste
47293233Semastevoid get_pos(int *x, int *y);
48293233Semastevoid curs_move(int *_x, int *_y, int x, int y);
49293233Semastestatic void CL(int);
50293233Semaste#endif
51293233Semaste
52293233Semastestatic void efi_cons_probe(struct console *);
53293233Semastestatic int efi_cons_init(int);
54293233Semastevoid efi_cons_putchar(int);
55293233Semasteint efi_cons_getchar(void);
56293233Semastevoid efi_cons_efiputchar(int);
57293233Semasteint efi_cons_poll(void);
58293233Semaste
59293233Semastestruct console efi_console = {
60293233Semaste	"efi",
61293233Semaste	"EFI console",
62293233Semaste	0,
63293233Semaste	efi_cons_probe,
64293233Semaste	efi_cons_init,
65293233Semaste	efi_cons_putchar,
66293233Semaste	efi_cons_getchar,
67293233Semaste	efi_cons_poll
68293233Semaste};
69293233Semaste
70293233Semaste#ifdef TERM_EMU
71293233Semaste
72293233Semaste/* Get cursor position. */
73293233Semastevoid
74293233Semasteget_pos(int *x, int *y)
75293233Semaste{
76293233Semaste	*x = conout->Mode->CursorColumn;
77293233Semaste	*y = conout->Mode->CursorRow;
78293233Semaste}
79293233Semaste
80293233Semaste/* Move cursor to x rows and y cols (0-based). */
81293233Semastevoid
82293233Semastecurs_move(int *_x, int *_y, int x, int y)
83293233Semaste{
84293233Semaste	conout->SetCursorPosition(conout, x, y);
85293233Semaste	if (_x != NULL)
86293233Semaste		*_x = conout->Mode->CursorColumn;
87293233Semaste	if (_y != NULL)
88293233Semaste		*_y = conout->Mode->CursorRow;
89293233Semaste}
90293233Semaste
91293233Semaste/* Clear internal state of the terminal emulation code. */
92293233Semastevoid
93293233Semasteend_term(void)
94293233Semaste{
95293233Semaste	esc = 0;
96293233Semaste	argc = -1;
97293233Semaste}
98293233Semaste
99293233Semaste#endif
100293233Semaste
10177943Sdfrstatic void
10277943Sdfrefi_cons_probe(struct console *cp)
10377943Sdfr{
10477943Sdfr	conout = ST->ConOut;
10577943Sdfr	conin = ST->ConIn;
10677943Sdfr	cp->c_flags |= C_PRESENTIN | C_PRESENTOUT;
10777943Sdfr}
10877943Sdfr
10977943Sdfrstatic int
11077943Sdfrefi_cons_init(int arg)
11177943Sdfr{
112293233Semaste	conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR,
113293233Semaste	    DEFAULT_BGCOLOR));
114293233Semaste#ifdef TERM_EMU
115293233Semaste	end_term();
116293233Semaste	get_pos(&curx, &cury);
117293233Semaste	curs_move(&curx, &cury, curx, cury);
118293233Semaste	fg_c = DEFAULT_FGCOLOR;
119293233Semaste	bg_c = DEFAULT_BGCOLOR;
120293233Semaste#endif
121293233Semaste	conout->EnableCursor(conout, TRUE);
12277943Sdfr	return 0;
12377943Sdfr}
12477943Sdfr
125293233Semastestatic void
126293233Semasteefi_cons_rawputchar(int c)
127293233Semaste{
128293233Semaste	int i;
129293233Semaste	UINTN x, y;
130293233Semaste	conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
131293233Semaste
132293233Semaste	if (c == '\t')
133293233Semaste		/* XXX lame tab expansion */
134293233Semaste		for (i = 0; i < 8; i++)
135293233Semaste			efi_cons_rawputchar(' ');
136293233Semaste	else {
137293233Semaste#ifndef	TERM_EMU
138293233Semaste		if (c == '\n')
139293233Semaste			efi_cons_efiputchar('\r');
140293233Semaste		else
141293233Semaste			efi_cons_efiputchar(c);
142293233Semaste#else
143293233Semaste		switch (c) {
144293233Semaste		case '\r':
145293233Semaste			curx = 0;
146293233Semaste			curs_move(&curx, &cury, curx, cury);
147293233Semaste			return;
148293233Semaste		case '\n':
149293233Semaste			cury++;
150293233Semaste			if (cury >= y) {
151293233Semaste				efi_cons_efiputchar('\n');
152293233Semaste				cury--;
153293233Semaste			} else
154293233Semaste				curs_move(&curx, &cury, curx, cury);
155293233Semaste			return;
156293233Semaste		case '\b':
157293233Semaste			if (curx > 0) {
158293233Semaste				curx--;
159293233Semaste				curs_move(&curx, &cury, curx, cury);
160293233Semaste			}
161293233Semaste			return;
162293233Semaste		default:
163293233Semaste			efi_cons_efiputchar(c);
164293233Semaste			curx++;
165293233Semaste			if (curx > x-1) {
166293233Semaste				curx = 0;
167293233Semaste				cury++;
168293233Semaste			}
169293233Semaste			if (cury > y-1) {
170293233Semaste				curx = 0;
171293233Semaste				cury--;
172293233Semaste			}
173293233Semaste		}
174293233Semaste		curs_move(&curx, &cury, curx, cury);
175293233Semaste#endif
176293233Semaste	}
177293233Semaste}
178293233Semaste
179293233Semaste/* Gracefully exit ESC-sequence processing in case of misunderstanding. */
180293233Semastestatic void
181293233Semastebail_out(int c)
182293233Semaste{
183293233Semaste	char buf[16], *ch;
184293233Semaste	int i;
185293233Semaste
186293233Semaste	if (esc) {
187293233Semaste		efi_cons_rawputchar('\033');
188293233Semaste		if (esc != '\033')
189293233Semaste			efi_cons_rawputchar(esc);
190293233Semaste		for (i = 0; i <= argc; ++i) {
191293233Semaste			sprintf(buf, "%d", args[i]);
192293233Semaste			ch = buf;
193293233Semaste			while (*ch)
194293233Semaste				efi_cons_rawputchar(*ch++);
195293233Semaste		}
196293233Semaste	}
197293233Semaste	efi_cons_rawputchar(c);
198293233Semaste	end_term();
199293233Semaste}
200293233Semaste
201293233Semaste/* Clear display from current position to end of screen. */
202293233Semastestatic void
203293233SemasteCD(void) {
204293233Semaste	int i;
205293233Semaste	UINTN x, y;
206293233Semaste
207293233Semaste	get_pos(&curx, &cury);
208293233Semaste	if (curx == 0 && cury == 0) {
209293233Semaste		conout->ClearScreen(conout);
210293233Semaste		end_term();
211293233Semaste		return;
212293233Semaste	}
213293233Semaste
214293233Semaste	conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
215293233Semaste	CL(0);  /* clear current line from cursor to end */
216293233Semaste	for (i = cury + 1; i < y-1; i++) {
217293233Semaste		curs_move(NULL, NULL, 0, i);
218293233Semaste		CL(0);
219293233Semaste	}
220293233Semaste	curs_move(NULL, NULL, curx, cury);
221293233Semaste	end_term();
222293233Semaste}
223293233Semaste
224293233Semaste/*
225293233Semaste * Absolute cursor move to args[0] rows and args[1] columns
226293233Semaste * (the coordinates are 1-based).
227293233Semaste */
228293233Semastestatic void
229293233SemasteCM(void)
230293233Semaste{
231293233Semaste	if (args[0] > 0)
232293233Semaste		args[0]--;
233293233Semaste	if (args[1] > 0)
234293233Semaste		args[1]--;
235293233Semaste	curs_move(&curx, &cury, args[1], args[0]);
236293233Semaste	end_term();
237293233Semaste}
238293233Semaste
239293233Semaste/* Home cursor (left top corner), also called from mode command. */
24077943Sdfrvoid
241293233SemasteHO(void)
24277943Sdfr{
243293233Semaste	argc = 1;
244293233Semaste	args[0] = args[1] = 1;
245293233Semaste	CM();
246293233Semaste}
24777943Sdfr
248293233Semaste/* Clear line from current position to end of line */
249293233Semastestatic void
250293233SemasteCL(int direction)
251293233Semaste{
252293233Semaste	int i, len;
253293233Semaste	UINTN x, y;
254293233Semaste	CHAR16 *line;
25577943Sdfr
256293233Semaste	conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
257293233Semaste	switch (direction) {
258293233Semaste	case 0:         /* from cursor to end */
259293233Semaste		len = x - curx + 1;
260293233Semaste		break;
261293233Semaste	case 1:         /* from beginning to cursor */
262293233Semaste		len = curx;
263293233Semaste		break;
264293233Semaste	case 2:         /* entire line */
265293233Semaste		len = x;
266293233Semaste		break;
267293233Semaste	}
26877943Sdfr
269293233Semaste	if (cury == y - 1)
270293233Semaste		len--;
271293233Semaste
272293233Semaste	line = malloc(len * sizeof (CHAR16));
273293233Semaste	if (line == NULL) {
274293233Semaste		printf("out of memory\n");
275293233Semaste		return;
276293233Semaste	}
277293233Semaste	for (i = 0; i < len; i++)
278293233Semaste		line[i] = ' ';
279293233Semaste	line[len-1] = 0;
280293233Semaste
281293233Semaste	if (direction != 0)
282293233Semaste		curs_move(NULL, NULL, 0, cury);
283293233Semaste
284293233Semaste	conout->OutputString(conout, line);
285293233Semaste	/* restore cursor position */
286293233Semaste	curs_move(NULL, NULL, curx, cury);
287293233Semaste	free(line);
288293233Semaste	end_term();
28977943Sdfr}
29077943Sdfr
291293233Semastestatic void
292293233Semasteget_arg(int c)
293293233Semaste{
294293233Semaste	if (argc < 0)
295293233Semaste		argc = 0;
296293233Semaste	args[argc] *= 10;
297293233Semaste	args[argc] += c - '0';
298293233Semaste}
299293233Semaste
300293233Semaste/* Emulate basic capabilities of cons25 terminal */
301293233Semastestatic void
302293233Semasteefi_term_emu(int c)
303293233Semaste{
304293233Semaste	static int ansi_col[] = {
305293233Semaste		0, 4, 2, 6, 1, 5, 3, 7
306293233Semaste	};
307293233Semaste	int t, i;
308293233Semaste
309293233Semaste	switch (esc) {
310293233Semaste	case 0:
311293233Semaste		switch (c) {
312293233Semaste		case '\033':
313293233Semaste			esc = c;
314293233Semaste			break;
315293233Semaste		default:
316293233Semaste			efi_cons_rawputchar(c);
317293233Semaste			break;
318293233Semaste		}
319293233Semaste		break;
320293233Semaste	case '\033':
321293233Semaste		switch (c) {
322293233Semaste		case '[':
323293233Semaste			esc = c;
324293233Semaste			args[0] = 0;
325293233Semaste			argc = -1;
326293233Semaste			break;
327293233Semaste		default:
328293233Semaste			bail_out(c);
329293233Semaste			break;
330293233Semaste		}
331293233Semaste		break;
332293233Semaste	case '[':
333293233Semaste		switch (c) {
334293233Semaste		case ';':
335293233Semaste			if (argc < 0)
336293233Semaste				argc = 0;
337293233Semaste			else if (argc + 1 >= MAXARGS)
338293233Semaste				bail_out(c);
339293233Semaste			else
340293233Semaste				args[++argc] = 0;
341293233Semaste			break;
342293233Semaste		case 'H':               /* ho = \E[H */
343293233Semaste			if (argc < 0)
344293233Semaste				HO();
345293233Semaste			else if (argc == 1)
346293233Semaste				CM();
347293233Semaste			else
348293233Semaste				bail_out(c);
349293233Semaste			break;
350293233Semaste		case 'J':               /* cd = \E[J */
351293233Semaste			if (argc < 0)
352293233Semaste				CD();
353293233Semaste			else
354293233Semaste				bail_out(c);
355293233Semaste			break;
356293233Semaste		case 'm':
357293233Semaste			if (argc < 0) {
358293233Semaste				fg_c = DEFAULT_FGCOLOR;
359293233Semaste				bg_c = DEFAULT_BGCOLOR;
360293233Semaste			}
361293233Semaste			for (i = 0; i <= argc; ++i) {
362293233Semaste				switch (args[i]) {
363293233Semaste				case 0:         /* back to normal */
364293233Semaste					fg_c = DEFAULT_FGCOLOR;
365293233Semaste					bg_c = DEFAULT_BGCOLOR;
366293233Semaste					break;
367293233Semaste				case 1:         /* bold */
368293233Semaste					fg_c |= 0x8;
369293233Semaste					break;
370293233Semaste				case 4:         /* underline */
371293233Semaste				case 5:         /* blink */
372293233Semaste					bg_c |= 0x8;
373293233Semaste					break;
374293233Semaste				case 7:         /* reverse */
375293233Semaste					t = fg_c;
376293233Semaste					fg_c = bg_c;
377293233Semaste					bg_c = t;
378293233Semaste					break;
379293233Semaste				case 30: case 31: case 32: case 33:
380293233Semaste				case 34: case 35: case 36: case 37:
381293233Semaste					fg_c = ansi_col[args[i] - 30];
382293233Semaste					break;
383293233Semaste				case 39:        /* normal */
384293233Semaste					fg_c = DEFAULT_FGCOLOR;
385293233Semaste					break;
386293233Semaste				case 40: case 41: case 42: case 43:
387293233Semaste				case 44: case 45: case 46: case 47:
388293233Semaste					bg_c = ansi_col[args[i] - 40];
389293233Semaste					break;
390293233Semaste				case 49:        /* normal */
391293233Semaste					bg_c = DEFAULT_BGCOLOR;
392293233Semaste					break;
393293233Semaste				}
394293233Semaste			}
395293233Semaste			conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c));
396293233Semaste			end_term();
397293233Semaste			break;
398293233Semaste		default:
399293233Semaste			if (isdigit(c))
400293233Semaste				get_arg(c);
401293233Semaste			else
402293233Semaste				bail_out(c);
403293233Semaste			break;
404293233Semaste		}
405293233Semaste		break;
406293233Semaste	default:
407293233Semaste		bail_out(c);
408293233Semaste		break;
409293233Semaste	}
410293233Semaste}
411293233Semaste
412293233Semastevoid
413293233Semasteefi_cons_putchar(int c)
414293233Semaste{
415293233Semaste#ifdef TERM_EMU
416293233Semaste	efi_term_emu(c);
417293233Semaste#else
418293233Semaste	efi_cons_rawputchar(c);
419293233Semaste#endif
420293233Semaste}
421293233Semaste
42277943Sdfrint
42377943Sdfrefi_cons_getchar()
42477943Sdfr{
42577943Sdfr	EFI_INPUT_KEY key;
426107682Smarcel	EFI_STATUS status;
42777943Sdfr	UINTN junk;
42877943Sdfr
429107682Smarcel	/* Try to read a key stroke. We wait for one if none is pending. */
430107682Smarcel	status = conin->ReadKeyStroke(conin, &key);
431107682Smarcel	if (status == EFI_NOT_READY) {
432107682Smarcel		BS->WaitForEvent(1, &conin->WaitForKey, &junk);
433107682Smarcel		status = conin->ReadKeyStroke(conin, &key);
434107682Smarcel	}
435293233Semaste	switch (key.ScanCode) {
436293233Semaste	case 0x17: /* ESC */
437293233Semaste		return (0x1b);  /* esc */
438293233Semaste	}
439293233Semaste
440293233Semaste	/* this can return  */
441107682Smarcel	return (key.UnicodeChar);
44277943Sdfr}
44377943Sdfr
44477943Sdfrint
44577943Sdfrefi_cons_poll()
44677943Sdfr{
447107682Smarcel	/* This can clear the signaled state. */
448107682Smarcel	return (BS->CheckEvent(conin->WaitForKey) == EFI_SUCCESS);
44977943Sdfr}
45077943Sdfr
451293233Semaste/* Plain direct access to EFI OutputString(). */
452293233Semastevoid
453293233Semasteefi_cons_efiputchar(int c)
454293233Semaste{
455293233Semaste	CHAR16 buf[2];
456293233Semaste
457293233Semaste	/*
458293233Semaste	 * translate box chars to unicode
459293233Semaste	 */
460293233Semaste	switch (c) {
461293233Semaste	/* single frame */
462293233Semaste	case 0xb3: buf[0] = BOXDRAW_VERTICAL; break;
463293233Semaste	case 0xbf: buf[0] = BOXDRAW_DOWN_LEFT; break;
464293233Semaste	case 0xc0: buf[0] = BOXDRAW_UP_RIGHT; break;
465293233Semaste	case 0xc4: buf[0] = BOXDRAW_HORIZONTAL; break;
466293233Semaste	case 0xda: buf[0] = BOXDRAW_DOWN_RIGHT; break;
467293233Semaste	case 0xd9: buf[0] = BOXDRAW_UP_LEFT; break;
468293233Semaste
469293233Semaste	/* double frame */
470293233Semaste	case 0xba: buf[0] = BOXDRAW_DOUBLE_VERTICAL; break;
471293233Semaste	case 0xbb: buf[0] = BOXDRAW_DOUBLE_DOWN_LEFT; break;
472293233Semaste	case 0xbc: buf[0] = BOXDRAW_DOUBLE_UP_LEFT; break;
473293233Semaste	case 0xc8: buf[0] = BOXDRAW_DOUBLE_UP_RIGHT; break;
474293233Semaste	case 0xc9: buf[0] = BOXDRAW_DOUBLE_DOWN_RIGHT; break;
475293233Semaste	case 0xcd: buf[0] = BOXDRAW_DOUBLE_HORIZONTAL; break;
476293233Semaste
477293233Semaste	default:
478293233Semaste		buf[0] = c;
479293233Semaste	}
480293233Semaste        buf[1] = 0;     /* terminate string */
481293233Semaste
482293233Semaste	conout->OutputString(conout, buf);
483293233Semaste}
484