btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / add-ons / kernel / drivers / common / console.cpp
blob804230b48453d8cdcd9124607de540cc22654520
1 /*
2 * Copyright 2005-2010, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
5 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
6 * Distributed under the terms of the NewOS License.
7 */
10 #include <Drivers.h>
11 #include <KernelExport.h>
13 #include <console.h>
14 #include <lock.h>
16 #include <string.h>
17 #include <stdio.h>
18 #include <termios.h>
21 #define DEVICE_NAME "console"
23 #define TAB_SIZE 8
24 #define TAB_MASK 7
26 #define FMASK 0x0f
27 #define BMASK 0x70
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;
37 typedef enum {
38 SCREEN_ERASE_WHOLE,
39 SCREEN_ERASE_UP,
40 SCREEN_ERASE_DOWN
41 } erase_screen_mode;
43 typedef enum {
44 LINE_ERASE_WHOLE,
45 LINE_ERASE_LEFT,
46 LINE_ERASE_RIGHT
47 } erase_line_mode;
49 #define MAX_ARGS 8
51 static struct console_desc {
52 mutex lock;
54 int32 lines;
55 int32 columns;
57 uint8 attr;
58 uint8 saved_attr;
59 bool bright_attr;
60 bool reverse_attr;
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;
67 int32 scroll_top; /* top of the scroll region */
68 int32 scroll_bottom; /* bottom of the scroll region */
70 /* state machine */
71 console_state state;
72 int32 arg_count;
73 int32 args[MAX_ARGS];
75 char module_name[B_FILE_NAME_LENGTH];
76 console_module_info *module;
77 } sConsole;
79 int32 api_version = B_CUR_DRIVER_API_VERSION;
82 static inline void
83 update_cursor(struct console_desc *console, int x, int y)
85 console->module->move_cursor(x, y);
89 static void
90 gotoxy(struct console_desc *console, int newX, int newY)
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;
101 console->x = newX;
102 console->y = newY;
106 static void
107 reset_console(struct console_desc *console)
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;
117 /*! Scroll from the cursor line up to the top of the scroll region up one line.
119 static void
120 scrup(struct console_desc *console)
122 // see if cursor is outside of scroll region
123 if (console->y < console->scroll_top || console->y > console->scroll_bottom)
124 return;
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);
132 // clear the bottom line
133 console->module->fill_glyph(0, console->y, console->columns, 1, ' ',
134 console->attr);
138 /*! Scroll from the cursor line down to the bottom of the scroll region down
139 one line.
141 static void
142 scrdown(struct console_desc *console)
144 // see if cursor is outside of scroll region
145 if (console->y < console->scroll_top || console->y > console->scroll_bottom)
146 return;
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);
154 // clear the top line
155 console->module->fill_glyph(0, console->y, console->columns, 1, ' ',
156 console->attr);
160 static void
161 lf(struct console_desc *console)
163 //dprintf("lf: y %d x %d scroll_top %d scoll_bottom %d\n", console->y, console->x, console->scroll_top, console->scroll_bottom);
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++;
174 static void
175 rlf(struct console_desc *console)
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--;
186 static void
187 cr(struct console_desc *console)
189 console->x = 0;
193 static void
194 del(struct console_desc *console)
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;
208 console->module->put_glyph(console->x, console->y, ' ', console->attr);
212 static void
213 erase_line(struct console_desc *console, erase_line_mode mode)
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;
234 static void
235 erase_screen(struct console_desc *console, erase_screen_mode mode)
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;
255 static void
256 save_cur(struct console_desc *console, bool saveAttrs)
258 console->saved_x = console->x;
259 console->saved_y = console->y;
261 if (saveAttrs)
262 console->saved_attr = console->attr;
266 static void
267 restore_cur(struct console_desc *console, bool restoreAttrs)
269 console->x = console->saved_x;
270 console->y = console->saved_y;
272 if (restoreAttrs)
273 console->attr = console->saved_attr;
277 static char
278 console_putch(struct console_desc *console, const char c)
280 if (++console->x >= console->columns) {
281 cr(console);
282 lf(console);
284 console->module->put_glyph(console->x - 1, console->y, c, console->attr);
285 return c;
289 static void
290 tab(struct console_desc *console)
292 console->x = (console->x + TAB_SIZE) & ~TAB_MASK;
293 if (console->x >= console->columns) {
294 console->x -= console->columns;
295 lf(console);
300 static void
301 set_scroll_region(struct console_desc *console, int top, int bottom)
303 if (top < 0)
304 top = 0;
305 if (bottom >= console->lines)
306 bottom = console->lines - 1;
307 if (top > bottom)
308 return;
310 console->scroll_top = top;
311 console->scroll_bottom = bottom;
315 static void
316 set_vt100_attributes(struct console_desc *console, int32 *args, int32 argCount)
318 if (argCount == 0) {
319 // that's the default (attributes off)
320 argCount++;
321 args[0] = 0;
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;
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
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
379 static bool
380 process_vt100_command(struct console_desc *console, const char c,
381 bool seenBracket, int32 *args, int32 argCount)
383 bool ret = true;
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);
388 if (seenBracket) {
389 switch(c) {
390 case 'H': // set cursor position
391 case 'f':
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;
402 case 'A': // move up
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;
410 case 'e':
411 case 'B': // move down
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;
419 case 'D': // move left
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;
427 case 'a':
428 case 'C': // move right
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;
436 case '`':
437 case 'G': // set X position
439 int32 newX = argCount > 0 ? args[0] : 1;
440 if (newX > 0)
441 newX--;
442 gotoxy(console, newX, console->y);
443 break;
445 case 'd': // set y position
447 int32 newY = argCount > 0 ? args[0] : 1;
448 if (newY > 0)
449 newY--;
450 gotoxy(console, console->x, newY);
451 break;
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
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;
467 case 'L': // scroll virtual down at cursor
469 int32 lines = argCount > 0 ? args[0] : 1;
470 while (lines > 0) {
471 scrdown(console);
472 lines--;
474 break;
476 case 'M': // scroll virtual up at cursor
478 int32 lines = argCount > 0 ? args[0] : 1;
479 while (lines > 0) {
480 scrup(console);
481 lines--;
483 break;
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);
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);
506 break;
507 case 'm':
508 if (argCount >= 0)
509 set_vt100_attributes(console, args, argCount);
510 break;
511 default:
512 ret = false;
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;
536 return ret;
540 static ssize_t
541 _console_write(struct console_desc *console, const void *buffer, size_t length)
543 const char *c;
544 size_t pos = 0;
546 while (pos < length) {
547 c = &((const char *)buffer)[pos++];
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);
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,
591 console->state = CONSOLE_STATE_NORMAL;
592 break;
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,
611 console->state = CONSOLE_STATE_NORMAL;
612 break;
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;
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;
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;
652 return pos;
656 // #pragma mark -
659 static status_t
660 console_open(const char *name, uint32 flags, void **cookie)
662 *cookie = &sConsole;
664 status_t status = get_module(sConsole.module_name, (module_info **)&sConsole.module);
665 if (status == B_OK)
666 sConsole.module->clear(0x0f);
668 return status;
672 static status_t
673 console_freecookie(void *cookie)
675 if (sConsole.module != NULL) {
676 put_module(sConsole.module_name);
677 sConsole.module = NULL;
680 return B_OK;
684 static status_t
685 console_close(void *cookie)
687 // dprintf("console_close: entry\n");
689 return 0;
693 static status_t
694 console_read(void *cookie, off_t pos, void *buffer, size_t *_length)
696 return B_NOT_ALLOWED;
700 static status_t
701 console_write(void *cookie, off_t pos, const void *buffer, size_t *_length)
703 struct console_desc *console = (struct console_desc *)cookie;
704 ssize_t written;
706 #if 0
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]);
716 dprintf("\"\n");
718 #endif
720 mutex_lock(&console->lock);
722 update_cursor(console, -1, -1); // hide it
723 written = _console_write(console, buffer, *_length);
724 update_cursor(console, console->x, console->y);
726 mutex_unlock(&console->lock);
728 if (written >= 0) {
729 *_length = written;
730 return B_OK;
732 return written;
736 static status_t
737 console_ioctl(void *cookie, uint32 op, void *buffer, size_t length)
739 struct console_desc *console = (struct console_desc *)cookie;
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;
746 return user_memcpy(buffer, &size, sizeof(struct winsize));
749 return B_BAD_VALUE;
753 // #pragma mark -
756 status_t
757 init_hardware(void)
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;
764 bool found = false;
766 char buffer[B_FILE_NAME_LENGTH];
767 size_t bufferSize = sizeof(buffer);
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;
778 bufferSize = sizeof(buffer);
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);
786 reset_console(&sConsole);
787 gotoxy(&sConsole, 0, 0);
788 save_cur(&sConsole, true);
791 close_module_list(cookie);
792 return found ? B_OK : B_ERROR;
796 const char **
797 publish_devices(void)
799 static const char *devices[] = {
800 DEVICE_NAME,
801 NULL
804 return devices;
808 device_hooks *
809 find_device(const char *name)
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
827 if (!strcmp(name, DEVICE_NAME))
828 return &hooks;
830 return NULL;
834 status_t
835 init_driver(void)
837 return B_OK;
841 void
842 uninit_driver(void)