1 /* 2 * Copyright 2005-2010, Axel Dörfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 * 5 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. 6 * Distributed under the terms of the NewOS License. 7 */ 8 9 10 #include <Drivers.h> 11 #include <KernelExport.h> 12 13 #include <console.h> 14 #include <lock.h> 15 16 #include <string.h> 17 #include <stdio.h> 18 #include <termios.h> 19 20 21 #define DEVICE_NAME "console" 22 23 #define TAB_SIZE 8 24 #define TAB_MASK 7 25 26 #define FMASK 0x0f 27 #define BMASK 0x70 28 29 typedef enum { 30 CONSOLE_STATE_NORMAL = 0, 31 CONSOLE_STATE_GOT_ESCAPE, 32 CONSOLE_STATE_SEEN_BRACKET, 33 CONSOLE_STATE_NEW_ARG, 34 CONSOLE_STATE_PARSING_ARG, 35 } console_state; 36 37 typedef enum { 38 SCREEN_ERASE_WHOLE, 39 SCREEN_ERASE_UP, 40 SCREEN_ERASE_DOWN 41 } erase_screen_mode; 42 43 typedef enum { 44 LINE_ERASE_WHOLE, 45 LINE_ERASE_LEFT, 46 LINE_ERASE_RIGHT 47 } erase_line_mode; 48 49 #define MAX_ARGS 8 50 51 static struct console_desc { 52 mutex lock; 53 54 int32 lines; 55 int32 columns; 56 57 uint8 attr; 58 uint8 saved_attr; 59 bool bright_attr; 60 bool reverse_attr; 61 62 int32 x; /* current x coordinate */ 63 int32 y; /* current y coordinate */ 64 int32 saved_x; /* used to save x and y */ 65 int32 saved_y; 66 67 int32 scroll_top; /* top of the scroll region */ 68 int32 scroll_bottom; /* bottom of the scroll region */ 69 70 /* state machine */ 71 console_state state; 72 int32 arg_count; 73 int32 args[MAX_ARGS]; 74 75 char module_name[B_FILE_NAME_LENGTH]; 76 console_module_info *module; 77 } sConsole; 78 79 int32 api_version = B_CUR_DRIVER_API_VERSION; 80 81 82 static inline void 83 update_cursor(struct console_desc *console, int x, int y) 84 { 85 console->module->move_cursor(x, y); 86 } 87 88 89 static void 90 gotoxy(struct console_desc *console, int newX, int newY) 91 { 92 if (newX >= console->columns) 93 newX = console->columns - 1; 94 if (newX < 0) 95 newX = 0; 96 if (newY >= console->lines) 97 newY = console->lines - 1; 98 if (newY < 0) 99 newY = 0; 100 101 console->x = newX; 102 console->y = newY; 103 } 104 105 106 static void 107 reset_console(struct console_desc *console) 108 { 109 console->attr = 0x0f; 110 console->scroll_top = 0; 111 console->scroll_bottom = console->lines - 1; 112 console->bright_attr = true; 113 console->reverse_attr = false; 114 } 115 116 117 /*! Scroll from the cursor line up to the top of the scroll region up one line. 118 */ 119 static void 120 scrup(struct console_desc *console) 121 { 122 // see if cursor is outside of scroll region 123 if (console->y < console->scroll_top || console->y > console->scroll_bottom) 124 return; 125 126 if (console->y - console->scroll_top > 1) { 127 // move the screen up one 128 console->module->blit(0, console->scroll_top + 1, console->columns, 129 console->y - console->scroll_top, 0, console->scroll_top); 130 } 131 132 // clear the bottom line 133 console->module->fill_glyph(0, console->y, console->columns, 1, ' ', 134 console->attr); 135 } 136 137 138 /*! Scroll from the cursor line down to the bottom of the scroll region down 139 one line. 140 */ 141 static void 142 scrdown(struct console_desc *console) 143 { 144 // see if cursor is outside of scroll region 145 if (console->y < console->scroll_top || console->y > console->scroll_bottom) 146 return; 147 148 if (console->scroll_bottom - console->y > 1) { 149 // move the screen down one 150 console->module->blit(0, console->y, console->columns, 151 console->scroll_bottom - console->y, 0, console->y + 1); 152 } 153 154 // clear the top line 155 console->module->fill_glyph(0, console->y, console->columns, 1, ' ', 156 console->attr); 157 } 158 159 160 static void 161 lf(struct console_desc *console) 162 { 163 //dprintf("lf: y %d x %d scroll_top %d scoll_bottom %d\n", console->y, console->x, console->scroll_top, console->scroll_bottom); 164 165 if (console->y == console->scroll_bottom ) { 166 // we hit the bottom of our scroll region 167 scrup(console); 168 } else if(console->y < console->scroll_bottom) { 169 console->y++; 170 } 171 } 172 173 174 static void 175 rlf(struct console_desc *console) 176 { 177 if (console->y == console->scroll_top) { 178 // we hit the top of our scroll region 179 scrdown(console); 180 } else if (console->y > console->scroll_top) { 181 console->y--; 182 } 183 } 184 185 186 static void 187 cr(struct console_desc *console) 188 { 189 console->x = 0; 190 } 191 192 193 static void 194 del(struct console_desc *console) 195 { 196 if (console->x > 0) { 197 console->x--; 198 } else if (console->y > 0) { 199 console->y--; 200 console->x = console->columns - 1; 201 } else { 202 //This doesn't work... 203 //scrdown(console); 204 //console->y--; 205 //console->x = console->columns - 1; 206 return; 207 } 208 console->module->put_glyph(console->x, console->y, ' ', console->attr); 209 } 210 211 212 static void 213 erase_line(struct console_desc *console, erase_line_mode mode) 214 { 215 switch (mode) { 216 case LINE_ERASE_WHOLE: 217 console->module->fill_glyph(0, console->y, console->columns, 1, ' ', 218 console->attr); 219 break; 220 case LINE_ERASE_LEFT: 221 console->module->fill_glyph(0, console->y, console->x + 1, 1, ' ', 222 console->attr); 223 break; 224 case LINE_ERASE_RIGHT: 225 console->module->fill_glyph(console->x, console->y, 226 console->columns - console->x, 1, ' ', console->attr); 227 break; 228 default: 229 return; 230 } 231 } 232 233 234 static void 235 erase_screen(struct console_desc *console, erase_screen_mode mode) 236 { 237 switch (mode) { 238 case SCREEN_ERASE_WHOLE: 239 console->module->clear(console->attr); 240 break; 241 case SCREEN_ERASE_UP: 242 console->module->fill_glyph(0, 0, console->columns, console->y + 1, 243 ' ', console->attr); 244 break; 245 case SCREEN_ERASE_DOWN: 246 console->module->fill_glyph(console->y, 0, console->columns, 247 console->lines - console->y, ' ', console->attr); 248 break; 249 default: 250 return; 251 } 252 } 253 254 255 static void 256 save_cur(struct console_desc *console, bool saveAttrs) 257 { 258 console->saved_x = console->x; 259 console->saved_y = console->y; 260 261 if (saveAttrs) 262 console->saved_attr = console->attr; 263 } 264 265 266 static void 267 restore_cur(struct console_desc *console, bool restoreAttrs) 268 { 269 console->x = console->saved_x; 270 console->y = console->saved_y; 271 272 if (restoreAttrs) 273 console->attr = console->saved_attr; 274 } 275 276 277 static char 278 console_putch(struct console_desc *console, const char c) 279 { 280 if (++console->x >= console->columns) { 281 cr(console); 282 lf(console); 283 } 284 console->module->put_glyph(console->x - 1, console->y, c, console->attr); 285 return c; 286 } 287 288 289 static void 290 tab(struct console_desc *console) 291 { 292 console->x = (console->x + TAB_SIZE) & ~TAB_MASK; 293 if (console->x >= console->columns) { 294 console->x -= console->columns; 295 lf(console); 296 } 297 } 298 299 300 static void 301 set_scroll_region(struct console_desc *console, int top, int bottom) 302 { 303 if (top < 0) 304 top = 0; 305 if (bottom >= console->lines) 306 bottom = console->lines - 1; 307 if (top > bottom) 308 return; 309 310 console->scroll_top = top; 311 console->scroll_bottom = bottom; 312 } 313 314 315 static void 316 set_vt100_attributes(struct console_desc *console, int32 *args, int32 argCount) 317 { 318 if (argCount == 0) { 319 // that's the default (attributes off) 320 argCount++; 321 args[0] = 0; 322 } 323 324 for (int32 i = 0; i < argCount; i++) { 325 //dprintf("set_vt100_attributes: %ld\n", args[i]); 326 switch (args[i]) { 327 case 0: // reset 328 console->attr = 0x0f; 329 console->bright_attr = true; 330 console->reverse_attr = false; 331 break; 332 case 1: // bright 333 console->bright_attr = true; 334 console->attr |= 0x08; // set the bright bit 335 break; 336 case 2: // dim 337 console->bright_attr = false; 338 console->attr &= ~0x08; // unset the bright bit 339 break; 340 case 4: // underscore we can't do 341 break; 342 case 5: // blink 343 console->attr |= 0x80; // set the blink bit 344 break; 345 case 7: // reverse 346 console->reverse_attr = true; 347 console->attr = ((console->attr & BMASK) >> 4) 348 | ((console->attr & FMASK) << 4); 349 if (console->bright_attr) 350 console->attr |= 0x08; 351 break; 352 case 8: // hidden? 353 break; 354 355 /* foreground colors */ 356 case 30: console->attr = (console->attr & ~FMASK) | 0 | (console->bright_attr ? 0x08 : 0); break; // black 357 case 31: console->attr = (console->attr & ~FMASK) | 4 | (console->bright_attr ? 0x08 : 0); break; // red 358 case 32: console->attr = (console->attr & ~FMASK) | 2 | (console->bright_attr ? 0x08 : 0); break; // green 359 case 33: console->attr = (console->attr & ~FMASK) | 6 | (console->bright_attr ? 0x08 : 0); break; // yellow 360 case 34: console->attr = (console->attr & ~FMASK) | 1 | (console->bright_attr ? 0x08 : 0); break; // blue 361 case 35: console->attr = (console->attr & ~FMASK) | 5 | (console->bright_attr ? 0x08 : 0); break; // magenta 362 case 36: console->attr = (console->attr & ~FMASK) | 3 | (console->bright_attr ? 0x08 : 0); break; // cyan 363 case 37: console->attr = (console->attr & ~FMASK) | 7 | (console->bright_attr ? 0x08 : 0); break; // white 364 365 /* background colors */ 366 case 40: console->attr = (console->attr & ~BMASK) | (0 << 4); break; // black 367 case 41: console->attr = (console->attr & ~BMASK) | (4 << 4); break; // red 368 case 42: console->attr = (console->attr & ~BMASK) | (2 << 4); break; // green 369 case 43: console->attr = (console->attr & ~BMASK) | (6 << 4); break; // yellow 370 case 44: console->attr = (console->attr & ~BMASK) | (1 << 4); break; // blue 371 case 45: console->attr = (console->attr & ~BMASK) | (5 << 4); break; // magenta 372 case 46: console->attr = (console->attr & ~BMASK) | (3 << 4); break; // cyan 373 case 47: console->attr = (console->attr & ~BMASK) | (7 << 4); break; // white 374 } 375 } 376 } 377 378 379 static bool 380 process_vt100_command(struct console_desc *console, const char c, 381 bool seenBracket, int32 *args, int32 argCount) 382 { 383 bool ret = true; 384 385 //dprintf("process_vt100_command: c '%c', argCount %ld, arg[0] %ld, arg[1] %ld, seenBracket %d\n", 386 // c, argCount, args[0], args[1], seenBracket); 387 388 if (seenBracket) { 389 switch(c) { 390 case 'H': // set cursor position 391 case 'f': 392 { 393 int32 row = argCount > 0 ? args[0] : 1; 394 int32 col = argCount > 1 ? args[1] : 1; 395 if (row > 0) 396 row--; 397 if (col > 0) 398 col--; 399 gotoxy(console, col, row); 400 break; 401 } 402 case 'A': // move up 403 { 404 int32 deltaY = argCount > 0 ? -args[0] : -1; 405 if (deltaY == 0) 406 deltaY = -1; 407 gotoxy(console, console->x, console->y + deltaY); 408 break; 409 } 410 case 'e': 411 case 'B': // move down 412 { 413 int32 deltaY = argCount > 0 ? args[0] : 1; 414 if (deltaY == 0) 415 deltaY = 1; 416 gotoxy(console, console->x, console->y + deltaY); 417 break; 418 } 419 case 'D': // move left 420 { 421 int32 deltaX = argCount > 0 ? -args[0] : -1; 422 if (deltaX == 0) 423 deltaX = -1; 424 gotoxy(console, console->x + deltaX, console->y); 425 break; 426 } 427 case 'a': 428 case 'C': // move right 429 { 430 int32 deltaX = argCount > 0 ? args[0] : 1; 431 if (deltaX == 0) 432 deltaX = 1; 433 gotoxy(console, console->x + deltaX, console->y); 434 break; 435 } 436 case '`': 437 case 'G': // set X position 438 { 439 int32 newX = argCount > 0 ? args[0] : 1; 440 if (newX > 0) 441 newX--; 442 gotoxy(console, newX, console->y); 443 break; 444 } 445 case 'd': // set y position 446 { 447 int32 newY = argCount > 0 ? args[0] : 1; 448 if (newY > 0) 449 newY--; 450 gotoxy(console, console->x, newY); 451 break; 452 } 453 case 's': // save current cursor 454 save_cur(console, false); 455 break; 456 case 'u': // restore cursor 457 restore_cur(console, false); 458 break; 459 case 'r': // set scroll region 460 { 461 int32 low = argCount > 0 ? args[0] : 1; 462 int32 high = argCount > 1 ? args[1] : console->lines; 463 if (low <= high) 464 set_scroll_region(console, low - 1, high - 1); 465 break; 466 } 467 case 'L': // scroll virtual down at cursor 468 { 469 int32 lines = argCount > 0 ? args[0] : 1; 470 while (lines > 0) { 471 scrdown(console); 472 lines--; 473 } 474 break; 475 } 476 case 'M': // scroll virtual up at cursor 477 { 478 int32 lines = argCount > 0 ? args[0] : 1; 479 while (lines > 0) { 480 scrup(console); 481 lines--; 482 } 483 break; 484 } 485 case 'K': 486 if (argCount == 0 || args[0] == 0) { 487 // erase to end of line 488 erase_line(console, LINE_ERASE_RIGHT); 489 } else if (argCount > 0) { 490 if (args[0] == 1) 491 erase_line(console, LINE_ERASE_LEFT); 492 else if (args[0] == 2) 493 erase_line(console, LINE_ERASE_WHOLE); 494 } 495 break; 496 case 'J': 497 if (argCount == 0 || args[0] == 0) { 498 // erase to end of screen 499 erase_screen(console, SCREEN_ERASE_DOWN); 500 } else { 501 if (args[0] == 1) 502 erase_screen(console, SCREEN_ERASE_UP); 503 else if (args[0] == 2) 504 erase_screen(console, SCREEN_ERASE_WHOLE); 505 } 506 break; 507 case 'm': 508 if (argCount >= 0) 509 set_vt100_attributes(console, args, argCount); 510 break; 511 default: 512 ret = false; 513 } 514 } else { 515 switch (c) { 516 case 'c': 517 reset_console(console); 518 break; 519 case 'D': 520 rlf(console); 521 break; 522 case 'M': 523 lf(console); 524 break; 525 case '7': 526 save_cur(console, true); 527 break; 528 case '8': 529 restore_cur(console, true); 530 break; 531 default: 532 ret = false; 533 } 534 } 535 536 return ret; 537 } 538 539 540 static ssize_t 541 _console_write(struct console_desc *console, const void *buffer, size_t length) 542 { 543 const char *c; 544 size_t pos = 0; 545 546 while (pos < length) { 547 c = &((const char *)buffer)[pos++]; 548 549 switch (console->state) { 550 case CONSOLE_STATE_NORMAL: 551 // just output the stuff 552 switch (*c) { 553 case '\n': 554 lf(console); 555 break; 556 case '\r': 557 cr(console); 558 break; 559 case 0x8: // backspace 560 del(console); 561 break; 562 case '\t': 563 tab(console); 564 break; 565 case '\a': 566 // beep 567 dprintf("<BEEP>\n"); 568 break; 569 case '\0': 570 break; 571 case 0x1b: 572 // escape character 573 console->arg_count = 0; 574 console->state = CONSOLE_STATE_GOT_ESCAPE; 575 break; 576 default: 577 console_putch(console, *c); 578 } 579 break; 580 case CONSOLE_STATE_GOT_ESCAPE: 581 // Look for either commands with no argument, or the '[' 582 // character 583 switch (*c) { 584 case '[': 585 console->state = CONSOLE_STATE_SEEN_BRACKET; 586 break; 587 default: 588 console->args[0] = 0; 589 process_vt100_command(console, *c, false, console->args, 590 0); 591 console->state = CONSOLE_STATE_NORMAL; 592 break; 593 } 594 break; 595 case CONSOLE_STATE_SEEN_BRACKET: 596 switch (*c) { 597 case '0'...'9': 598 console->arg_count = 0; 599 console->args[console->arg_count] = *c - '0'; 600 console->state = CONSOLE_STATE_PARSING_ARG; 601 break; 602 case '?': 603 // Private DEC mode parameter follows - we ignore those 604 // anyway 605 // TODO: check if it was really used in combination with 606 // a mode command 607 break; 608 default: 609 process_vt100_command(console, *c, true, console->args, 610 0); 611 console->state = CONSOLE_STATE_NORMAL; 612 break; 613 } 614 break; 615 case CONSOLE_STATE_NEW_ARG: 616 switch (*c) { 617 case '0'...'9': 618 console->arg_count++; 619 if (console->arg_count == MAX_ARGS) { 620 console->state = CONSOLE_STATE_NORMAL; 621 break; 622 } 623 console->args[console->arg_count] = *c - '0'; 624 console->state = CONSOLE_STATE_PARSING_ARG; 625 break; 626 default: 627 process_vt100_command(console, *c, true, console->args, 628 console->arg_count + 1); 629 console->state = CONSOLE_STATE_NORMAL; 630 break; 631 } 632 break; 633 case CONSOLE_STATE_PARSING_ARG: 634 // parse args 635 switch (*c) { 636 case '0'...'9': 637 console->args[console->arg_count] *= 10; 638 console->args[console->arg_count] += *c - '0'; 639 break; 640 case ';': 641 console->state = CONSOLE_STATE_NEW_ARG; 642 break; 643 default: 644 process_vt100_command(console, *c, true, console->args, 645 console->arg_count + 1); 646 console->state = CONSOLE_STATE_NORMAL; 647 break; 648 } 649 } 650 } 651 652 return pos; 653 } 654 655 656 // #pragma mark - 657 658 659 static status_t 660 console_open(const char *name, uint32 flags, void **cookie) 661 { 662 *cookie = &sConsole; 663 664 status_t status = get_module(sConsole.module_name, (module_info **)&sConsole.module); 665 if (status == B_OK) 666 sConsole.module->clear(0x0f); 667 668 return status; 669 } 670 671 672 static status_t 673 console_freecookie(void *cookie) 674 { 675 if (sConsole.module != NULL) { 676 put_module(sConsole.module_name); 677 sConsole.module = NULL; 678 } 679 680 return B_OK; 681 } 682 683 684 static status_t 685 console_close(void *cookie) 686 { 687 // dprintf("console_close: entry\n"); 688 689 return 0; 690 } 691 692 693 static status_t 694 console_read(void *cookie, off_t pos, void *buffer, size_t *_length) 695 { 696 return B_NOT_ALLOWED; 697 } 698 699 700 static status_t 701 console_write(void *cookie, off_t pos, const void *buffer, size_t *_length) 702 { 703 struct console_desc *console = (struct console_desc *)cookie; 704 ssize_t written; 705 706 #if 0 707 { 708 const char *input = (const char *)buffer; 709 dprintf("console_write (%lu bytes): \"", *_length); 710 for (uint32 i = 0; i < *_length; i++) { 711 if (input[i] < ' ') 712 dprintf("(%d:0x%x)", input[i], input[i]); 713 else 714 dprintf("%c", input[i]); 715 } 716 dprintf("\"\n"); 717 } 718 #endif 719 720 mutex_lock(&console->lock); 721 722 update_cursor(console, -1, -1); // hide it 723 written = _console_write(console, buffer, *_length); 724 update_cursor(console, console->x, console->y); 725 726 mutex_unlock(&console->lock); 727 728 if (written >= 0) { 729 *_length = written; 730 return B_OK; 731 } 732 return written; 733 } 734 735 736 static status_t 737 console_ioctl(void *cookie, uint32 op, void *buffer, size_t length) 738 { 739 struct console_desc *console = (struct console_desc *)cookie; 740 741 if (op == TIOCGWINSZ) { 742 struct winsize size; 743 size.ws_xpixel = size.ws_col = console->columns; 744 size.ws_ypixel = size.ws_row = console->lines; 745 746 return user_memcpy(buffer, &size, sizeof(struct winsize)); 747 } 748 749 return B_BAD_VALUE; 750 } 751 752 753 // #pragma mark - 754 755 756 status_t 757 init_hardware(void) 758 { 759 // iterate through the list of console modules until we find one that accepts the job 760 void *cookie = open_module_list("console"); 761 if (cookie == NULL) 762 return B_ERROR; 763 764 bool found = false; 765 766 char buffer[B_FILE_NAME_LENGTH]; 767 size_t bufferSize = sizeof(buffer); 768 769 while (read_next_module_name(cookie, buffer, &bufferSize) == B_OK) { 770 dprintf("con_init: trying module %s\n", buffer); 771 if (get_module(buffer, (module_info **)&sConsole.module) == B_OK) { 772 strlcpy(sConsole.module_name, buffer, sizeof(sConsole.module_name)); 773 put_module(buffer); 774 found = true; 775 break; 776 } 777 778 bufferSize = sizeof(buffer); 779 } 780 781 if (found) { 782 // set up the console structure 783 mutex_init(&sConsole.lock, "console lock"); 784 sConsole.module->get_size(&sConsole.columns, &sConsole.lines); 785 786 reset_console(&sConsole); 787 gotoxy(&sConsole, 0, 0); 788 save_cur(&sConsole, true); 789 } 790 791 close_module_list(cookie); 792 return found ? B_OK : B_ERROR; 793 } 794 795 796 const char ** 797 publish_devices(void) 798 { 799 static const char *devices[] = { 800 DEVICE_NAME, 801 NULL 802 }; 803 804 return devices; 805 } 806 807 808 device_hooks * 809 find_device(const char *name) 810 { 811 static device_hooks hooks = { 812 &console_open, 813 &console_close, 814 &console_freecookie, 815 &console_ioctl, 816 &console_read, 817 &console_write, 818 /* Leave select/deselect/readv/writev undefined. The kernel will 819 * use its own default implementation. The basic hooks above this 820 * line MUST be defined, however. */ 821 NULL, 822 NULL, 823 NULL, 824 NULL 825 }; 826 827 if (!strcmp(name, DEVICE_NAME)) 828 return &hooks; 829 830 return NULL; 831 } 832 833 834 status_t 835 init_driver(void) 836 { 837 return B_OK; 838 } 839 840 841 void 842 uninit_driver(void) 843 { 844 } 845 846