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
29typedef 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
37typedef enum {
38	SCREEN_ERASE_WHOLE,
39	SCREEN_ERASE_UP,
40	SCREEN_ERASE_DOWN
41} erase_screen_mode;
42
43typedef enum {
44	LINE_ERASE_WHOLE,
45	LINE_ERASE_LEFT,
46	LINE_ERASE_RIGHT
47} erase_line_mode;
48
49#define MAX_ARGS 8
50
51static 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
79int32 api_version = B_CUR_DRIVER_API_VERSION;
80
81static int32 sOpenMask;
82
83
84static inline void
85update_cursor(struct console_desc *console, int x, int y)
86{
87	console->module->move_cursor(x, y);
88}
89
90
91static void
92gotoxy(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
108static void
109reset_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*/
121static void
122scrup(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*/
143static void
144scrdown(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
162static void
163lf(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
176static void
177rlf(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
188static void
189cr(struct console_desc *console)
190{
191	console->x = 0;
192}
193
194
195static void
196del(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
214static void
215erase_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
236static void
237erase_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
257static void
258save_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
268static void
269restore_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
279static char
280console_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
291static void
292tab(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
302static void
303set_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
317static void
318set_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
381static bool
382process_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
542static 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
661static status_t
662console_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
677static status_t
678console_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
691static status_t
692console_close(void *cookie)
693{
694//	dprintf("console_close: entry\n");
695
696	return 0;
697}
698
699
700static status_t
701console_read(void *cookie, off_t pos, void *buffer, size_t *_length)
702{
703	return B_NOT_ALLOWED;
704}
705
706
707static status_t
708console_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
755static status_t
756console_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
775status_t
776init_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
815const char **
816publish_devices(void)
817{
818	static const char *devices[] = {
819		DEVICE_NAME,
820		NULL
821	};
822
823	return devices;
824}
825
826
827device_hooks *
828find_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
853status_t
854init_driver(void)
855{
856	return B_OK;
857}
858
859
860void
861uninit_driver(void)
862{
863}
864
865