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$");
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
42 static int probe_keyboard(void);
43 #endif
44 static void vidc_probe(struct console *cp);
45 static int vidc_init(int arg);
46 static void vidc_putchar(int c);
47 static int vidc_getchar(void);
48 static int vidc_ischar(void);
49
50 static int vidc_started;
51
52 #ifdef TERM_EMU
53 #define MAXARGS 8
54 #define DEFAULT_FGCOLOR 7
55 #define DEFAULT_BGCOLOR 0
56
57 void end_term(void);
58 void bail_out(int c);
59 void vidc_term_emu(int c);
60 void get_pos(int *x, int *y);
61 void curs_move(int *_x, int *_y, int x, int y);
62 void write_char(int c, int fg, int bg);
63 void scroll_up(int rows, int fg, int bg);
64 void CD(void);
65 void CM(void);
66 void HO(void);
67
68 static int args[MAXARGS], argc;
69 static int fg_c, bg_c, curx, cury;
70 static int esc;
71 #endif
72
73
74 struct 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
85 static void
vidc_probe(struct console * cp)86 vidc_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
102 static int
vidc_init(int arg)103 vidc_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
123 void
vidc_biosputchar(int c)124 vidc_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
134 static void
vidc_rawputchar(int c)135 vidc_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 */
196 void
get_pos(int * x,int * y)197 get_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). */
210 void
curs_move(int * _x,int * _y,int x,int y)211 curs_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 */
240 void
scroll_up(int rows,int fgcol,int bgcol)241 scroll_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. */
256 void
write_char(int c,int fgcol,int bgcol)257 write_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 */
276 void
CD(void)277 CD(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 */
307 void
CM(void)308 CM(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) */
320 void
HO(void)321 HO(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 */
330 void
end_term(void)331 end_term(void)
332 {
333
334 esc = 0;
335 argc = -1;
336 }
337
338 /* Gracefully exit ESC-sequence processing in case of misunderstanding */
339 void
bail_out(int c)340 bail_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
360 static void
get_arg(int c)361 get_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 */
371 void
vidc_term_emu(int c)372 vidc_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
486 static void
vidc_putchar(int c)487 vidc_putchar(int c)
488 {
489 #ifdef TERM_EMU
490 vidc_term_emu(c);
491 #else
492 vidc_rawputchar(c);
493 #endif
494 }
495
496 static int
vidc_getchar(void)497 vidc_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
511 static int
vidc_ischar(void)512 vidc_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 */
541 static void
delay7(void)542 delay7(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 */
562 static void
delay1ms(void)563 delay1ms(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 */
578 static int
probe_keyboard(void)579 probe_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