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