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