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