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