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