1198160Srrs/*-
2198160Srrs * Copyright (c) 2000 Doug Rabson
3198160Srrs * All rights reserved.
4198160Srrs *
5198160Srrs * Redistribution and use in source and binary forms, with or without
6198160Srrs * modification, are permitted provided that the following conditions
7198160Srrs * are met:
8198160Srrs * 1. Redistributions of source code must retain the above copyright
9198160Srrs *    notice, this list of conditions and the following disclaimer.
10198160Srrs * 2. Redistributions in binary form must reproduce the above copyright
11198160Srrs *    notice, this list of conditions and the following disclaimer in the
12198160Srrs *    documentation and/or other materials provided with the distribution.
13198160Srrs *
14198160Srrs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15198160Srrs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16198160Srrs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17198160Srrs * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18198160Srrs * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19198160Srrs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20198160Srrs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21198160Srrs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22198160Srrs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23198160Srrs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24198160Srrs * SUCH DAMAGE.
25198160Srrs */
26198160Srrs
27198160Srrs#include <sys/cdefs.h>
28198160Srrs__FBSDID("$FreeBSD: releng/10.3/sys/boot/efi/libefi/efi_console.c 294981 2016-01-28 12:11:42Z smh $");
29211994Sjchandra
30211994Sjchandra#include <efi.h>
31211994Sjchandra#include <efilib.h>
32198160Srrs
33198160Srrs#include "bootstrap.h"
34198160Srrs
35198160Srrsstatic SIMPLE_TEXT_OUTPUT_INTERFACE	*conout;
36198956Srrsstatic SIMPLE_INPUT_INTERFACE		*conin;
37198160Srrs
38198160Srrs#ifdef TERM_EMU
39198160Srrs#define	DEFAULT_FGCOLOR	EFI_LIGHTGRAY
40198160Srrs#define	DEFAULT_BGCOLOR	EFI_BLACK
41198160Srrs
42198160Srrs#define	MAXARGS	8
43198160Srrsstatic int args[MAXARGS], argc;
44198160Srrsstatic int fg_c, bg_c, curx, cury;
45198160Srrsstatic int esc;
46198160Srrs
47198160Srrsvoid get_pos(int *x, int *y);
48198160Srrsvoid curs_move(int *_x, int *_y, int x, int y);
49198160Srrsstatic void CL(int);
50198160Srrsvoid HO(void);
51198160Srrsvoid end_term(void);
52198160Srrs#endif
53198160Srrs
54198160Srrsstatic void efi_cons_probe(struct console *);
55198160Srrsstatic int efi_cons_init(int);
56198160Srrsvoid efi_cons_putchar(int);
57198160Srrsint efi_cons_getchar(void);
58198160Srrsvoid efi_cons_efiputchar(int);
59198160Srrsint efi_cons_poll(void);
60198160Srrs
61198160Srrsstruct console efi_console = {
62198160Srrs	"efi",
63198160Srrs	"EFI console",
64198160Srrs	0,
65198160Srrs	efi_cons_probe,
66198160Srrs	efi_cons_init,
67198160Srrs	efi_cons_putchar,
68198160Srrs	efi_cons_getchar,
69198160Srrs	efi_cons_poll
70198956Srrs};
71198160Srrs
72198956Srrs#ifdef TERM_EMU
73198956Srrs
74198160Srrs/* Get cursor position. */
75198160Srrsvoid
76198160Srrsget_pos(int *x, int *y)
77198160Srrs{
78198160Srrs	*x = conout->Mode->CursorColumn;
79198160Srrs	*y = conout->Mode->CursorRow;
80198160Srrs}
81198160Srrs
82198160Srrs/* Move cursor to x rows and y cols (0-based). */
83198160Srrsvoid
84198160Srrscurs_move(int *_x, int *_y, int x, int y)
85198160Srrs{
86198160Srrs	conout->SetCursorPosition(conout, x, y);
87198160Srrs	if (_x != NULL)
88198160Srrs		*_x = conout->Mode->CursorColumn;
89198160Srrs	if (_y != NULL)
90198160Srrs		*_y = conout->Mode->CursorRow;
91198160Srrs}
92198160Srrs
93198160Srrs/* Clear internal state of the terminal emulation code. */
94198160Srrsvoid
95198160Srrsend_term(void)
96198160Srrs{
97198160Srrs	esc = 0;
98198160Srrs	argc = -1;
99198160Srrs}
100198625Srrs
101198160Srrs#endif
102198160Srrs
103198160Srrsstatic void
104198160Srrsefi_cons_probe(struct console *cp)
105198160Srrs{
106198160Srrs	conout = ST->ConOut;
107198160Srrs	conin = ST->ConIn;
108198160Srrs	cp->c_flags |= C_PRESENTIN | C_PRESENTOUT;
109198160Srrs}
110198160Srrs
111198160Srrsstatic int
112198160Srrsefi_cons_init(int arg)
113198160Srrs{
114198160Srrs	conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR,
115198625Srrs	    DEFAULT_BGCOLOR));
116#ifdef TERM_EMU
117	end_term();
118	get_pos(&curx, &cury);
119	curs_move(&curx, &cury, curx, cury);
120	fg_c = DEFAULT_FGCOLOR;
121	bg_c = DEFAULT_BGCOLOR;
122#endif
123	conout->EnableCursor(conout, TRUE);
124	return 0;
125}
126
127static void
128efi_cons_rawputchar(int c)
129{
130	int i;
131	UINTN x, y;
132	conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
133
134	if (c == '\t')
135		/* XXX lame tab expansion */
136		for (i = 0; i < 8; i++)
137			efi_cons_rawputchar(' ');
138	else {
139#ifndef	TERM_EMU
140		if (c == '\n')
141			efi_cons_efiputchar('\r');
142		else
143			efi_cons_efiputchar(c);
144#else
145		switch (c) {
146		case '\r':
147			curx = 0;
148			curs_move(&curx, &cury, curx, cury);
149			return;
150		case '\n':
151			cury++;
152			if (cury >= y) {
153				efi_cons_efiputchar('\n');
154				cury--;
155			} else
156				curs_move(&curx, &cury, curx, cury);
157			return;
158		case '\b':
159			if (curx > 0) {
160				curx--;
161				curs_move(&curx, &cury, curx, cury);
162			}
163			return;
164		default:
165			efi_cons_efiputchar(c);
166			curx++;
167			if (curx > x-1) {
168				curx = 0;
169				cury++;
170			}
171			if (cury > y-1) {
172				curx = 0;
173				cury--;
174			}
175		}
176		curs_move(&curx, &cury, curx, cury);
177#endif
178	}
179}
180
181/* Gracefully exit ESC-sequence processing in case of misunderstanding. */
182static void
183bail_out(int c)
184{
185	char buf[16], *ch;
186	int i;
187
188	if (esc) {
189		efi_cons_rawputchar('\033');
190		if (esc != '\033')
191			efi_cons_rawputchar(esc);
192		for (i = 0; i <= argc; ++i) {
193			sprintf(buf, "%d", args[i]);
194			ch = buf;
195			while (*ch)
196				efi_cons_rawputchar(*ch++);
197		}
198	}
199	efi_cons_rawputchar(c);
200	end_term();
201}
202
203/* Clear display from current position to end of screen. */
204static void
205CD(void) {
206	int i;
207	UINTN x, y;
208
209	get_pos(&curx, &cury);
210	if (curx == 0 && cury == 0) {
211		conout->ClearScreen(conout);
212		end_term();
213		return;
214	}
215
216	conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
217	CL(0);  /* clear current line from cursor to end */
218	for (i = cury + 1; i < y-1; i++) {
219		curs_move(NULL, NULL, 0, i);
220		CL(0);
221	}
222	curs_move(NULL, NULL, curx, cury);
223	end_term();
224}
225
226/*
227 * Absolute cursor move to args[0] rows and args[1] columns
228 * (the coordinates are 1-based).
229 */
230static void
231CM(void)
232{
233	if (args[0] > 0)
234		args[0]--;
235	if (args[1] > 0)
236		args[1]--;
237	curs_move(&curx, &cury, args[1], args[0]);
238	end_term();
239}
240
241/* Home cursor (left top corner), also called from mode command. */
242void
243HO(void)
244{
245	argc = 1;
246	args[0] = args[1] = 1;
247	CM();
248}
249
250/* Clear line from current position to end of line */
251static void
252CL(int direction)
253{
254	int i, len;
255	UINTN x, y;
256	CHAR16 *line;
257
258	conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
259	switch (direction) {
260	case 0:         /* from cursor to end */
261		len = x - curx + 1;
262		break;
263	case 1:         /* from beginning to cursor */
264		len = curx;
265		break;
266	case 2:         /* entire line */
267		len = x;
268		break;
269	}
270
271	if (cury == y - 1)
272		len--;
273
274	line = malloc(len * sizeof (CHAR16));
275	if (line == NULL) {
276		printf("out of memory\n");
277		return;
278	}
279	for (i = 0; i < len; i++)
280		line[i] = ' ';
281	line[len-1] = 0;
282
283	if (direction != 0)
284		curs_move(NULL, NULL, 0, cury);
285
286	conout->OutputString(conout, line);
287	/* restore cursor position */
288	curs_move(NULL, NULL, curx, cury);
289	free(line);
290	end_term();
291}
292
293static void
294get_arg(int c)
295{
296	if (argc < 0)
297		argc = 0;
298	args[argc] *= 10;
299	args[argc] += c - '0';
300}
301
302/* Emulate basic capabilities of cons25 terminal */
303static void
304efi_term_emu(int c)
305{
306	static int ansi_col[] = {
307		0, 4, 2, 6, 1, 5, 3, 7
308	};
309	int t, i;
310
311	switch (esc) {
312	case 0:
313		switch (c) {
314		case '\033':
315			esc = c;
316			break;
317		default:
318			efi_cons_rawputchar(c);
319			break;
320		}
321		break;
322	case '\033':
323		switch (c) {
324		case '[':
325			esc = c;
326			args[0] = 0;
327			argc = -1;
328			break;
329		default:
330			bail_out(c);
331			break;
332		}
333		break;
334	case '[':
335		switch (c) {
336		case ';':
337			if (argc < 0)
338				argc = 0;
339			else if (argc + 1 >= MAXARGS)
340				bail_out(c);
341			else
342				args[++argc] = 0;
343			break;
344		case 'H':               /* ho = \E[H */
345			if (argc < 0)
346				HO();
347			else if (argc == 1)
348				CM();
349			else
350				bail_out(c);
351			break;
352		case 'J':               /* cd = \E[J */
353			if (argc < 0)
354				CD();
355			else
356				bail_out(c);
357			break;
358		case 'm':
359			if (argc < 0) {
360				fg_c = DEFAULT_FGCOLOR;
361				bg_c = DEFAULT_BGCOLOR;
362			}
363			for (i = 0; i <= argc; ++i) {
364				switch (args[i]) {
365				case 0:         /* back to normal */
366					fg_c = DEFAULT_FGCOLOR;
367					bg_c = DEFAULT_BGCOLOR;
368					break;
369				case 1:         /* bold */
370					fg_c |= 0x8;
371					break;
372				case 4:         /* underline */
373				case 5:         /* blink */
374					bg_c |= 0x8;
375					break;
376				case 7:         /* reverse */
377					t = fg_c;
378					fg_c = bg_c;
379					bg_c = t;
380					break;
381				case 30: case 31: case 32: case 33:
382				case 34: case 35: case 36: case 37:
383					fg_c = ansi_col[args[i] - 30];
384					break;
385				case 39:        /* normal */
386					fg_c = DEFAULT_FGCOLOR;
387					break;
388				case 40: case 41: case 42: case 43:
389				case 44: case 45: case 46: case 47:
390					bg_c = ansi_col[args[i] - 40];
391					break;
392				case 49:        /* normal */
393					bg_c = DEFAULT_BGCOLOR;
394					break;
395				}
396			}
397			conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c));
398			end_term();
399			break;
400		default:
401			if (isdigit(c))
402				get_arg(c);
403			else
404				bail_out(c);
405			break;
406		}
407		break;
408	default:
409		bail_out(c);
410		break;
411	}
412}
413
414void
415efi_cons_putchar(int c)
416{
417#ifdef TERM_EMU
418	efi_term_emu(c);
419#else
420	efi_cons_rawputchar(c);
421#endif
422}
423
424int
425efi_cons_getchar()
426{
427	EFI_INPUT_KEY key;
428	EFI_STATUS status;
429	UINTN junk;
430
431	/* Try to read a key stroke. We wait for one if none is pending. */
432	status = conin->ReadKeyStroke(conin, &key);
433	if (status == EFI_NOT_READY) {
434		BS->WaitForEvent(1, &conin->WaitForKey, &junk);
435		status = conin->ReadKeyStroke(conin, &key);
436	}
437	switch (key.ScanCode) {
438	case 0x17: /* ESC */
439		return (0x1b);  /* esc */
440	}
441
442	/* this can return  */
443	return (key.UnicodeChar);
444}
445
446int
447efi_cons_poll()
448{
449	/* This can clear the signaled state. */
450	return (BS->CheckEvent(conin->WaitForKey) == EFI_SUCCESS);
451}
452
453/* Plain direct access to EFI OutputString(). */
454void
455efi_cons_efiputchar(int c)
456{
457	CHAR16 buf[2];
458
459	/*
460	 * translate box chars to unicode
461	 */
462	switch (c) {
463	/* single frame */
464	case 0xb3: buf[0] = BOXDRAW_VERTICAL; break;
465	case 0xbf: buf[0] = BOXDRAW_DOWN_LEFT; break;
466	case 0xc0: buf[0] = BOXDRAW_UP_RIGHT; break;
467	case 0xc4: buf[0] = BOXDRAW_HORIZONTAL; break;
468	case 0xda: buf[0] = BOXDRAW_DOWN_RIGHT; break;
469	case 0xd9: buf[0] = BOXDRAW_UP_LEFT; break;
470
471	/* double frame */
472	case 0xba: buf[0] = BOXDRAW_DOUBLE_VERTICAL; break;
473	case 0xbb: buf[0] = BOXDRAW_DOUBLE_DOWN_LEFT; break;
474	case 0xbc: buf[0] = BOXDRAW_DOUBLE_UP_LEFT; break;
475	case 0xc8: buf[0] = BOXDRAW_DOUBLE_UP_RIGHT; break;
476	case 0xc9: buf[0] = BOXDRAW_DOUBLE_DOWN_RIGHT; break;
477	case 0xcd: buf[0] = BOXDRAW_DOUBLE_HORIZONTAL; break;
478
479	default:
480		buf[0] = c;
481	}
482        buf[1] = 0;     /* terminate string */
483
484	conout->OutputString(conout, buf);
485}
486