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