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: releng/11.0/sys/boot/efi/libefi/efi_console.c 301703 2016-06-08 23:13:20Z andrew $");
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);
50293724Ssmhvoid HO(void);
51293724Ssmhvoid end_term(void);
52293233Semaste#endif
53293233Semaste
54293233Semastestatic void efi_cons_probe(struct console *);
55293233Semastestatic int efi_cons_init(int);
56293233Semastevoid efi_cons_putchar(int);
57293233Semasteint efi_cons_getchar(void);
58293233Semastevoid efi_cons_efiputchar(int);
59293233Semasteint efi_cons_poll(void);
60293233Semaste
61293233Semastestruct console efi_console = {
62293233Semaste	"efi",
63293233Semaste	"EFI console",
64300056Simp	C_WIDEOUT,
65293233Semaste	efi_cons_probe,
66293233Semaste	efi_cons_init,
67293233Semaste	efi_cons_putchar,
68293233Semaste	efi_cons_getchar,
69293233Semaste	efi_cons_poll
70293233Semaste};
71293233Semaste
72293233Semaste#ifdef TERM_EMU
73293233Semaste
74293233Semaste/* Get cursor position. */
75293233Semastevoid
76293233Semasteget_pos(int *x, int *y)
77293233Semaste{
78293233Semaste	*x = conout->Mode->CursorColumn;
79293233Semaste	*y = conout->Mode->CursorRow;
80293233Semaste}
81293233Semaste
82293233Semaste/* Move cursor to x rows and y cols (0-based). */
83293233Semastevoid
84293233Semastecurs_move(int *_x, int *_y, int x, int y)
85293233Semaste{
86293233Semaste	conout->SetCursorPosition(conout, x, y);
87293233Semaste	if (_x != NULL)
88293233Semaste		*_x = conout->Mode->CursorColumn;
89293233Semaste	if (_y != NULL)
90293233Semaste		*_y = conout->Mode->CursorRow;
91293233Semaste}
92293233Semaste
93293233Semaste/* Clear internal state of the terminal emulation code. */
94293233Semastevoid
95293233Semasteend_term(void)
96293233Semaste{
97293233Semaste	esc = 0;
98293233Semaste	argc = -1;
99293233Semaste}
100293233Semaste
101293233Semaste#endif
102293233Semaste
10377943Sdfrstatic void
10477943Sdfrefi_cons_probe(struct console *cp)
10577943Sdfr{
10677943Sdfr	conout = ST->ConOut;
10777943Sdfr	conin = ST->ConIn;
10877943Sdfr	cp->c_flags |= C_PRESENTIN | C_PRESENTOUT;
10977943Sdfr}
11077943Sdfr
11177943Sdfrstatic int
11277943Sdfrefi_cons_init(int arg)
11377943Sdfr{
114301702Sandrew#ifdef TERM_EMU
115293233Semaste	conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR,
116293233Semaste	    DEFAULT_BGCOLOR));
117293233Semaste	end_term();
118293233Semaste	get_pos(&curx, &cury);
119293233Semaste	curs_move(&curx, &cury, curx, cury);
120293233Semaste	fg_c = DEFAULT_FGCOLOR;
121293233Semaste	bg_c = DEFAULT_BGCOLOR;
122293233Semaste#endif
123293233Semaste	conout->EnableCursor(conout, TRUE);
12477943Sdfr	return 0;
12577943Sdfr}
12677943Sdfr
127293233Semastestatic void
128293233Semasteefi_cons_rawputchar(int c)
129293233Semaste{
130293233Semaste	int i;
131293233Semaste	UINTN x, y;
132293233Semaste	conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
133293233Semaste
134293233Semaste	if (c == '\t')
135293233Semaste		/* XXX lame tab expansion */
136293233Semaste		for (i = 0; i < 8; i++)
137293233Semaste			efi_cons_rawputchar(' ');
138293233Semaste	else {
139293233Semaste#ifndef	TERM_EMU
140293233Semaste		if (c == '\n')
141293233Semaste			efi_cons_efiputchar('\r');
142301703Sandrew		efi_cons_efiputchar(c);
143293233Semaste#else
144293233Semaste		switch (c) {
145293233Semaste		case '\r':
146293233Semaste			curx = 0;
147293233Semaste			curs_move(&curx, &cury, curx, cury);
148293233Semaste			return;
149293233Semaste		case '\n':
150293233Semaste			cury++;
151293233Semaste			if (cury >= y) {
152293233Semaste				efi_cons_efiputchar('\n');
153293233Semaste				cury--;
154293233Semaste			} else
155293233Semaste				curs_move(&curx, &cury, curx, cury);
156293233Semaste			return;
157293233Semaste		case '\b':
158293233Semaste			if (curx > 0) {
159293233Semaste				curx--;
160293233Semaste				curs_move(&curx, &cury, curx, cury);
161293233Semaste			}
162293233Semaste			return;
163293233Semaste		default:
164293233Semaste			efi_cons_efiputchar(c);
165293233Semaste			curx++;
166293233Semaste			if (curx > x-1) {
167293233Semaste				curx = 0;
168293233Semaste				cury++;
169293233Semaste			}
170293233Semaste			if (cury > y-1) {
171293233Semaste				curx = 0;
172293233Semaste				cury--;
173293233Semaste			}
174293233Semaste		}
175293233Semaste		curs_move(&curx, &cury, curx, cury);
176293233Semaste#endif
177293233Semaste	}
178293233Semaste}
179293233Semaste
180301702Sandrew#ifdef TERM_EMU
181293233Semaste/* Gracefully exit ESC-sequence processing in case of misunderstanding. */
182293233Semastestatic void
183293233Semastebail_out(int c)
184293233Semaste{
185293233Semaste	char buf[16], *ch;
186293233Semaste	int i;
187293233Semaste
188293233Semaste	if (esc) {
189293233Semaste		efi_cons_rawputchar('\033');
190293233Semaste		if (esc != '\033')
191293233Semaste			efi_cons_rawputchar(esc);
192293233Semaste		for (i = 0; i <= argc; ++i) {
193293233Semaste			sprintf(buf, "%d", args[i]);
194293233Semaste			ch = buf;
195293233Semaste			while (*ch)
196293233Semaste				efi_cons_rawputchar(*ch++);
197293233Semaste		}
198293233Semaste	}
199293233Semaste	efi_cons_rawputchar(c);
200293233Semaste	end_term();
201293233Semaste}
202293233Semaste
203293233Semaste/* Clear display from current position to end of screen. */
204293233Semastestatic void
205293233SemasteCD(void) {
206293233Semaste	int i;
207293233Semaste	UINTN x, y;
208293233Semaste
209293233Semaste	get_pos(&curx, &cury);
210293233Semaste	if (curx == 0 && cury == 0) {
211293233Semaste		conout->ClearScreen(conout);
212293233Semaste		end_term();
213293233Semaste		return;
214293233Semaste	}
215293233Semaste
216293233Semaste	conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
217293233Semaste	CL(0);  /* clear current line from cursor to end */
218293233Semaste	for (i = cury + 1; i < y-1; i++) {
219293233Semaste		curs_move(NULL, NULL, 0, i);
220293233Semaste		CL(0);
221293233Semaste	}
222293233Semaste	curs_move(NULL, NULL, curx, cury);
223293233Semaste	end_term();
224293233Semaste}
225293233Semaste
226293233Semaste/*
227293233Semaste * Absolute cursor move to args[0] rows and args[1] columns
228293233Semaste * (the coordinates are 1-based).
229293233Semaste */
230293233Semastestatic void
231293233SemasteCM(void)
232293233Semaste{
233293233Semaste	if (args[0] > 0)
234293233Semaste		args[0]--;
235293233Semaste	if (args[1] > 0)
236293233Semaste		args[1]--;
237293233Semaste	curs_move(&curx, &cury, args[1], args[0]);
238293233Semaste	end_term();
239293233Semaste}
240293233Semaste
241293233Semaste/* Home cursor (left top corner), also called from mode command. */
24277943Sdfrvoid
243293233SemasteHO(void)
24477943Sdfr{
245293233Semaste	argc = 1;
246293233Semaste	args[0] = args[1] = 1;
247293233Semaste	CM();
248293233Semaste}
24977943Sdfr
250293233Semaste/* Clear line from current position to end of line */
251293233Semastestatic void
252293233SemasteCL(int direction)
253293233Semaste{
254293233Semaste	int i, len;
255293233Semaste	UINTN x, y;
256293233Semaste	CHAR16 *line;
25777943Sdfr
258293233Semaste	conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
259293233Semaste	switch (direction) {
260293233Semaste	case 0:         /* from cursor to end */
261293233Semaste		len = x - curx + 1;
262293233Semaste		break;
263293233Semaste	case 1:         /* from beginning to cursor */
264293233Semaste		len = curx;
265293233Semaste		break;
266293233Semaste	case 2:         /* entire line */
267293233Semaste		len = x;
268293233Semaste		break;
269299972Spfg	default:	/* NOTREACHED */
270299972Spfg		__unreachable();
271293233Semaste	}
27277943Sdfr
273293233Semaste	if (cury == y - 1)
274293233Semaste		len--;
275293233Semaste
276293233Semaste	line = malloc(len * sizeof (CHAR16));
277293233Semaste	if (line == NULL) {
278293233Semaste		printf("out of memory\n");
279293233Semaste		return;
280293233Semaste	}
281293233Semaste	for (i = 0; i < len; i++)
282293233Semaste		line[i] = ' ';
283293233Semaste	line[len-1] = 0;
284293233Semaste
285293233Semaste	if (direction != 0)
286293233Semaste		curs_move(NULL, NULL, 0, cury);
287293233Semaste
288293233Semaste	conout->OutputString(conout, line);
289293233Semaste	/* restore cursor position */
290293233Semaste	curs_move(NULL, NULL, curx, cury);
291293233Semaste	free(line);
292293233Semaste	end_term();
29377943Sdfr}
29477943Sdfr
295293233Semastestatic void
296293233Semasteget_arg(int c)
297293233Semaste{
298293233Semaste	if (argc < 0)
299293233Semaste		argc = 0;
300293233Semaste	args[argc] *= 10;
301293233Semaste	args[argc] += c - '0';
302293233Semaste}
303293233Semaste
304293233Semaste/* Emulate basic capabilities of cons25 terminal */
305293233Semastestatic void
306293233Semasteefi_term_emu(int c)
307293233Semaste{
308293233Semaste	static int ansi_col[] = {
309293233Semaste		0, 4, 2, 6, 1, 5, 3, 7
310293233Semaste	};
311293233Semaste	int t, i;
312293233Semaste
313293233Semaste	switch (esc) {
314293233Semaste	case 0:
315293233Semaste		switch (c) {
316293233Semaste		case '\033':
317293233Semaste			esc = c;
318293233Semaste			break;
319293233Semaste		default:
320293233Semaste			efi_cons_rawputchar(c);
321293233Semaste			break;
322293233Semaste		}
323293233Semaste		break;
324293233Semaste	case '\033':
325293233Semaste		switch (c) {
326293233Semaste		case '[':
327293233Semaste			esc = c;
328293233Semaste			args[0] = 0;
329293233Semaste			argc = -1;
330293233Semaste			break;
331293233Semaste		default:
332293233Semaste			bail_out(c);
333293233Semaste			break;
334293233Semaste		}
335293233Semaste		break;
336293233Semaste	case '[':
337293233Semaste		switch (c) {
338293233Semaste		case ';':
339293233Semaste			if (argc < 0)
340293233Semaste				argc = 0;
341293233Semaste			else if (argc + 1 >= MAXARGS)
342293233Semaste				bail_out(c);
343293233Semaste			else
344293233Semaste				args[++argc] = 0;
345293233Semaste			break;
346293233Semaste		case 'H':               /* ho = \E[H */
347293233Semaste			if (argc < 0)
348293233Semaste				HO();
349293233Semaste			else if (argc == 1)
350293233Semaste				CM();
351293233Semaste			else
352293233Semaste				bail_out(c);
353293233Semaste			break;
354293233Semaste		case 'J':               /* cd = \E[J */
355293233Semaste			if (argc < 0)
356293233Semaste				CD();
357293233Semaste			else
358293233Semaste				bail_out(c);
359293233Semaste			break;
360293233Semaste		case 'm':
361293233Semaste			if (argc < 0) {
362293233Semaste				fg_c = DEFAULT_FGCOLOR;
363293233Semaste				bg_c = DEFAULT_BGCOLOR;
364293233Semaste			}
365293233Semaste			for (i = 0; i <= argc; ++i) {
366293233Semaste				switch (args[i]) {
367293233Semaste				case 0:         /* back to normal */
368293233Semaste					fg_c = DEFAULT_FGCOLOR;
369293233Semaste					bg_c = DEFAULT_BGCOLOR;
370293233Semaste					break;
371293233Semaste				case 1:         /* bold */
372293233Semaste					fg_c |= 0x8;
373293233Semaste					break;
374293233Semaste				case 4:         /* underline */
375293233Semaste				case 5:         /* blink */
376293233Semaste					bg_c |= 0x8;
377293233Semaste					break;
378293233Semaste				case 7:         /* reverse */
379293233Semaste					t = fg_c;
380293233Semaste					fg_c = bg_c;
381293233Semaste					bg_c = t;
382293233Semaste					break;
383293233Semaste				case 30: case 31: case 32: case 33:
384293233Semaste				case 34: case 35: case 36: case 37:
385293233Semaste					fg_c = ansi_col[args[i] - 30];
386293233Semaste					break;
387293233Semaste				case 39:        /* normal */
388293233Semaste					fg_c = DEFAULT_FGCOLOR;
389293233Semaste					break;
390293233Semaste				case 40: case 41: case 42: case 43:
391293233Semaste				case 44: case 45: case 46: case 47:
392293233Semaste					bg_c = ansi_col[args[i] - 40];
393293233Semaste					break;
394293233Semaste				case 49:        /* normal */
395293233Semaste					bg_c = DEFAULT_BGCOLOR;
396293233Semaste					break;
397293233Semaste				}
398293233Semaste			}
399293233Semaste			conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c));
400293233Semaste			end_term();
401293233Semaste			break;
402293233Semaste		default:
403293233Semaste			if (isdigit(c))
404293233Semaste				get_arg(c);
405293233Semaste			else
406293233Semaste				bail_out(c);
407293233Semaste			break;
408293233Semaste		}
409293233Semaste		break;
410293233Semaste	default:
411293233Semaste		bail_out(c);
412293233Semaste		break;
413293233Semaste	}
414293233Semaste}
415301702Sandrew#else
416301702Sandrewvoid
417301702SandrewHO(void)
418301702Sandrew{
419301702Sandrew}
420301702Sandrew#endif
421293233Semaste
422293233Semastevoid
423293233Semasteefi_cons_putchar(int c)
424293233Semaste{
425293233Semaste#ifdef TERM_EMU
426293233Semaste	efi_term_emu(c);
427293233Semaste#else
428293233Semaste	efi_cons_rawputchar(c);
429293233Semaste#endif
430293233Semaste}
431293233Semaste
43277943Sdfrint
43377943Sdfrefi_cons_getchar()
43477943Sdfr{
43577943Sdfr	EFI_INPUT_KEY key;
436107682Smarcel	EFI_STATUS status;
43777943Sdfr	UINTN junk;
43877943Sdfr
439107682Smarcel	/* Try to read a key stroke. We wait for one if none is pending. */
440107682Smarcel	status = conin->ReadKeyStroke(conin, &key);
441107682Smarcel	if (status == EFI_NOT_READY) {
442107682Smarcel		BS->WaitForEvent(1, &conin->WaitForKey, &junk);
443107682Smarcel		status = conin->ReadKeyStroke(conin, &key);
444107682Smarcel	}
445293233Semaste	switch (key.ScanCode) {
446293233Semaste	case 0x17: /* ESC */
447293233Semaste		return (0x1b);  /* esc */
448293233Semaste	}
449293233Semaste
450293233Semaste	/* this can return  */
451107682Smarcel	return (key.UnicodeChar);
45277943Sdfr}
45377943Sdfr
45477943Sdfrint
45577943Sdfrefi_cons_poll()
45677943Sdfr{
457107682Smarcel	/* This can clear the signaled state. */
458107682Smarcel	return (BS->CheckEvent(conin->WaitForKey) == EFI_SUCCESS);
45977943Sdfr}
46077943Sdfr
461293233Semaste/* Plain direct access to EFI OutputString(). */
462293233Semastevoid
463293233Semasteefi_cons_efiputchar(int c)
464293233Semaste{
465293233Semaste	CHAR16 buf[2];
466293233Semaste
467293233Semaste	/*
468293233Semaste	 * translate box chars to unicode
469293233Semaste	 */
470293233Semaste	switch (c) {
471293233Semaste	/* single frame */
472293233Semaste	case 0xb3: buf[0] = BOXDRAW_VERTICAL; break;
473293233Semaste	case 0xbf: buf[0] = BOXDRAW_DOWN_LEFT; break;
474293233Semaste	case 0xc0: buf[0] = BOXDRAW_UP_RIGHT; break;
475293233Semaste	case 0xc4: buf[0] = BOXDRAW_HORIZONTAL; break;
476293233Semaste	case 0xda: buf[0] = BOXDRAW_DOWN_RIGHT; break;
477293233Semaste	case 0xd9: buf[0] = BOXDRAW_UP_LEFT; break;
478293233Semaste
479293233Semaste	/* double frame */
480293233Semaste	case 0xba: buf[0] = BOXDRAW_DOUBLE_VERTICAL; break;
481293233Semaste	case 0xbb: buf[0] = BOXDRAW_DOUBLE_DOWN_LEFT; break;
482293233Semaste	case 0xbc: buf[0] = BOXDRAW_DOUBLE_UP_LEFT; break;
483293233Semaste	case 0xc8: buf[0] = BOXDRAW_DOUBLE_UP_RIGHT; break;
484293233Semaste	case 0xc9: buf[0] = BOXDRAW_DOUBLE_DOWN_RIGHT; break;
485293233Semaste	case 0xcd: buf[0] = BOXDRAW_DOUBLE_HORIZONTAL; break;
486293233Semaste
487293233Semaste	default:
488293233Semaste		buf[0] = c;
489293233Semaste	}
490293233Semaste        buf[1] = 0;     /* terminate string */
491293233Semaste
492293233Semaste	conout->OutputString(conout, buf);
493293233Semaste}
494