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