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 * $FreeBSD: src/sys/boot/i386/libi386/vidconsole.c,v 1.19 2003/08/25 23:28:31 obrien Exp $ 29 * $DragonFly: src/sys/boot/pc32/libi386/vidconsole.c,v 1.4 2004/09/09 03:47:08 joerg Exp $ 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 41 static int probe_keyboard(void); 42 #endif 43 static void vidc_probe(struct console *cp); 44 static int vidc_init(int arg); 45 static void vidc_putchar(int c); 46 static int vidc_getchar(void); 47 static int vidc_ischar(void); 48 49 static int vidc_started; 50 51 #ifdef TERM_EMU 52 #define MAXARGS 8 53 #define DEFAULT_FGCOLOR 7 54 #define DEFAULT_BGCOLOR 0 55 56 void end_term(void); 57 void bail_out(int c); 58 void vidc_term_emu(int c); 59 void get_pos(void); 60 void curs_move(int x, int y); 61 void write_char(int c, int fg, int bg); 62 void scroll_up(int rows, int fg, int bg); 63 void CD(void); 64 void CM(void); 65 void HO(void); 66 67 static int args[MAXARGS], argc; 68 static int fg_c, bg_c, curx, cury; 69 static int esc; 70 #endif 71 72 73 struct 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 84 static void 85 vidc_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 101 static int 102 vidc_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 122 static void 123 vidc_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 133 static void 134 vidc_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 */ 195 void 196 get_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). */ 209 void 210 curs_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 */ 239 void 240 scroll_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. */ 255 void 256 write_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 */ 275 void 276 CD(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 */ 306 void 307 CM(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) */ 319 void 320 HO(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 */ 329 void 330 end_term(void) 331 { 332 333 esc = 0; 334 argc = -1; 335 } 336 337 /* Gracefully exit ESC-sequence processing in case of misunderstanding */ 338 void 339 bail_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 359 static void 360 get_arg(int 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 */ 370 void 371 vidc_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 485 static void 486 vidc_putchar(int c) 487 { 488 #ifdef TERM_EMU 489 vidc_term_emu(c); 490 #else 491 vidc_rawputchar(c); 492 #endif 493 } 494 495 static int 496 vidc_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 510 static int 511 vidc_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 */ 540 static void 541 delay7(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 */ 561 static void 562 delay1ms(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 */ 577 static int 578 probe_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 (1); 626 } 627 } 628 629 return (0); 630 } 631 #endif /* KEYBOARD_PROBE */ 632