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