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