vidconsole.c revision 84276
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 84276 2001-10-01 11:42:25Z 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 52void end_term(void); 53void bail_out(int c); 54void vidc_term_emu(int c); 55void get_pos(void); 56void curs_move(int x, int y); 57void write_char(int c, int fg, int bg); 58void scroll_up(int rows, int fg, int bg); 59int pow10(int i); 60void AB(void); 61void AF(void); 62void CD(void); 63void CM(void); 64void HO(void); 65void ME(void); 66 67static int args[2],argc,br; 68static int fg,bg,dig; 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(); 114 curs_move(curx, cury); 115 fg_c = 7; 116 bg_c = 0; 117#endif 118 for (i = 0; i < 10 && vidc_ischar(); i++) 119 (void)vidc_getchar(); 120 return (0); /* XXX reinit? */ 121} 122 123static void 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); 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); 163 } 164 return; 165 case '\b': 166 if (curx > 0) { 167 curx--; 168 curs_move(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); 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(void) 198{ 199 200 v86.ctl = 0; 201 v86.addr = 0x10; 202 v86.eax = 0x0300; 203 v86.ebx = 0x0; 204 v86int(); 205 curx = v86.edx & 0x00ff; 206 cury = (v86.edx & 0xff00) >> 8; 207} 208 209/* Move cursor to x rows and y cols (0-based). */ 210void 211curs_move(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 curx = x; 221 cury = 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/* Calculate power of 10 */ 269int 270pow10(int i) 271{ 272 int res = 1; 273 274 while (i-- > 0) { 275 res *= 10; 276 } 277 return res; 278} 279 280/**************************************************************/ 281/* 282 * Screen manipulation functions. They use accumulated data in 283 * args[] and argc variables. 284 * 285 */ 286 287/* Set background color */ 288void 289AB(void) 290{ 291 292 bg_c = args[0]; 293 end_term(); 294} 295 296/* Set foreground color */ 297void 298AF(void) 299{ 300 301 fg_c = args[0]; 302 end_term(); 303} 304 305/* Clear display from current position to end of screen */ 306void 307CD(void) 308{ 309 310 get_pos(); 311 v86.ctl = 0; 312 v86.addr = 0x10; 313 v86.eax = 0x0600; 314 v86.ebx = (bg_c << 4) + fg_c; 315 v86.ecx = v86.edx; 316 v86.edx = 0x184f; 317 v86int(); 318 curx = 0; 319 curs_move(curx, cury); 320 end_term(); 321} 322 323/* Absolute cursor move to args[0] rows and args[1] columns 324 * (the coordinates are 1-based). 325 */ 326void 327CM(void) 328{ 329 330 if (args[0] > 0) 331 args[0]--; 332 if (args[1] > 0) 333 args[1]--; 334 curs_move(args[1], args[0]); 335 end_term(); 336} 337 338/* Home cursor (left top corner) */ 339void 340HO(void) 341{ 342 343 argc = 1; 344 args[0] = args[1] = 1; 345 CM(); 346} 347 348/* Exit attribute mode (reset fore/back-ground colors to defaults) */ 349void 350ME(void) 351{ 352 353 fg_c = 7; 354 bg_c = 0; 355 end_term(); 356} 357 358/* Clear internal state of the terminal emulation code */ 359void 360end_term(void) 361{ 362 363 esc = 0; 364 argc = -1; 365 fg = bg = br = 0; 366 args[0] = args[1] = 0; 367 dig = 0; 368} 369 370/* Gracefully exit ESC-sequence processing in case of misunderstanding */ 371void 372bail_out(int c) 373{ 374 char buf[6],*ch; 375 376 if (esc) 377 vidc_rawputchar('\033'); 378 if (br) 379 vidc_rawputchar('['); 380 if (argc > -1) { 381 sprintf(buf, "%d", args[0]); 382 ch = buf; 383 while (*ch) 384 vidc_rawputchar(*ch++); 385 386 if (argc > 0) { 387 vidc_rawputchar(';'); 388 sprintf(buf, "%d", args[1]); 389 ch = buf; 390 while (*ch) 391 vidc_rawputchar(*ch++); 392 } 393 } 394 vidc_rawputchar(c); 395 end_term(); 396} 397 398/* Emulate basic capabilities of cons25 terminal */ 399void 400vidc_term_emu(int c) 401{ 402 403 if (!esc) { 404 if (c == '\033') { 405 esc = 1; 406 } else { 407 vidc_rawputchar(c); 408 } 409 return; 410 } 411 412 /* Do ESC sequences processing */ 413 switch (c) { 414 case '\033': 415 /* ESC in ESC sequence - error */ 416 bail_out(c); 417 break; 418 case '[': 419 /* Check if it's first char after ESC */ 420 if (argc < 0) { 421 br = 1; 422 } else { 423 bail_out(c); 424 } 425 break; 426 case 'H': 427 /* Emulate \E[H (cursor home) and 428 * \E%d;%dH (cursor absolute move) */ 429 if (br) { 430 switch (argc) { 431 case -1: 432 HO(); 433 break; 434 case 1: 435 if (fg) 436 args[0] += pow10(dig)*3; 437 if (bg) 438 args[0] += pow10(dig)*4; 439 CM(); 440 break; 441 default: 442 bail_out(c); 443 } 444 } else bail_out(c); 445 break; 446 case 'J': 447 /* Emulate \EJ (clear to end of screen) */ 448 if (br && argc < 0) { 449 CD(); 450 } else bail_out(c); 451 break; 452 case ';': 453 /* perhaps args separator */ 454 if (br && (argc > -1)) { 455 argc++; 456 } else bail_out(c); 457 break; 458 case 'm': 459 /* Change char attributes */ 460 if (br) { 461 switch (argc) { 462 case -1: 463 ME(); 464 break; 465 case 0: 466 if (fg) 467 AF(); 468 else 469 AB(); 470 break; 471 default: 472 bail_out(c); 473 } 474 } else bail_out(c); 475 break; 476 default: 477 if (isdigit(c)) { 478 /* Carefully collect numeric arguments */ 479 /* XXX this is ugly. */ 480 if (br) { 481 if (argc == -1) { 482 argc = 0; 483 args[argc] = 0; 484 dig = 0; 485 /* in case we're in error... */ 486 if (c == '3') { 487 fg = 1; 488 return; 489 } 490 if (c == '4') { 491 bg = 1; 492 return; 493 } 494 args[argc] = (int)(c - '0'); 495 dig = 1; 496 args[argc + 1] = 0; 497 } else { 498 args[argc] = args[argc]*10 + (int)(c - '0'); 499 if (argc == 0) 500 dig++; 501 } 502 } else bail_out(c); 503 } else bail_out(c); 504 break; 505 } 506} 507#endif 508 509static void 510vidc_putchar(int c) 511{ 512#ifdef TERM_EMU 513 vidc_term_emu(c); 514#else 515 vidc_rawputchar(c); 516#endif 517} 518 519static int 520vidc_getchar(void) 521{ 522 523 if (vidc_ischar()) { 524 v86.ctl = 0; 525 v86.addr = 0x16; 526 v86.eax = 0x0; 527 v86int(); 528 return (v86.eax & 0xff); 529 } else { 530 return (-1); 531 } 532} 533 534static int 535vidc_ischar(void) 536{ 537 538 v86.ctl = V86_FLAGS; 539 v86.addr = 0x16; 540 v86.eax = 0x100; 541 v86int(); 542 return (!(v86.efl & PSL_Z)); 543} 544 545#if KEYBOARD_PROBE 546 547#define PROBE_MAXRETRY 5 548#define PROBE_MAXWAIT 400 549#define IO_DUMMY 0x84 550#define IO_KBD 0x060 /* 8042 Keyboard */ 551 552/* selected defines from kbdio.h */ 553#define KBD_STATUS_PORT 4 /* status port, read */ 554#define KBD_DATA_PORT 0 /* data port, read/write 555 * also used as keyboard command 556 * and mouse command port 557 */ 558#define KBDC_ECHO 0x00ee 559#define KBDS_ANY_BUFFER_FULL 0x0001 560#define KBDS_INPUT_BUFFER_FULL 0x0002 561#define KBD_ECHO 0x00ee 562 563/* 7 microsec delay necessary for some keyboard controllers */ 564static void 565delay7(void) 566{ 567 /* 568 * I know this is broken, but no timer is available yet at this stage... 569 * See also comments in `delay1ms()'. 570 */ 571 inb(IO_DUMMY); inb(IO_DUMMY); 572 inb(IO_DUMMY); inb(IO_DUMMY); 573 inb(IO_DUMMY); inb(IO_DUMMY); 574} 575 576/* 577 * This routine uses an inb to an unused port, the time to execute that 578 * inb is approximately 1.25uS. This value is pretty constant across 579 * all CPU's and all buses, with the exception of some PCI implentations 580 * that do not forward this I/O address to the ISA bus as they know it 581 * is not a valid ISA bus address, those machines execute this inb in 582 * 60 nS :-(. 583 * 584 */ 585static void 586delay1ms(void) 587{ 588 int i = 800; 589 while (--i >= 0) 590 (void)inb(0x84); 591} 592 593/* 594 * We use the presence/absence of a keyboard to determine whether the internal 595 * console can be used for input. 596 * 597 * Perform a simple test on the keyboard; issue the ECHO command and see 598 * if the right answer is returned. We don't do anything as drastic as 599 * full keyboard reset; it will be too troublesome and take too much time. 600 */ 601static int 602probe_keyboard(void) 603{ 604 int retry = PROBE_MAXRETRY; 605 int wait; 606 int i; 607 608 while (--retry >= 0) { 609 /* flush any noise */ 610 while (inb(IO_KBD + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL) { 611 delay7(); 612 inb(IO_KBD + KBD_DATA_PORT); 613 delay1ms(); 614 } 615 616 /* wait until the controller can accept a command */ 617 for (wait = PROBE_MAXWAIT; wait > 0; --wait) { 618 if (((i = inb(IO_KBD + KBD_STATUS_PORT)) 619 & (KBDS_INPUT_BUFFER_FULL | KBDS_ANY_BUFFER_FULL)) == 0) 620 break; 621 if (i & KBDS_ANY_BUFFER_FULL) { 622 delay7(); 623 inb(IO_KBD + KBD_DATA_PORT); 624 } 625 delay1ms(); 626 } 627 if (wait <= 0) 628 continue; 629 630 /* send the ECHO command */ 631 outb(IO_KBD + KBD_DATA_PORT, KBDC_ECHO); 632 633 /* wait for a response */ 634 for (wait = PROBE_MAXWAIT; wait > 0; --wait) { 635 if (inb(IO_KBD + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL) 636 break; 637 delay1ms(); 638 } 639 if (wait <= 0) 640 continue; 641 642 delay7(); 643 i = inb(IO_KBD + KBD_DATA_PORT); 644#ifdef PROBE_KBD_BEBUG 645 printf("probe_keyboard: got 0x%x.\n", i); 646#endif 647 if (i == KBD_ECHO) { 648 /* got the right answer */ 649 return (0); 650 } 651 } 652 653 return (1); 654} 655#endif /* KEYBOARD_PROBE */ 656