Retired DEV_{READ,WRITE,GATHER,SCATTER,IOCTL} (safe versions *_S are to
[minix3.git] / drivers / tty / console.c
blob4fd10c30b9474f5e13785df3d670a904c08af0f3
1 /* Code and data for the IBM console driver.
3 * The 6845 video controller used by the IBM PC shares its video memory with
4 * the CPU somewhere in the 0xB0000 memory bank. To the 6845 this memory
5 * consists of 16-bit words. Each word has a character code in the low byte
6 * and a so-called attribute byte in the high byte. The CPU directly modifies
7 * video memory to display characters, and sets two registers on the 6845 that
8 * specify the video origin and the cursor position. The video origin is the
9 * place in video memory where the first character (upper left corner) can
10 * be found. Moving the origin is a fast way to scroll the screen. Some
11 * video adapters wrap around the top of video memory, so the origin can
12 * move without bounds. For other adapters screen memory must sometimes be
13 * moved to reset the origin. All computations on video memory use character
14 * (word) addresses for simplicity and assume there is no wrapping. The
15 * assembly support functions translate the word addresses to byte addresses
16 * and the scrolling function worries about wrapping.
19 #include "../drivers.h"
20 #include <termios.h>
21 #include <sys/ioctl.h>
22 #include <sys/vm.h>
23 #include <minix/callnr.h>
24 #include <minix/com.h>
25 #include "tty.h"
27 #include "../../kernel/const.h"
28 #include "../../kernel/config.h"
29 #include "../../kernel/type.h"
31 /* Definitions used by the console driver. */
32 #define MONO_BASE 0xB0000L /* base of mono video memory */
33 #define COLOR_BASE 0xB8000L /* base of color video memory */
34 #define MONO_SIZE 0x1000 /* 4K mono video memory */
35 #define COLOR_SIZE 0x4000 /* 16K color video memory */
36 #define EGA_SIZE 0x8000 /* EGA & VGA have at least 32K */
37 #define BLANK_COLOR 0x0700 /* determines cursor color on blank screen */
38 #define SCROLL_UP 0 /* scroll forward */
39 #define SCROLL_DOWN 1 /* scroll backward */
40 #define BLANK_MEM ((u16_t *) 0) /* tells mem_vid_copy() to blank the screen */
41 #define CONS_RAM_WORDS 80 /* video ram buffer size */
42 #define MAX_ESC_PARMS 4 /* number of escape sequence params allowed */
44 /* Constants relating to the controller chips. */
45 #define M_6845 0x3B4 /* port for 6845 mono */
46 #define C_6845 0x3D4 /* port for 6845 color */
47 #define INDEX 0 /* 6845's index register */
48 #define DATA 1 /* 6845's data register */
49 #define STATUS 6 /* 6845's status register */
50 #define VID_ORG 12 /* 6845's origin register */
51 #define CURSOR 14 /* 6845's cursor register */
53 /* The clock task should provide an interface for this */
54 #define TIMER_FREQ 1193182L /* clock frequency for timer in PC and AT */
56 /* Beeper. */
57 #define BEEP_FREQ 0x0533 /* value to put into timer to set beep freq */
58 #define B_TIME 3 /* length of CTRL-G beep is ticks */
60 /* definitions used for font management */
61 #define GA_SEQUENCER_INDEX 0x3C4
62 #define GA_SEQUENCER_DATA 0x3C5
63 #define GA_GRAPHICS_INDEX 0x3CE
64 #define GA_GRAPHICS_DATA 0x3CF
65 #define GA_VIDEO_ADDRESS 0xA0000L
66 #define GA_FONT_SIZE 8192
68 /* Global variables used by the console driver and assembly support. */
69 PUBLIC int vid_index; /* index of video segment in remote mem map */
70 PUBLIC u16_t vid_seg;
71 PUBLIC vir_bytes vid_off; /* video ram is found at vid_seg:vid_off */
72 PUBLIC unsigned vid_size; /* 0x2000 for color or 0x0800 for mono */
73 PUBLIC unsigned vid_mask; /* 0x1FFF for color or 0x07FF for mono */
74 PUBLIC unsigned blank_color = BLANK_COLOR; /* display code for blank */
76 /* Private variables used by the console driver. */
77 PRIVATE int vid_port; /* I/O port for accessing 6845 */
78 PRIVATE int wrap; /* hardware can wrap? */
79 PRIVATE int softscroll; /* 1 = software scrolling, 0 = hardware */
80 PRIVATE int beeping; /* speaker is beeping? */
81 PRIVATE unsigned font_lines; /* font lines per character */
82 PRIVATE unsigned scr_width; /* # characters on a line */
83 PRIVATE unsigned scr_lines; /* # lines on the screen */
84 PRIVATE unsigned scr_size; /* # characters on the screen */
86 PRIVATE int disabled_vc = -1; /* Virtual console that was active when
87 * disable_console was called.
89 PRIVATE int disabled_sm; /* Scroll mode to be restored when re-enabling
90 * console
93 /* Per console data. */
94 typedef struct console {
95 tty_t *c_tty; /* associated TTY struct */
96 int c_column; /* current column number (0-origin) */
97 int c_row; /* current row (0 at top of screen) */
98 int c_rwords; /* number of WORDS (not bytes) in outqueue */
99 unsigned c_start; /* start of video memory of this console */
100 unsigned c_limit; /* limit of this console's video memory */
101 unsigned c_org; /* location in RAM where 6845 base points */
102 unsigned c_cur; /* current position of cursor in video RAM */
103 unsigned c_attr; /* character attribute */
104 unsigned c_blank; /* blank attribute */
105 char c_reverse; /* reverse video */
106 char c_esc_state; /* 0=normal, 1=ESC, 2=ESC[ */
107 char c_esc_intro; /* Distinguishing character following ESC */
108 int *c_esc_parmp; /* pointer to current escape parameter */
109 int c_esc_parmv[MAX_ESC_PARMS]; /* list of escape parameters */
110 u16_t c_ramqueue[CONS_RAM_WORDS]; /* buffer for video RAM */
111 } console_t;
113 PRIVATE int nr_cons= 1; /* actual number of consoles */
114 PRIVATE console_t cons_table[NR_CONS];
115 PRIVATE console_t *curcons; /* currently visible */
117 /* Color if using a color controller. */
118 #define color (vid_port == C_6845)
120 /* Map from ANSI colors to the attributes used by the PC */
121 PRIVATE int ansi_colors[8] = {0, 4, 2, 6, 1, 5, 3, 7};
123 /* Structure used for font management */
124 struct sequence {
125 unsigned short index;
126 unsigned char port;
127 unsigned char value;
130 FORWARD _PROTOTYPE( int cons_write, (struct tty *tp, int try) );
131 FORWARD _PROTOTYPE( void cons_echo, (tty_t *tp, int c) );
132 FORWARD _PROTOTYPE( void out_char, (console_t *cons, int c) );
133 FORWARD _PROTOTYPE( void cons_putk, (int c) );
134 FORWARD _PROTOTYPE( void beep, (void) );
135 FORWARD _PROTOTYPE( void do_escape, (console_t *cons, int c) );
136 FORWARD _PROTOTYPE( void flush, (console_t *cons) );
137 FORWARD _PROTOTYPE( void parse_escape, (console_t *cons, int c) );
138 FORWARD _PROTOTYPE( void scroll_screen, (console_t *cons, int dir) );
139 FORWARD _PROTOTYPE( void set_6845, (int reg, unsigned val) );
140 FORWARD _PROTOTYPE( void get_6845, (int reg, unsigned *val) );
141 FORWARD _PROTOTYPE( void stop_beep, (timer_t *tmrp) );
142 FORWARD _PROTOTYPE( void cons_org0, (void) );
143 FORWARD _PROTOTYPE( void disable_console, (void) );
144 FORWARD _PROTOTYPE( void reenable_console, (void) );
145 FORWARD _PROTOTYPE( int ga_program, (struct sequence *seq) );
146 FORWARD _PROTOTYPE( int cons_ioctl, (tty_t *tp, int) );
147 FORWARD _PROTOTYPE( void ser_putc, (char c) );
149 /*===========================================================================*
150 * cons_write *
151 *===========================================================================*/
152 PRIVATE int cons_write(tp, try)
153 register struct tty *tp; /* tells which terminal is to be used */
154 int try;
156 /* Copy as much data as possible to the output queue, then start I/O. On
157 * memory-mapped terminals, such as the IBM console, the I/O will also be
158 * finished, and the counts updated. Keep repeating until all I/O done.
161 int count;
162 int result;
163 register char *tbuf;
164 char buf[64];
165 console_t *cons = tp->tty_priv;
167 if (try) return 1; /* we can always write to console */
169 /* Check quickly for nothing to do, so this can be called often without
170 * unmodular tests elsewhere.
172 if ((count = tp->tty_outleft) == 0 || tp->tty_inhibited) return;
174 /* Copy the user bytes to buf[] for decent addressing. Loop over the
175 * copies, since the user buffer may be much larger than buf[].
177 do {
178 if (count > sizeof(buf)) count = sizeof(buf);
179 if(tp->tty_out_safe) {
180 if ((result = sys_safecopyfrom(tp->tty_outproc, tp->tty_out_vir_g,
181 tp->tty_out_vir_offset, (vir_bytes) buf, count, D)) != OK)
182 break;
183 tp->tty_out_vir_offset += count;
184 } else {
185 if ((result = sys_vircopy(tp->tty_outproc, D, tp->tty_out_vir_g,
186 SELF, D, (vir_bytes) buf, (vir_bytes) count)) != OK)
187 break;
188 tp->tty_out_vir_g += count;
190 tbuf = buf;
192 /* Update terminal data structure. */
193 tp->tty_outcum += count;
194 tp->tty_outleft -= count;
196 /* Output each byte of the copy to the screen. Avoid calling
197 * out_char() for the "easy" characters, put them into the buffer
198 * directly.
200 do {
201 if ((unsigned) *tbuf < ' ' || cons->c_esc_state > 0
202 || cons->c_column >= scr_width
203 || cons->c_rwords >= buflen(cons->c_ramqueue))
205 out_char(cons, *tbuf++);
206 } else {
207 cons->c_ramqueue[cons->c_rwords++] =
208 cons->c_attr | (*tbuf++ & BYTE);
209 cons->c_column++;
211 } while (--count != 0);
212 } while ((count = tp->tty_outleft) != 0 && !tp->tty_inhibited);
214 flush(cons); /* transfer anything buffered to the screen */
216 /* Reply to the writer if all output is finished or if an error occured. */
217 if (tp->tty_outleft == 0 || result != OK) {
218 /* REVIVE is not possible. I/O on memory mapped consoles finishes. */
219 tty_reply(tp->tty_outrepcode, tp->tty_outcaller, tp->tty_outproc,
220 tp->tty_outcum);
221 tp->tty_outcum = 0;
225 /*===========================================================================*
226 * cons_echo *
227 *===========================================================================*/
228 PRIVATE void cons_echo(tp, c)
229 register tty_t *tp; /* pointer to tty struct */
230 int c; /* character to be echoed */
232 /* Echo keyboard input (print & flush). */
233 console_t *cons = tp->tty_priv;
235 out_char(cons, c);
236 flush(cons);
239 /*===========================================================================*
240 * out_char *
241 *===========================================================================*/
242 PRIVATE void out_char(cons, c)
243 register console_t *cons; /* pointer to console struct */
244 int c; /* character to be output */
246 /* Output a character on the console. Check for escape sequences first. */
247 if (cons->c_esc_state > 0) {
248 parse_escape(cons, c);
249 return;
252 switch(c) {
253 case 000: /* null is typically used for padding */
254 return; /* better not do anything */
256 case 007: /* ring the bell */
257 flush(cons); /* print any chars queued for output */
258 beep();
259 return;
261 case '\b': /* backspace */
262 if (--cons->c_column < 0) {
263 if (--cons->c_row >= 0) cons->c_column += scr_width;
265 flush(cons);
266 return;
268 case '\n': /* line feed */
269 if ((cons->c_tty->tty_termios.c_oflag & (OPOST|ONLCR))
270 == (OPOST|ONLCR)) {
271 cons->c_column = 0;
273 /*FALL THROUGH*/
274 case 013: /* CTRL-K */
275 case 014: /* CTRL-L */
276 if (cons->c_row == scr_lines-1) {
277 scroll_screen(cons, SCROLL_UP);
278 } else {
279 cons->c_row++;
281 flush(cons);
282 return;
284 case '\r': /* carriage return */
285 cons->c_column = 0;
286 flush(cons);
287 return;
289 case '\t': /* tab */
290 cons->c_column = (cons->c_column + TAB_SIZE) & ~TAB_MASK;
291 if (cons->c_column > scr_width) {
292 cons->c_column -= scr_width;
293 if (cons->c_row == scr_lines-1) {
294 scroll_screen(cons, SCROLL_UP);
295 } else {
296 cons->c_row++;
299 flush(cons);
300 return;
302 case 033: /* ESC - start of an escape sequence */
303 flush(cons); /* print any chars queued for output */
304 cons->c_esc_state = 1; /* mark ESC as seen */
305 return;
307 default: /* printable chars are stored in ramqueue */
308 if (cons->c_column >= scr_width) {
309 if (!LINEWRAP) return;
310 if (cons->c_row == scr_lines-1) {
311 scroll_screen(cons, SCROLL_UP);
312 } else {
313 cons->c_row++;
315 cons->c_column = 0;
316 flush(cons);
318 if (cons->c_rwords == buflen(cons->c_ramqueue)) flush(cons);
319 cons->c_ramqueue[cons->c_rwords++] = cons->c_attr | (c & BYTE);
320 cons->c_column++; /* next column */
321 return;
325 /*===========================================================================*
326 * scroll_screen *
327 *===========================================================================*/
328 PRIVATE void scroll_screen(cons, dir)
329 register console_t *cons; /* pointer to console struct */
330 int dir; /* SCROLL_UP or SCROLL_DOWN */
332 unsigned new_line, new_org, chars;
334 flush(cons);
335 chars = scr_size - scr_width; /* one screen minus one line */
337 /* Scrolling the screen is a real nuisance due to the various incompatible
338 * video cards. This driver supports software scrolling (Hercules?),
339 * hardware scrolling (mono and CGA cards) and hardware scrolling without
340 * wrapping (EGA cards). In the latter case we must make sure that
341 * c_start <= c_org && c_org + scr_size <= c_limit
342 * holds, because EGA doesn't wrap around the end of video memory.
344 if (dir == SCROLL_UP) {
345 /* Scroll one line up in 3 ways: soft, avoid wrap, use origin. */
346 if (softscroll) {
347 vid_vid_copy(cons->c_start + scr_width, cons->c_start, chars);
348 } else
349 if (!wrap && cons->c_org + scr_size + scr_width >= cons->c_limit) {
350 vid_vid_copy(cons->c_org + scr_width, cons->c_start, chars);
351 cons->c_org = cons->c_start;
352 } else {
353 cons->c_org = (cons->c_org + scr_width) & vid_mask;
355 new_line = (cons->c_org + chars) & vid_mask;
356 } else {
357 /* Scroll one line down in 3 ways: soft, avoid wrap, use origin. */
358 if (softscroll) {
359 vid_vid_copy(cons->c_start, cons->c_start + scr_width, chars);
360 } else
361 if (!wrap && cons->c_org < cons->c_start + scr_width) {
362 new_org = cons->c_limit - scr_size;
363 vid_vid_copy(cons->c_org, new_org + scr_width, chars);
364 cons->c_org = new_org;
365 } else {
366 cons->c_org = (cons->c_org - scr_width) & vid_mask;
368 new_line = cons->c_org;
370 /* Blank the new line at top or bottom. */
371 blank_color = cons->c_blank;
372 mem_vid_copy(BLANK_MEM, new_line, scr_width);
374 /* Set the new video origin. */
375 if (cons == curcons) set_6845(VID_ORG, cons->c_org);
376 flush(cons);
379 /*===========================================================================*
380 * flush *
381 *===========================================================================*/
382 PRIVATE void flush(cons)
383 register console_t *cons; /* pointer to console struct */
385 /* Send characters buffered in 'ramqueue' to screen memory, check the new
386 * cursor position, compute the new hardware cursor position and set it.
388 unsigned cur;
389 tty_t *tp = cons->c_tty;
391 /* Have the characters in 'ramqueue' transferred to the screen. */
392 if (cons->c_rwords > 0) {
393 mem_vid_copy(cons->c_ramqueue, cons->c_cur, cons->c_rwords);
394 cons->c_rwords = 0;
396 /* TTY likes to know the current column and if echoing messed up. */
397 tp->tty_position = cons->c_column;
398 tp->tty_reprint = TRUE;
401 /* Check and update the cursor position. */
402 if (cons->c_column < 0) cons->c_column = 0;
403 if (cons->c_column > scr_width) cons->c_column = scr_width;
404 if (cons->c_row < 0) cons->c_row = 0;
405 if (cons->c_row >= scr_lines) cons->c_row = scr_lines - 1;
406 cur = cons->c_org + cons->c_row * scr_width + cons->c_column;
407 if (cur != cons->c_cur) {
408 if (cons == curcons) set_6845(CURSOR, cur);
409 cons->c_cur = cur;
413 /*===========================================================================*
414 * parse_escape *
415 *===========================================================================*/
416 PRIVATE void parse_escape(cons, c)
417 register console_t *cons; /* pointer to console struct */
418 char c; /* next character in escape sequence */
420 /* The following ANSI escape sequences are currently supported.
421 * If n and/or m are omitted, they default to 1.
422 * ESC [nA moves up n lines
423 * ESC [nB moves down n lines
424 * ESC [nC moves right n spaces
425 * ESC [nD moves left n spaces
426 * ESC [m;nH" moves cursor to (m,n)
427 * ESC [J clears screen from cursor
428 * ESC [K clears line from cursor
429 * ESC [nL inserts n lines ar cursor
430 * ESC [nM deletes n lines at cursor
431 * ESC [nP deletes n chars at cursor
432 * ESC [n@ inserts n chars at cursor
433 * ESC [nm enables rendition n (0=normal, 4=bold, 5=blinking, 7=reverse)
434 * ESC M scrolls the screen backwards if the cursor is on the top line
437 switch (cons->c_esc_state) {
438 case 1: /* ESC seen */
439 cons->c_esc_intro = '\0';
440 cons->c_esc_parmp = bufend(cons->c_esc_parmv);
441 do {
442 *--cons->c_esc_parmp = 0;
443 } while (cons->c_esc_parmp > cons->c_esc_parmv);
444 switch (c) {
445 case '[': /* Control Sequence Introducer */
446 cons->c_esc_intro = c;
447 cons->c_esc_state = 2;
448 break;
449 case 'M': /* Reverse Index */
450 do_escape(cons, c);
451 break;
452 default:
453 cons->c_esc_state = 0;
455 break;
457 case 2: /* ESC [ seen */
458 if (c >= '0' && c <= '9') {
459 if (cons->c_esc_parmp < bufend(cons->c_esc_parmv))
460 *cons->c_esc_parmp = *cons->c_esc_parmp * 10 + (c-'0');
461 } else
462 if (c == ';') {
463 if (cons->c_esc_parmp < bufend(cons->c_esc_parmv))
464 cons->c_esc_parmp++;
465 } else {
466 do_escape(cons, c);
468 break;
472 /*===========================================================================*
473 * do_escape *
474 *===========================================================================*/
475 PRIVATE void do_escape(cons, c)
476 register console_t *cons; /* pointer to console struct */
477 char c; /* next character in escape sequence */
479 int value, n;
480 unsigned src, dst, count;
481 int *parmp;
483 /* Some of these things hack on screen RAM, so it had better be up to date */
484 flush(cons);
486 if (cons->c_esc_intro == '\0') {
487 /* Handle a sequence beginning with just ESC */
488 switch (c) {
489 case 'M': /* Reverse Index */
490 if (cons->c_row == 0) {
491 scroll_screen(cons, SCROLL_DOWN);
492 } else {
493 cons->c_row--;
495 flush(cons);
496 break;
498 default: break;
500 } else
501 if (cons->c_esc_intro == '[') {
502 /* Handle a sequence beginning with ESC [ and parameters */
503 value = cons->c_esc_parmv[0];
504 switch (c) {
505 case 'A': /* ESC [nA moves up n lines */
506 n = (value == 0 ? 1 : value);
507 cons->c_row -= n;
508 flush(cons);
509 break;
511 case 'B': /* ESC [nB moves down n lines */
512 n = (value == 0 ? 1 : value);
513 cons->c_row += n;
514 flush(cons);
515 break;
517 case 'C': /* ESC [nC moves right n spaces */
518 n = (value == 0 ? 1 : value);
519 cons->c_column += n;
520 flush(cons);
521 break;
523 case 'D': /* ESC [nD moves left n spaces */
524 n = (value == 0 ? 1 : value);
525 cons->c_column -= n;
526 flush(cons);
527 break;
529 case 'H': /* ESC [m;nH" moves cursor to (m,n) */
530 cons->c_row = cons->c_esc_parmv[0] - 1;
531 cons->c_column = cons->c_esc_parmv[1] - 1;
532 flush(cons);
533 break;
535 case 'J': /* ESC [sJ clears in display */
536 switch (value) {
537 case 0: /* Clear from cursor to end of screen */
538 count = scr_size - (cons->c_cur - cons->c_org);
539 dst = cons->c_cur;
540 break;
541 case 1: /* Clear from start of screen to cursor */
542 count = cons->c_cur - cons->c_org;
543 dst = cons->c_org;
544 break;
545 case 2: /* Clear entire screen */
546 count = scr_size;
547 dst = cons->c_org;
548 break;
549 default: /* Do nothing */
550 count = 0;
551 dst = cons->c_org;
553 blank_color = cons->c_blank;
554 mem_vid_copy(BLANK_MEM, dst, count);
555 break;
557 case 'K': /* ESC [sK clears line from cursor */
558 switch (value) {
559 case 0: /* Clear from cursor to end of line */
560 count = scr_width - cons->c_column;
561 dst = cons->c_cur;
562 break;
563 case 1: /* Clear from beginning of line to cursor */
564 count = cons->c_column;
565 dst = cons->c_cur - cons->c_column;
566 break;
567 case 2: /* Clear entire line */
568 count = scr_width;
569 dst = cons->c_cur - cons->c_column;
570 break;
571 default: /* Do nothing */
572 count = 0;
573 dst = cons->c_cur;
575 blank_color = cons->c_blank;
576 mem_vid_copy(BLANK_MEM, dst, count);
577 break;
579 case 'L': /* ESC [nL inserts n lines at cursor */
580 n = value;
581 if (n < 1) n = 1;
582 if (n > (scr_lines - cons->c_row))
583 n = scr_lines - cons->c_row;
585 src = cons->c_org + cons->c_row * scr_width;
586 dst = src + n * scr_width;
587 count = (scr_lines - cons->c_row - n) * scr_width;
588 vid_vid_copy(src, dst, count);
589 blank_color = cons->c_blank;
590 mem_vid_copy(BLANK_MEM, src, n * scr_width);
591 break;
593 case 'M': /* ESC [nM deletes n lines at cursor */
594 n = value;
595 if (n < 1) n = 1;
596 if (n > (scr_lines - cons->c_row))
597 n = scr_lines - cons->c_row;
599 dst = cons->c_org + cons->c_row * scr_width;
600 src = dst + n * scr_width;
601 count = (scr_lines - cons->c_row - n) * scr_width;
602 vid_vid_copy(src, dst, count);
603 blank_color = cons->c_blank;
604 mem_vid_copy(BLANK_MEM, dst + count, n * scr_width);
605 break;
607 case '@': /* ESC [n@ inserts n chars at cursor */
608 n = value;
609 if (n < 1) n = 1;
610 if (n > (scr_width - cons->c_column))
611 n = scr_width - cons->c_column;
613 src = cons->c_cur;
614 dst = src + n;
615 count = scr_width - cons->c_column - n;
616 vid_vid_copy(src, dst, count);
617 blank_color = cons->c_blank;
618 mem_vid_copy(BLANK_MEM, src, n);
619 break;
621 case 'P': /* ESC [nP deletes n chars at cursor */
622 n = value;
623 if (n < 1) n = 1;
624 if (n > (scr_width - cons->c_column))
625 n = scr_width - cons->c_column;
627 dst = cons->c_cur;
628 src = dst + n;
629 count = scr_width - cons->c_column - n;
630 vid_vid_copy(src, dst, count);
631 blank_color = cons->c_blank;
632 mem_vid_copy(BLANK_MEM, dst + count, n);
633 break;
635 case 'm': /* ESC [nm enables rendition n */
636 for (parmp = cons->c_esc_parmv; parmp <= cons->c_esc_parmp
637 && parmp < bufend(cons->c_esc_parmv); parmp++) {
638 if (cons->c_reverse) {
639 /* Unswap fg and bg colors */
640 cons->c_attr = ((cons->c_attr & 0x7000) >> 4) |
641 ((cons->c_attr & 0x0700) << 4) |
642 ((cons->c_attr & 0x8800));
644 switch (n = *parmp) {
645 case 0: /* NORMAL */
646 cons->c_attr = cons->c_blank = BLANK_COLOR;
647 cons->c_reverse = FALSE;
648 break;
650 case 1: /* BOLD */
651 /* Set intensity bit */
652 cons->c_attr |= 0x0800;
653 break;
655 case 4: /* UNDERLINE */
656 if (color) {
657 /* Change white to cyan, i.e. lose red
659 cons->c_attr = (cons->c_attr & 0xBBFF);
660 } else {
661 /* Set underline attribute */
662 cons->c_attr = (cons->c_attr & 0x99FF);
664 break;
666 case 5: /* BLINKING */
667 /* Set the blink bit */
668 cons->c_attr |= 0x8000;
669 break;
671 case 7: /* REVERSE */
672 cons->c_reverse = TRUE;
673 break;
675 default: /* COLOR */
676 if (n == 39) n = 37; /* set default color */
677 if (n == 49) n = 40;
679 if (!color) {
680 /* Don't mess up a monochrome screen */
681 } else
682 if (30 <= n && n <= 37) {
683 /* Foreground color */
684 cons->c_attr =
685 (cons->c_attr & 0xF8FF) |
686 (ansi_colors[(n - 30)] << 8);
687 cons->c_blank =
688 (cons->c_blank & 0xF8FF) |
689 (ansi_colors[(n - 30)] << 8);
690 } else
691 if (40 <= n && n <= 47) {
692 /* Background color */
693 cons->c_attr =
694 (cons->c_attr & 0x8FFF) |
695 (ansi_colors[(n - 40)] << 12);
696 cons->c_blank =
697 (cons->c_blank & 0x8FFF) |
698 (ansi_colors[(n - 40)] << 12);
701 if (cons->c_reverse) {
702 /* Swap fg and bg colors */
703 cons->c_attr = ((cons->c_attr & 0x7000) >> 4) |
704 ((cons->c_attr & 0x0700) << 4) |
705 ((cons->c_attr & 0x8800));
708 break;
711 cons->c_esc_state = 0;
714 /*===========================================================================*
715 * set_6845 *
716 *===========================================================================*/
717 PRIVATE void set_6845(reg, val)
718 int reg; /* which register pair to set */
719 unsigned val; /* 16-bit value to set it to */
721 /* Set a register pair inside the 6845.
722 * Registers 12-13 tell the 6845 where in video ram to start
723 * Registers 14-15 tell the 6845 where to put the cursor
725 pvb_pair_t char_out[4];
726 pv_set(char_out[0], vid_port + INDEX, reg); /* set index register */
727 pv_set(char_out[1], vid_port + DATA, (val>>8) & BYTE); /* high byte */
728 pv_set(char_out[2], vid_port + INDEX, reg + 1); /* again */
729 pv_set(char_out[3], vid_port + DATA, val&BYTE); /* low byte */
730 sys_voutb(char_out, 4); /* do actual output */
733 /*===========================================================================*
734 * get_6845 *
735 *===========================================================================*/
736 PRIVATE void get_6845(reg, val)
737 int reg; /* which register pair to set */
738 unsigned *val; /* 16-bit value to set it to */
740 char v1, v2;
741 unsigned long v;
742 /* Get a register pair inside the 6845. */
743 sys_outb(vid_port + INDEX, reg);
744 sys_inb(vid_port + DATA, &v);
745 v1 = v;
746 sys_outb(vid_port + INDEX, reg+1);
747 sys_inb(vid_port + DATA, &v);
748 v2 = v;
749 *val = (v1 << 8) | v2;
752 /*===========================================================================*
753 * beep *
754 *===========================================================================*/
755 PRIVATE void beep()
757 /* Making a beeping sound on the speaker (output for CRTL-G).
758 * This routine works by turning on the bits 0 and 1 in port B of the 8255
759 * chip that drive the speaker.
761 static timer_t tmr_stop_beep;
762 pvb_pair_t char_out[3];
763 clock_t now;
764 unsigned long port_b_val;
765 int s;
767 /* Fetch current time in advance to prevent beeping delay. */
768 if ((s=getuptime(&now)) != OK)
769 panic("TTY","Console couldn't get clock's uptime.", s);
770 if (!beeping) {
771 /* Set timer channel 2, square wave, with given frequency. */
772 pv_set(char_out[0], TIMER_MODE, 0xB6);
773 pv_set(char_out[1], TIMER2, (BEEP_FREQ >> 0) & BYTE);
774 pv_set(char_out[2], TIMER2, (BEEP_FREQ >> 8) & BYTE);
775 if (sys_voutb(char_out, 3)==OK) {
776 if (sys_inb(PORT_B, &port_b_val)==OK &&
777 sys_outb(PORT_B, (port_b_val|3))==OK)
778 beeping = TRUE;
781 /* Add a timer to the timers list. Possibly reschedule the alarm. */
782 tmrs_settimer(&tty_timers, &tmr_stop_beep, now+B_TIME, stop_beep, NULL);
783 if (tty_timers->tmr_exp_time != tty_next_timeout) {
784 tty_next_timeout = tty_timers->tmr_exp_time;
785 if ((s=sys_setalarm(tty_next_timeout, 1)) != OK)
786 panic("TTY","Console couldn't set alarm.", s);
791 /*===========================================================================*
792 * do_video *
793 *===========================================================================*/
794 PUBLIC void do_video(message *m)
796 int i, n, r, ops, watch, safe = 0;
797 unsigned char c;
799 /* Execute the requested device driver function. */
800 r= EINVAL; /* just in case */
801 switch (m->m_type) {
802 case DEV_OPEN:
803 /* Should grant IOPL */
804 disable_console();
805 r= OK;
806 break;
807 case DEV_CLOSE:
808 reenable_console();
809 r= OK;
810 break;
811 case DEV_IOCTL_S:
812 safe=1;
813 if (m->TTY_REQUEST == MIOCMAP || m->TTY_REQUEST == MIOCUNMAP)
815 int r, do_map;
816 struct mapreq mapreq;
818 do_map= (m->REQUEST == MIOCMAP); /* else unmap */
820 /* Get request structure */
821 if(safe) {
822 r = sys_safecopyfrom(m->IO_ENDPT,
823 (vir_bytes)m->ADDRESS, 0, (vir_bytes) &mapreq,
824 sizeof(mapreq), D);
825 } else {
826 r= sys_vircopy(m->IO_ENDPT, D,
827 (vir_bytes)m->ADDRESS,
828 SELF, D, (vir_bytes)&mapreq, sizeof(mapreq));
830 if (r != OK)
832 tty_reply(TASK_REPLY, m->m_source, m->IO_ENDPT,
834 return;
837 /* In safe ioctl mode, the POSITION field contains
838 * the endpt number of the original requestor.
839 * IO_ENDPT is always FS.
842 r= sys_vm_map(safe ? m->POSITION : m->IO_ENDPT,
843 do_map, (phys_bytes)mapreq.base, mapreq.size,
844 mapreq.offset);
845 tty_reply(TASK_REPLY, m->m_source, m->IO_ENDPT, r);
846 return;
848 r= ENOTTY;
849 break;
851 default:
852 printf(
853 "Warning, TTY(video) got unexpected request %d from %d\n",
854 m->m_type, m->m_source);
855 r= EINVAL;
857 tty_reply(TASK_REPLY, m->m_source, m->IO_ENDPT, r);
861 /*===========================================================================*
862 * beep_x *
863 *===========================================================================*/
864 PUBLIC void beep_x(freq, dur)
865 unsigned freq;
866 clock_t dur;
868 /* Making a beeping sound on the speaker.
869 * This routine works by turning on the bits 0 and 1 in port B of the 8255
870 * chip that drive the speaker.
872 static timer_t tmr_stop_beep;
873 pvb_pair_t char_out[3];
874 clock_t now;
875 unsigned long port_b_val;
876 int s;
878 unsigned long ival= TIMER_FREQ / freq;
879 if (ival == 0 || ival > 0xffff)
880 return; /* Frequency out of range */
882 /* Fetch current time in advance to prevent beeping delay. */
883 if ((s=getuptime(&now)) != OK)
884 panic("TTY","Console couldn't get clock's uptime.", s);
885 if (!beeping) {
886 /* Set timer channel 2, square wave, with given frequency. */
887 pv_set(char_out[0], TIMER_MODE, 0xB6);
888 pv_set(char_out[1], TIMER2, (ival >> 0) & BYTE);
889 pv_set(char_out[2], TIMER2, (ival >> 8) & BYTE);
890 if (sys_voutb(char_out, 3)==OK) {
891 if (sys_inb(PORT_B, &port_b_val)==OK &&
892 sys_outb(PORT_B, (port_b_val|3))==OK)
893 beeping = TRUE;
896 /* Add a timer to the timers list. Possibly reschedule the alarm. */
897 tmrs_settimer(&tty_timers, &tmr_stop_beep, now+dur, stop_beep, NULL);
898 if (tty_timers->tmr_exp_time != tty_next_timeout) {
899 tty_next_timeout = tty_timers->tmr_exp_time;
900 if ((s=sys_setalarm(tty_next_timeout, 1)) != OK)
901 panic("TTY","Console couldn't set alarm.", s);
905 /*===========================================================================*
906 * stop_beep *
907 *===========================================================================*/
908 PRIVATE void stop_beep(tmrp)
909 timer_t *tmrp;
911 /* Turn off the beeper by turning off bits 0 and 1 in PORT_B. */
912 unsigned long port_b_val;
913 if (sys_inb(PORT_B, &port_b_val)==OK &&
914 sys_outb(PORT_B, (port_b_val & ~3))==OK)
915 beeping = FALSE;
918 /*===========================================================================*
919 * scr_init *
920 *===========================================================================*/
921 PUBLIC void scr_init(tp)
922 tty_t *tp;
924 /* Initialize the screen driver. */
925 console_t *cons;
926 phys_bytes vid_base;
927 u16_t bios_columns, bios_crtbase, bios_fontlines;
928 u8_t bios_rows;
929 int line;
930 int s;
931 static int vdu_initialized = 0;
932 unsigned page_size;
934 /* Associate console and TTY. */
935 line = tp - &tty_table[0];
936 if (line >= nr_cons) return;
937 cons = &cons_table[line];
938 cons->c_tty = tp;
939 tp->tty_priv = cons;
941 /* Fill in TTY function hooks. */
942 tp->tty_devwrite = cons_write;
943 tp->tty_echo = cons_echo;
944 tp->tty_ioctl = cons_ioctl;
946 /* Get the BIOS parameters that describe the VDU. */
947 if (! vdu_initialized++) {
949 /* How about error checking? What to do on failure??? */
950 s=sys_readbios(VDU_SCREEN_COLS_ADDR, &bios_columns,
951 VDU_SCREEN_COLS_SIZE);
952 s=sys_readbios(VDU_CRT_BASE_ADDR, &bios_crtbase,
953 VDU_CRT_BASE_SIZE);
954 s=sys_readbios( VDU_SCREEN_ROWS_ADDR, &bios_rows,
955 VDU_SCREEN_ROWS_SIZE);
956 s=sys_readbios(VDU_FONTLINES_ADDR, &bios_fontlines,
957 VDU_FONTLINES_SIZE);
959 vid_port = bios_crtbase;
960 scr_width = bios_columns;
961 font_lines = bios_fontlines;
962 scr_lines = machine.vdu_ega ? bios_rows+1 : 25;
964 if (color) {
965 vid_base = COLOR_BASE;
966 vid_size = COLOR_SIZE;
967 } else {
968 vid_base = MONO_BASE;
969 vid_size = MONO_SIZE;
971 if (machine.vdu_ega) vid_size = EGA_SIZE;
972 wrap = ! machine.vdu_ega;
974 s = sys_segctl(&vid_index, &vid_seg, &vid_off, vid_base, vid_size);
976 vid_size >>= 1; /* word count */
977 vid_mask = vid_size - 1;
979 /* Size of the screen (number of displayed characters.) */
980 scr_size = scr_lines * scr_width;
982 /* There can be as many consoles as video memory allows. */
983 nr_cons = vid_size / scr_size;
984 if (nr_cons > NR_CONS) nr_cons = NR_CONS;
985 if (nr_cons > 1) wrap = 0;
986 page_size = vid_size / nr_cons;
989 cons->c_start = line * page_size;
990 cons->c_limit = cons->c_start + page_size;
991 cons->c_cur = cons->c_org = cons->c_start;
992 cons->c_attr = cons->c_blank = BLANK_COLOR;
994 if (line != 0) {
995 /* Clear the non-console vtys. */
996 blank_color = BLANK_COLOR;
997 mem_vid_copy(BLANK_MEM, cons->c_start, scr_size);
998 } else {
999 int i, n;
1000 /* Set the cursor of the console vty at the bottom. c_cur
1001 * is updated automatically later.
1003 scroll_screen(cons, SCROLL_UP);
1004 cons->c_row = scr_lines - 1;
1005 cons->c_column = 0;
1007 select_console(0);
1008 cons_ioctl(tp, 0);
1011 /*===========================================================================*
1012 * kputc *
1013 *===========================================================================*/
1014 PUBLIC void kputc(c)
1015 int c;
1017 /* Accumulate a single character for a kernel message. Send a notification
1018 * the to output driver if an END_OF_KMESS is encountered.
1020 #if 0
1021 ser_putc(c);
1022 return;
1023 #endif
1025 #if 0
1026 if (panicing)
1027 #endif
1028 cons_putk(c);
1029 if (c != 0) {
1030 kmess.km_buf[kmess.km_next] = c; /* put normal char in buffer */
1031 if (kmess.km_size < KMESS_BUF_SIZE)
1032 kmess.km_size += 1;
1033 kmess.km_next = (kmess.km_next + 1) % KMESS_BUF_SIZE;
1034 } else {
1035 notify(LOG_PROC_NR);
1039 /*===========================================================================*
1040 * do_new_kmess *
1041 *===========================================================================*/
1042 PUBLIC void do_new_kmess(m)
1043 message *m;
1045 /* Notification for a new kernel message. */
1046 struct kmessages kmess; /* kmessages structure */
1047 static int prev_next = 0; /* previous next seen */
1048 int size, next;
1049 int bytes;
1050 int r;
1052 /* Try to get a fresh copy of the buffer with kernel messages. */
1053 #if DEAD_CODE
1054 /* During shutdown, the reply is garbled because new notifications arrive
1055 * while the system task makes a copy of the kernel messages buffer.
1056 * Hence, don't check the return value.
1058 if ((r=sys_getkmessages(&kmess)) != OK) {
1059 printf("TTY: couldn't get copy of kmessages: %d, 0x%x\n", r,r);
1060 return;
1062 #endif
1063 sys_getkmessages(&kmess);
1065 /* Print only the new part. Determine how many new bytes there are with
1066 * help of the current and previous 'next' index. Note that the kernel
1067 * buffer is circular. This works fine if less then KMESS_BUF_SIZE bytes
1068 * is new data; else we miss % KMESS_BUF_SIZE here.
1069 * Check for size being positive, the buffer might as well be emptied!
1071 if (kmess.km_size > 0) {
1072 bytes = ((kmess.km_next + KMESS_BUF_SIZE) - prev_next) % KMESS_BUF_SIZE;
1073 r=prev_next; /* start at previous old */
1074 while (bytes > 0) {
1075 cons_putk( kmess.km_buf[(r%KMESS_BUF_SIZE)] );
1076 bytes --;
1077 r ++;
1079 cons_putk(0); /* terminate to flush output */
1082 /* Almost done, store 'next' so that we can determine what part of the
1083 * kernel messages buffer to print next time a notification arrives.
1085 prev_next = kmess.km_next;
1088 /*===========================================================================*
1089 * do_diagnostics *
1090 *===========================================================================*/
1091 PUBLIC void do_diagnostics(m_ptr, safe)
1092 message *m_ptr; /* pointer to request message */
1093 int safe;
1095 /* Print a string for a server. */
1096 char c;
1097 vir_bytes src;
1098 int count, offset = 0;
1099 int result = OK;
1100 int proc_nr = m_ptr->m_source;
1102 src = (vir_bytes) m_ptr->DIAG_PRINT_BUF_G;
1103 for (count = m_ptr->DIAG_BUF_COUNT; count > 0; count--) {
1104 int r;
1105 if(safe) {
1106 r = sys_safecopyfrom(proc_nr, src, offset, (vir_bytes) &c, 1, D);
1107 } else {
1108 r = sys_vircopy(proc_nr, D, src+offset, SELF, D, (vir_bytes) &c, 1);
1110 offset++;
1111 if(r != OK) {
1112 result = EFAULT;
1113 break;
1115 cons_putk(c);
1117 cons_putk(0); /* always terminate, even with EFAULT */
1118 m_ptr->m_type = result;
1119 send(m_ptr->m_source, m_ptr);
1122 /*===========================================================================*
1123 * do_get_kmess *
1124 *===========================================================================*/
1125 PUBLIC void do_get_kmess(m_ptr)
1126 message *m_ptr; /* pointer to request message */
1128 /* Provide the log device with debug output */
1129 vir_bytes dst;
1130 int r;
1132 dst = (vir_bytes) m_ptr->GETKM_PTR;
1133 r= OK;
1134 if (sys_vircopy(SELF, D, (vir_bytes)&kmess, m_ptr->m_source, D,
1135 dst, sizeof(kmess)) != OK) {
1136 r = EFAULT;
1138 m_ptr->m_type = r;
1139 send(m_ptr->m_source, m_ptr);
1142 /*===========================================================================*
1143 * do_get_kmess_s *
1144 *===========================================================================*/
1145 PUBLIC void do_get_kmess_s(m_ptr)
1146 message *m_ptr; /* pointer to request message */
1148 /* Provide the log device with debug output */
1149 cp_grant_id_t gid;
1150 int r;
1152 gid = m_ptr->GETKM_GRANT;
1153 r= OK;
1154 if (sys_safecopyto(m_ptr->m_source, gid, 0, (vir_bytes)&kmess, sizeof(kmess),
1155 D) != OK) {
1156 r = EFAULT;
1158 m_ptr->m_type = r;
1159 send(m_ptr->m_source, m_ptr);
1162 /*===========================================================================*
1163 * cons_putk *
1164 *===========================================================================*/
1165 PRIVATE void cons_putk(c)
1166 int c; /* character to print */
1168 /* This procedure is used to print a character on the console.
1170 if (c != 0) {
1171 if (c == '\n') cons_putk('\r');
1172 out_char(&cons_table[0], (int) c);
1173 } else {
1174 flush(&cons_table[0]);
1178 /*===========================================================================*
1179 * toggle_scroll *
1180 *===========================================================================*/
1181 PUBLIC void toggle_scroll()
1183 /* Toggle between hardware and software scroll. */
1185 cons_org0();
1186 softscroll = !softscroll;
1187 printf("%sware scrolling enabled.\n", softscroll ? "Soft" : "Hard");
1190 /*===========================================================================*
1191 * cons_stop *
1192 *===========================================================================*/
1193 PUBLIC void cons_stop()
1195 /* Prepare for halt or reboot. */
1196 cons_org0();
1197 softscroll = 1;
1198 select_console(0);
1199 cons_table[0].c_attr = cons_table[0].c_blank = BLANK_COLOR;
1202 /*===========================================================================*
1203 * cons_org0 *
1204 *===========================================================================*/
1205 PRIVATE void cons_org0()
1207 /* Scroll video memory back to put the origin at 0. */
1208 int cons_line;
1209 console_t *cons;
1210 unsigned n;
1212 for (cons_line = 0; cons_line < nr_cons; cons_line++) {
1213 cons = &cons_table[cons_line];
1214 while (cons->c_org > cons->c_start) {
1215 n = vid_size - scr_size; /* amount of unused memory */
1216 if (n > cons->c_org - cons->c_start)
1217 n = cons->c_org - cons->c_start;
1218 vid_vid_copy(cons->c_org, cons->c_org - n, scr_size);
1219 cons->c_org -= n;
1221 flush(cons);
1223 select_console(ccurrent);
1226 /*===========================================================================*
1227 * disable_console *
1228 *===========================================================================*/
1229 PRIVATE void disable_console()
1231 if (disabled_vc != -1)
1232 return;
1234 disabled_vc = ccurrent;
1235 disabled_sm = softscroll;
1237 cons_org0();
1238 softscroll = 1;
1239 select_console(0);
1241 /* Should also disable further output to virtual consoles */
1244 /*===========================================================================*
1245 * reenable_console *
1246 *===========================================================================*/
1247 PRIVATE void reenable_console()
1249 if (disabled_vc == -1)
1250 return;
1252 softscroll = disabled_sm;
1253 select_console(disabled_vc);
1254 disabled_vc = -1;
1257 /*===========================================================================*
1258 * select_console *
1259 *===========================================================================*/
1260 PUBLIC void select_console(int cons_line)
1262 /* Set the current console to console number 'cons_line'. */
1264 if (cons_line < 0 || cons_line >= nr_cons) return;
1265 ccurrent = cons_line;
1266 curcons = &cons_table[cons_line];
1267 set_6845(VID_ORG, curcons->c_org);
1268 set_6845(CURSOR, curcons->c_cur);
1271 /*===========================================================================*
1272 * con_loadfont *
1273 *===========================================================================*/
1274 PUBLIC int con_loadfont(m)
1275 message *m;
1277 /* Load a font into the EGA or VGA adapter. */
1278 int result;
1279 static struct sequence seq1[7] = {
1280 { GA_SEQUENCER_INDEX, 0x00, 0x01 },
1281 { GA_SEQUENCER_INDEX, 0x02, 0x04 },
1282 { GA_SEQUENCER_INDEX, 0x04, 0x07 },
1283 { GA_SEQUENCER_INDEX, 0x00, 0x03 },
1284 { GA_GRAPHICS_INDEX, 0x04, 0x02 },
1285 { GA_GRAPHICS_INDEX, 0x05, 0x00 },
1286 { GA_GRAPHICS_INDEX, 0x06, 0x00 },
1288 static struct sequence seq2[7] = {
1289 { GA_SEQUENCER_INDEX, 0x00, 0x01 },
1290 { GA_SEQUENCER_INDEX, 0x02, 0x03 },
1291 { GA_SEQUENCER_INDEX, 0x04, 0x03 },
1292 { GA_SEQUENCER_INDEX, 0x00, 0x03 },
1293 { GA_GRAPHICS_INDEX, 0x04, 0x00 },
1294 { GA_GRAPHICS_INDEX, 0x05, 0x10 },
1295 { GA_GRAPHICS_INDEX, 0x06, 0 },
1298 seq2[6].value= color ? 0x0E : 0x0A;
1300 if (!machine.vdu_ega) return(ENOTTY);
1301 result = ga_program(seq1); /* bring font memory into view */
1303 result = sys_physcopy(m->IO_ENDPT, GRANT_SEG, (vir_bytes) m->ADDRESS,
1304 NONE, PHYS_SEG, (phys_bytes) GA_VIDEO_ADDRESS, (phys_bytes)GA_FONT_SIZE);
1306 result = ga_program(seq2); /* restore */
1308 return(result);
1311 /*===========================================================================*
1312 * ga_program *
1313 *===========================================================================*/
1314 PRIVATE int ga_program(seq)
1315 struct sequence *seq;
1317 pvb_pair_t char_out[14];
1318 int i;
1319 for (i=0; i<7; i++) {
1320 pv_set(char_out[2*i], seq->index, seq->port);
1321 pv_set(char_out[2*i+1], seq->index+1, seq->value);
1322 seq++;
1324 return sys_voutb(char_out, 14);
1327 /*===========================================================================*
1328 * cons_ioctl *
1329 *===========================================================================*/
1330 PRIVATE int cons_ioctl(tp, try)
1331 tty_t *tp;
1332 int try;
1334 /* Set the screen dimensions. */
1336 tp->tty_winsize.ws_row= scr_lines;
1337 tp->tty_winsize.ws_col= scr_width;
1338 tp->tty_winsize.ws_xpixel= scr_width * 8;
1339 tp->tty_winsize.ws_ypixel= scr_lines * font_lines;
1342 #define COM1_BASE 0x3F8
1343 #define COM1_THR (COM1_BASE + 0)
1344 #define LSR_THRE 0x20
1345 #define COM1_LSR (COM1_BASE + 5)
1347 PRIVATE void ser_putc(char c)
1349 unsigned long b;
1350 int i;
1351 int lsr, thr;
1353 lsr= COM1_LSR;
1354 thr= COM1_THR;
1355 for (i= 0; i<100; i++)
1357 sys_inb(lsr, &b);
1358 if (b & LSR_THRE)
1359 break;
1361 sys_outb(thr, c);