memory: use sys_safememset() for /dev/zero
[minix.git] / drivers / tty / console.c
blob6ec7fb4ebf1c96efd0e674e7c50748c22a6b36f6
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 <minix/drivers.h>
20 #include <termios.h>
21 #include <assert.h>
22 #include <sys/ioctl.h>
23 #include <sys/vm.h>
24 #include <sys/video.h>
25 #include <sys/mman.h>
26 #include <minix/tty.h>
27 #include <minix/callnr.h>
28 #include <minix/com.h>
29 #include <minix/sys_config.h>
30 #include <minix/vm.h>
31 #include "tty.h"
33 /* Set this to 1 if you want console output duplicated on the first
34 * serial line.
36 #define DUP_CONS_TO_SER 0
38 /* The clock task should provide an interface for this */
39 #define TIMER_FREQ 1193182L /* clock frequency for timer in PC and AT */
41 /* Global variables used by the console driver and assembly support. */
42 static phys_bytes vid_size; /* 0x2000 for color or 0x0800 for mono */
43 static phys_bytes vid_base;
44 static unsigned vid_mask; /* 0x1FFF for color or 0x07FF for mono */
45 static unsigned blank_color = BLANK_COLOR; /* display code for blank */
47 /* Private variables used by the console driver. */
48 static int vid_port; /* I/O port for accessing 6845 */
49 static int wrap; /* hardware can wrap? */
50 static int softscroll; /* 1 = software scrolling, 0 = hardware */
51 static int beeping; /* speaker is beeping? */
52 static unsigned font_lines; /* font lines per character */
53 static unsigned scr_width; /* # characters on a line */
54 static unsigned scr_lines; /* # lines on the screen */
55 static unsigned scr_size; /* # characters on the screen */
57 /* tells mem_vid_copy() to blank the screen */
58 #define BLANK_MEM ((vir_bytes) 0)
60 static int disabled_vc = -1; /* Virtual console that was active when
61 * disable_console was called.
63 static int disabled_sm; /* Scroll mode to be restored when re-enabling
64 * console
67 static char *console_memory = NULL;
68 static char *font_memory = NULL;
70 /* Per console data. */
71 typedef struct console {
72 tty_t *c_tty; /* associated TTY struct */
73 int c_column; /* current column number (0-origin) */
74 int c_row; /* current row (0 at top of screen) */
75 int c_rwords; /* number of WORDS (not bytes) in outqueue */
76 unsigned c_start; /* start of video memory of this console */
77 unsigned c_limit; /* limit of this console's video memory */
78 unsigned c_org; /* location in RAM where 6845 base points */
79 unsigned c_cur; /* current position of cursor in video RAM */
80 unsigned c_attr; /* character attribute */
81 unsigned c_blank; /* blank attribute */
82 char c_reverse; /* reverse video */
83 char c_esc_state; /* 0=normal, 1=ESC, 2=ESC[ */
84 char c_esc_intro; /* Distinguishing character following ESC */
85 int *c_esc_parmp; /* pointer to current escape parameter */
86 int c_esc_parmv[MAX_ESC_PARMS]; /* list of escape parameters */
87 u16_t c_ramqueue[CONS_RAM_WORDS]; /* buffer for video RAM */
88 int c_line; /* line no */
89 } console_t;
91 #define UPDATE_CURSOR(ccons, cursor) { \
92 ccons->c_cur = cursor; \
93 if(curcons && ccons == curcons) \
94 set_6845(CURSOR, ccons->c_cur); \
97 #define UPDATE_ORIGIN(ccons, origin) { \
98 ccons->c_org = origin; \
99 if (curcons && ccons == curcons) \
100 set_6845(VID_ORG, ccons->c_org); \
103 static int nr_cons= 1; /* actual number of consoles */
104 static console_t cons_table[NR_CONS];
105 static console_t *curcons = NULL; /* currently visible */
107 static int shutting_down = FALSE; /* don't allow console switches */
109 /* Color if using a color controller. */
110 #define color (vid_port == C_6845)
112 /* Map from ANSI colors to the attributes used by the PC */
113 static int ansi_colors[8] = {0, 4, 2, 6, 1, 5, 3, 7};
115 /* Structure used for font management */
116 struct sequence {
117 unsigned short index;
118 unsigned char port;
119 unsigned char value;
122 static int cons_write(struct tty *tp, int try);
123 static void cons_echo(tty_t *tp, int c);
124 static void out_char(console_t *cons, int c);
125 static void cons_putk(int c);
126 static void beep(void);
127 static void do_escape(console_t *cons, int c);
128 static void flush(console_t *cons);
129 static void parse_escape(console_t *cons, int c);
130 static void scroll_screen(console_t *cons, int dir);
131 static void set_6845(int reg, unsigned val);
132 static void stop_beep(timer_t *tmrp);
133 static void cons_org0(void);
134 static void disable_console(void);
135 static void reenable_console(void);
136 static int ga_program(struct sequence *seq);
137 static int cons_ioctl(tty_t *tp, int);
138 static void mem_vid_copy(vir_bytes src, int dst, int count);
139 static void vid_vid_copy(int src, int dst, int count);
141 #if 0
142 static void get_6845(int reg, unsigned *val);
143 #endif
145 /*===========================================================================*
146 * cons_write *
147 *===========================================================================*/
148 static int cons_write(tp, try)
149 register struct tty *tp; /* tells which terminal is to be used */
150 int try;
152 /* Copy as much data as possible to the output queue, then start I/O. On
153 * memory-mapped terminals, such as the IBM console, the I/O will also be
154 * finished, and the counts updated. Keep repeating until all I/O done.
157 int count;
158 int result;
159 register char *tbuf;
160 char buf[64];
161 console_t *cons = tp->tty_priv;
163 if (try) return 1; /* we can always write to console */
165 /* Check quickly for nothing to do, so this can be called often without
166 * unmodular tests elsewhere.
168 if ((count = tp->tty_outleft) == 0 || tp->tty_inhibited) return 0;
170 /* Copy the user bytes to buf[] for decent addressing. Loop over the
171 * copies, since the user buffer may be much larger than buf[].
173 do {
174 if (count > sizeof(buf)) count = sizeof(buf);
175 if ((result = sys_safecopyfrom(tp->tty_outcaller, tp->tty_outgrant,
176 tp->tty_outoffset, (vir_bytes) buf, count)) != OK)
177 break;
178 tp->tty_outoffset += count;
179 tbuf = buf;
181 /* Update terminal data structure. */
182 tp->tty_outcum += count;
183 tp->tty_outleft -= count;
185 /* Output each byte of the copy to the screen. Avoid calling
186 * out_char() for the "easy" characters, put them into the buffer
187 * directly.
189 do {
190 if ((unsigned) *tbuf < ' ' || cons->c_esc_state > 0
191 || cons->c_column >= scr_width
192 || cons->c_rwords >= buflen(cons->c_ramqueue))
194 out_char(cons, *tbuf++);
195 } else {
196 #if DUP_CONS_TO_SER
197 if (cons == &cons_table[0]) ser_putc(*tbuf);
198 #endif
199 cons->c_ramqueue[cons->c_rwords++] =
200 cons->c_attr | (*tbuf++ & BYTE);
201 cons->c_column++;
203 } while (--count != 0);
204 } while ((count = tp->tty_outleft) != 0 && !tp->tty_inhibited);
206 flush(cons); /* transfer anything buffered to the screen */
208 /* Reply to the writer if all output is finished or if an error occured. */
209 if (tp->tty_outleft == 0 || result != OK) {
210 if(tp->tty_outrepcode == TTY_REVIVE) {
211 notify(tp->tty_outcaller);
212 tp->tty_outrevived = 1;
213 } else {
214 tty_reply(tp->tty_outrepcode, tp->tty_outcaller,
215 tp->tty_outproc, tp->tty_outcum);
216 tp->tty_outcum = 0;
220 return 0;
223 /*===========================================================================*
224 * cons_echo *
225 *===========================================================================*/
226 static void cons_echo(tp, c)
227 register tty_t *tp; /* pointer to tty struct */
228 int c; /* character to be echoed */
230 /* Echo keyboard input (print & flush). */
231 console_t *cons = tp->tty_priv;
233 out_char(cons, c);
234 flush(cons);
237 /*===========================================================================*
238 * out_char *
239 *===========================================================================*/
240 static void out_char(cons, c)
241 register console_t *cons; /* pointer to console struct */
242 int c; /* character to be output */
244 /* Output a character on the console. Check for escape sequences first. */
245 if (cons->c_esc_state > 0) {
246 parse_escape(cons, c);
247 return;
250 #if DUP_CONS_TO_SER
251 if (cons == &cons_table[0] && c != '\0')
253 if (c == '\n')
254 ser_putc('\r');
255 ser_putc(c);
257 #endif
259 switch(c) {
260 case 000: /* null is typically used for padding */
261 return; /* better not do anything */
263 case 007: /* ring the bell */
264 flush(cons); /* print any chars queued for output */
265 beep();
266 return;
268 case '\b': /* backspace */
269 if (--cons->c_column < 0) {
270 if (--cons->c_row >= 0) cons->c_column += scr_width;
272 flush(cons);
273 return;
275 case '\n': /* line feed */
276 if ((cons->c_tty->tty_termios.c_oflag & (OPOST|ONLCR))
277 == (OPOST|ONLCR)) {
278 cons->c_column = 0;
280 /*FALL THROUGH*/
281 case 013: /* CTRL-K */
282 case 014: /* CTRL-L */
283 if (cons->c_row == scr_lines-1) {
284 scroll_screen(cons, SCROLL_UP);
285 } else {
286 cons->c_row++;
288 flush(cons);
289 return;
291 case '\r': /* carriage return */
292 cons->c_column = 0;
293 flush(cons);
294 return;
296 case '\t': /* tab */
297 cons->c_column = (cons->c_column + TAB_SIZE) & ~TAB_MASK;
298 if (cons->c_column > scr_width) {
299 cons->c_column -= scr_width;
300 if (cons->c_row == scr_lines-1) {
301 scroll_screen(cons, SCROLL_UP);
302 } else {
303 cons->c_row++;
306 flush(cons);
307 return;
309 case 033: /* ESC - start of an escape sequence */
310 flush(cons); /* print any chars queued for output */
311 cons->c_esc_state = 1; /* mark ESC as seen */
312 return;
314 default: /* printable chars are stored in ramqueue */
315 if (cons->c_column >= scr_width) {
316 if (!LINEWRAP) return;
317 if (cons->c_row == scr_lines-1) {
318 scroll_screen(cons, SCROLL_UP);
319 } else {
320 cons->c_row++;
322 cons->c_column = 0;
323 flush(cons);
325 if (cons->c_rwords == buflen(cons->c_ramqueue)) flush(cons);
326 cons->c_ramqueue[cons->c_rwords++] = cons->c_attr | (c & BYTE);
327 cons->c_column++; /* next column */
328 return;
332 /*===========================================================================*
333 * scroll_screen *
334 *===========================================================================*/
335 static void scroll_screen(cons, dir)
336 register console_t *cons; /* pointer to console struct */
337 int dir; /* SCROLL_UP or SCROLL_DOWN */
339 unsigned new_line, new_org, chars;
341 flush(cons);
342 chars = scr_size - scr_width; /* one screen minus one line */
344 /* Scrolling the screen is a real nuisance due to the various incompatible
345 * video cards. This driver supports software scrolling (Hercules?),
346 * hardware scrolling (mono and CGA cards) and hardware scrolling without
347 * wrapping (EGA cards). In the latter case we must make sure that
348 * c_start <= c_org && c_org + scr_size <= c_limit
349 * holds, because EGA doesn't wrap around the end of video memory.
351 if (dir == SCROLL_UP) {
352 /* Scroll one line up in 3 ways: soft, avoid wrap, use origin. */
353 if (softscroll) {
354 vid_vid_copy(cons->c_start + scr_width, cons->c_start, chars);
355 } else
356 if (!wrap && cons->c_org + scr_size + scr_width >= cons->c_limit) {
357 vid_vid_copy(cons->c_org + scr_width, cons->c_start, chars);
358 UPDATE_ORIGIN(cons, cons->c_start);
359 } else {
360 UPDATE_ORIGIN(cons, (cons->c_org + scr_width) & vid_mask);
362 new_line = (cons->c_org + chars) & vid_mask;
363 } else {
364 /* Scroll one line down in 3 ways: soft, avoid wrap, use origin. */
365 if (softscroll) {
366 vid_vid_copy(cons->c_start, cons->c_start + scr_width, chars);
367 } else
368 if (!wrap && cons->c_org < cons->c_start + scr_width) {
369 new_org = cons->c_limit - scr_size;
370 vid_vid_copy(cons->c_org, new_org + scr_width, chars);
371 UPDATE_ORIGIN(cons, new_org);
372 } else {
373 UPDATE_ORIGIN(cons, (cons->c_org - scr_width) & vid_mask);
375 new_line = cons->c_org;
377 /* Blank the new line at top or bottom. */
378 blank_color = cons->c_blank;
379 mem_vid_copy(BLANK_MEM, new_line, scr_width);
381 flush(cons);
384 /*===========================================================================*
385 * flush *
386 *===========================================================================*/
387 static void flush(cons)
388 register console_t *cons; /* pointer to console struct */
390 /* Send characters buffered in 'ramqueue' to screen memory, check the new
391 * cursor position, compute the new hardware cursor position and set it.
393 unsigned cur;
394 tty_t *tp = cons->c_tty;
396 /* Have the characters in 'ramqueue' transferred to the screen. */
397 if (cons->c_rwords > 0) {
398 mem_vid_copy((vir_bytes) cons->c_ramqueue, cons->c_cur, cons->c_rwords);
399 cons->c_rwords = 0;
401 /* TTY likes to know the current column and if echoing messed up. */
402 tp->tty_position = cons->c_column;
403 tp->tty_reprint = TRUE;
406 /* Check and update the cursor position. */
407 if (cons->c_column < 0) cons->c_column = 0;
408 if (cons->c_column > scr_width) cons->c_column = scr_width;
409 if (cons->c_row < 0) cons->c_row = 0;
410 if (cons->c_row >= scr_lines) cons->c_row = scr_lines - 1;
411 cur = cons->c_org + cons->c_row * scr_width + cons->c_column;
412 if (cur != cons->c_cur)
413 UPDATE_CURSOR(cons, cur);
416 /*===========================================================================*
417 * parse_escape *
418 *===========================================================================*/
419 static void parse_escape(cons, c)
420 register console_t *cons; /* pointer to console struct */
421 char c; /* next character in escape sequence */
423 /* The following ANSI escape sequences are currently supported.
424 * If n and/or m are omitted, they default to 1.
425 * ESC [nA moves up n lines
426 * ESC [nB moves down n lines
427 * ESC [nC moves right n spaces
428 * ESC [nD moves left n spaces
429 * ESC [m;nH" moves cursor to (m,n)
430 * ESC [J clears screen from cursor
431 * ESC [K clears line from cursor
432 * ESC [nL inserts n lines ar cursor
433 * ESC [nM deletes n lines at cursor
434 * ESC [nP deletes n chars at cursor
435 * ESC [n@ inserts n chars at cursor
436 * ESC [nm enables rendition n (0=normal, 4=bold, 5=blinking, 7=reverse)
437 * ESC M scrolls the screen backwards if the cursor is on the top line
440 switch (cons->c_esc_state) {
441 case 1: /* ESC seen */
442 cons->c_esc_intro = '\0';
443 cons->c_esc_parmp = bufend(cons->c_esc_parmv);
444 do {
445 *--cons->c_esc_parmp = 0;
446 } while (cons->c_esc_parmp > cons->c_esc_parmv);
447 switch (c) {
448 case '[': /* Control Sequence Introducer */
449 cons->c_esc_intro = c;
450 cons->c_esc_state = 2;
451 break;
452 case 'M': /* Reverse Index */
453 do_escape(cons, c);
454 break;
455 default:
456 cons->c_esc_state = 0;
458 break;
460 case 2: /* ESC [ seen */
461 if (c >= '0' && c <= '9') {
462 if (cons->c_esc_parmp < bufend(cons->c_esc_parmv))
463 *cons->c_esc_parmp = *cons->c_esc_parmp * 10 + (c-'0');
464 } else
465 if (c == ';') {
466 if (cons->c_esc_parmp < bufend(cons->c_esc_parmv))
467 cons->c_esc_parmp++;
468 } else {
469 do_escape(cons, c);
471 break;
475 /*===========================================================================*
476 * do_escape *
477 *===========================================================================*/
478 static void do_escape(cons, c)
479 register console_t *cons; /* pointer to console struct */
480 char c; /* next character in escape sequence */
482 int value, n;
483 unsigned src, dst, count;
484 int *parmp;
486 /* Some of these things hack on screen RAM, so it had better be up to date */
487 flush(cons);
489 if (cons->c_esc_intro == '\0') {
490 /* Handle a sequence beginning with just ESC */
491 switch (c) {
492 case 'M': /* Reverse Index */
493 if (cons->c_row == 0) {
494 scroll_screen(cons, SCROLL_DOWN);
495 } else {
496 cons->c_row--;
498 flush(cons);
499 break;
501 default: break;
503 } else
504 if (cons->c_esc_intro == '[') {
505 /* Handle a sequence beginning with ESC [ and parameters */
506 value = cons->c_esc_parmv[0];
507 switch (c) {
508 case 'A': /* ESC [nA moves up n lines */
509 n = (value == 0 ? 1 : value);
510 cons->c_row -= n;
511 flush(cons);
512 break;
514 case 'B': /* ESC [nB moves down n lines */
515 n = (value == 0 ? 1 : value);
516 cons->c_row += n;
517 flush(cons);
518 break;
520 case 'C': /* ESC [nC moves right n spaces */
521 n = (value == 0 ? 1 : value);
522 cons->c_column += n;
523 flush(cons);
524 break;
526 case 'D': /* ESC [nD moves left n spaces */
527 n = (value == 0 ? 1 : value);
528 cons->c_column -= n;
529 flush(cons);
530 break;
532 case 'H': /* ESC [m;nH" moves cursor to (m,n) */
533 cons->c_row = cons->c_esc_parmv[0] - 1;
534 cons->c_column = cons->c_esc_parmv[1] - 1;
535 flush(cons);
536 break;
538 case 'J': /* ESC [sJ clears in display */
539 switch (value) {
540 case 0: /* Clear from cursor to end of screen */
541 count = scr_size - (cons->c_cur - cons->c_org);
542 dst = cons->c_cur;
543 break;
544 case 1: /* Clear from start of screen to cursor */
545 count = cons->c_cur - cons->c_org;
546 dst = cons->c_org;
547 break;
548 case 2: /* Clear entire screen */
549 count = scr_size;
550 dst = cons->c_org;
551 break;
552 default: /* Do nothing */
553 count = 0;
554 dst = cons->c_org;
556 blank_color = cons->c_blank;
557 mem_vid_copy(BLANK_MEM, dst, count);
558 break;
560 case 'K': /* ESC [sK clears line from cursor */
561 switch (value) {
562 case 0: /* Clear from cursor to end of line */
563 count = scr_width - cons->c_column;
564 dst = cons->c_cur;
565 break;
566 case 1: /* Clear from beginning of line to cursor */
567 count = cons->c_column;
568 dst = cons->c_cur - cons->c_column;
569 break;
570 case 2: /* Clear entire line */
571 count = scr_width;
572 dst = cons->c_cur - cons->c_column;
573 break;
574 default: /* Do nothing */
575 count = 0;
576 dst = cons->c_cur;
578 blank_color = cons->c_blank;
579 mem_vid_copy(BLANK_MEM, dst, count);
580 break;
582 case 'L': /* ESC [nL inserts n lines at cursor */
583 n = value;
584 if (n < 1) n = 1;
585 if (n > (scr_lines - cons->c_row))
586 n = scr_lines - cons->c_row;
588 src = cons->c_org + cons->c_row * scr_width;
589 dst = src + n * scr_width;
590 count = (scr_lines - cons->c_row - n) * scr_width;
591 vid_vid_copy(src, dst, count);
592 blank_color = cons->c_blank;
593 mem_vid_copy(BLANK_MEM, src, n * scr_width);
594 break;
596 case 'M': /* ESC [nM deletes n lines at cursor */
597 n = value;
598 if (n < 1) n = 1;
599 if (n > (scr_lines - cons->c_row))
600 n = scr_lines - cons->c_row;
602 dst = cons->c_org + cons->c_row * scr_width;
603 src = dst + n * scr_width;
604 count = (scr_lines - cons->c_row - n) * scr_width;
605 vid_vid_copy(src, dst, count);
606 blank_color = cons->c_blank;
607 mem_vid_copy(BLANK_MEM, dst + count, n * scr_width);
608 break;
610 case '@': /* ESC [n@ inserts n chars at cursor */
611 n = value;
612 if (n < 1) n = 1;
613 if (n > (scr_width - cons->c_column))
614 n = scr_width - cons->c_column;
616 src = cons->c_cur;
617 dst = src + n;
618 count = scr_width - cons->c_column - n;
619 vid_vid_copy(src, dst, count);
620 blank_color = cons->c_blank;
621 mem_vid_copy(BLANK_MEM, src, n);
622 break;
624 case 'P': /* ESC [nP deletes n chars at cursor */
625 n = value;
626 if (n < 1) n = 1;
627 if (n > (scr_width - cons->c_column))
628 n = scr_width - cons->c_column;
630 dst = cons->c_cur;
631 src = dst + n;
632 count = scr_width - cons->c_column - n;
633 vid_vid_copy(src, dst, count);
634 blank_color = cons->c_blank;
635 mem_vid_copy(BLANK_MEM, dst + count, n);
636 break;
638 case 'm': /* ESC [nm enables rendition n */
639 for (parmp = cons->c_esc_parmv; parmp <= cons->c_esc_parmp
640 && parmp < bufend(cons->c_esc_parmv); parmp++) {
641 if (cons->c_reverse) {
642 /* Unswap fg and bg colors */
643 cons->c_attr = ((cons->c_attr & 0x7000) >> 4) |
644 ((cons->c_attr & 0x0700) << 4) |
645 ((cons->c_attr & 0x8800));
647 switch (n = *parmp) {
648 case 0: /* NORMAL */
649 cons->c_attr = cons->c_blank = BLANK_COLOR;
650 cons->c_reverse = FALSE;
651 break;
653 case 1: /* BOLD */
654 /* Set intensity bit */
655 cons->c_attr |= 0x0800;
656 break;
658 case 4: /* UNDERLINE */
659 if (color) {
660 /* Change white to cyan, i.e. lose red
662 cons->c_attr = (cons->c_attr & 0xBBFF);
663 } else {
664 /* Set underline attribute */
665 cons->c_attr = (cons->c_attr & 0x99FF);
667 break;
669 case 5: /* BLINKING */
670 /* Set the blink bit */
671 cons->c_attr |= 0x8000;
672 break;
674 case 7: /* REVERSE */
675 cons->c_reverse = TRUE;
676 break;
678 default: /* COLOR */
679 if (n == 39) n = 37; /* set default color */
680 if (n == 49) n = 40;
682 if (!color) {
683 /* Don't mess up a monochrome screen */
684 } else
685 if (30 <= n && n <= 37) {
686 /* Foreground color */
687 cons->c_attr =
688 (cons->c_attr & 0xF8FF) |
689 (ansi_colors[(n - 30)] << 8);
690 cons->c_blank =
691 (cons->c_blank & 0xF8FF) |
692 (ansi_colors[(n - 30)] << 8);
693 } else
694 if (40 <= n && n <= 47) {
695 /* Background color */
696 cons->c_attr =
697 (cons->c_attr & 0x8FFF) |
698 (ansi_colors[(n - 40)] << 12);
699 cons->c_blank =
700 (cons->c_blank & 0x8FFF) |
701 (ansi_colors[(n - 40)] << 12);
704 if (cons->c_reverse) {
705 /* Swap fg and bg colors */
706 cons->c_attr = ((cons->c_attr & 0x7000) >> 4) |
707 ((cons->c_attr & 0x0700) << 4) |
708 ((cons->c_attr & 0x8800));
711 break;
714 cons->c_esc_state = 0;
717 /*===========================================================================*
718 * set_6845 *
719 *===========================================================================*/
720 static void set_6845(reg, val)
721 int reg; /* which register pair to set */
722 unsigned val; /* 16-bit value to set it to */
724 /* Set a register pair inside the 6845.
725 * Registers 12-13 tell the 6845 where in video ram to start
726 * Registers 14-15 tell the 6845 where to put the cursor
728 pvb_pair_t char_out[4];
729 pv_set(char_out[0], vid_port + INDEX, reg); /* set index register */
730 pv_set(char_out[1], vid_port + DATA, (val>>8) & BYTE); /* high byte */
731 pv_set(char_out[2], vid_port + INDEX, reg + 1); /* again */
732 pv_set(char_out[3], vid_port + DATA, val&BYTE); /* low byte */
733 sys_voutb(char_out, 4); /* do actual output */
736 #if 0
737 /*===========================================================================*
738 * get_6845 *
739 *===========================================================================*/
740 static void get_6845(reg, val)
741 int reg; /* which register pair to set */
742 unsigned *val; /* 16-bit value to set it to */
744 char v1, v2;
745 u32_t v;
746 /* Get a register pair inside the 6845. */
747 sys_outb(vid_port + INDEX, reg);
748 sys_inb(vid_port + DATA, &v);
749 v1 = v;
750 sys_outb(vid_port + INDEX, reg+1);
751 sys_inb(vid_port + DATA, &v);
752 v2 = v;
753 *val = (v1 << 8) | v2;
755 #endif
757 /*===========================================================================*
758 * beep *
759 *===========================================================================*/
760 static void beep()
762 /* Making a beeping sound on the speaker (output for CRTL-G).
763 * This routine works by turning on the bits 0 and 1 in port B of the 8255
764 * chip that drive the speaker.
766 static timer_t tmr_stop_beep;
767 pvb_pair_t char_out[3];
768 u32_t port_b_val;
770 /* Set timer in advance to prevent beeping delay. */
771 set_timer(&tmr_stop_beep, B_TIME, stop_beep, 0);
773 if (!beeping) {
774 /* Set timer channel 2, square wave, with given frequency. */
775 pv_set(char_out[0], TIMER_MODE, 0xB6);
776 pv_set(char_out[1], TIMER2, (BEEP_FREQ >> 0) & BYTE);
777 pv_set(char_out[2], TIMER2, (BEEP_FREQ >> 8) & BYTE);
778 if (sys_voutb(char_out, 3)==OK) {
779 if (sys_inb(PORT_B, &port_b_val)==OK &&
780 sys_outb(PORT_B, (port_b_val|3))==OK)
781 beeping = TRUE;
787 /*===========================================================================*
788 * do_video *
789 *===========================================================================*/
790 void do_video(message *m)
792 int r;
794 /* Execute the requested device driver function. */
795 r= EINVAL; /* just in case */
796 switch (m->m_type) {
797 case DEV_OPEN:
798 /* Should grant IOPL */
799 disable_console();
800 r= OK;
801 break;
802 case DEV_CLOSE:
803 reenable_console();
804 r= OK;
805 break;
806 case DEV_IOCTL_S:
807 switch(m->TTY_REQUEST) {
808 case TIOCMAPMEM:
809 case TIOCUNMAPMEM: {
810 int r, do_map;
811 struct mapreqvm mapreqvm;
813 do_map= (m->REQUEST == TIOCMAPMEM); /* else unmap */
815 r = sys_safecopyfrom(m->m_source,
816 (cp_grant_id_t) m->IO_GRANT, 0,
817 (vir_bytes) &mapreqvm, sizeof(mapreqvm));
819 if (r != OK)
821 printf("tty: sys_safecopyfrom failed\n");
822 tty_reply(TASK_REPLY, m->m_source,
823 m->USER_ENDPT, r);
824 return;
827 /* In safe ioctl mode, the POSITION field contains
828 * the endpt number of the original requestor.
829 * USER_ENDPT is always FS.
832 if(do_map) {
833 mapreqvm.vaddr_ret = vm_map_phys(m->POSITION,
834 (void *) mapreqvm.phys_offset, mapreqvm.size);
835 if((r = sys_safecopyto(m->m_source,
836 (cp_grant_id_t) m->IO_GRANT, 0,
837 (vir_bytes) &mapreqvm,
838 sizeof(mapreqvm))) != OK) {
839 printf("tty: sys_safecopyto failed\n");
841 } else {
842 r = vm_unmap_phys(m->POSITION,
843 mapreqvm.vaddr, mapreqvm.size);
845 tty_reply(TASK_REPLY, m->m_source, m->USER_ENDPT, r);
846 return;
849 r= ENOTTY;
850 break;
852 default:
853 printf(
854 "Warning, TTY(video) got unexpected request %d from %d\n",
855 m->m_type, m->m_source);
856 r= EINVAL;
858 tty_reply(TASK_REPLY, m->m_source, m->USER_ENDPT, r);
862 /*===========================================================================*
863 * beep_x *
864 *===========================================================================*/
865 void beep_x(freq, dur)
866 unsigned freq;
867 clock_t dur;
869 /* Making a beeping sound on the speaker.
870 * This routine works by turning on the bits 0 and 1 in port B of the 8255
871 * chip that drive the speaker.
873 static timer_t tmr_stop_beep;
874 pvb_pair_t char_out[3];
875 u32_t port_b_val;
877 unsigned long ival= TIMER_FREQ / freq;
878 if (ival == 0 || ival > 0xffff)
879 return; /* Frequency out of range */
881 /* Set timer in advance to prevent beeping delay. */
882 set_timer(&tmr_stop_beep, dur, stop_beep, 0);
884 if (!beeping) {
885 /* Set timer channel 2, square wave, with given frequency. */
886 pv_set(char_out[0], TIMER_MODE, 0xB6);
887 pv_set(char_out[1], TIMER2, (ival >> 0) & BYTE);
888 pv_set(char_out[2], TIMER2, (ival >> 8) & BYTE);
889 if (sys_voutb(char_out, 3)==OK) {
890 if (sys_inb(PORT_B, &port_b_val)==OK &&
891 sys_outb(PORT_B, (port_b_val|3))==OK)
892 beeping = TRUE;
897 /*===========================================================================*
898 * stop_beep *
899 *===========================================================================*/
900 static void stop_beep(timer_t *UNUSED(tmrp))
902 /* Turn off the beeper by turning off bits 0 and 1 in PORT_B. */
903 u32_t port_b_val;
904 if (sys_inb(PORT_B, &port_b_val)==OK &&
905 sys_outb(PORT_B, (port_b_val & ~3))==OK)
906 beeping = FALSE;
909 /*===========================================================================*
910 * scr_init *
911 *===========================================================================*/
912 void scr_init(tp)
913 tty_t *tp;
915 /* Initialize the screen driver. */
916 console_t *cons;
917 u16_t bios_columns, bios_crtbase, bios_fontlines;
918 u8_t bios_rows;
919 int line;
920 int s;
921 static int vdu_initialized = 0;
922 static unsigned page_size;
924 /* Associate console and TTY. */
925 line = tp - &tty_table[0];
926 if (line >= nr_cons) return;
927 cons = &cons_table[line];
928 cons->c_tty = tp;
929 cons->c_line = line;
930 tp->tty_priv = cons;
932 /* Fill in TTY function hooks. */
933 tp->tty_devwrite = cons_write;
934 tp->tty_echo = cons_echo;
935 tp->tty_ioctl = cons_ioctl;
937 /* Get the BIOS parameters that describe the VDU. */
938 if (! vdu_initialized++) {
940 /* FIXME: How about error checking? What to do on failure??? */
941 s=sys_readbios(VDU_SCREEN_COLS_ADDR, &bios_columns,
942 VDU_SCREEN_COLS_SIZE);
943 s=sys_readbios(VDU_CRT_BASE_ADDR, &bios_crtbase,
944 VDU_CRT_BASE_SIZE);
945 s=sys_readbios( VDU_SCREEN_ROWS_ADDR, &bios_rows,
946 VDU_SCREEN_ROWS_SIZE);
947 s=sys_readbios(VDU_FONTLINES_ADDR, &bios_fontlines,
948 VDU_FONTLINES_SIZE);
950 vid_port = bios_crtbase;
951 scr_width = bios_columns;
952 font_lines = bios_fontlines;
953 scr_lines = bios_rows+1;
955 if (color) {
956 vid_base = COLOR_BASE;
957 vid_size = COLOR_SIZE;
958 } else {
959 vid_base = MONO_BASE;
960 vid_size = MONO_SIZE;
962 vid_size = EGA_SIZE;
963 wrap = 0;
965 console_memory = vm_map_phys(SELF, (void *) vid_base, vid_size);
967 if(console_memory == MAP_FAILED)
968 panic("Console couldn't map video memory");
970 font_memory = vm_map_phys(SELF, (void *)GA_VIDEO_ADDRESS, GA_FONT_SIZE);
972 if(font_memory == MAP_FAILED)
973 panic("Console couldn't map font memory");
975 vid_size >>= 1; /* word count */
976 vid_mask = vid_size - 1;
978 /* Size of the screen (number of displayed characters.) */
979 scr_size = scr_lines * scr_width;
981 /* There can be as many consoles as video memory allows. */
982 nr_cons = vid_size / scr_size;
984 if (nr_cons > NR_CONS) nr_cons = NR_CONS;
985 if (nr_cons > 1) wrap = 0;
986 if (nr_cons < 1) panic("no consoles");
987 page_size = vid_size / nr_cons;
990 cons->c_start = line * page_size;
991 cons->c_limit = cons->c_start + page_size;
992 cons->c_cur = cons->c_org = cons->c_start;
993 cons->c_attr = cons->c_blank = BLANK_COLOR;
995 if (line != 0) {
996 /* Clear the non-console vtys. */
997 blank_color = BLANK_COLOR;
998 mem_vid_copy(BLANK_MEM, cons->c_start, scr_size);
999 } else {
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 extern struct minix_kerninfo *_minix_kerninfo;
1013 /*===========================================================================*
1014 * do_new_kmess *
1015 *===========================================================================*/
1016 void do_new_kmess()
1018 /* Notification for a new kernel message. */
1019 struct kmessages *kmess_ptr; /* kmessages structure */
1020 static int prev_next = 0; /* previous next seen */
1021 int bytes;
1022 int r;
1024 assert(_minix_kerninfo);
1025 kmess_ptr = _minix_kerninfo->kmessages;
1027 /* Print only the new part. Determine how many new bytes there are with
1028 * help of the current and previous 'next' index. Note that the kernel
1029 * buffer is circular. This works fine if less then _KMESS_BUF_SIZE bytes
1030 * is new data; else we miss % _KMESS_BUF_SIZE here.
1031 * Check for size being positive, the buffer might as well be emptied!
1033 if (kmess_ptr->km_size > 0) {
1034 bytes = ((kmess_ptr->km_next + _KMESS_BUF_SIZE) - prev_next) % _KMESS_BUF_SIZE;
1035 r=prev_next; /* start at previous old */
1036 while (bytes > 0) {
1037 cons_putk( kmess_ptr->km_buf[(r%_KMESS_BUF_SIZE)] );
1038 bytes --;
1039 r ++;
1041 cons_putk(0); /* terminate to flush output */
1044 /* Almost done, store 'next' so that we can determine what part of the
1045 * kernel messages buffer to print next time a notification arrives.
1047 prev_next = kmess_ptr->km_next;
1050 /*===========================================================================*
1051 * cons_putk *
1052 *===========================================================================*/
1053 static void cons_putk(c)
1054 int c; /* character to print */
1056 /* This procedure is used to print a character on the console.
1058 if (c != 0) {
1059 if (c == '\n') cons_putk('\r');
1060 out_char(&cons_table[0], (int) c);
1061 #if 0
1062 ser_putc(c);
1063 #endif
1064 } else {
1065 flush(&cons_table[0]);
1069 /*===========================================================================*
1070 * toggle_scroll *
1071 *===========================================================================*/
1072 void toggle_scroll()
1074 /* Toggle between hardware and software scroll. */
1076 cons_org0();
1077 softscroll = !softscroll;
1078 printf("%sware scrolling enabled.\n", softscroll ? "Soft" : "Hard");
1081 /*===========================================================================*
1082 * cons_stop *
1083 *===========================================================================*/
1084 void cons_stop()
1086 /* Prepare for halt or reboot. */
1087 cons_org0();
1088 softscroll = 1;
1089 select_console(0);
1090 cons_table[0].c_attr = cons_table[0].c_blank = BLANK_COLOR;
1091 shutting_down = TRUE;
1094 /*===========================================================================*
1095 * cons_org0 *
1096 *===========================================================================*/
1097 static void cons_org0()
1099 /* Scroll video memory back to put the origin at 0. */
1100 int cons_line;
1101 console_t *cons;
1102 unsigned n;
1104 for (cons_line = 0; cons_line < nr_cons; cons_line++) {
1105 cons = &cons_table[cons_line];
1106 while (cons->c_org > cons->c_start) {
1107 n = vid_size - scr_size; /* amount of unused memory */
1108 if (n > cons->c_org - cons->c_start)
1109 n = cons->c_org - cons->c_start;
1110 vid_vid_copy(cons->c_org, cons->c_org - n, scr_size);
1111 UPDATE_ORIGIN(cons, cons->c_org - n);
1113 flush(cons);
1115 select_console(ccurrent);
1118 /*===========================================================================*
1119 * disable_console *
1120 *===========================================================================*/
1121 static void disable_console()
1123 if (disabled_vc != -1)
1124 return;
1126 disabled_vc = ccurrent;
1127 disabled_sm = softscroll;
1129 cons_org0();
1130 softscroll = 1;
1131 select_console(0);
1133 /* Should also disable further output to virtual consoles */
1136 /*===========================================================================*
1137 * reenable_console *
1138 *===========================================================================*/
1139 static void reenable_console()
1141 if (disabled_vc == -1)
1142 return;
1144 softscroll = disabled_sm;
1145 select_console(disabled_vc);
1146 disabled_vc = -1;
1149 /*===========================================================================*
1150 * select_console *
1151 *===========================================================================*/
1152 void select_console(int cons_line)
1154 /* Set the current console to console number 'cons_line'. */
1156 if (shutting_down) return;
1158 if (cons_line < 0 || cons_line >= nr_cons) return;
1160 ccurrent = cons_line;
1161 curcons = &cons_table[cons_line];
1163 UPDATE_CURSOR(curcons, curcons->c_cur);
1164 UPDATE_ORIGIN(curcons, curcons->c_org);
1167 /*===========================================================================*
1168 * con_loadfont *
1169 *===========================================================================*/
1170 int con_loadfont(m)
1171 message *m;
1174 /* Load a font into the EGA or VGA adapter. */
1175 int result;
1176 static struct sequence seq1[7] = {
1177 { GA_SEQUENCER_INDEX, 0x00, 0x01 },
1178 { GA_SEQUENCER_INDEX, 0x02, 0x04 },
1179 { GA_SEQUENCER_INDEX, 0x04, 0x07 },
1180 { GA_SEQUENCER_INDEX, 0x00, 0x03 },
1181 { GA_GRAPHICS_INDEX, 0x04, 0x02 },
1182 { GA_GRAPHICS_INDEX, 0x05, 0x00 },
1183 { GA_GRAPHICS_INDEX, 0x06, 0x00 },
1185 static struct sequence seq2[7] = {
1186 { GA_SEQUENCER_INDEX, 0x00, 0x01 },
1187 { GA_SEQUENCER_INDEX, 0x02, 0x03 },
1188 { GA_SEQUENCER_INDEX, 0x04, 0x03 },
1189 { GA_SEQUENCER_INDEX, 0x00, 0x03 },
1190 { GA_GRAPHICS_INDEX, 0x04, 0x00 },
1191 { GA_GRAPHICS_INDEX, 0x05, 0x10 },
1192 { GA_GRAPHICS_INDEX, 0x06, 0 },
1195 seq2[6].value= color ? 0x0E : 0x0A;
1197 result = ga_program(seq1); /* bring font memory into view */
1199 if(sys_safecopyfrom(m->m_source, (cp_grant_id_t) m->IO_GRANT, 0,
1200 (vir_bytes) font_memory, GA_FONT_SIZE) != OK) {
1201 printf("tty: copying from %d failed\n", m->m_source);
1202 return EFAULT;
1205 result = ga_program(seq2); /* restore */
1207 return(result);
1210 /*===========================================================================*
1211 * ga_program *
1212 *===========================================================================*/
1213 static int ga_program(seq)
1214 struct sequence *seq;
1216 pvb_pair_t char_out[14];
1217 int i;
1218 for (i=0; i<7; i++) {
1219 pv_set(char_out[2*i], seq->index, seq->port);
1220 pv_set(char_out[2*i+1], seq->index+1, seq->value);
1221 seq++;
1223 return sys_voutb(char_out, 14);
1226 /*===========================================================================*
1227 * cons_ioctl *
1228 *===========================================================================*/
1229 static int cons_ioctl(tty_t *tp, int UNUSED(try))
1231 /* Set the screen dimensions. */
1233 tp->tty_winsize.ws_row= scr_lines;
1234 tp->tty_winsize.ws_col= scr_width;
1235 tp->tty_winsize.ws_xpixel= scr_width * 8;
1236 tp->tty_winsize.ws_ypixel= scr_lines * font_lines;
1238 return 0;
1241 #define LIMITINDEX(mask, start, size, ct) { \
1242 int countlimit = size - start; \
1243 start &= mask; \
1244 if(ct > countlimit) ct = countlimit; \
1247 /*===========================================================================*
1248 * mem_vid_copy *
1249 *===========================================================================*/
1250 static void mem_vid_copy(vir_bytes src, int dst_index, int count)
1252 u16_t *src_mem = (u16_t *) src;
1253 while(count > 0) {
1254 int i, subcount = count;
1255 u16_t *dst_mem;
1256 LIMITINDEX(vid_mask, dst_index, vid_size, subcount);
1257 dst_mem = (u16_t *) console_memory + dst_index;
1258 if(!src)
1259 for(i = 0; i < subcount; i++)
1260 *dst_mem++ = blank_color;
1261 else
1262 for(i = 0; i < subcount; i++)
1263 *dst_mem++ = *src_mem++;
1264 count -= subcount;
1265 dst_index += subcount;
1269 /*===========================================================================*
1270 * vid_vid_copy *
1271 *===========================================================================*/
1272 static void vid_vid_copy(int src_index, int dst_index, int count)
1274 int backwards = 0;
1275 if(src_index < dst_index)
1276 backwards = 1;
1277 while(count > 0) {
1278 int i, subcount = count;
1279 u16_t *dst_mem, *src_mem;
1280 LIMITINDEX(vid_mask, src_index, vid_size, subcount);
1281 LIMITINDEX(vid_mask, dst_index, vid_size, subcount);
1282 src_mem = (u16_t *) console_memory + src_index;
1283 dst_mem = (u16_t *) console_memory + dst_index;
1284 if(backwards) {
1285 src_mem += subcount - 1;
1286 dst_mem += subcount - 1;
1287 for(i = 0; i < subcount; i++)
1288 *dst_mem-- = *src_mem--;
1289 } else {
1290 for(i = 0; i < subcount; i++)
1291 *dst_mem++ = *src_mem++;
1293 count -= subcount;
1294 dst_index += subcount;
1295 src_index += subcount;