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 void get_pos(int *x, int *y); 53 54 #ifdef TERM_EMU 55 #define MAXARGS 8 56 #define DEFAULT_FGCOLOR 7 57 #define DEFAULT_BGCOLOR 0 58 59 void end_term(void); 60 void bail_out(int c); 61 void vidc_term_emu(int c); 62 void curs_move(int *_x, int *_y, int x, int y); 63 void write_char(int c, int fg, int bg); 64 void scroll_up(int rows, int fg, int bg); 65 void CD(void); 66 void CM(void); 67 void HO(void); 68 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 textvidc = { 76 "vidconsole", 77 "internal video/keyboard", 78 0, 79 vidc_probe, 80 vidc_init, 81 vidc_putchar, 82 vidc_getchar, 83 vidc_ischar 84 }; 85 86 static void 87 vidc_probe(struct console *cp) 88 { 89 /* look for a keyboard */ 90 #if KEYBOARD_PROBE 91 if (probe_keyboard()) 92 #endif 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(&curx, &cury); 113 curs_move(&curx, &cury, 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 int n; 140 #ifndef TERM_EMU 141 int curx, cury; 142 143 get_pos(&curx, &cury); 144 #endif 145 146 n = 8 - ((curx + 8) % 8); 147 for (i = 0; i < n; i++) 148 vidc_rawputchar(' '); 149 } else { 150 #ifndef TERM_EMU 151 vidc_biosputchar(c); 152 #else 153 /* Emulate AH=0eh (teletype output) */ 154 switch(c) { 155 case '\a': 156 vidc_biosputchar(c); 157 return; 158 case '\r': 159 curx = 0; 160 curs_move(&curx, &cury, curx, cury); 161 return; 162 case '\n': 163 cury++; 164 if (cury > 24) { 165 scroll_up(1, fg_c, bg_c); 166 cury--; 167 } else { 168 curs_move(&curx, &cury, curx, cury); 169 } 170 return; 171 case '\b': 172 if (curx > 0) { 173 curx--; 174 curs_move(&curx, &cury, curx, cury); 175 /* write_char(' ', fg_c, bg_c); XXX destructive(!) */ 176 return; 177 } 178 return; 179 default: 180 write_char(c, fg_c, bg_c); 181 curx++; 182 if (curx > 79) { 183 curx = 0; 184 cury++; 185 } 186 if (cury > 24) { 187 curx = 0; 188 scroll_up(1, fg_c, bg_c); 189 cury--; 190 } 191 } 192 curs_move(&curx, &cury, curx, cury); 193 #endif 194 } 195 } 196 197 /* Get cursor position on the screen. Result is in edx. Sets 198 * curx and cury appropriately. 199 */ 200 void 201 get_pos(int *x, int *y) 202 { 203 204 v86.ctl = 0; 205 v86.addr = 0x10; 206 v86.eax = 0x0300; 207 v86.ebx = 0x0; 208 v86int(); 209 *x = v86.edx & 0x00ff; 210 *y = (v86.edx & 0xff00) >> 8; 211 } 212 213 #ifdef TERM_EMU 214 215 /* Move cursor to x rows and y cols (0-based). */ 216 void 217 curs_move(int *_x, int *_y, int x, int y) 218 { 219 220 v86.ctl = 0; 221 v86.addr = 0x10; 222 v86.eax = 0x0200; 223 v86.ebx = 0x0; 224 v86.edx = ((0x00ff & y) << 8) + (0x00ff & x); 225 v86int(); 226 *_x = x; 227 *_y = y; 228 /* If there is ctrl char at this position, cursor would be invisible. 229 * Make it a space instead. 230 */ 231 v86.ctl = 0; 232 v86.addr = 0x10; 233 v86.eax = 0x0800; 234 v86.ebx = 0x0; 235 v86int(); 236 #define isvisible(c) (((c) >= 32) && ((c) < 255)) 237 if (!isvisible(v86.eax & 0x00ff)) { 238 write_char(' ', fg_c, bg_c); 239 } 240 } 241 242 /* Scroll up the whole window by a number of rows. If rows==0, 243 * clear the window. fg and bg are attributes for the new lines 244 * inserted in the window. 245 */ 246 void 247 scroll_up(int rows, int fgcol, int bgcol) 248 { 249 250 if (rows == 0) 251 rows = 25; 252 v86.ctl = 0; 253 v86.addr = 0x10; 254 v86.eax = 0x0600 + (0x00ff & rows); 255 v86.ebx = (bgcol << 12) + (fgcol << 8); 256 v86.ecx = 0x0; 257 v86.edx = 0x184f; 258 v86int(); 259 } 260 261 /* Write character and attribute at cursor position. */ 262 void 263 write_char(int c, int fgcol, int bgcol) 264 { 265 266 v86.ctl = 0; 267 v86.addr = 0x10; 268 v86.eax = 0x0900 + (0x00ff & c); 269 v86.ebx = (bgcol << 4) + fgcol; 270 v86.ecx = 0x1; 271 v86int(); 272 } 273 274 /**************************************************************/ 275 /* 276 * Screen manipulation functions. They use accumulated data in 277 * args[] and argc variables. 278 * 279 */ 280 281 /* Clear display from current position to end of screen */ 282 void 283 CD(void) 284 { 285 286 get_pos(&curx, &cury); 287 if (curx > 0) { 288 v86.ctl = 0; 289 v86.addr = 0x10; 290 v86.eax = 0x0600; 291 v86.ebx = (bg_c << 4) + fg_c; 292 v86.ecx = (cury << 8) + curx; 293 v86.edx = (cury << 8) + 79; 294 v86int(); 295 if (++cury > 24) { 296 end_term(); 297 return; 298 } 299 } 300 v86.ctl = 0; 301 v86.addr = 0x10; 302 v86.eax = 0x0600; 303 v86.ebx = (bg_c << 4) + fg_c; 304 v86.ecx = (cury << 8) + 0; 305 v86.edx = (24 << 8) + 79; 306 v86int(); 307 end_term(); 308 } 309 310 /* Absolute cursor move to args[0] rows and args[1] columns 311 * (the coordinates are 1-based). 312 */ 313 void 314 CM(void) 315 { 316 317 if (args[0] > 0) 318 args[0]--; 319 if (args[1] > 0) 320 args[1]--; 321 curs_move(&curx, &cury, args[1], args[0]); 322 end_term(); 323 } 324 325 /* Home cursor (left top corner) */ 326 void 327 HO(void) 328 { 329 330 argc = 1; 331 args[0] = args[1] = 1; 332 CM(); 333 } 334 335 /* Clear internal state of the terminal emulation code */ 336 void 337 end_term(void) 338 { 339 340 esc = 0; 341 argc = -1; 342 } 343 344 /* Gracefully exit ESC-sequence processing in case of misunderstanding */ 345 void 346 bail_out(int c) 347 { 348 char buf[16], *ch; 349 int i; 350 351 if (esc) { 352 vidc_rawputchar('\033'); 353 if (esc != '\033') 354 vidc_rawputchar(esc); 355 for (i = 0; i <= argc; ++i) { 356 sprintf(buf, "%d", args[i]); 357 ch = buf; 358 while (*ch) 359 vidc_rawputchar(*ch++); 360 } 361 } 362 vidc_rawputchar(c); 363 end_term(); 364 } 365 366 static void 367 get_arg(int c) 368 { 369 370 if (argc < 0) 371 argc = 0; 372 args[argc] *= 10; 373 args[argc] += c - '0'; 374 } 375 376 /* Emulate basic capabilities of cons25 terminal */ 377 void 378 vidc_term_emu(int c) 379 { 380 static int ansi_col[] = { 381 0, 4, 2, 6, 1, 5, 3, 7, 382 }; 383 int t; 384 int i; 385 386 switch (esc) { 387 case 0: 388 switch (c) { 389 case '\033': 390 esc = c; 391 break; 392 default: 393 vidc_rawputchar(c); 394 break; 395 } 396 break; 397 398 case '\033': 399 switch (c) { 400 case '[': 401 esc = c; 402 args[0] = 0; 403 argc = -1; 404 break; 405 default: 406 bail_out(c); 407 break; 408 } 409 break; 410 411 case '[': 412 switch (c) { 413 case ';': 414 if (argc < 0) /* XXX */ 415 argc = 0; 416 else if (argc + 1 >= MAXARGS) 417 bail_out(c); 418 else 419 args[++argc] = 0; 420 break; 421 case 'H': 422 if (argc < 0) 423 HO(); 424 else if (argc == 1) 425 CM(); 426 else 427 bail_out(c); 428 break; 429 case 'J': 430 if (argc < 0) 431 CD(); 432 else 433 bail_out(c); 434 break; 435 case 'm': 436 if (argc < 0) { 437 fg_c = DEFAULT_FGCOLOR; 438 bg_c = DEFAULT_BGCOLOR; 439 } 440 for (i = 0; i <= argc; ++i) { 441 switch (args[i]) { 442 case 0: /* back to normal */ 443 fg_c = DEFAULT_FGCOLOR; 444 bg_c = DEFAULT_BGCOLOR; 445 break; 446 case 1: /* bold */ 447 fg_c |= 0x8; 448 break; 449 case 4: /* underline */ 450 case 5: /* blink */ 451 bg_c |= 0x8; 452 break; 453 case 7: /* reverse */ 454 t = fg_c; 455 fg_c = bg_c; 456 bg_c = t; 457 break; 458 case 22: /* normal intensity */ 459 fg_c &= ~0x8; 460 break; 461 case 24: /* not underline */ 462 case 25: /* not blinking */ 463 bg_c &= ~0x8; 464 break; 465 case 30: case 31: case 32: case 33: 466 case 34: case 35: case 36: case 37: 467 fg_c = ansi_col[args[i] - 30]; 468 break; 469 case 39: /* normal */ 470 fg_c = DEFAULT_FGCOLOR; 471 break; 472 case 40: case 41: case 42: case 43: 473 case 44: case 45: case 46: case 47: 474 bg_c = ansi_col[args[i] - 40]; 475 break; 476 case 49: /* normal */ 477 bg_c = DEFAULT_BGCOLOR; 478 break; 479 } 480 } 481 end_term(); 482 break; 483 default: 484 if (isdigit(c)) 485 get_arg(c); 486 else 487 bail_out(c); 488 break; 489 } 490 break; 491 492 default: 493 bail_out(c); 494 break; 495 } 496 } 497 #endif 498 499 static void 500 vidc_putchar(int c) 501 { 502 #ifdef TERM_EMU 503 vidc_term_emu(c); 504 #else 505 vidc_rawputchar(c); 506 #endif 507 } 508 509 static int 510 vidc_getchar(void) 511 { 512 513 if (vidc_ischar()) { 514 v86.ctl = 0; 515 v86.addr = 0x16; 516 v86.eax = 0x0; 517 v86int(); 518 return (v86.eax & 0xff); 519 } else { 520 return (-1); 521 } 522 } 523 524 static int 525 vidc_ischar(void) 526 { 527 528 v86.ctl = V86_FLAGS; 529 v86.addr = 0x16; 530 v86.eax = 0x100; 531 v86int(); 532 return (!V86_ZR(v86.efl)); 533 } 534 535 #if KEYBOARD_PROBE 536 537 #define PROBE_MAXRETRY 5 538 #define PROBE_MAXWAIT 400 539 #define IO_DUMMY 0x84 540 #define IO_KBD 0x060 /* 8042 Keyboard */ 541 542 /* selected defines from kbdio.h */ 543 #define KBD_STATUS_PORT 4 /* status port, read */ 544 #define KBD_DATA_PORT 0 /* data port, read/write 545 * also used as keyboard command 546 * and mouse command port 547 */ 548 #define KBDC_ECHO 0x00ee 549 #define KBDS_ANY_BUFFER_FULL 0x0001 550 #define KBDS_INPUT_BUFFER_FULL 0x0002 551 #define KBD_ECHO 0x00ee 552 553 /* 7 microsec delay necessary for some keyboard controllers */ 554 static void 555 delay7(void) 556 { 557 /* 558 * I know this is broken, but no timer is available yet at this stage... 559 * See also comments in `delay1ms()'. 560 */ 561 inb(IO_DUMMY); inb(IO_DUMMY); 562 inb(IO_DUMMY); inb(IO_DUMMY); 563 inb(IO_DUMMY); inb(IO_DUMMY); 564 } 565 566 /* 567 * This routine uses an inb to an unused port, the time to execute that 568 * inb is approximately 1.25uS. This value is pretty constant across 569 * all CPU's and all buses, with the exception of some PCI implentations 570 * that do not forward this I/O address to the ISA bus as they know it 571 * is not a valid ISA bus address, those machines execute this inb in 572 * 60 nS :-(. 573 * 574 */ 575 static void 576 delay1ms(void) 577 { 578 int i = 800; 579 while (--i >= 0) 580 (void)inb(0x84); 581 } 582 583 /* 584 * We use the presence/absence of a keyboard to determine whether the internal 585 * console can be used for input. 586 * 587 * Perform a simple test on the keyboard; issue the ECHO command and see 588 * if the right answer is returned. We don't do anything as drastic as 589 * full keyboard reset; it will be too troublesome and take too much time. 590 */ 591 static int 592 probe_keyboard(void) 593 { 594 int retry = PROBE_MAXRETRY; 595 int wait; 596 int i; 597 598 while (--retry >= 0) { 599 /* flush any noise */ 600 while (inb(IO_KBD + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL) { 601 delay7(); 602 inb(IO_KBD + KBD_DATA_PORT); 603 delay1ms(); 604 } 605 606 /* wait until the controller can accept a command */ 607 for (wait = PROBE_MAXWAIT; wait > 0; --wait) { 608 if (((i = inb(IO_KBD + KBD_STATUS_PORT)) 609 & (KBDS_INPUT_BUFFER_FULL | KBDS_ANY_BUFFER_FULL)) == 0) 610 break; 611 if (i & KBDS_ANY_BUFFER_FULL) { 612 delay7(); 613 inb(IO_KBD + KBD_DATA_PORT); 614 } 615 delay1ms(); 616 } 617 if (wait <= 0) 618 continue; 619 620 /* send the ECHO command */ 621 outb(IO_KBD + KBD_DATA_PORT, KBDC_ECHO); 622 623 /* wait for a response */ 624 for (wait = PROBE_MAXWAIT; wait > 0; --wait) { 625 if (inb(IO_KBD + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL) 626 break; 627 delay1ms(); 628 } 629 if (wait <= 0) 630 continue; 631 632 delay7(); 633 i = inb(IO_KBD + KBD_DATA_PORT); 634 #ifdef PROBE_KBD_BEBUG 635 printf("probe_keyboard: got 0x%x.\n", i); 636 #endif 637 if (i == KBD_ECHO) { 638 /* got the right answer */ 639 return (1); 640 } 641 } 642 643 return (0); 644 } 645 #endif /* KEYBOARD_PROBE */ 646