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