vidconsole.c revision 64187
1219820Sjeff/* 2219820Sjeff * Copyright (c) 1998 Michael Smith (msmith@freebsd.org) 3219820Sjeff * Copyright (c) 1997 Kazutaka YOKOTA (yokota@zodiac.mech.utsunomiya-u.ac.jp) 4219820Sjeff * All rights reserved. 5270710Shselasky * 6219820Sjeff * Redistribution and use in source and binary forms, with or without 7219820Sjeff * modification, are permitted provided that the following conditions 8219820Sjeff * are met: 9219820Sjeff * 1. Redistributions of source code must retain the above copyright 10219820Sjeff * notice, this list of conditions and the following disclaimer. 11219820Sjeff * 2. Redistributions in binary form must reproduce the above copyright 12219820Sjeff * notice, this list of conditions and the following disclaimer in the 13219820Sjeff * documentation and/or other materials provided with the distribution. 14219820Sjeff * 15219820Sjeff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16219820Sjeff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17219820Sjeff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18219820Sjeff * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19219820Sjeff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20219820Sjeff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21219820Sjeff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22219820Sjeff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23219820Sjeff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24219820Sjeff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25219820Sjeff * SUCH DAMAGE. 26219820Sjeff * 27219820Sjeff * From Id: probe_keyboard.c,v 1.13 1997/06/09 05:10:55 bde Exp 28219820Sjeff * 29219820Sjeff * $FreeBSD: head/sys/boot/i386/libi386/vidconsole.c 64187 2000-08-03 09:14:02Z jhb $ 30219820Sjeff */ 31219820Sjeff 32219820Sjeff#include <stand.h> 33219820Sjeff#include <bootstrap.h> 34219820Sjeff#include <btxv86.h> 35219820Sjeff#include <machine/psl.h> 36219820Sjeff#include "libi386.h" 37219820Sjeff 38219820Sjeff#if KEYBOARD_PROBE 39219820Sjeff#include <machine/cpufunc.h> 40219820Sjeff 41219820Sjeffstatic int probe_keyboard(void); 42219820Sjeff#endif 43219820Sjeffstatic void vidc_probe(struct console *cp); 44219820Sjeffstatic int vidc_init(int arg); 45219820Sjeffstatic void vidc_putchar(int c); 46219820Sjeffstatic int vidc_getchar(void); 47219820Sjeffstatic int vidc_ischar(void); 48219820Sjeff 49219820Sjeffstatic int vidc_started; 50219820Sjeff 51219820Sjeff#ifdef TERM_EMU 52219820Sjeffvoid end_term(void); 53219820Sjeffvoid bail_out(int c); 54219820Sjeffvoid vidc_term_emu(int c); 55219820Sjeffvoid get_pos(void); 56219820Sjeffvoid curs_move(int x, int y); 57219820Sjeffvoid write_char(int c, int fg, int bg); 58219820Sjeffvoid scroll_up(int rows, int fg, int bg); 59219820Sjeffint pow10(int i); 60219820Sjeffvoid AB(void); 61219820Sjeffvoid AF(void); 62219820Sjeffvoid CD(void); 63219820Sjeffvoid CM(void); 64219820Sjeffvoid HO(void); 65219820Sjeffvoid ME(void); 66219820Sjeff 67219820Sjeffstatic int args[2],argc,br; 68219820Sjeffstatic int fg,bg,dig; 69219820Sjeffstatic int fg_c,bg_c,curx,cury; 70219820Sjeffstatic int esc; 71219820Sjeff#endif 72219820Sjeff 73219820Sjeff 74219820Sjeffstruct console vidconsole = { 75219820Sjeff "vidconsole", 76219820Sjeff "internal video/keyboard", 77219820Sjeff 0, 78219820Sjeff vidc_probe, 79219820Sjeff vidc_init, 80219820Sjeff vidc_putchar, 81219820Sjeff vidc_getchar, 82219820Sjeff vidc_ischar 83219820Sjeff}; 84219820Sjeff 85219820Sjeffstatic void 86219820Sjeffvidc_probe(struct console *cp) 87219820Sjeff{ 88219820Sjeff 89219820Sjeff /* look for a keyboard */ 90219820Sjeff#if KEYBOARD_PROBE 91219820Sjeff if (probe_keyboard()) 92219820Sjeff#endif 93219820Sjeff { 94219820Sjeff 95219820Sjeff cp->c_flags |= C_PRESENTIN; 96219820Sjeff } 97219820Sjeff 98219820Sjeff /* XXX for now, always assume we can do BIOS screen output */ 99219820Sjeff cp->c_flags |= C_PRESENTOUT; 100219820Sjeff} 101219820Sjeff 102219820Sjeffstatic int 103219820Sjeffvidc_init(int arg) 104219820Sjeff{ 105219820Sjeff int i; 106219820Sjeff 107219820Sjeff if (vidc_started && arg == 0) 108219820Sjeff return(0); 109219820Sjeff vidc_started = 1; 110219820Sjeff#ifdef TERM_EMU 111219820Sjeff /* Init terminal emulator */ 112219820Sjeff end_term(); 113219820Sjeff get_pos(); 114 curs_move(curx,cury); 115 fg_c=7; 116 bg_c=0; 117#endif 118 for(i = 0; i < 10 && vidc_ischar(); i++) 119 (void)vidc_getchar(); 120 return(0); /* XXX reinit? */ 121} 122 123static void 124vidc_biosputchar(int c) 125{ 126 v86.ctl = 0; 127 v86.addr = 0x10; 128 v86.eax = 0xe00 | (c & 0xff); 129 v86.ebx = 0x7; 130 v86int(); 131} 132 133static void 134vidc_rawputchar(int c) 135{ 136 int i; 137 138 if(c == '\t') 139 /* lame tab expansion */ 140 for (i = 0; i < 8; i++) 141 vidc_rawputchar(' '); 142 else { 143#ifndef TERM_EMU 144 vidc_biosputchar(c); 145#else 146 /* Emulate AH=0eh (teletype output) */ 147 switch(c) { 148 case '\a': 149 vidc_biosputchar(c); 150 return; 151 case '\r': 152 curx=0; 153 curs_move(curx,cury); 154 return; 155 case '\n': 156 cury++; 157 if(cury>24) { 158 scroll_up(1,fg_c,bg_c); 159 cury--; 160 } else { 161 curs_move(curx,cury); 162 } 163 return; 164 case '\b': 165 if(curx>0) { 166 curx--; 167 curs_move(curx,cury); 168 /* write_char(' ',fg_c,bg_c); XXX destructive(!) */ 169 return; 170 } 171 return; 172 default: 173 write_char(c,fg_c,bg_c); 174 curx++; 175 if(curx>79) { 176 curx=0; 177 cury++; 178 } 179 if(cury>24) { 180 curx=0; 181 scroll_up(1,fg_c,bg_c); 182 cury--; 183 } 184 } 185 curs_move(curx,cury); 186#endif 187 } 188} 189 190#ifdef TERM_EMU 191 192/* Get cursor position on the screen. Result is in edx. Sets 193 * curx and cury appropriately. 194 */ 195void 196get_pos(void) 197{ 198 v86.ctl = 0; 199 v86.addr = 0x10; 200 v86.eax = 0x0300; 201 v86.ebx = 0x0; 202 v86int(); 203 curx=v86.edx & 0x00ff; 204 cury=(v86.edx & 0xff00)>>8; 205} 206 207/* Move cursor to x rows and y cols (0-based). */ 208void 209curs_move(int x, int y) 210{ 211 v86.ctl = 0; 212 v86.addr = 0x10; 213 v86.eax = 0x0200; 214 v86.ebx = 0x0; 215 v86.edx = ((0x00ff & y)<<8)+(0x00ff & x); 216 v86int(); 217 curx=x; 218 cury=y; 219 /* If there is ctrl char at this position, cursor would be invisible. 220 * Make it a space instead. 221 */ 222 v86.ctl=0; 223 v86.addr = 0x10; 224 v86.eax = 0x0800; 225 v86.ebx= 0x0; 226 v86int(); 227#define isvisible(c) (((c)>32) && ((c)<255)) 228 if(!isvisible(v86.eax & 0x00ff)) { 229 write_char(' ',fg_c,bg_c); 230 } 231} 232 233/* Scroll up the whole window by a number of rows. If rows==0, 234 * clear the window. fg and bg are attributes for the new lines 235 * inserted in the window. 236 */ 237void 238scroll_up(int rows, int fgcol, int bgcol) 239{ 240 if(rows==0) rows=25; 241 v86.ctl = 0; 242 v86.addr = 0x10; 243 v86.eax = 0x0600+(0x00ff & rows); 244 v86.ebx = (bgcol<<12)+(fgcol<<8); 245 v86.ecx = 0x0; 246 v86.edx = 0x184f; 247 v86int(); 248} 249 250/* Write character and attribute at cursor position. */ 251void 252write_char(int c, int fgcol, int bgcol) 253{ 254 v86.ctl=0; 255 v86.addr = 0x10; 256 v86.eax = 0x0900+(0x00ff & c); 257 v86.ebx = (bgcol<<4)+fgcol; 258 v86.ecx = 0x1; 259 v86int(); 260} 261 262/* Calculate power of 10 */ 263int 264pow10(int i) 265{ 266 int res=1; 267 268 while(i-->0) { 269 res*=10; 270 } 271 return res; 272} 273 274/**************************************************************/ 275/* 276 * Screen manipulation functions. They use accumulated data in 277 * args[] and argc variables. 278 * 279 */ 280 281/* Set background color */ 282void 283AB(void){ 284 bg_c=args[0]; 285 end_term(); 286} 287 288/* Set foreground color */ 289void 290AF(void) 291{ 292 fg_c=args[0]; 293 end_term(); 294} 295 296/* Clear display from current position to end of screen */ 297void 298CD(void) 299{ 300 get_pos(); 301 v86.ctl = 0; 302 v86.addr = 0x10; 303 v86.eax = 0x0600; 304 v86.ebx = (bg_c<<4)+fg_c; 305 v86.ecx = v86.edx; 306 v86.edx = 0x184f; 307 v86int(); 308 curx=0; 309 curs_move(curx,cury); 310 end_term(); 311} 312 313/* Absolute cursor move to args[0] rows and args[1] columns 314 * (the coordinates are 1-based). 315 */ 316void 317CM(void) 318{ 319 if(args[0]>0) args[0]--; 320 if(args[1]>0) args[1]--; 321 curs_move(args[1],args[0]); 322 end_term(); 323} 324 325/* Home cursor (left top corner) */ 326void 327HO(void) 328{ 329 argc=1; 330 args[0]=args[1]=1; 331 CM(); 332} 333 334/* Exit attribute mode (reset fore/back-ground colors to defaults) */ 335void 336ME(void) 337{ 338 fg_c=7; 339 bg_c=0; 340 end_term(); 341} 342 343/* Clear internal state of the terminal emulation code */ 344void 345end_term(void) 346{ 347 esc=0; 348 argc=-1; 349 fg=bg=br=0; 350 args[0]=args[1]=0; 351 dig=0; 352} 353 354/* Gracefully exit ESC-sequence processing in case of misunderstanding */ 355void 356bail_out(int c) 357{ 358 char buf[6],*ch; 359 360 if(esc) vidc_rawputchar('\033'); 361 if(br) vidc_rawputchar('['); 362 if(argc>-1) { 363 sprintf(buf,"%d",args[0]); 364 ch=buf; 365 while(*ch) vidc_rawputchar(*ch++); 366 367 if(argc>0) { 368 vidc_rawputchar(';'); 369 sprintf(buf,"%d",args[1]); 370 ch=buf; 371 while(*ch) vidc_rawputchar(*ch++); 372 } 373 } 374 vidc_rawputchar(c); 375 end_term(); 376} 377 378/* Emulate basic capabilities of cons25 terminal */ 379void 380vidc_term_emu(int c) 381{ 382 383 if(!esc) { 384 if(c=='\033') { 385 esc=1; 386 } else { 387 vidc_rawputchar(c); 388 } 389 return; 390 } 391 392 /* Do ESC sequences processing */ 393 switch(c) { 394 case '\033': 395 /* ESC in ESC sequence - error */ 396 bail_out(c); 397 break; 398 case '[': 399 /* Check if it's first char after ESC */ 400 if(argc<0) { 401 br=1; 402 } else { 403 bail_out(c); 404 } 405 break; 406 case 'H': 407 /* Emulate \E[H (cursor home) and 408 * \E%d;%dH (cursor absolute move) */ 409 if(br) { 410 switch(argc) { 411 case -1: 412 HO(); 413 break; 414 case 1: 415 if(fg) args[0]+=pow10(dig)*3; 416 if(bg) args[0]+=pow10(dig)*4; 417 CM(); 418 break; 419 default: 420 bail_out(c); 421 } 422 } else bail_out(c); 423 break; 424 case 'J': 425 /* Emulate \EJ (clear to end of screen) */ 426 if(br && argc<0) { 427 CD(); 428 } else bail_out(c); 429 break; 430 case ';': 431 /* perhaps args separator */ 432 if(br && (argc>-1)) { 433 argc++; 434 } else bail_out(c); 435 break; 436 case 'm': 437 /* Change char attributes */ 438 if(br) { 439 switch(argc) { 440 case -1: 441 ME(); 442 break; 443 case 0: 444 if(fg) AF(); 445 else AB(); 446 break; 447 default: 448 bail_out(c); 449 } 450 } else bail_out(c); 451 break; 452 default: 453 if(isdigit(c)) { 454 /* Carefully collect numeric arguments */ 455 /* XXX this is ugly. */ 456 if(br) { 457 if(argc==-1) { 458 argc=0; 459 args[argc]=0; 460 dig=0; 461 /* in case we're in error... */ 462 if(c=='3') { 463 fg=1; 464 return; 465 } 466 if(c=='4') { 467 bg=1; 468 return; 469 } 470 args[argc]=(int)(c-'0'); 471 dig=1; 472 args[argc+1]=0; 473 } else { 474 args[argc]=args[argc]*10+(int)(c-'0'); 475 if(argc==0) dig++; 476 } 477 } else bail_out(c); 478 } else bail_out(c); 479 break; 480 } 481} 482#endif 483 484static void 485vidc_putchar(int c) 486{ 487#ifdef TERM_EMU 488 vidc_term_emu(c); 489#else 490 vidc_rawputchar(c); 491#endif 492} 493 494static int 495vidc_getchar(void) 496{ 497 if (vidc_ischar()) { 498 v86.ctl = 0; 499 v86.addr = 0x16; 500 v86.eax = 0x0; 501 v86int(); 502 return(v86.eax & 0xff); 503 } else { 504 return(-1); 505 } 506} 507 508static int 509vidc_ischar(void) 510{ 511 v86.ctl = V86_FLAGS; 512 v86.addr = 0x16; 513 v86.eax = 0x100; 514 v86int(); 515 return(!(v86.efl & PSL_Z)); 516} 517 518#if KEYBOARD_PROBE 519 520#define PROBE_MAXRETRY 5 521#define PROBE_MAXWAIT 400 522#define IO_DUMMY 0x84 523#define IO_KBD 0x060 /* 8042 Keyboard */ 524 525/* selected defines from kbdio.h */ 526#define KBD_STATUS_PORT 4 /* status port, read */ 527#define KBD_DATA_PORT 0 /* data port, read/write 528 * also used as keyboard command 529 * and mouse command port 530 */ 531#define KBDC_ECHO 0x00ee 532#define KBDS_ANY_BUFFER_FULL 0x0001 533#define KBDS_INPUT_BUFFER_FULL 0x0002 534#define KBD_ECHO 0x00ee 535 536/* 7 microsec delay necessary for some keyboard controllers */ 537static void 538delay7(void) 539{ 540 /* 541 * I know this is broken, but no timer is available yet at this stage... 542 * See also comments in `delay1ms()'. 543 */ 544 inb(IO_DUMMY); inb(IO_DUMMY); 545 inb(IO_DUMMY); inb(IO_DUMMY); 546 inb(IO_DUMMY); inb(IO_DUMMY); 547} 548 549/* 550 * This routine uses an inb to an unused port, the time to execute that 551 * inb is approximately 1.25uS. This value is pretty constant across 552 * all CPU's and all buses, with the exception of some PCI implentations 553 * that do not forward this I/O adress to the ISA bus as they know it 554 * is not a valid ISA bus address, those machines execute this inb in 555 * 60 nS :-(. 556 * 557 */ 558static void 559delay1ms(void) 560{ 561 int i = 800; 562 while (--i >= 0) 563 (void)inb(0x84); 564} 565 566/* 567 * We use the presence/absence of a keyboard to determine whether the internal 568 * console can be used for input. 569 * 570 * Perform a simple test on the keyboard; issue the ECHO command and see 571 * if the right answer is returned. We don't do anything as drastic as 572 * full keyboard reset; it will be too troublesome and take too much time. 573 */ 574static int 575probe_keyboard(void) 576{ 577 int retry = PROBE_MAXRETRY; 578 int wait; 579 int i; 580 581 while (--retry >= 0) { 582 /* flush any noise */ 583 while (inb(IO_KBD + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL) { 584 delay7(); 585 inb(IO_KBD + KBD_DATA_PORT); 586 delay1ms(); 587 } 588 589 /* wait until the controller can accept a command */ 590 for (wait = PROBE_MAXWAIT; wait > 0; --wait) { 591 if (((i = inb(IO_KBD + KBD_STATUS_PORT)) 592 & (KBDS_INPUT_BUFFER_FULL | KBDS_ANY_BUFFER_FULL)) == 0) 593 break; 594 if (i & KBDS_ANY_BUFFER_FULL) { 595 delay7(); 596 inb(IO_KBD + KBD_DATA_PORT); 597 } 598 delay1ms(); 599 } 600 if (wait <= 0) 601 continue; 602 603 /* send the ECHO command */ 604 outb(IO_KBD + KBD_DATA_PORT, KBDC_ECHO); 605 606 /* wait for a response */ 607 for (wait = PROBE_MAXWAIT; wait > 0; --wait) { 608 if (inb(IO_KBD + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL) 609 break; 610 delay1ms(); 611 } 612 if (wait <= 0) 613 continue; 614 615 delay7(); 616 i = inb(IO_KBD + KBD_DATA_PORT); 617#ifdef PROBE_KBD_BEBUG 618 printf("probe_keyboard: got 0x%x.\n", i); 619#endif 620 if (i == KBD_ECHO) { 621 /* got the right answer */ 622 return (0); 623 } 624 } 625 626 return (1); 627} 628#endif /* KEYBOARD_PROBE */ 629