vidconsole.c revision 302408
1/*-
2 * Copyright (c) 1998 Michael Smith (msmith@freebsd.org)
3 * Copyright (c) 1997 Kazutaka YOKOTA (yokota@zodiac.mech.utsunomiya-u.ac.jp)
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * 	Id: probe_keyboard.c,v 1.13 1997/06/09 05:10:55 bde Exp
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: stable/11/sys/boot/i386/libi386/vidconsole.c 232309 2012-02-29 18:11:33Z jkim $");
32
33#include <stand.h>
34#include <bootstrap.h>
35#include <btxv86.h>
36#include <machine/psl.h>
37#include "libi386.h"
38
39#if KEYBOARD_PROBE
40#include <machine/cpufunc.h>
41
42static int	probe_keyboard(void);
43#endif
44static void	vidc_probe(struct console *cp);
45static int	vidc_init(int arg);
46static void	vidc_putchar(int c);
47static int	vidc_getchar(void);
48static int	vidc_ischar(void);
49
50static int	vidc_started;
51
52#ifdef TERM_EMU
53#define MAXARGS		8
54#define DEFAULT_FGCOLOR	7
55#define DEFAULT_BGCOLOR	0
56
57void		end_term(void);
58void		bail_out(int c);
59void		vidc_term_emu(int c);
60void		get_pos(int *x, int *y);
61void		curs_move(int *_x, int *_y, int x, int y);
62void		write_char(int c, int fg, int bg);
63void		scroll_up(int rows, int fg, int bg);
64void		CD(void);
65void		CM(void);
66void		HO(void);
67
68static int	args[MAXARGS], argc;
69static int	fg_c, bg_c, curx, cury;
70static int	esc;
71#endif
72
73
74struct console vidconsole = {
75    "vidconsole",
76    "internal video/keyboard",
77    0,
78    vidc_probe,
79    vidc_init,
80    vidc_putchar,
81    vidc_getchar,
82    vidc_ischar
83};
84
85static void
86vidc_probe(struct console *cp)
87{
88
89    /* look for a keyboard */
90#if KEYBOARD_PROBE
91    if (probe_keyboard())
92#endif
93    {
94
95	cp->c_flags |= C_PRESENTIN;
96    }
97
98    /* XXX for now, always assume we can do BIOS screen output */
99    cp->c_flags |= C_PRESENTOUT;
100}
101
102static int
103vidc_init(int arg)
104{
105    int		i;
106
107    if (vidc_started && arg == 0)
108	return (0);
109    vidc_started = 1;
110#ifdef TERM_EMU
111    /* Init terminal emulator */
112    end_term();
113    get_pos(&curx, &cury);
114    curs_move(&curx, &cury, curx, cury);
115    fg_c = DEFAULT_FGCOLOR;
116    bg_c = DEFAULT_BGCOLOR;
117#endif
118    for (i = 0; i < 10 && vidc_ischar(); i++)
119	(void)vidc_getchar();
120    return (0);	/* XXX reinit? */
121}
122
123void
124vidc_biosputchar(int c)
125{
126
127    v86.ctl = 0;
128    v86.addr = 0x10;
129    v86.eax = 0xe00 | (c & 0xff);
130    v86.ebx = 0x7;
131    v86int();
132}
133
134static void
135vidc_rawputchar(int c)
136{
137    int		i;
138
139    if (c == '\t')
140	/* lame tab expansion */
141	for (i = 0; i < 8; i++)
142	    vidc_rawputchar(' ');
143    else {
144#ifndef TERM_EMU
145        vidc_biosputchar(c);
146#else
147	/* Emulate AH=0eh (teletype output) */
148	switch(c) {
149	case '\a':
150	    vidc_biosputchar(c);
151	    return;
152	case '\r':
153	    curx = 0;
154	    curs_move(&curx, &cury, curx, cury);
155	    return;
156	case '\n':
157	    cury++;
158	    if (cury > 24) {
159		scroll_up(1, fg_c, bg_c);
160		cury--;
161	    } else {
162		curs_move(&curx, &cury, curx, cury);
163	    }
164	    return;
165	case '\b':
166	    if (curx > 0) {
167		curx--;
168		curs_move(&curx, &cury, curx, cury);
169		/* write_char(' ', fg_c, bg_c); XXX destructive(!) */
170		return;
171	    }
172	    return;
173	default:
174	    write_char(c, fg_c, bg_c);
175	    curx++;
176	    if (curx > 79) {
177		curx = 0;
178		cury++;
179	    }
180	    if (cury > 24) {
181		curx = 0;
182		scroll_up(1, fg_c, bg_c);
183		cury--;
184	    }
185	}
186	curs_move(&curx, &cury, curx, cury);
187#endif
188    }
189}
190
191#ifdef TERM_EMU
192
193/* Get cursor position on the screen. Result is in edx. Sets
194 * curx and cury appropriately.
195 */
196void
197get_pos(int *x, int *y)
198{
199
200    v86.ctl = 0;
201    v86.addr = 0x10;
202    v86.eax = 0x0300;
203    v86.ebx = 0x0;
204    v86int();
205    *x = v86.edx & 0x00ff;
206    *y = (v86.edx & 0xff00) >> 8;
207}
208
209/* Move cursor to x rows and y cols (0-based). */
210void
211curs_move(int *_x, int *_y, int x, int y)
212{
213
214    v86.ctl = 0;
215    v86.addr = 0x10;
216    v86.eax = 0x0200;
217    v86.ebx = 0x0;
218    v86.edx = ((0x00ff & y) << 8) + (0x00ff & x);
219    v86int();
220    *_x = x;
221    *_y = y;
222    /* If there is ctrl char at this position, cursor would be invisible.
223     * Make it a space instead.
224     */
225    v86.ctl = 0;
226    v86.addr = 0x10;
227    v86.eax = 0x0800;
228    v86.ebx = 0x0;
229    v86int();
230#define isvisible(c)	(((c) >= 32) && ((c) < 255))
231    if (!isvisible(v86.eax & 0x00ff)) {
232	write_char(' ', fg_c, bg_c);
233    }
234}
235
236/* Scroll up the whole window by a number of rows. If rows==0,
237 * clear the window. fg and bg are attributes for the new lines
238 * inserted in the window.
239 */
240void
241scroll_up(int rows, int fgcol, int bgcol)
242{
243
244    if (rows == 0)
245	rows = 25;
246    v86.ctl = 0;
247    v86.addr = 0x10;
248    v86.eax = 0x0600 + (0x00ff & rows);
249    v86.ebx = (bgcol << 12) + (fgcol << 8);
250    v86.ecx = 0x0;
251    v86.edx = 0x184f;
252    v86int();
253}
254
255/* Write character and attribute at cursor position. */
256void
257write_char(int c, int fgcol, int bgcol)
258{
259
260    v86.ctl = 0;
261    v86.addr = 0x10;
262    v86.eax = 0x0900 + (0x00ff & c);
263    v86.ebx = (bgcol << 4) + fgcol;
264    v86.ecx = 0x1;
265    v86int();
266}
267
268/**************************************************************/
269/*
270 * Screen manipulation functions. They use accumulated data in
271 * args[] and argc variables.
272 *
273 */
274
275/* Clear display from current position to end of screen */
276void
277CD(void)
278{
279
280    get_pos(&curx, &cury);
281    if (curx > 0) {
282	v86.ctl = 0;
283	v86.addr = 0x10;
284	v86.eax = 0x0600;
285	v86.ebx = (bg_c << 4) + fg_c;
286	v86.ecx = (cury << 8) + curx;
287	v86.edx = (cury << 8) + 79;
288	v86int();
289	if (++cury > 24) {
290	    end_term();
291	    return;
292	}
293    }
294    v86.ctl = 0;
295    v86.addr = 0x10;
296    v86.eax = 0x0600;
297    v86.ebx = (bg_c << 4) + fg_c;
298    v86.ecx = (cury << 8) + 0;
299    v86.edx = (24 << 8) + 79;
300    v86int();
301    end_term();
302}
303
304/* Absolute cursor move to args[0] rows and args[1] columns
305 * (the coordinates are 1-based).
306 */
307void
308CM(void)
309{
310
311    if (args[0] > 0)
312	args[0]--;
313    if (args[1] > 0)
314	args[1]--;
315    curs_move(&curx, &cury, args[1], args[0]);
316    end_term();
317}
318
319/* Home cursor (left top corner) */
320void
321HO(void)
322{
323
324    argc = 1;
325    args[0] = args[1] = 1;
326    CM();
327}
328
329/* Clear internal state of the terminal emulation code */
330void
331end_term(void)
332{
333
334    esc = 0;
335    argc = -1;
336}
337
338/* Gracefully exit ESC-sequence processing in case of misunderstanding */
339void
340bail_out(int c)
341{
342    char buf[16], *ch;
343    int i;
344
345    if (esc) {
346	vidc_rawputchar('\033');
347	if (esc != '\033')
348	    vidc_rawputchar(esc);
349	for (i = 0; i <= argc; ++i) {
350	    sprintf(buf, "%d", args[i]);
351	    ch = buf;
352	    while (*ch)
353		vidc_rawputchar(*ch++);
354	}
355    }
356    vidc_rawputchar(c);
357    end_term();
358}
359
360static void
361get_arg(int c)
362{
363
364    if (argc < 0)
365	argc = 0;
366    args[argc] *= 10;
367    args[argc] += c - '0';
368}
369
370/* Emulate basic capabilities of cons25 terminal */
371void
372vidc_term_emu(int c)
373{
374    static int ansi_col[] = {
375	0, 4, 2, 6, 1, 5, 3, 7,
376    };
377    int t;
378    int i;
379
380    switch (esc) {
381    case 0:
382	switch (c) {
383	case '\033':
384	    esc = c;
385	    break;
386	default:
387	    vidc_rawputchar(c);
388	    break;
389	}
390	break;
391
392    case '\033':
393	switch (c) {
394	case '[':
395	    esc = c;
396	    args[0] = 0;
397	    argc = -1;
398	    break;
399	default:
400	    bail_out(c);
401	    break;
402	}
403	break;
404
405    case '[':
406	switch (c) {
407	case ';':
408	    if (argc < 0)	/* XXX */
409		argc = 0;
410	    else if (argc + 1 >= MAXARGS)
411		bail_out(c);
412	    else
413		args[++argc] = 0;
414	    break;
415	case 'H':
416	    if (argc < 0)
417		HO();
418	    else if (argc == 1)
419		CM();
420	    else
421		bail_out(c);
422	    break;
423	case 'J':
424	    if (argc < 0)
425		CD();
426	    else
427		bail_out(c);
428	    break;
429	case 'm':
430	    if (argc < 0) {
431		fg_c = DEFAULT_FGCOLOR;
432		bg_c = DEFAULT_BGCOLOR;
433	    }
434	    for (i = 0; i <= argc; ++i) {
435		switch (args[i]) {
436		case 0:		/* back to normal */
437		    fg_c = DEFAULT_FGCOLOR;
438		    bg_c = DEFAULT_BGCOLOR;
439		    break;
440		case 1:		/* bold */
441		    fg_c |= 0x8;
442		    break;
443		case 4:		/* underline */
444		case 5:		/* blink */
445		    bg_c |= 0x8;
446		    break;
447		case 7:		/* reverse */
448		    t = fg_c;
449		    fg_c = bg_c;
450		    bg_c = t;
451		    break;
452		case 30: case 31: case 32: case 33:
453		case 34: case 35: case 36: case 37:
454		    fg_c = ansi_col[args[i] - 30];
455		    break;
456		case 39:	/* normal */
457		    fg_c = DEFAULT_FGCOLOR;
458		    break;
459		case 40: case 41: case 42: case 43:
460		case 44: case 45: case 46: case 47:
461		    bg_c = ansi_col[args[i] - 40];
462		    break;
463		case 49:	/* normal */
464		    bg_c = DEFAULT_BGCOLOR;
465		    break;
466		}
467	    }
468	    end_term();
469	    break;
470	default:
471	    if (isdigit(c))
472		get_arg(c);
473	    else
474		bail_out(c);
475	    break;
476	}
477	break;
478
479    default:
480	bail_out(c);
481	break;
482    }
483}
484#endif
485
486static void
487vidc_putchar(int c)
488{
489#ifdef TERM_EMU
490    vidc_term_emu(c);
491#else
492    vidc_rawputchar(c);
493#endif
494}
495
496static int
497vidc_getchar(void)
498{
499
500    if (vidc_ischar()) {
501	v86.ctl = 0;
502	v86.addr = 0x16;
503	v86.eax = 0x0;
504	v86int();
505	return (v86.eax & 0xff);
506    } else {
507	return (-1);
508    }
509}
510
511static int
512vidc_ischar(void)
513{
514
515    v86.ctl = V86_FLAGS;
516    v86.addr = 0x16;
517    v86.eax = 0x100;
518    v86int();
519    return (!V86_ZR(v86.efl));
520}
521
522#if KEYBOARD_PROBE
523
524#define PROBE_MAXRETRY	5
525#define PROBE_MAXWAIT	400
526#define IO_DUMMY	0x84
527#define IO_KBD		0x060		/* 8042 Keyboard */
528
529/* selected defines from kbdio.h */
530#define KBD_STATUS_PORT 	4	/* status port, read */
531#define KBD_DATA_PORT		0	/* data port, read/write
532					 * also used as keyboard command
533					 * and mouse command port
534					 */
535#define KBDC_ECHO		0x00ee
536#define KBDS_ANY_BUFFER_FULL	0x0001
537#define KBDS_INPUT_BUFFER_FULL	0x0002
538#define KBD_ECHO		0x00ee
539
540/* 7 microsec delay necessary for some keyboard controllers */
541static void
542delay7(void)
543{
544    /*
545     * I know this is broken, but no timer is available yet at this stage...
546     * See also comments in `delay1ms()'.
547     */
548    inb(IO_DUMMY); inb(IO_DUMMY);
549    inb(IO_DUMMY); inb(IO_DUMMY);
550    inb(IO_DUMMY); inb(IO_DUMMY);
551}
552
553/*
554 * This routine uses an inb to an unused port, the time to execute that
555 * inb is approximately 1.25uS.  This value is pretty constant across
556 * all CPU's and all buses, with the exception of some PCI implentations
557 * that do not forward this I/O address to the ISA bus as they know it
558 * is not a valid ISA bus address, those machines execute this inb in
559 * 60 nS :-(.
560 *
561 */
562static void
563delay1ms(void)
564{
565    int i = 800;
566    while (--i >= 0)
567	(void)inb(0x84);
568}
569
570/*
571 * We use the presence/absence of a keyboard to determine whether the internal
572 * console can be used for input.
573 *
574 * Perform a simple test on the keyboard; issue the ECHO command and see
575 * if the right answer is returned. We don't do anything as drastic as
576 * full keyboard reset; it will be too troublesome and take too much time.
577 */
578static int
579probe_keyboard(void)
580{
581    int retry = PROBE_MAXRETRY;
582    int wait;
583    int i;
584
585    while (--retry >= 0) {
586	/* flush any noise */
587	while (inb(IO_KBD + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL) {
588	    delay7();
589	    inb(IO_KBD + KBD_DATA_PORT);
590	    delay1ms();
591	}
592
593	/* wait until the controller can accept a command */
594	for (wait = PROBE_MAXWAIT; wait > 0; --wait) {
595	    if (((i = inb(IO_KBD + KBD_STATUS_PORT))
596                & (KBDS_INPUT_BUFFER_FULL | KBDS_ANY_BUFFER_FULL)) == 0)
597		break;
598	    if (i & KBDS_ANY_BUFFER_FULL) {
599		delay7();
600	        inb(IO_KBD + KBD_DATA_PORT);
601	    }
602	    delay1ms();
603	}
604	if (wait <= 0)
605	    continue;
606
607	/* send the ECHO command */
608	outb(IO_KBD + KBD_DATA_PORT, KBDC_ECHO);
609
610	/* wait for a response */
611	for (wait = PROBE_MAXWAIT; wait > 0; --wait) {
612	     if (inb(IO_KBD + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL)
613		 break;
614	     delay1ms();
615	}
616	if (wait <= 0)
617	    continue;
618
619	delay7();
620	i = inb(IO_KBD + KBD_DATA_PORT);
621#ifdef PROBE_KBD_BEBUG
622        printf("probe_keyboard: got 0x%x.\n", i);
623#endif
624	if (i == KBD_ECHO) {
625	    /* got the right answer */
626	    return (1);
627	}
628    }
629
630    return (0);
631}
632#endif /* KEYBOARD_PROBE */
633