minix/drivers/tty/console.c

1267 lines
39 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 "../drivers.h"
#include <termios.h>
#include <sys/ioctl.h>
#include <sys/vm.h>
2005-04-21 16:53:53 +02:00
#include <minix/callnr.h>
#include <minix/com.h>
#include "tty.h"
#include "../../kernel/const.h"
#include "../../kernel/config.h"
#include "../../kernel/type.h"
2005-04-21 16:53:53 +02:00
/* Definitions used by the console driver. */
#define MONO_BASE 0xB0000L /* base of mono video memory */
#define COLOR_BASE 0xB8000L /* base of color video memory */
#define MONO_SIZE 0x1000 /* 4K mono video memory */
#define COLOR_SIZE 0x4000 /* 16K color video memory */
#define EGA_SIZE 0x8000 /* EGA & VGA have at least 32K */
#define BLANK_COLOR 0x0700 /* determines cursor color on blank screen */
#define SCROLL_UP 0 /* scroll forward */
#define SCROLL_DOWN 1 /* scroll backward */
#define BLANK_MEM ((u16_t *) 0) /* tells mem_vid_copy() to blank the screen */
#define CONS_RAM_WORDS 80 /* video ram buffer size */
#define MAX_ESC_PARMS 4 /* number of escape sequence params allowed */
/* Constants relating to the controller chips. */
#define M_6845 0x3B4 /* port for 6845 mono */
#define C_6845 0x3D4 /* port for 6845 color */
#define INDEX 0 /* 6845's index register */
#define DATA 1 /* 6845's data register */
#define STATUS 6 /* 6845's status register */
2005-04-21 16:53:53 +02:00
#define VID_ORG 12 /* 6845's origin register */
#define CURSOR 14 /* 6845's cursor register */
/* 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
/* Beeper. */
#define BEEP_FREQ 0x0533 /* value to put into timer to set beep freq */
#define B_TIME 3 /* length of CTRL-G beep is ticks */
/* definitions used for font management */
#define GA_SEQUENCER_INDEX 0x3C4
#define GA_SEQUENCER_DATA 0x3C5
#define GA_GRAPHICS_INDEX 0x3CE
#define GA_GRAPHICS_DATA 0x3CF
#define GA_VIDEO_ADDRESS 0xA0000L
#define GA_FONT_SIZE 8192
/* Global variables used by the console driver and assembly support. */
2005-04-29 17:36:43 +02:00
PUBLIC int vid_index; /* index of video segment in remote mem map */
2005-04-21 16:53:53 +02:00
PUBLIC u16_t vid_seg;
PUBLIC vir_bytes vid_off; /* video ram is found at vid_seg:vid_off */
PUBLIC unsigned vid_size; /* 0x2000 for color or 0x0800 for mono */
PUBLIC unsigned vid_mask; /* 0x1FFF for color or 0x07FF for mono */
PUBLIC unsigned blank_color = BLANK_COLOR; /* display code for blank */
/* Private variables used by the console driver. */
PRIVATE int vid_port; /* I/O port for accessing 6845 */
PRIVATE int wrap; /* hardware can wrap? */
PRIVATE int softscroll; /* 1 = software scrolling, 0 = hardware */
PRIVATE int beeping; /* speaker is beeping? */
PRIVATE unsigned font_lines; /* font lines per character */
PRIVATE unsigned scr_width; /* # characters on a line */
PRIVATE unsigned scr_lines; /* # lines on the screen */
PRIVATE unsigned scr_size; /* # characters on the screen */
/* 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 */
} console_t;
PRIVATE int nr_cons= 1; /* actual number of consoles */
PRIVATE console_t cons_table[NR_CONS];
PRIVATE console_t *curcons; /* currently visible */
/* Color if using a color controller. */
#define color (vid_port == C_6845)
/* Map from ANSI colors to the attributes used by the PC */
PRIVATE int ansi_colors[8] = {0, 4, 2, 6, 1, 5, 3, 7};
/* Structure used for font management */
struct sequence {
unsigned short index;
unsigned char port;
unsigned char value;
};
2005-09-11 19:09:11 +02:00
FORWARD _PROTOTYPE( int cons_write, (struct tty *tp, int try) );
2005-04-21 16:53:53 +02:00
FORWARD _PROTOTYPE( void cons_echo, (tty_t *tp, int c) );
FORWARD _PROTOTYPE( void out_char, (console_t *cons, int c) );
FORWARD _PROTOTYPE( void cons_putk, (int c) );
2005-04-21 16:53:53 +02:00
FORWARD _PROTOTYPE( void beep, (void) );
FORWARD _PROTOTYPE( void do_escape, (console_t *cons, int c) );
FORWARD _PROTOTYPE( void flush, (console_t *cons) );
FORWARD _PROTOTYPE( void parse_escape, (console_t *cons, int c) );
FORWARD _PROTOTYPE( void scroll_screen, (console_t *cons, int dir) );
FORWARD _PROTOTYPE( void set_6845, (int reg, unsigned val) );
FORWARD _PROTOTYPE( void get_6845, (int reg, unsigned *val) );
2005-09-11 19:09:11 +02:00
FORWARD _PROTOTYPE( void stop_beep, (timer_t *tmrp) );
2005-04-21 16:53:53 +02:00
FORWARD _PROTOTYPE( void cons_org0, (void) );
FORWARD _PROTOTYPE( int ga_program, (struct sequence *seq) );
FORWARD _PROTOTYPE( int cons_ioctl, (tty_t *tp, int) );
PRIVATE _PROTOTYPE( void ser_putc, (char c) );
2005-04-21 16:53:53 +02:00
/*===========================================================================*
* cons_write *
*===========================================================================*/
PRIVATE 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;
/* 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_vircopy(tp->tty_outproc, D, tp->tty_out_vir,
SELF, D, (vir_bytes) buf, (vir_bytes) count)) != OK)
break;
tbuf = buf;
/* Update terminal data structure. */
tp->tty_out_vir += count;
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 {
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) {
/* REVIVE is not possible. I/O on memory mapped consoles finishes. */
2005-04-21 16:53:53 +02:00
tty_reply(tp->tty_outrepcode, tp->tty_outcaller, tp->tty_outproc,
tp->tty_outcum);
tp->tty_outcum = 0;
}
}
/*===========================================================================*
* cons_echo *
*===========================================================================*/
PRIVATE void cons_echo(tp, c)
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 *
*===========================================================================*/
PRIVATE void out_char(cons, c)
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;
}
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 *
*===========================================================================*/
PRIVATE void scroll_screen(cons, dir)
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);
cons->c_org = cons->c_start;
} else {
cons->c_org = (cons->c_org + scr_width) & vid_mask;
}
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);
cons->c_org = new_org;
} else {
cons->c_org = (cons->c_org - scr_width) & vid_mask;
}
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);
/* Set the new video origin. */
if (cons == curcons) set_6845(VID_ORG, cons->c_org);
flush(cons);
}
/*===========================================================================*
* flush *
*===========================================================================*/
PRIVATE void flush(cons)
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(cons->c_ramqueue, cons->c_cur, cons->c_rwords);
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) {
if (cons == curcons) set_6845(CURSOR, cur);
cons->c_cur = cur;
}
}
/*===========================================================================*
* parse_escape *
*===========================================================================*/
PRIVATE void parse_escape(cons, c)
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 *
*===========================================================================*/
PRIVATE void do_escape(cons, c)
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 *
*===========================================================================*/
PRIVATE void set_6845(reg, val)
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 */
}
/*===========================================================================*
* get_6845 *
*===========================================================================*/
PRIVATE void get_6845(reg, val)
int reg; /* which register pair to set */
unsigned *val; /* 16-bit value to set it to */
{
char v1, v2;
/* Get a register pair inside the 6845. */
sys_outb(vid_port + INDEX, reg);
sys_inb(vid_port + DATA, &v1);
sys_outb(vid_port + INDEX, reg+1);
sys_inb(vid_port + DATA, &v2);
*val = (v1 << 8) | v2;
}
2005-04-21 16:53:53 +02:00
/*===========================================================================*
* beep *
*===========================================================================*/
PRIVATE void beep()
{
/* 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];
clock_t now;
int port_b_val, s;
/* Fetch current time in advance to prevent beeping delay. */
if ((s=getuptime(&now)) != OK)
panic("TTY","Console couldn't get clock's uptime.", s);
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;
}
}
/* Add a timer to the timers list. Possibly reschedule the alarm. */
tmrs_settimer(&tty_timers, &tmr_stop_beep, now+B_TIME, stop_beep, NULL);
2005-04-21 16:53:53 +02:00
if (tty_timers->tmr_exp_time != tty_next_timeout) {
tty_next_timeout = tty_timers->tmr_exp_time;
if ((s=sys_setalarm(tty_next_timeout, 1)) != OK)
panic("TTY","Console couldn't set alarm.", s);
2005-04-21 16:53:53 +02:00
}
}
/*===========================================================================*
* do_video *
*===========================================================================*/
PUBLIC void do_video(message *m)
{
int i, n, r, ops, watch;
unsigned char c;
/* Execute the requested device driver function. */
r= EINVAL; /* just in case */
switch (m->m_type) {
case DEV_OPEN:
/* Should grant IOPL */
r= OK;
break;
case DEV_CLOSE:
r= OK;
break;
case DEV_IOCTL:
if (m->TTY_REQUEST == MIOCMAP || m->TTY_REQUEST == MIOCUNMAP)
{
int r, do_map;
struct mapreq mapreq;
do_map= (m->REQUEST == MIOCMAP); /* else unmap */
/* Get request structure */
r= sys_vircopy(m->IO_ENDPT, D,
(vir_bytes)m->ADDRESS,
SELF, D, (vir_bytes)&mapreq, sizeof(mapreq));
if (r != OK)
{
tty_reply(TASK_REPLY, m->m_source, m->IO_ENDPT,
r);
return;
}
r= sys_vm_map(m->IO_ENDPT, do_map,
(phys_bytes)mapreq.base, mapreq.size,
mapreq.offset);
tty_reply(TASK_REPLY, m->m_source, m->IO_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->IO_ENDPT, r);
}
/*===========================================================================*
* beep_x *
*===========================================================================*/
PUBLIC 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];
clock_t now;
int port_b_val, s;
unsigned long ival= TIMER_FREQ / freq;
if (ival == 0 || ival > 0xffff)
return; /* Frequency out of range */
/* Fetch current time in advance to prevent beeping delay. */
if ((s=getuptime(&now)) != OK)
panic("TTY","Console couldn't get clock's uptime.", s);
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;
}
}
/* Add a timer to the timers list. Possibly reschedule the alarm. */
tmrs_settimer(&tty_timers, &tmr_stop_beep, now+dur, stop_beep, NULL);
if (tty_timers->tmr_exp_time != tty_next_timeout) {
tty_next_timeout = tty_timers->tmr_exp_time;
if ((s=sys_setalarm(tty_next_timeout, 1)) != OK)
panic("TTY","Console couldn't set alarm.", s);
}
}
2005-04-21 16:53:53 +02:00
/*===========================================================================*
* stop_beep *
*===========================================================================*/
PRIVATE void stop_beep(tmrp)
timer_t *tmrp;
{
/* Turn off the beeper by turning off bits 0 and 1 in PORT_B. */
int port_b_val;
if (sys_inb(PORT_B, &port_b_val)==OK &&
sys_outb(PORT_B, (port_b_val & ~3))==OK)
beeping = FALSE;
}
/*===========================================================================*
* scr_init *
*===========================================================================*/
PUBLIC void scr_init(tp)
tty_t *tp;
{
/* Initialize the screen driver. */
console_t *cons;
phys_bytes vid_base;
u16_t bios_columns, bios_crtbase, bios_fontlines;
u8_t bios_rows;
int line;
int s;
static int vdu_initialized = 0;
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;
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++) {
/* How about error checking? What to do on failure??? */
2005-04-29 17:36:43 +02:00
s=sys_vircopy(SELF, BIOS_SEG, (vir_bytes) VDU_SCREEN_COLS_ADDR,
SELF, D, (vir_bytes) &bios_columns, VDU_SCREEN_COLS_SIZE);
s=sys_vircopy(SELF, BIOS_SEG, (vir_bytes) VDU_CRT_BASE_ADDR,
SELF, D, (vir_bytes) &bios_crtbase, VDU_CRT_BASE_SIZE);
s=sys_vircopy(SELF, BIOS_SEG, (vir_bytes) VDU_SCREEN_ROWS_ADDR,
SELF, D, (vir_bytes) &bios_rows, VDU_SCREEN_ROWS_SIZE);
s=sys_vircopy(SELF, BIOS_SEG, (vir_bytes) VDU_FONTLINES_ADDR,
SELF, D, (vir_bytes) &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;
2005-04-29 17:36:43 +02:00
scr_lines = machine.vdu_ega ? bios_rows+1 : 25;
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;
}
2005-04-29 17:36:43 +02:00
if (machine.vdu_ega) vid_size = EGA_SIZE;
wrap = ! machine.vdu_ega;
2005-04-21 16:53:53 +02:00
2005-04-29 17:36:43 +02:00
s = sys_segctl(&vid_index, &vid_seg, &vid_off, vid_base, vid_size);
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;
if (nr_cons > NR_CONS) nr_cons = NR_CONS;
if (nr_cons > 1) wrap = 0;
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 {
int i, n;
/* 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
}
2005-09-11 19:09:11 +02:00
/*===========================================================================*
* kputc *
*===========================================================================*/
2005-04-21 16:53:53 +02:00
PUBLIC void kputc(c)
int c;
{
/* Accumulate a single character for a kernel message. Send a notification
* the to output driver if an END_OF_KMESS is encountered.
*/
#if 0
ser_putc(c);
return;
#endif
if (panicing)
cons_putk(c);
if (c != 0) {
kmess.km_buf[kmess.km_next] = c; /* put normal char in buffer */
if (kmess.km_size < KMESS_BUF_SIZE)
kmess.km_size += 1;
kmess.km_next = (kmess.km_next + 1) % KMESS_BUF_SIZE;
} else {
notify(LOG_PROC_NR);
}
2005-04-21 16:53:53 +02:00
}
2005-09-11 19:09:11 +02:00
/*===========================================================================*
* do_new_kmess *
*===========================================================================*/
2005-04-21 16:53:53 +02:00
PUBLIC void do_new_kmess(m)
message *m;
{
/* Notification for a new kernel message. */
struct kmessages kmess; /* kmessages structure */
static int prev_next = 0; /* previous next seen */
int size, next;
int bytes;
int r;
/* Try to get a fresh copy of the buffer with kernel messages. */
#if DEAD_CODE
/* During shutdown, the reply is garbled because new notifications arrive
* while the system task makes a copy of the kernel messages buffer.
* Hence, don't check the return value.
*/
2005-04-21 16:53:53 +02:00
if ((r=sys_getkmessages(&kmess)) != OK) {
printf("TTY: couldn't get copy of kmessages: %d, 0x%x\n", r,r);
2005-04-21 16:53:53 +02:00
return;
}
#endif
sys_getkmessages(&kmess);
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.
* Check for size being positive, the buffer might as well be emptied!
*/
if (kmess.km_size > 0) {
bytes = ((kmess.km_next + KMESS_BUF_SIZE) - prev_next) % KMESS_BUF_SIZE;
r=prev_next; /* start at previous old */
while (bytes > 0) {
cons_putk( kmess.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.km_next;
}
2005-09-11 19:09:11 +02:00
/*===========================================================================*
* do_diagnostics *
*===========================================================================*/
2005-04-21 16:53:53 +02:00
PUBLIC void do_diagnostics(m_ptr)
message *m_ptr; /* pointer to request message */
{
/* Print a string for a server. */
char c;
vir_bytes src;
int count;
int result = OK;
int proc_nr = m_ptr->DIAG_ENDPT;
2005-04-21 16:53:53 +02:00
if (proc_nr == SELF) proc_nr = m_ptr->m_source;
src = (vir_bytes) m_ptr->DIAG_PRINT_BUF;
for (count = m_ptr->DIAG_BUF_COUNT; count > 0; count--) {
if (sys_vircopy(proc_nr, D, src++, SELF, D, (vir_bytes) &c, 1) != OK) {
result = EFAULT;
break;
}
cons_putk(c);
2005-04-21 16:53:53 +02:00
}
cons_putk(0); /* always terminate, even with EFAULT */
2005-04-21 16:53:53 +02:00
m_ptr->m_type = result;
send(m_ptr->m_source, m_ptr);
}
/*===========================================================================*
* do_get_kmess *
*===========================================================================*/
PUBLIC void do_get_kmess(m_ptr)
message *m_ptr; /* pointer to request message */
{
/* Provide the log device with debug output */
vir_bytes dst;
int r;
dst = (vir_bytes) m_ptr->GETKM_PTR;
r= OK;
if (sys_vircopy(SELF, D, (vir_bytes)&kmess, m_ptr->m_source, D,
dst, sizeof(kmess)) != OK) {
r = EFAULT;
}
m_ptr->m_type = r;
send(m_ptr->m_source, m_ptr);
}
/*===========================================================================*
* cons_putk *
2005-04-21 16:53:53 +02:00
*===========================================================================*/
PRIVATE 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);
} else {
flush(&cons_table[0]);
}
}
/*===========================================================================*
* toggle_scroll *
*===========================================================================*/
PUBLIC void toggle_scroll()
{
/* Toggle between hardware and software scroll. */
cons_org0();
softscroll = !softscroll;
printf("%sware scrolling enabled.\n", softscroll ? "Soft" : "Hard");
}
/*===========================================================================*
* cons_stop *
*===========================================================================*/
PUBLIC void cons_stop()
{
/* Prepare for halt or reboot. */
cons_org0();
softscroll = 1;
select_console(0);
cons_table[0].c_attr = cons_table[0].c_blank = BLANK_COLOR;
}
/*===========================================================================*
* cons_org0 *
*===========================================================================*/
PRIVATE void cons_org0()
{
/* 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);
cons->c_org -= n;
}
flush(cons);
}
select_console(ccurrent);
}
/*===========================================================================*
* select_console *
*===========================================================================*/
PUBLIC void select_console(int cons_line)
{
/* Set the current console to console number 'cons_line'. */
if (cons_line < 0 || cons_line >= nr_cons) return;
ccurrent = cons_line;
curcons = &cons_table[cons_line];
set_6845(VID_ORG, curcons->c_org);
set_6845(CURSOR, curcons->c_cur);
}
/*===========================================================================*
* con_loadfont *
*===========================================================================*/
PUBLIC int con_loadfont(m)
message *m;
{
/* 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;
2005-04-29 17:36:43 +02:00
if (!machine.vdu_ega) return(ENOTTY);
2005-04-21 16:53:53 +02:00
result = ga_program(seq1); /* bring font memory into view */
result = sys_physcopy(m->IO_ENDPT, D, (vir_bytes) m->ADDRESS,
2005-04-29 17:36:43 +02:00
NONE, PHYS_SEG, (phys_bytes) GA_VIDEO_ADDRESS, (phys_bytes)GA_FONT_SIZE);
2005-04-21 16:53:53 +02:00
result = ga_program(seq2); /* restore */
return(result);
}
/*===========================================================================*
* ga_program *
*===========================================================================*/
PRIVATE int ga_program(seq)
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 *
*===========================================================================*/
PRIVATE int cons_ioctl(tp, try)
2005-04-21 16:53:53 +02:00
tty_t *tp;
int 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;
}
#define COM1_BASE 0x3F8
#define COM1_THR (COM1_BASE + 0)
#define LSR_THRE 0x20
#define COM1_LSR (COM1_BASE + 5)
PRIVATE void ser_putc(char c)
{
unsigned long b;
int i;
int lsr, thr;
lsr= COM1_LSR;
thr= COM1_THR;
for (i= 0; i<100; i++)
{
sys_inb(lsr, &b);
if (b & LSR_THRE)
break;
}
sys_outb(thr, c);
}