minix/drivers/tty/console.c

1298 lines
38 KiB
C
Raw Normal View History

2005-04-21 16:53:53 +02:00
/* Code and data for the IBM console driver.
*
* The 6845 video controller used by the IBM PC shares its video memory with
* the CPU somewhere in the 0xB0000 memory bank. To the 6845 this memory
* consists of 16-bit words. Each word has a character code in the low byte
* and a so-called attribute byte in the high byte. The CPU directly modifies
* video memory to display characters, and sets two registers on the 6845 that
* specify the video origin and the cursor position. The video origin is the
* place in video memory where the first character (upper left corner) can
* be found. Moving the origin is a fast way to scroll the screen. Some
* video adapters wrap around the top of video memory, so the origin can
* move without bounds. For other adapters screen memory must sometimes be
* moved to reset the origin. All computations on video memory use character
* (word) addresses for simplicity and assume there is no wrapping. The
* assembly support functions translate the word addresses to byte addresses
* and the scrolling function worries about wrapping.
*/
#include <minix/drivers.h>
2005-04-21 16:53:53 +02:00
#include <termios.h>
#include <assert.h>
#include <sys/ioctl.h>
#include <sys/vm.h>
#include <sys/video.h>
#include <sys/mman.h>
#include <minix/tty.h>
2005-04-21 16:53:53 +02:00
#include <minix/callnr.h>
#include <minix/com.h>
#include <minix/sys_config.h>
#include <minix/vm.h>
2005-04-21 16:53:53 +02:00
#include "tty.h"
/* Set this to 1 if you want console output duplicated on the first
* serial line.
*/
#define DUP_CONS_TO_SER 0
/* The clock task should provide an interface for this */
#define TIMER_FREQ 1193182L /* clock frequency for timer in PC and AT */
2005-04-21 16:53:53 +02:00
/* Global variables used by the console driver and assembly support. */
2012-03-25 20:25:53 +02:00
static phys_bytes vid_size; /* 0x2000 for color or 0x0800 for mono */
static phys_bytes vid_base;
static unsigned vid_mask; /* 0x1FFF for color or 0x07FF for mono */
static unsigned blank_color = BLANK_COLOR; /* display code for blank */
2005-04-21 16:53:53 +02:00
/* Private variables used by the console driver. */
2012-03-25 20:25:53 +02:00
static int vid_port; /* I/O port for accessing 6845 */
static int wrap; /* hardware can wrap? */
static int softscroll; /* 1 = software scrolling, 0 = hardware */
static int beeping; /* speaker is beeping? */
static unsigned font_lines; /* font lines per character */
static unsigned scr_width; /* # characters on a line */
static unsigned scr_lines; /* # lines on the screen */
static unsigned scr_size; /* # characters on the screen */
2005-04-21 16:53:53 +02:00
/* tells mem_vid_copy() to blank the screen */
#define BLANK_MEM ((vir_bytes) 0)
2012-03-25 20:25:53 +02:00
static int disabled_vc = -1; /* Virtual console that was active when
* disable_console was called.
*/
2012-03-25 20:25:53 +02:00
static int disabled_sm; /* Scroll mode to be restored when re-enabling
* console
*/
2012-03-25 20:25:53 +02:00
static char *console_memory = NULL;
static char *font_memory = NULL;
2005-04-21 16:53:53 +02:00
/* Per console data. */
typedef struct console {
tty_t *c_tty; /* associated TTY struct */
int c_column; /* current column number (0-origin) */
int c_row; /* current row (0 at top of screen) */
int c_rwords; /* number of WORDS (not bytes) in outqueue */
unsigned c_start; /* start of video memory of this console */
unsigned c_limit; /* limit of this console's video memory */
unsigned c_org; /* location in RAM where 6845 base points */
unsigned c_cur; /* current position of cursor in video RAM */
unsigned c_attr; /* character attribute */
unsigned c_blank; /* blank attribute */
char c_reverse; /* reverse video */
char c_esc_state; /* 0=normal, 1=ESC, 2=ESC[ */
char c_esc_intro; /* Distinguishing character following ESC */
int *c_esc_parmp; /* pointer to current escape parameter */
int c_esc_parmv[MAX_ESC_PARMS]; /* list of escape parameters */
u16_t c_ramqueue[CONS_RAM_WORDS]; /* buffer for video RAM */
int c_line; /* line no */
2005-04-21 16:53:53 +02:00
} console_t;
#define UPDATE_CURSOR(ccons, cursor) { \
ccons->c_cur = cursor; \
if(curcons && ccons == curcons) \
set_6845(CURSOR, ccons->c_cur); \
}
#define UPDATE_ORIGIN(ccons, origin) { \
ccons->c_org = origin; \
if (curcons && ccons == curcons) \
set_6845(VID_ORG, ccons->c_org); \
}
2012-03-25 20:25:53 +02:00
static int nr_cons= 1; /* actual number of consoles */
static console_t cons_table[NR_CONS];
static console_t *curcons = NULL; /* currently visible */
2005-04-21 16:53:53 +02:00
2012-03-25 20:25:53 +02:00
static int shutting_down = FALSE; /* don't allow console switches */
2005-04-21 16:53:53 +02:00
/* Color if using a color controller. */
#define color (vid_port == C_6845)
/* Map from ANSI colors to the attributes used by the PC */
2012-03-25 20:25:53 +02:00
static int ansi_colors[8] = {0, 4, 2, 6, 1, 5, 3, 7};
2005-04-21 16:53:53 +02:00
/* Structure used for font management */
struct sequence {
unsigned short index;
unsigned char port;
unsigned char value;
};
2012-03-25 20:25:53 +02:00
static int cons_write(struct tty *tp, int try);
static void cons_echo(tty_t *tp, int c);
static void out_char(console_t *cons, int c);
static void cons_putk(int c);
static void beep(void);
static void do_escape(console_t *cons, int c);
static void flush(console_t *cons);
static void parse_escape(console_t *cons, int c);
static void scroll_screen(console_t *cons, int dir);
static void set_6845(int reg, unsigned val);
static void stop_beep(timer_t *tmrp);
static void cons_org0(void);
static void disable_console(void);
static void reenable_console(void);
static int ga_program(struct sequence *seq);
static int cons_ioctl(tty_t *tp, int);
static void mem_vid_copy(vir_bytes src, int dst, int count);
static void vid_vid_copy(int src, int dst, int count);
#if 0
2012-03-25 20:25:53 +02:00
static void get_6845(int reg, unsigned *val);
#endif
2005-04-21 16:53:53 +02:00
/*===========================================================================*
* cons_write *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
static int cons_write(tp, try)
2005-04-21 16:53:53 +02:00
register struct tty *tp; /* tells which terminal is to be used */
int try;
2005-04-21 16:53:53 +02:00
{
/* Copy as much data as possible to the output queue, then start I/O. On
* memory-mapped terminals, such as the IBM console, the I/O will also be
* finished, and the counts updated. Keep repeating until all I/O done.
*/
int count;
int result;
register char *tbuf;
char buf[64];
console_t *cons = tp->tty_priv;
2005-09-11 19:09:11 +02:00
if (try) return 1; /* we can always write to console */
2005-04-21 16:53:53 +02:00
/* Check quickly for nothing to do, so this can be called often without
* unmodular tests elsewhere.
*/
if ((count = tp->tty_outleft) == 0 || tp->tty_inhibited) return 0;
2005-04-21 16:53:53 +02:00
/* Copy the user bytes to buf[] for decent addressing. Loop over the
* copies, since the user buffer may be much larger than buf[].
*/
do {
if (count > sizeof(buf)) count = sizeof(buf);
if ((result = sys_safecopyfrom(tp->tty_outcaller, tp->tty_outgrant,
tp->tty_outoffset, (vir_bytes) buf, count)) != OK)
break;
tp->tty_outoffset += count;
2005-04-21 16:53:53 +02:00
tbuf = buf;
/* Update terminal data structure. */
tp->tty_outcum += count;
tp->tty_outleft -= count;
/* Output each byte of the copy to the screen. Avoid calling
* out_char() for the "easy" characters, put them into the buffer
* directly.
*/
do {
if ((unsigned) *tbuf < ' ' || cons->c_esc_state > 0
|| cons->c_column >= scr_width
|| cons->c_rwords >= buflen(cons->c_ramqueue))
{
out_char(cons, *tbuf++);
} else {
#if DUP_CONS_TO_SER
if (cons == &cons_table[0]) ser_putc(*tbuf);
#endif
2005-04-21 16:53:53 +02:00
cons->c_ramqueue[cons->c_rwords++] =
cons->c_attr | (*tbuf++ & BYTE);
cons->c_column++;
}
} while (--count != 0);
} while ((count = tp->tty_outleft) != 0 && !tp->tty_inhibited);
flush(cons); /* transfer anything buffered to the screen */
/* Reply to the writer if all output is finished or if an error occured. */
if (tp->tty_outleft == 0 || result != OK) {
if(tp->tty_outrepcode == TTY_REVIVE) {
notify(tp->tty_outcaller);
tp->tty_outrevived = 1;
} else {
tty_reply(tp->tty_outrepcode, tp->tty_outcaller,
tp->tty_outproc, tp->tty_outcum);
tp->tty_outcum = 0;
}
2005-04-21 16:53:53 +02:00
}
return 0;
2005-04-21 16:53:53 +02:00
}
/*===========================================================================*
* cons_echo *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
static void cons_echo(tp, c)
2005-04-21 16:53:53 +02:00
register tty_t *tp; /* pointer to tty struct */
int c; /* character to be echoed */
{
/* Echo keyboard input (print & flush). */
console_t *cons = tp->tty_priv;
out_char(cons, c);
flush(cons);
}
/*===========================================================================*
* out_char *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
static void out_char(cons, c)
2005-04-21 16:53:53 +02:00
register console_t *cons; /* pointer to console struct */
int c; /* character to be output */
{
/* Output a character on the console. Check for escape sequences first. */
if (cons->c_esc_state > 0) {
parse_escape(cons, c);
return;
}
#if DUP_CONS_TO_SER
if (cons == &cons_table[0] && c != '\0')
{
if (c == '\n')
ser_putc('\r');
ser_putc(c);
}
#endif
2005-04-21 16:53:53 +02:00
switch(c) {
case 000: /* null is typically used for padding */
return; /* better not do anything */
case 007: /* ring the bell */
flush(cons); /* print any chars queued for output */
beep();
return;
case '\b': /* backspace */
if (--cons->c_column < 0) {
if (--cons->c_row >= 0) cons->c_column += scr_width;
}
flush(cons);
return;
case '\n': /* line feed */
if ((cons->c_tty->tty_termios.c_oflag & (OPOST|ONLCR))
== (OPOST|ONLCR)) {
cons->c_column = 0;
}
/*FALL THROUGH*/
case 013: /* CTRL-K */
case 014: /* CTRL-L */
if (cons->c_row == scr_lines-1) {
scroll_screen(cons, SCROLL_UP);
} else {
cons->c_row++;
}
flush(cons);
return;
case '\r': /* carriage return */
cons->c_column = 0;
flush(cons);
return;
case '\t': /* tab */
cons->c_column = (cons->c_column + TAB_SIZE) & ~TAB_MASK;
if (cons->c_column > scr_width) {
cons->c_column -= scr_width;
if (cons->c_row == scr_lines-1) {
scroll_screen(cons, SCROLL_UP);
} else {
cons->c_row++;
}
}
flush(cons);
return;
case 033: /* ESC - start of an escape sequence */
flush(cons); /* print any chars queued for output */
cons->c_esc_state = 1; /* mark ESC as seen */
return;
default: /* printable chars are stored in ramqueue */
if (cons->c_column >= scr_width) {
if (!LINEWRAP) return;
if (cons->c_row == scr_lines-1) {
scroll_screen(cons, SCROLL_UP);
} else {
cons->c_row++;
}
cons->c_column = 0;
flush(cons);
}
if (cons->c_rwords == buflen(cons->c_ramqueue)) flush(cons);
cons->c_ramqueue[cons->c_rwords++] = cons->c_attr | (c & BYTE);
cons->c_column++; /* next column */
return;
}
}
/*===========================================================================*
* scroll_screen *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
static void scroll_screen(cons, dir)
2005-04-21 16:53:53 +02:00
register console_t *cons; /* pointer to console struct */
int dir; /* SCROLL_UP or SCROLL_DOWN */
{
unsigned new_line, new_org, chars;
flush(cons);
chars = scr_size - scr_width; /* one screen minus one line */
/* Scrolling the screen is a real nuisance due to the various incompatible
* video cards. This driver supports software scrolling (Hercules?),
* hardware scrolling (mono and CGA cards) and hardware scrolling without
* wrapping (EGA cards). In the latter case we must make sure that
* c_start <= c_org && c_org + scr_size <= c_limit
* holds, because EGA doesn't wrap around the end of video memory.
*/
if (dir == SCROLL_UP) {
/* Scroll one line up in 3 ways: soft, avoid wrap, use origin. */
if (softscroll) {
vid_vid_copy(cons->c_start + scr_width, cons->c_start, chars);
} else
if (!wrap && cons->c_org + scr_size + scr_width >= cons->c_limit) {
vid_vid_copy(cons->c_org + scr_width, cons->c_start, chars);
UPDATE_ORIGIN(cons, cons->c_start);
2005-04-21 16:53:53 +02:00
} else {
UPDATE_ORIGIN(cons, (cons->c_org + scr_width) & vid_mask);
2005-04-21 16:53:53 +02:00
}
new_line = (cons->c_org + chars) & vid_mask;
} else {
/* Scroll one line down in 3 ways: soft, avoid wrap, use origin. */
if (softscroll) {
vid_vid_copy(cons->c_start, cons->c_start + scr_width, chars);
} else
if (!wrap && cons->c_org < cons->c_start + scr_width) {
new_org = cons->c_limit - scr_size;
vid_vid_copy(cons->c_org, new_org + scr_width, chars);
UPDATE_ORIGIN(cons, new_org);
2005-04-21 16:53:53 +02:00
} else {
UPDATE_ORIGIN(cons, (cons->c_org - scr_width) & vid_mask);
2005-04-21 16:53:53 +02:00
}
new_line = cons->c_org;
}
/* Blank the new line at top or bottom. */
blank_color = cons->c_blank;
mem_vid_copy(BLANK_MEM, new_line, scr_width);
flush(cons);
}
/*===========================================================================*
* flush *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
static void flush(cons)
2005-04-21 16:53:53 +02:00
register console_t *cons; /* pointer to console struct */
{
/* Send characters buffered in 'ramqueue' to screen memory, check the new
* cursor position, compute the new hardware cursor position and set it.
*/
unsigned cur;
tty_t *tp = cons->c_tty;
/* Have the characters in 'ramqueue' transferred to the screen. */
if (cons->c_rwords > 0) {
mem_vid_copy((vir_bytes) cons->c_ramqueue, cons->c_cur, cons->c_rwords);
2005-04-21 16:53:53 +02:00
cons->c_rwords = 0;
/* TTY likes to know the current column and if echoing messed up. */
tp->tty_position = cons->c_column;
tp->tty_reprint = TRUE;
}
/* Check and update the cursor position. */
if (cons->c_column < 0) cons->c_column = 0;
if (cons->c_column > scr_width) cons->c_column = scr_width;
if (cons->c_row < 0) cons->c_row = 0;
if (cons->c_row >= scr_lines) cons->c_row = scr_lines - 1;
cur = cons->c_org + cons->c_row * scr_width + cons->c_column;
if (cur != cons->c_cur)
UPDATE_CURSOR(cons, cur);
2005-04-21 16:53:53 +02:00
}
/*===========================================================================*
* parse_escape *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
static void parse_escape(cons, c)
2005-04-21 16:53:53 +02:00
register console_t *cons; /* pointer to console struct */
char c; /* next character in escape sequence */
{
/* The following ANSI escape sequences are currently supported.
* If n and/or m are omitted, they default to 1.
* ESC [nA moves up n lines
* ESC [nB moves down n lines
* ESC [nC moves right n spaces
* ESC [nD moves left n spaces
* ESC [m;nH" moves cursor to (m,n)
* ESC [J clears screen from cursor
* ESC [K clears line from cursor
* ESC [nL inserts n lines ar cursor
* ESC [nM deletes n lines at cursor
* ESC [nP deletes n chars at cursor
* ESC [n@ inserts n chars at cursor
* ESC [nm enables rendition n (0=normal, 4=bold, 5=blinking, 7=reverse)
* ESC M scrolls the screen backwards if the cursor is on the top line
*/
switch (cons->c_esc_state) {
case 1: /* ESC seen */
cons->c_esc_intro = '\0';
cons->c_esc_parmp = bufend(cons->c_esc_parmv);
do {
*--cons->c_esc_parmp = 0;
} while (cons->c_esc_parmp > cons->c_esc_parmv);
switch (c) {
case '[': /* Control Sequence Introducer */
cons->c_esc_intro = c;
cons->c_esc_state = 2;
break;
case 'M': /* Reverse Index */
do_escape(cons, c);
break;
default:
cons->c_esc_state = 0;
}
break;
case 2: /* ESC [ seen */
if (c >= '0' && c <= '9') {
if (cons->c_esc_parmp < bufend(cons->c_esc_parmv))
*cons->c_esc_parmp = *cons->c_esc_parmp * 10 + (c-'0');
} else
if (c == ';') {
if (cons->c_esc_parmp < bufend(cons->c_esc_parmv))
cons->c_esc_parmp++;
} else {
do_escape(cons, c);
}
break;
}
}
/*===========================================================================*
* do_escape *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
static void do_escape(cons, c)
2005-04-21 16:53:53 +02:00
register console_t *cons; /* pointer to console struct */
char c; /* next character in escape sequence */
{
int value, n;
unsigned src, dst, count;
int *parmp;
/* Some of these things hack on screen RAM, so it had better be up to date */
flush(cons);
if (cons->c_esc_intro == '\0') {
/* Handle a sequence beginning with just ESC */
switch (c) {
case 'M': /* Reverse Index */
if (cons->c_row == 0) {
scroll_screen(cons, SCROLL_DOWN);
} else {
cons->c_row--;
}
flush(cons);
break;
default: break;
}
} else
if (cons->c_esc_intro == '[') {
/* Handle a sequence beginning with ESC [ and parameters */
value = cons->c_esc_parmv[0];
switch (c) {
case 'A': /* ESC [nA moves up n lines */
n = (value == 0 ? 1 : value);
cons->c_row -= n;
flush(cons);
break;
case 'B': /* ESC [nB moves down n lines */
n = (value == 0 ? 1 : value);
cons->c_row += n;
flush(cons);
break;
case 'C': /* ESC [nC moves right n spaces */
n = (value == 0 ? 1 : value);
cons->c_column += n;
flush(cons);
break;
case 'D': /* ESC [nD moves left n spaces */
n = (value == 0 ? 1 : value);
cons->c_column -= n;
flush(cons);
break;
case 'H': /* ESC [m;nH" moves cursor to (m,n) */
cons->c_row = cons->c_esc_parmv[0] - 1;
cons->c_column = cons->c_esc_parmv[1] - 1;
flush(cons);
break;
case 'J': /* ESC [sJ clears in display */
switch (value) {
case 0: /* Clear from cursor to end of screen */
count = scr_size - (cons->c_cur - cons->c_org);
dst = cons->c_cur;
break;
case 1: /* Clear from start of screen to cursor */
count = cons->c_cur - cons->c_org;
dst = cons->c_org;
break;
case 2: /* Clear entire screen */
count = scr_size;
dst = cons->c_org;
break;
default: /* Do nothing */
count = 0;
dst = cons->c_org;
}
blank_color = cons->c_blank;
mem_vid_copy(BLANK_MEM, dst, count);
break;
case 'K': /* ESC [sK clears line from cursor */
switch (value) {
case 0: /* Clear from cursor to end of line */
count = scr_width - cons->c_column;
dst = cons->c_cur;
break;
case 1: /* Clear from beginning of line to cursor */
count = cons->c_column;
dst = cons->c_cur - cons->c_column;
break;
case 2: /* Clear entire line */
count = scr_width;
dst = cons->c_cur - cons->c_column;
break;
default: /* Do nothing */
count = 0;
dst = cons->c_cur;
}
blank_color = cons->c_blank;
mem_vid_copy(BLANK_MEM, dst, count);
break;
case 'L': /* ESC [nL inserts n lines at cursor */
n = value;
if (n < 1) n = 1;
if (n > (scr_lines - cons->c_row))
n = scr_lines - cons->c_row;
src = cons->c_org + cons->c_row * scr_width;
dst = src + n * scr_width;
count = (scr_lines - cons->c_row - n) * scr_width;
vid_vid_copy(src, dst, count);
blank_color = cons->c_blank;
mem_vid_copy(BLANK_MEM, src, n * scr_width);
break;
case 'M': /* ESC [nM deletes n lines at cursor */
n = value;
if (n < 1) n = 1;
if (n > (scr_lines - cons->c_row))
n = scr_lines - cons->c_row;
dst = cons->c_org + cons->c_row * scr_width;
src = dst + n * scr_width;
count = (scr_lines - cons->c_row - n) * scr_width;
vid_vid_copy(src, dst, count);
blank_color = cons->c_blank;
mem_vid_copy(BLANK_MEM, dst + count, n * scr_width);
break;
case '@': /* ESC [n@ inserts n chars at cursor */
n = value;
if (n < 1) n = 1;
if (n > (scr_width - cons->c_column))
n = scr_width - cons->c_column;
src = cons->c_cur;
dst = src + n;
count = scr_width - cons->c_column - n;
vid_vid_copy(src, dst, count);
blank_color = cons->c_blank;
mem_vid_copy(BLANK_MEM, src, n);
break;
case 'P': /* ESC [nP deletes n chars at cursor */
n = value;
if (n < 1) n = 1;
if (n > (scr_width - cons->c_column))
n = scr_width - cons->c_column;
dst = cons->c_cur;
src = dst + n;
count = scr_width - cons->c_column - n;
vid_vid_copy(src, dst, count);
blank_color = cons->c_blank;
mem_vid_copy(BLANK_MEM, dst + count, n);
break;
case 'm': /* ESC [nm enables rendition n */
for (parmp = cons->c_esc_parmv; parmp <= cons->c_esc_parmp
&& parmp < bufend(cons->c_esc_parmv); parmp++) {
if (cons->c_reverse) {
/* Unswap fg and bg colors */
cons->c_attr = ((cons->c_attr & 0x7000) >> 4) |
((cons->c_attr & 0x0700) << 4) |
((cons->c_attr & 0x8800));
}
switch (n = *parmp) {
case 0: /* NORMAL */
cons->c_attr = cons->c_blank = BLANK_COLOR;
cons->c_reverse = FALSE;
break;
case 1: /* BOLD */
/* Set intensity bit */
cons->c_attr |= 0x0800;
break;
case 4: /* UNDERLINE */
if (color) {
/* Change white to cyan, i.e. lose red
*/
cons->c_attr = (cons->c_attr & 0xBBFF);
} else {
/* Set underline attribute */
cons->c_attr = (cons->c_attr & 0x99FF);
}
break;
case 5: /* BLINKING */
/* Set the blink bit */
cons->c_attr |= 0x8000;
break;
case 7: /* REVERSE */
cons->c_reverse = TRUE;
break;
default: /* COLOR */
if (n == 39) n = 37; /* set default color */
if (n == 49) n = 40;
if (!color) {
/* Don't mess up a monochrome screen */
} else
if (30 <= n && n <= 37) {
/* Foreground color */
cons->c_attr =
(cons->c_attr & 0xF8FF) |
(ansi_colors[(n - 30)] << 8);
cons->c_blank =
(cons->c_blank & 0xF8FF) |
(ansi_colors[(n - 30)] << 8);
} else
if (40 <= n && n <= 47) {
/* Background color */
cons->c_attr =
(cons->c_attr & 0x8FFF) |
(ansi_colors[(n - 40)] << 12);
cons->c_blank =
(cons->c_blank & 0x8FFF) |
(ansi_colors[(n - 40)] << 12);
}
}
if (cons->c_reverse) {
/* Swap fg and bg colors */
cons->c_attr = ((cons->c_attr & 0x7000) >> 4) |
((cons->c_attr & 0x0700) << 4) |
((cons->c_attr & 0x8800));
}
}
break;
}
}
cons->c_esc_state = 0;
}
/*===========================================================================*
* set_6845 *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
static void set_6845(reg, val)
2005-04-21 16:53:53 +02:00
int reg; /* which register pair to set */
unsigned val; /* 16-bit value to set it to */
{
/* Set a register pair inside the 6845.
* Registers 12-13 tell the 6845 where in video ram to start
* Registers 14-15 tell the 6845 where to put the cursor
*/
pvb_pair_t char_out[4];
pv_set(char_out[0], vid_port + INDEX, reg); /* set index register */
pv_set(char_out[1], vid_port + DATA, (val>>8) & BYTE); /* high byte */
pv_set(char_out[2], vid_port + INDEX, reg + 1); /* again */
pv_set(char_out[3], vid_port + DATA, val&BYTE); /* low byte */
sys_voutb(char_out, 4); /* do actual output */
}
#if 0
/*===========================================================================*
* get_6845 *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
static void get_6845(reg, val)
int reg; /* which register pair to set */
unsigned *val; /* 16-bit value to set it to */
{
char v1, v2;
2012-03-05 00:11:41 +01:00
u32_t v;
/* Get a register pair inside the 6845. */
sys_outb(vid_port + INDEX, reg);
2006-03-24 15:16:13 +01:00
sys_inb(vid_port + DATA, &v);
v1 = v;
sys_outb(vid_port + INDEX, reg+1);
2006-03-24 15:16:13 +01:00
sys_inb(vid_port + DATA, &v);
v2 = v;
*val = (v1 << 8) | v2;
}
#endif
2005-04-21 16:53:53 +02:00
/*===========================================================================*
* beep *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
static void beep()
2005-04-21 16:53:53 +02:00
{
/* Making a beeping sound on the speaker (output for CRTL-G).
* This routine works by turning on the bits 0 and 1 in port B of the 8255
* chip that drive the speaker.
*/
static timer_t tmr_stop_beep;
pvb_pair_t char_out[3];
2012-03-05 00:11:41 +01:00
u32_t port_b_val;
2005-04-21 16:53:53 +02:00
2010-07-09 14:58:18 +02:00
/* Set timer in advance to prevent beeping delay. */
set_timer(&tmr_stop_beep, B_TIME, stop_beep, 0);
2005-04-21 16:53:53 +02:00
if (!beeping) {
/* Set timer channel 2, square wave, with given frequency. */
pv_set(char_out[0], TIMER_MODE, 0xB6);
pv_set(char_out[1], TIMER2, (BEEP_FREQ >> 0) & BYTE);
pv_set(char_out[2], TIMER2, (BEEP_FREQ >> 8) & BYTE);
if (sys_voutb(char_out, 3)==OK) {
if (sys_inb(PORT_B, &port_b_val)==OK &&
sys_outb(PORT_B, (port_b_val|3))==OK)
beeping = TRUE;
}
}
}
/*===========================================================================*
* do_video *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
void do_video(message *m)
{
int r;
/* Execute the requested device driver function. */
r= EINVAL; /* just in case */
switch (m->m_type) {
case DEV_OPEN:
/* Should grant IOPL */
disable_console();
r= OK;
break;
case DEV_CLOSE:
reenable_console();
r= OK;
break;
case DEV_IOCTL_S:
switch(m->TTY_REQUEST) {
case TIOCMAPMEM:
case TIOCUNMAPMEM: {
int r, do_map;
struct mapreqvm mapreqvm;
do_map= (m->REQUEST == TIOCMAPMEM); /* else unmap */
r = sys_safecopyfrom(m->m_source,
(cp_grant_id_t) m->IO_GRANT, 0,
(vir_bytes) &mapreqvm, sizeof(mapreqvm));
if (r != OK)
{
printf("tty: sys_safecopyfrom failed\n");
tty_reply(TASK_REPLY, m->m_source,
m->USER_ENDPT, r);
return;
}
/* In safe ioctl mode, the POSITION field contains
* the endpt number of the original requestor.
* USER_ENDPT is always FS.
*/
if(do_map) {
mapreqvm.vaddr_ret = vm_map_phys(m->POSITION,
(void *) mapreqvm.phys_offset, mapreqvm.size);
if((r = sys_safecopyto(m->m_source,
(cp_grant_id_t) m->IO_GRANT, 0,
(vir_bytes) &mapreqvm,
sizeof(mapreqvm))) != OK) {
printf("tty: sys_safecopyto failed\n");
}
} else {
r = vm_unmap_phys(m->POSITION,
mapreqvm.vaddr, mapreqvm.size);
}
tty_reply(TASK_REPLY, m->m_source, m->USER_ENDPT, r);
return;
}
}
r= ENOTTY;
break;
default:
printf(
"Warning, TTY(video) got unexpected request %d from %d\n",
m->m_type, m->m_source);
r= EINVAL;
}
tty_reply(TASK_REPLY, m->m_source, m->USER_ENDPT, r);
}
/*===========================================================================*
* beep_x *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
void beep_x(freq, dur)
unsigned freq;
clock_t dur;
{
/* Making a beeping sound on the speaker.
* This routine works by turning on the bits 0 and 1 in port B of the 8255
* chip that drive the speaker.
*/
static timer_t tmr_stop_beep;
pvb_pair_t char_out[3];
2012-03-05 00:11:41 +01:00
u32_t port_b_val;
unsigned long ival= TIMER_FREQ / freq;
if (ival == 0 || ival > 0xffff)
return; /* Frequency out of range */
2010-07-09 14:58:18 +02:00
/* Set timer in advance to prevent beeping delay. */
set_timer(&tmr_stop_beep, dur, stop_beep, 0);
if (!beeping) {
/* Set timer channel 2, square wave, with given frequency. */
pv_set(char_out[0], TIMER_MODE, 0xB6);
pv_set(char_out[1], TIMER2, (ival >> 0) & BYTE);
pv_set(char_out[2], TIMER2, (ival >> 8) & BYTE);
if (sys_voutb(char_out, 3)==OK) {
if (sys_inb(PORT_B, &port_b_val)==OK &&
sys_outb(PORT_B, (port_b_val|3))==OK)
beeping = TRUE;
}
}
}
2005-04-21 16:53:53 +02:00
/*===========================================================================*
* stop_beep *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
static void stop_beep(timer_t *UNUSED(tmrp))
2005-04-21 16:53:53 +02:00
{
/* Turn off the beeper by turning off bits 0 and 1 in PORT_B. */
2012-03-05 00:11:41 +01:00
u32_t port_b_val;
2005-04-21 16:53:53 +02:00
if (sys_inb(PORT_B, &port_b_val)==OK &&
sys_outb(PORT_B, (port_b_val & ~3))==OK)
beeping = FALSE;
}
/*===========================================================================*
* scr_init *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
void scr_init(tp)
2005-04-21 16:53:53 +02:00
tty_t *tp;
{
/* Initialize the screen driver. */
console_t *cons;
u16_t bios_columns, bios_crtbase, bios_fontlines;
u8_t bios_rows;
int line;
int s;
static int vdu_initialized = 0;
static unsigned page_size;
2005-04-21 16:53:53 +02:00
/* Associate console and TTY. */
line = tp - &tty_table[0];
if (line >= nr_cons) return;
cons = &cons_table[line];
cons->c_tty = tp;
cons->c_line = line;
2005-04-21 16:53:53 +02:00
tp->tty_priv = cons;
/* Fill in TTY function hooks. */
tp->tty_devwrite = cons_write;
tp->tty_echo = cons_echo;
tp->tty_ioctl = cons_ioctl;
/* Get the BIOS parameters that describe the VDU. */
if (! vdu_initialized++) {
/* FIXME: How about error checking? What to do on failure??? */
s=sys_readbios(VDU_SCREEN_COLS_ADDR, &bios_columns,
VDU_SCREEN_COLS_SIZE);
s=sys_readbios(VDU_CRT_BASE_ADDR, &bios_crtbase,
VDU_CRT_BASE_SIZE);
s=sys_readbios( VDU_SCREEN_ROWS_ADDR, &bios_rows,
VDU_SCREEN_ROWS_SIZE);
s=sys_readbios(VDU_FONTLINES_ADDR, &bios_fontlines,
VDU_FONTLINES_SIZE);
2005-04-21 16:53:53 +02:00
vid_port = bios_crtbase;
scr_width = bios_columns;
font_lines = bios_fontlines;
2011-09-16 20:03:15 +02:00
scr_lines = bios_rows+1;
2005-04-21 16:53:53 +02:00
if (color) {
vid_base = COLOR_BASE;
vid_size = COLOR_SIZE;
} else {
vid_base = MONO_BASE;
vid_size = MONO_SIZE;
}
2011-09-16 20:03:15 +02:00
vid_size = EGA_SIZE;
wrap = 0;
2005-04-21 16:53:53 +02:00
console_memory = vm_map_phys(SELF, (void *) vid_base, vid_size);
if(console_memory == MAP_FAILED)
panic("Console couldn't map video memory");
2005-04-21 16:53:53 +02:00
font_memory = vm_map_phys(SELF, (void *)GA_VIDEO_ADDRESS, GA_FONT_SIZE);
if(font_memory == MAP_FAILED)
panic("Console couldn't map font memory");
2005-04-21 16:53:53 +02:00
vid_size >>= 1; /* word count */
vid_mask = vid_size - 1;
/* Size of the screen (number of displayed characters.) */
scr_size = scr_lines * scr_width;
/* There can be as many consoles as video memory allows. */
nr_cons = vid_size / scr_size;
2005-04-21 16:53:53 +02:00
if (nr_cons > NR_CONS) nr_cons = NR_CONS;
if (nr_cons > 1) wrap = 0;
No more intel/minix segments. This commit removes all traces of Minix segments (the text/data/stack memory map abstraction in the kernel) and significance of Intel segments (hardware segments like CS, DS that add offsets to all addressing before page table translation). This ultimately simplifies the memory layout and addressing and makes the same layout possible on non-Intel architectures. There are only two types of addresses in the world now: virtual and physical; even the kernel and processes have the same virtual address space. Kernel and user processes can be distinguished at a glance as processes won't use 0xF0000000 and above. No static pre-allocated memory sizes exist any more. Changes to booting: . The pre_init.c leaves the kernel and modules exactly as they were left by the bootloader in physical memory . The kernel starts running using physical addressing, loaded at a fixed location given in its linker script by the bootloader. All code and data in this phase are linked to this fixed low location. . It makes a bootstrap pagetable to map itself to a fixed high location (also in linker script) and jumps to the high address. All code and data then use this high addressing. . All code/data symbols linked at the low addresses is prefixed by an objcopy step with __k_unpaged_*, so that that code cannot reference highly-linked symbols (which aren't valid yet) or vice versa (symbols that aren't valid any more). . The two addressing modes are separated in the linker script by collecting the unpaged_*.o objects and linking them with low addresses, and linking the rest high. Some objects are linked twice, once low and once high. . The bootstrap phase passes a lot of information (e.g. free memory list, physical location of the modules, etc.) using the kinfo struct. . After this bootstrap the low-linked part is freed. . The kernel maps in VM into the bootstrap page table so that VM can begin executing. Its first job is to make page tables for all other boot processes. So VM runs before RS, and RS gets a fully dynamic, VM-managed address space. VM gets its privilege info from RS as usual but that happens after RS starts running. . Both the kernel loading VM and VM organizing boot processes happen using the libexec logic. This removes the last reason for VM to still know much about exec() and vm/exec.c is gone. Further Implementation: . All segments are based at 0 and have a 4 GB limit. . The kernel is mapped in at the top of the virtual address space so as not to constrain the user processes. . Processes do not use segments from the LDT at all; there are no segments in the LDT any more, so no LLDT is needed. . The Minix segments T/D/S are gone and so none of the user-space or in-kernel copy functions use them. The copy functions use a process endpoint of NONE to realize it's a physical address, virtual otherwise. . The umap call only makes sense to translate a virtual address to a physical address now. . Segments-related calls like newmap and alloc_segments are gone. . All segments-related translation in VM is gone (vir2map etc). . Initialization in VM is simpler as no moving around is necessary. . VM and all other boot processes can be linked wherever they wish and will be mapped in at the right location by the kernel and VM respectively. Other changes: . The multiboot code is less special: it does not use mb_print for its diagnostics any more but uses printf() as normal, saving the output into the diagnostics buffer, only printing to the screen using the direct print functions if a panic() occurs. . The multiboot code uses the flexible 'free memory map list' style to receive the list of free memory if available. . The kernel determines the memory layout of the processes to a degree: it tells VM where the kernel starts and ends and where the kernel wants the top of the process to be. VM then uses this entire range, i.e. the stack is right at the top, and mmap()ped bits of memory are placed below that downwards, and the break grows upwards. Other Consequences: . Every process gets its own page table as address spaces can't be separated any more by segments. . As all segments are 0-based, there is no distinction between virtual and linear addresses, nor between userspace and kernel addresses. . Less work is done when context switching, leading to a net performance increase. (8% faster on my machine for 'make servers'.) . The layout and configuration of the GDT makes sysenter and syscall possible.
2012-05-07 16:03:35 +02:00
if (nr_cons < 1) panic("no consoles");
2005-04-21 16:53:53 +02:00
page_size = vid_size / nr_cons;
}
cons->c_start = line * page_size;
cons->c_limit = cons->c_start + page_size;
cons->c_cur = cons->c_org = cons->c_start;
cons->c_attr = cons->c_blank = BLANK_COLOR;
2005-09-11 19:09:11 +02:00
if (line != 0) {
/* Clear the non-console vtys. */
blank_color = BLANK_COLOR;
mem_vid_copy(BLANK_MEM, cons->c_start, scr_size);
} else {
/* Set the cursor of the console vty at the bottom. c_cur
* is updated automatically later.
*/
scroll_screen(cons, SCROLL_UP);
cons->c_row = scr_lines - 1;
cons->c_column = 0;
}
2005-04-21 16:53:53 +02:00
select_console(0);
cons_ioctl(tp, 0);
2005-04-21 16:53:53 +02:00
}
extern struct minix_kerninfo *_minix_kerninfo;
2005-09-11 19:09:11 +02:00
/*===========================================================================*
* do_new_kmess *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
void do_new_kmess()
2005-04-21 16:53:53 +02:00
{
/* Notification for a new kernel message. */
struct kmessages *kmess_ptr; /* kmessages structure */
2005-04-21 16:53:53 +02:00
static int prev_next = 0; /* previous next seen */
int bytes;
int r;
assert(_minix_kerninfo);
kmess_ptr = _minix_kerninfo->kmessages;
2005-04-21 16:53:53 +02:00
/* Print only the new part. Determine how many new bytes there are with
* help of the current and previous 'next' index. Note that the kernel
* buffer is circular. This works fine if less then _KMESS_BUF_SIZE bytes
* is new data; else we miss % _KMESS_BUF_SIZE here.
2005-04-21 16:53:53 +02:00
* Check for size being positive, the buffer might as well be emptied!
*/
if (kmess_ptr->km_size > 0) {
bytes = ((kmess_ptr->km_next + _KMESS_BUF_SIZE) - prev_next) % _KMESS_BUF_SIZE;
2005-04-21 16:53:53 +02:00
r=prev_next; /* start at previous old */
while (bytes > 0) {
cons_putk( kmess_ptr->km_buf[(r%_KMESS_BUF_SIZE)] );
2005-04-21 16:53:53 +02:00
bytes --;
r ++;
}
cons_putk(0); /* terminate to flush output */
2005-04-21 16:53:53 +02:00
}
/* Almost done, store 'next' so that we can determine what part of the
* kernel messages buffer to print next time a notification arrives.
*/
prev_next = kmess_ptr->km_next;
2005-04-21 16:53:53 +02:00
}
/*===========================================================================*
* cons_putk *
2005-04-21 16:53:53 +02:00
*===========================================================================*/
2012-03-25 20:25:53 +02:00
static void cons_putk(c)
2005-04-21 16:53:53 +02:00
int c; /* character to print */
{
/* This procedure is used to print a character on the console.
2005-04-21 16:53:53 +02:00
*/
if (c != 0) {
if (c == '\n') cons_putk('\r');
2005-04-21 16:53:53 +02:00
out_char(&cons_table[0], (int) c);
#if 0
ser_putc(c);
#endif
2005-04-21 16:53:53 +02:00
} else {
flush(&cons_table[0]);
}
}
/*===========================================================================*
* toggle_scroll *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
void toggle_scroll()
2005-04-21 16:53:53 +02:00
{
/* Toggle between hardware and software scroll. */
cons_org0();
softscroll = !softscroll;
printf("%sware scrolling enabled.\n", softscroll ? "Soft" : "Hard");
}
/*===========================================================================*
* cons_stop *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
void cons_stop()
2005-04-21 16:53:53 +02:00
{
/* Prepare for halt or reboot. */
cons_org0();
softscroll = 1;
select_console(0);
2005-04-21 16:53:53 +02:00
cons_table[0].c_attr = cons_table[0].c_blank = BLANK_COLOR;
shutting_down = TRUE;
2005-04-21 16:53:53 +02:00
}
/*===========================================================================*
* cons_org0 *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
static void cons_org0()
2005-04-21 16:53:53 +02:00
{
/* Scroll video memory back to put the origin at 0. */
int cons_line;
console_t *cons;
unsigned n;
for (cons_line = 0; cons_line < nr_cons; cons_line++) {
cons = &cons_table[cons_line];
while (cons->c_org > cons->c_start) {
n = vid_size - scr_size; /* amount of unused memory */
if (n > cons->c_org - cons->c_start)
n = cons->c_org - cons->c_start;
vid_vid_copy(cons->c_org, cons->c_org - n, scr_size);
UPDATE_ORIGIN(cons, cons->c_org - n);
2005-04-21 16:53:53 +02:00
}
flush(cons);
}
select_console(ccurrent);
}
/*===========================================================================*
* disable_console *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
static void disable_console()
{
if (disabled_vc != -1)
return;
disabled_vc = ccurrent;
disabled_sm = softscroll;
cons_org0();
softscroll = 1;
select_console(0);
/* Should also disable further output to virtual consoles */
}
/*===========================================================================*
* reenable_console *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
static void reenable_console()
{
if (disabled_vc == -1)
return;
softscroll = disabled_sm;
select_console(disabled_vc);
disabled_vc = -1;
}
2005-04-21 16:53:53 +02:00
/*===========================================================================*
* select_console *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
void select_console(int cons_line)
2005-04-21 16:53:53 +02:00
{
/* Set the current console to console number 'cons_line'. */
if (shutting_down) return;
2005-04-21 16:53:53 +02:00
if (cons_line < 0 || cons_line >= nr_cons) return;
2005-04-21 16:53:53 +02:00
ccurrent = cons_line;
curcons = &cons_table[cons_line];
UPDATE_CURSOR(curcons, curcons->c_cur);
UPDATE_ORIGIN(curcons, curcons->c_org);
2005-04-21 16:53:53 +02:00
}
/*===========================================================================*
* con_loadfont *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
int con_loadfont(m)
2005-04-21 16:53:53 +02:00
message *m;
{
2005-04-21 16:53:53 +02:00
/* Load a font into the EGA or VGA adapter. */
int result;
static struct sequence seq1[7] = {
{ GA_SEQUENCER_INDEX, 0x00, 0x01 },
{ GA_SEQUENCER_INDEX, 0x02, 0x04 },
{ GA_SEQUENCER_INDEX, 0x04, 0x07 },
{ GA_SEQUENCER_INDEX, 0x00, 0x03 },
{ GA_GRAPHICS_INDEX, 0x04, 0x02 },
{ GA_GRAPHICS_INDEX, 0x05, 0x00 },
{ GA_GRAPHICS_INDEX, 0x06, 0x00 },
};
static struct sequence seq2[7] = {
{ GA_SEQUENCER_INDEX, 0x00, 0x01 },
{ GA_SEQUENCER_INDEX, 0x02, 0x03 },
{ GA_SEQUENCER_INDEX, 0x04, 0x03 },
{ GA_SEQUENCER_INDEX, 0x00, 0x03 },
{ GA_GRAPHICS_INDEX, 0x04, 0x00 },
{ GA_GRAPHICS_INDEX, 0x05, 0x10 },
{ GA_GRAPHICS_INDEX, 0x06, 0 },
};
seq2[6].value= color ? 0x0E : 0x0A;
result = ga_program(seq1); /* bring font memory into view */
if(sys_safecopyfrom(m->m_source, (cp_grant_id_t) m->IO_GRANT, 0,
(vir_bytes) font_memory, GA_FONT_SIZE) != OK) {
printf("tty: copying from %d failed\n", m->m_source);
return EFAULT;
}
2005-04-21 16:53:53 +02:00
result = ga_program(seq2); /* restore */
return(result);
}
/*===========================================================================*
* ga_program *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
static int ga_program(seq)
2005-04-21 16:53:53 +02:00
struct sequence *seq;
{
pvb_pair_t char_out[14];
int i;
for (i=0; i<7; i++) {
pv_set(char_out[2*i], seq->index, seq->port);
pv_set(char_out[2*i+1], seq->index+1, seq->value);
seq++;
}
return sys_voutb(char_out, 14);
}
/*===========================================================================*
* cons_ioctl *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
static int cons_ioctl(tty_t *tp, int UNUSED(try))
2005-04-21 16:53:53 +02:00
{
/* Set the screen dimensions. */
tp->tty_winsize.ws_row= scr_lines;
tp->tty_winsize.ws_col= scr_width;
tp->tty_winsize.ws_xpixel= scr_width * 8;
tp->tty_winsize.ws_ypixel= scr_lines * font_lines;
return 0;
2005-04-21 16:53:53 +02:00
}
#define LIMITINDEX(mask, start, size, ct) { \
int countlimit = size - start; \
start &= mask; \
if(ct > countlimit) ct = countlimit; \
}
/*===========================================================================*
* mem_vid_copy *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
static void mem_vid_copy(vir_bytes src, int dst_index, int count)
{
u16_t *src_mem = (u16_t *) src;
while(count > 0) {
int i, subcount = count;
u16_t *dst_mem;
LIMITINDEX(vid_mask, dst_index, vid_size, subcount);
dst_mem = (u16_t *) console_memory + dst_index;
if(!src)
for(i = 0; i < subcount; i++)
*dst_mem++ = blank_color;
else
for(i = 0; i < subcount; i++)
*dst_mem++ = *src_mem++;
count -= subcount;
dst_index += subcount;
}
}
/*===========================================================================*
* vid_vid_copy *
*===========================================================================*/
2012-03-25 20:25:53 +02:00
static void vid_vid_copy(int src_index, int dst_index, int count)
{
int backwards = 0;
if(src_index < dst_index)
backwards = 1;
while(count > 0) {
int i, subcount = count;
u16_t *dst_mem, *src_mem;
LIMITINDEX(vid_mask, src_index, vid_size, subcount);
LIMITINDEX(vid_mask, dst_index, vid_size, subcount);
src_mem = (u16_t *) console_memory + src_index;
dst_mem = (u16_t *) console_memory + dst_index;
if(backwards) {
src_mem += subcount - 1;
dst_mem += subcount - 1;
for(i = 0; i < subcount; i++)
*dst_mem-- = *src_mem--;
} else {
for(i = 0; i < subcount; i++)
*dst_mem++ = *src_mem++;
}
count -= subcount;
dst_index += subcount;
src_index += subcount;
}
}