Minix1.5/kernel/tty.c
/* This file contains the terminal driver, both for the IBM console and regular
* ASCII terminals. It is split into two sections, a device-independent part
* and a device-dependent part. The device-independent part accepts
* characters to be printed from programs and queues them in a standard way
* for device-dependent output. It also accepts input and queues it for
* programs. This file contains 2 main entry points: tty_task() and keyboard().
* When a key is struck on a terminal, an interrupt to an assembly language
* routine is generated. This routine saves the machine state and registers
* and calls keyboard(), which enters the character in an internal table, and
* then sends a message to the terminal task. The main program of the terminal
* task is tty_task(). It accepts not only messages about typed input, but
* also requests to read and write from terminals, etc.
*
* The device-dependent part interfaces with the IBM console and ASCII
* terminals. The IBM keyboard is unusual in that keystrokes yield key numbers
* rather than ASCII codes, and furthermore, an interrupt is generated when a
* key is depressed and again when it is released. The IBM display is memory
* mapped, so outputting characters such as line feed, backspace and bell are
* tricky.
*
* The valid messages and their parameters are:
*
* HARD_INT: output has been completed or input has arrived
* TTY_READ: a process wants to read from a terminal
* TTY_WRITE: a process wants to write on a terminal
* TTY_IOCTL: a process wants to change a terminal's parameters
* TTY_SETPGRP: indicate a change in a control terminal
* CANCEL: terminate a previous incomplete system call immediately
*
* m_type TTY_LINE PROC_NR COUNT TTY_SPEK TTY_FLAGS ADDRESS
* |-------------+---------+---------+---------+---------+---------+---------|
* | HARD_INT | | | | | | |
* |-------------+---------+---------+---------+---------+---------+---------|
* | TTY_READ |minor dev| proc nr | count | | | buf ptr |
* |-------------+---------+---------+---------+---------+---------+---------|
* | TTY_WRITE |minor dev| proc nr | count | | | buf ptr |
* |-------------+---------+---------+---------+---------+---------+---------|
* | TTY_IOCTL |minor dev| proc nr |func code|erase etc| flags | |
* |-------------+---------+---------+---------+---------+---------+---------|
* | TTY_SETPGRP |minor dev| proc nr | | | | |
* |-------------+---------+---------+---------+---------+---------+---------
* | CANCEL |minor dev| proc nr | | | | |
* ---------------------------------------------------------------------------
*/
#include "kernel.h"
#include <sgtty.h>
#include <signal.h>
#include <minix/callnr.h>
#include <minix/com.h>
#include "tty.h"
/* Array and macros to convert line numbers to structure pointers. */
PRIVATE struct tty_struct *p_tty_addr[NR_CONS + NR_RS_LINES];
#define ctty_addr(line) (&tty_struct[(line)]) /* faster if line is const */
#define tty_addr(line) (p_tty_addr[(line)])
/* Macros for magic tty types. */
#define isconsole(tp) ((tp) < ctty_addr(NR_CONS))
#define isrs232(tp) ((tp) >= ctty_addr(NR_CONS))
/* Macros for magic tty line numbers. */
#define isttyline(line) ((unsigned) (line) < NR_CONS + NR_RS_LINES)
/* Macros for magic tty structure pointers. */
#define FIRST_TTY (ctty_addr(0))
#define END_TTY (ctty_addr(NR_CONS + NR_RS_LINES))
/* Miscellaneous. */
#define LF '\012' /* '\n' is not portablly a LF */
/* Test-and-set flag, set during tty_wakeup(). Remains set if do_int() is
* scheduled until do_int() is finished.
*/
PRIVATE int tty_awake;
FORWARD void back_over();
FORWARD int chuck();
FORWARD void do_cancel();
FORWARD void do_int();
FORWARD void do_ioctl();
FORWARD void do_read();
FORWARD void do_setpgrp();
FORWARD void do_write();
FORWARD void echo();
FORWARD void in1_char();
FORWARD void in_char();
FORWARD int out_process();
FORWARD int rd_chars();
FORWARD void rs_start();
FORWARD void tty_icancel();
FORWARD void tty_init();
FORWARD void tty_ocancel();
FORWARD void tty_reply();
FORWARD void uninhibit();
/*===========================================================================*
* tty_task *
*===========================================================================*/
PUBLIC void tty_task()
{
/* Main routine of the terminal task. */
message tty_mess; /* buffer for all incoming messages */
register struct tty_struct *tp;
tty_init();
while (TRUE) {
receive(ANY, &tty_mess);
if (!isttyline(tty_mess.TTY_LINE)) {
tty_mess.m_type = -1; /* force error */
tty_mess.TTY_LINE = 0; /* so hardware ints can get through */
}
tp = tty_addr(tty_mess.TTY_LINE);
switch(tty_mess.m_type) {
case HARD_INT: do_int(); break;
case TTY_READ: do_read(tp, &tty_mess); break;
case TTY_WRITE: do_write(tp, &tty_mess); break;
case TTY_IOCTL: do_ioctl(tp, &tty_mess); break;
case TTY_SETPGRP: do_setpgrp(tp, &tty_mess); break;
case CANCEL: do_cancel(tp, &tty_mess); break;
default: tty_reply(TASK_REPLY, tty_mess.m_source,
tty_mess.PROC_NR, EINVAL);
}
}
}
/*===========================================================================*
* do_int *
*===========================================================================*/
PRIVATE void do_int()
{
/* The TTY task can generate two kinds of interrupts:
* - a character has been received from the console or an RS232 line.
* - an RS232 line has completed a write request (on behalf of a user).
* The interrupt handler may delay the interrupt message at its discretion
* to avoid swamping the TTY task. Messages may be overwritten when the
* lines are fast or when there are races between different lines, input
* and output, because MINIX only provides single buffering for interrupt
* messages (in proc.c). This is handled by explicitly checking each line
* for fresh input and completed output on each interrupt. Input is given
* priority so signal characters are not delayed by lots of small output
* requests. This does not signifigantly delay the detection of output
* completions, since TTY will be scheduled to handle the output before
* any new user can request input.
*
* If a reply is sent (to FS), further input/output must not be processed
* for fear of sending a second message to FS, which would be lost under
* certain race conditions. E.g. when FS is now ready and is about to
* sendrec() to TTY (usually from rw_dev()). FS handles the deadlock
* resulting from the _first_ send() from TTY clashing with the sendrec()
* from FS. But then the scheduling causes the retried sendrec() to get
* through, so the second send() fails with an E_LOCKED error. This might
* be avoided by preempting tasks like TTY over servers like FS, or giving
* preference to senders over receivers. In practice, TTY relies on being
* woken at a later clock tick.
*/
char *buf;
static struct tty_struct *last_tp = FIRST_TTY; /* round-robin service */
unsigned char odone;
register char *rbuf;
unsigned remaining;
register struct tty_struct *tp;
unsigned wrapcount;
tp = last_tp;
do {
if (++tp >= END_TTY) tp = FIRST_TTY;
/* Transfer any fresh input to TTY's buffer, and test output done. */
remaining = (*tp->tty_devread)(tp->tty_line, &buf, &odone);
if (remaining == 0)
goto check_output; /* avoid even uglier indentation */
rbuf = buf;
if (!isconsole(tp) && tp->tty_mode & RAW) {
/* Avoid grotesquely inefficient in_char(), except for console
* which needs further translation.
* Line feeds need not be counted.
*/
/* If queue becomes too full, ask external device to stop. */
if (tp->tty_incount < tp->tty_ihighwater &&
tp->tty_incount + remaining >= tp->tty_ihighwater)
rs_istop(tp->tty_line);
if (remaining > tp->tty_insize - tp->tty_incount)
/* not all fit, discard */
remaining = tp->tty_insize - tp->tty_incount;
wrapcount = tp->tty_inbufend - tp->tty_inhead;
if (wrapcount < remaining) {
memcpy(tp->tty_inhead, rbuf, wrapcount);
tp->tty_inhead = tp->tty_inbuf;
rbuf += wrapcount;
tp->tty_incount += wrapcount;
remaining -= wrapcount;
}
memcpy(tp->tty_inhead, rbuf, remaining);
tp->tty_inhead += remaining;
tp->tty_incount += remaining;
} else {
do
in_char(tp, *rbuf++);
while (--remaining != 0);
}
/* Possibly restart output (in case there were xoffs or echoes). */
(*tp->tty_devstart)(tp);
/* See if a previously blocked reader can now be satisfied. */
if (tp->tty_inleft != 0 && tp->tty_incount != 0 &&
(tp->tty_mode & (RAW | CBREAK) || tp->tty_lfct != 0)) {
/* Tell hanging reader that chars have arrived. */
tty_reply(REVIVE, (int) tp->tty_incaller,
(int) tp->tty_inproc, rd_chars(tp));
}
check_output:
/* Finish off any completed block of output. */
if (odone) {
if (tp->tty_rwords > 0) {
/* not echo */
tp->tty_phys += tp->tty_rwords;
tp->tty_cum += tp->tty_rwords;
tp->tty_outleft -= tp->tty_rwords;
if (tp->tty_outleft == 0) {
finish(tp, tp->tty_cum);
continue;
}
}
tp->tty_rwords = 0;
rs_ocancel(tp->tty_line); /* tty_ocancel does too much*/
(*tp->tty_devstart)(tp); /* maybe continue output */
}
}
while (tp != last_tp || tty_events >= EVENT_THRESHOLD);
tty_awake = FALSE;
last_tp = tp;
}
/*===========================================================================*
* in_char *
*===========================================================================*/
PRIVATE void in_char(tp, ch)
register struct tty_struct *tp; /* terminal on which char arrived */
register char ch; /* scan code for character that arrived */
{
/* A character has just been typed in. Process, save, and echo it. */
int mode, sig, scode, c;
scode = ch; /* save the scan code */
/* Function keys are temporarily being used for debug dumps. */
if (isconsole(tp) && func_key(ch))
return; /* just processed function key */
mode = tp->tty_mode & (RAW | CBREAK);
if (tp->tty_makebreak == TWO_INTS) {
c = make_break(ch); /* console give 2 ints/ch */
if (c == -1) return;
ch = c;
} else if (mode != RAW)
ch &= 0177; /* 7-bit chars except in raw mode */
/* Processing for COOKED and CBREAK mode contains special checks. */
if (mode == COOKED || mode == CBREAK) {
/* Handle erase, kill and escape processing. */
if (mode == COOKED) {
/* First erase processing (rub out of last character). */
if (ch == tp->tty_erase) {
if (tp->tty_escaped == ESCAPED || chuck(tp) != -1) {
/* Removed it from buffer OK. */
tp->tty_escaped = NOT_ESCAPED;
back_over(tp); /* remove from screen too */
}
return;
}
/* Now do kill processing (remove current line). */
if (ch == tp->tty_kill && tp->tty_escaped == NOT_ESCAPED) {
while(chuck(tp) == OK) /* keep looping */ ;
echo(tp, tp->tty_kill);
echo(tp, '\n');
return;
}
/* Handle EOT and the escape symbol (backslash). */
if (tp->tty_escaped == NOT_ESCAPED) {
/* Normal case: previous char was not backslash. */
if (ch == '\\') {
/* An escaped symbol has just been typed. */
tp->tty_escaped = ESCAPED;
echo(tp, ch);
return; /* do not store the '\' */
}
/* CTRL-D means end-of-file, unless it is escaped. It
* is stored in the text as MARKER, and counts as a
* line feed in terms of knowing whether a full line
* has been typed already.
*/
if (ch == tp->tty_eof) {
ch = MARKER;
if (tp->tty_incount < tp->tty_insize)
tp->tty_lfct++; /* counts as LF */
}
} else {
/* Previous character was backslash. */
tp->tty_escaped = NOT_ESCAPED; /* turn escaping off */
back_over(tp); /* to overwrite or re-echo */
if (ch != tp->tty_erase && ch != tp->tty_kill &&
ch != tp->tty_eof)
/* Store the escape previously skipped over */
in1_char(tp, '\\', '\\');
}
}
/* Both COOKED and CBREAK modes come here; first map CR to LF. */
if (ch == '\r' && (tp->tty_mode & CRMOD)) ch = '\n';
/* Check for interrupt and quit characters. */
if (ch == tp->tty_intr || ch == tp->tty_quit) {
sig = (ch == tp->tty_intr ? SIGINT : SIGQUIT);
sigchar(tp, sig);
return;
}
/* Check for and process CTRL-S (terminal stop). */
if (ch == tp->tty_xoff) {
tp->tty_inhibited = STOPPED;
if (isrs232(tp))
rs_inhibit(tp->tty_line, TRUE); /* sync avoid races */
return;
}
/* Check for and process terminal start character, now anything. */
if (tp->tty_inhibited == STOPPED) uninhibit(tp);
/* Check for and discard xon (terminal start). */
if (ch == tp->tty_xon) return;
}
/* All 3 modes come here. */
if (ch == '\n' && tp->tty_incount < tp->tty_insize)
tp->tty_lfct++; /* count line feeds */
/* The numeric pad generates ASCII escape sequences: ESC [ letter */
if (isconsole(tp) && (scode = letter_code(scode)) != 0) {
/* This key is to generate a three-character escape sequence. */
in1_char(tp, ESC, 'E');
in1_char(tp, BRACKET, BRACKET);
ch = scode;
}
in1_char(tp, ch, ch);
}
/*===========================================================================*
* echo *
*===========================================================================*/
PRIVATE void echo(tp, c)
register struct tty_struct *tp; /* terminal on which to echo */
register char c; /* character to echo */
{
/* Echo a character on the terminal. */
if ( (tp->tty_mode & ECHO) == 0) return; /* if no echoing, don't echo */
/* MARKER is meaningful only in cooked mode */
if (c != MARKER || tp->tty_mode & (CBREAK | RAW)) {
if (isconsole(tp)) {
out_char(tp, c); /* echo to console */
flush(tp); /* force character out onto screen */
} else if (tp->tty_etail < tp->tty_ebufend)
*tp->tty_etail++ = c; /* echo to RS232 line */
}
}
/*===========================================================================*
* chuck *
*===========================================================================*/
PRIVATE int chuck(tp)
register struct tty_struct *tp; /* from which tty should chars be removed */
{
/* Delete one character from the input queue. Used for erase and kill. */
char *prev;
/* If input queue is empty, don't delete anything. */
if (tp->tty_incount == 0) return(-1);
/* Don't delete '\n' or '\r'. */
prev = (tp->tty_inhead != tp->tty_inbuf ? tp->tty_inhead - 1 :
tp->tty_inbufend - 1);
if (*prev == '\n' || *prev == '\r') return(-1);
tp->tty_inhead = prev;
/* If queue becomes empty enough, tell external device it can start. */
if (--tp->tty_incount == tp->tty_ilow_water && isrs232(tp))
rs_istart(tp->tty_line);
return(OK); /* char erasure was possible */
}
/*===========================================================================*
* do_read *
*===========================================================================*/
PRIVATE void do_read(tp, m_ptr)
register struct tty_struct *tp;
message *m_ptr; /* pointer to message sent to the task */
{
/* A process wants to read from a terminal. */
if (tp->tty_inleft > 0) { /* if someone else is hanging, give up */
tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EIO);
return;
}
/* Copy information from the message to the tty struct. */
tp->tty_incaller = m_ptr->m_source;
tp->tty_inproc = m_ptr->PROC_NR;
tp->tty_in_vir = m_ptr->ADDRESS;
tp->tty_inleft = m_ptr->COUNT;
/* Try to get chars. This call either gets enough, or gets nothing. */
tty_reply(TASK_REPLY, m_ptr->m_source, (int) tp->tty_inproc, rd_chars(tp));
}
/*===========================================================================*
* rd_chars *
*===========================================================================*/
PRIVATE int rd_chars(tp)
register struct tty_struct *tp; /* pointer to terminal to read from */
{
/* A process wants to read from a terminal. First check if enough data is
* available. If so, pass it to the user. If not, send FS a message telling
* it to suspend the user. When enough data arrives later, the tty driver
* copies it to the user space directly and notifies FS with a message.
*/
char *bufend;
int ct;
register char *rtail;
int user_ct;
int user_cum;
phys_bytes user_phys;
if (tp->tty_incount == 0 ||
!(tp->tty_mode & (RAW | CBREAK)) && tp->tty_lfct == 0)
return(SUSPEND);
if ( (user_phys = numap(tp->tty_inproc, (vir_bytes) tp->tty_in_vir,
(vir_bytes) tp->tty_inleft)) == 0)
return(E_BAD_ADDR);
if (tp->tty_inleft > tp->tty_incount) tp->tty_inleft = tp->tty_incount;
user_cum = 0;
do {
rtail = tp->tty_intail;
if ( (ct = tp->tty_inleft) > tp->tty_inbufend - rtail)
ct = tp->tty_inbufend - rtail;
/* Be careful about CTRL-D. In cooked
* mode it is not transmitted to user programs, and is not counted as
* a character as far as the count goes, but it does occupy space in
* the driver's tables and must be counted there.
*/
user_ct = ct;
if (!(tp->tty_mode & (RAW | CBREAK))) {
/* COOKED mode.
* Don't bother counting lines in CBREAK and RAW modes.
*/
for (bufend = rtail + ct; rtail < bufend;) {
if (*rtail++ == '\n') {
user_ct =
tp->tty_inleft =
ct = rtail - tp->tty_intail;
tp->tty_lfct--;;
break;
}
if (rtail[-1] == MARKER) {
tp->tty_inleft =
ct = rtail - tp->tty_intail;
user_ct = ct - 1;
tp->tty_lfct--;
break;
}
}
}
/* Copy at least half of buffer to user space. */
phys_copy(tp->tty_inphys + (tp->tty_intail - tp->tty_inbuf),
user_phys, (phys_bytes) user_ct);
user_phys += user_ct;
user_cum += user_ct;
if ( (tp->tty_intail += ct) == tp->tty_inbufend)
tp->tty_intail = tp->tty_inbuf;
tp->tty_inleft -= ct;
if ( (tp->tty_incount -= ct) <= tp->tty_ilow_water &&
tp->tty_incount + ct > tp->tty_ilow_water && isrs232(tp))
rs_istart(tp->tty_line);
}
while (tp->tty_inleft != 0);
return(user_cum);
}
/*===========================================================================*
* finish *
*===========================================================================*/
PUBLIC void finish(tp, code)
register struct tty_struct *tp;
int code; /* reply code */
{
/* A command has terminated (possibly due to DEL). Tell caller. */
if (tp->tty_waiting != NOT_WAITING)
tty_reply(tp->tty_waiting == SUSPENDED ? REVIVE : TASK_REPLY,
(int) tp->tty_otcaller, (int) tp->tty_outproc, code);
tty_ocancel(tp);
}
/*===========================================================================*
* do_write *
*===========================================================================*/
PRIVATE void do_write(tp, m_ptr)
register struct tty_struct *tp;
register message *m_ptr; /* pointer to message sent to the task */
{
/* A process wants to write on a terminal. */
vir_bytes out_vir, out_left;
/* If the slot is already in use, better return an error than mess it up. */
if (tp->tty_outleft > 0) { /* if someone else is hanging, give up */
tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EIO);
return;
}
/* Copy message parameters to the tty structure. */
tp->tty_otcaller = m_ptr->m_source;
tp->tty_outproc = m_ptr->PROC_NR;
tp->tty_out_vir = m_ptr->ADDRESS;
tp->tty_outleft = m_ptr->COUNT;
/* Compute the physical address where the data is in user space. */
out_vir = (vir_bytes) tp->tty_out_vir;
out_left = (vir_bytes) tp->tty_outleft;
if ( (tp->tty_phys = numap(tp->tty_outproc, out_vir, out_left)) == 0) {
/* Buffer address provided by user is outside its address space. */
tp->tty_outleft = 0;
tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, E_BAD_ADDR);
return;
}
/* Everything is OK. Fill in remaining tty fields. Only tty_waiting is
* critical - it must be held at NOT_WAITING together with tty_outleft == 0
* for the error cases.
*/
tp->tty_cum = 0;
tp->tty_waiting = WAITING;
/* Copy characters from the user process to the terminal. */
(*tp->tty_devstart)(tp); /* copy data to queue and start I/O */
/* If output is for a bitmapped terminal as the IBM-PC console, the output-
* routine will return at once so there is no need to suspend the caller,
* on ascii terminals however, the call is suspended and later revived.
* Oops, even bitmapped terminals need suspension after an XOFF.
*/
if (tp->tty_outleft > 0) {
tty_reply(TASK_REPLY, (int) tp->tty_otcaller, (int) tp->tty_outproc,
SUSPEND);
tp->tty_waiting = SUSPENDED;
}
}
/*===========================================================================*
* do_ioctl *
*===========================================================================*/
PRIVATE void do_ioctl(tp, m_ptr)
register struct tty_struct *tp;
message *m_ptr; /* pointer to message sent to task */
{
/* Perform IOCTL on this terminal. */
long flags, erki, erase, kill, intr, quit, xon, xoff, eof;
int r;
int speed;
message ioctl_mess;
r = OK;
flags = 0;
erki = 0;
switch(m_ptr->TTY_REQUEST) {
case TIOCSETP:
/* Set erase, kill, and flags. */
tp->tty_erase = (char) ((m_ptr->TTY_SPEK >> 8) & BYTE); /* erase */
tp->tty_kill = (char) ((m_ptr->TTY_SPEK >> 0) & BYTE); /* kill */
tp->tty_mode = (int) m_ptr->TTY_FLAGS & 0xFFFF; /* mode word*/
if (!(tp->tty_mode & (RAW | CBREAK))) {
/* (Re)calculate the line count. The logic of rd_chars()
* requires newlines and MARKERs returned in cooked mode
* to be interpreted as line ends, even if they were
* received in another mode.
*/
int ct;
register char *rtail;
tp->tty_lfct = 0;
for (rtail = tp->tty_intail, ct = tp->tty_incount;
ct-- != 0;) {
if (*rtail == '\n' || *rtail == MARKER)
++tp->tty_lfct;
if (++rtail == tp->tty_inbufend)
rtail = tp->tty_inbuf;
}
/* The column should really be recalculated for RS232, but
* is too much trouble.
*/
}
if (tp->tty_mode & RAW)
/* Inhibited RAW mode makes no sense since there is no way
* to uninhibit it. The inhibition flag must be cleared
* explicitly since the drivers check it in all modes.
*/
uninhibit(tp);
speed = (int) (m_ptr->TTY_SPEK >> 16);
if (speed != 0) tp->tty_speed = speed;
if (isrs232(tp)) tp->tty_speed = rs_ioctl(tp->tty_line, tp->tty_mode,
tp->tty_speed);
break;
case TIOCSETC:
/* Set intr, quit, xon, xoff, eof (brk not used). */
tp->tty_intr = (char) ((m_ptr->TTY_SPEK >> 24) & BYTE); /* interrupt */
tp->tty_quit = (char) ((m_ptr->TTY_SPEK >> 16) & BYTE); /* quit */
tp->tty_xon = (char) ((m_ptr->TTY_SPEK >> 8) & BYTE); /* CTRL-S */
tp->tty_xoff = (char) ((m_ptr->TTY_SPEK >> 0) & BYTE); /* CTRL-Q */
tp->tty_eof = (char) ((m_ptr->TTY_FLAGS >> 8) & BYTE); /* CTRL-D */
if (isrs232(tp)) rs_setc(tp->tty_line, tp->tty_xoff);
break;
case TIOCGETP:
/* Get erase, kill, and flags. */
erase = ((long) tp->tty_erase) & BYTE;
kill = ((long) tp->tty_kill) & BYTE;
erki = ((long) tp->tty_speed << 16) | (erase << 8) | kill;
flags = (long) tp->tty_mode;
break;
case TIOCGETC:
/* Get intr, quit, xon, xoff, eof. */
intr = ((long) tp->tty_intr) & BYTE;
quit = ((long) tp->tty_quit) & BYTE;
xon = ((long) tp->tty_xon) & BYTE;
xoff = ((long) tp->tty_xoff) & BYTE;
eof = ((long) tp->tty_eof) & BYTE;
erki = (intr << 24) | (quit << 16) | (xon << 8) | (xoff << 0);
flags = (eof <<8);
break;
#ifdef TIOCFLUSH
case TIOCFLUSH:
/* Discard current input and output. */
tty_icancel(tp);
tty_ocancel(tp);
break;
#endif
}
/* Send the reply. Like tty_reply() with extra arguments flags and erki. */
ioctl_mess.m_type = TASK_REPLY;
ioctl_mess.REP_PROC_NR = m_ptr->PROC_NR;
ioctl_mess.REP_STATUS = r;
ioctl_mess.TTY_FLAGS = flags;
ioctl_mess.TTY_SPEK = erki;
send(m_ptr->m_source, &ioctl_mess);
}
/*===========================================================================*
* do_setpgrp *
*===========================================================================*/
PRIVATE void do_setpgrp(tp, m_ptr)
register struct tty_struct *tp;
message *m_ptr; /* pointer to message sent to task */
{
/* A control process group has changed */
tp->tty_pgrp = m_ptr->TTY_PGRP;
tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, OK);
}
/*===========================================================================*
* do_cancel *
*===========================================================================*/
PRIVATE void do_cancel(tp, m_ptr)
register struct tty_struct *tp;
message *m_ptr; /* pointer to message sent to task */
{
/* A signal has been sent to a process that is hanging trying to read or write.
* The pending read or write must be finished off immediately.
*/
int caller;
int mode;
/* Check the parameters carefully, to avoid cancelling twice, but don't
* generate error replies since it is normal for sigchar() to have
* already done the cancellation.
*/
caller = m_ptr->PROC_NR;
mode = m_ptr->COUNT;
if (mode & R_BIT && tp->tty_inleft != 0 && caller == tp->tty_inproc) {
/* Process was reading when killed. Clean up input. */
tty_icancel(tp);
tp->tty_inleft = 0;
}
if (mode & W_BIT && tp->tty_outleft != 0 && caller == tp->tty_outproc)
/* Process was writing when killed. Clean up output. */
tty_ocancel(tp);
tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EINTR);
}
/*===========================================================================*
* tty_reply *
*===========================================================================*/
PRIVATE void tty_reply(code, replyee, proc_nr, status)
int code; /* TASK_REPLY or REVIVE */
int replyee; /* destination address for the reply */
int proc_nr; /* to whom should the reply go? */
int status; /* reply code */
{
/* Send a reply to a process that wanted to read or write data. */
message tty_mess;
tty_mess.m_type = code;
tty_mess.REP_PROC_NR = proc_nr;
tty_mess.REP_STATUS = status;
if ((status = send(replyee, &tty_mess)) != OK)
printf("\r\ntty_reply failed with status %d\r\n", status);
}
/*===========================================================================*
* sigchar *
*===========================================================================*/
PUBLIC void sigchar(tp, sig)
register struct tty_struct *tp;
int sig; /* SIGINT, SIGQUIT, or SIGKILL */
{
/* Process a SIGINT, SIGQUIT or SIGKILL char from the keyboard */
uninhibit(tp); /* do implied CRTL-Q */
finish(tp, EINTR); /* reply and/or cancel output if necessary */
tty_icancel(tp);
if (tp->tty_pgrp) cause_sig(tp->tty_pgrp, sig);
}
/*==========================================================================*
* back_over *
*==========================================================================*/
PRIVATE void back_over(tp)
register struct tty_struct *tp;
{
/* Backspace to previous character on screen and erase it. */
echo(tp, '\b');
echo(tp, ' ');
echo(tp, '\b');
}
/*==========================================================================*
* in1_char *
*==========================================================================*/
PRIVATE void in1_char(tp, ch, echoch)
register struct tty_struct *tp;
char ch; /* character to be queued */
char echoch; /* character to be echoed */
{
/* Put character in terminal input queue without preprocessing, and echo. */
if (tp->tty_incount >= tp->tty_insize)
return; /* no room, discard char */
*tp->tty_inhead++ = ch; /* save the character in the input queue */
if (tp->tty_inhead == tp->tty_inbufend)
tp->tty_inhead = tp->tty_inbuf; /* handle wraparound */
if (++tp->tty_incount == tp->tty_ihighwater && isrs232(tp))
rs_istop(tp->tty_line);
echo(tp, echoch);
}
/*==========================================================================*
* out_process *
*==========================================================================*/
PRIVATE int out_process(tp, ubuf, ucount)
register struct tty_struct *tp;
char *ubuf; /* input buffer */
int ucount; /* size of input buffer */
{
/* Perform output processing on a buffer, translating it into tty_ramqueue.
* The "RAM" queue is now mis-named and has a poorly chosen type even for RAM.
*/
unsigned char ch;
int spacecount;
register char *tbuf;
char *tbufend;
char *ubufstart;
tbuf = (char *) tp->tty_ramqueue;
tbufend = tbuf + (sizeof tp->tty_ramqueue - TAB_SIZE);
ubufstart = ubuf;
while (ucount-- != 0 && tbuf < tbufend) {
if ( (ch = *ubuf++) >= ' ') {
++tp->tty_column;
*tbuf++ = ch;
continue;
}
switch(ch) {
case '\b':
if (tp->tty_column != 0) --tp->tty_column;
break;
case '\r': tp->tty_column = 0; break;
case LF:
if (tp->tty_mode & CRMOD) {
/* Map LF to CR+LF. */
tp->tty_column = 0;
*tbuf++ = '\r';
}
break;
case '\t':
if (tp->tty_mode & XTABS) {
/* Tabs must be expanded, best guess. */
spacecount = TAB_SIZE - (tp->tty_column & TAB_MASK);
tp->tty_column += spacecount;
do
*tbuf++ = ' ';
while (--spacecount != 0);
continue;
}
/* Tabs are output directly, don't need column. */
break;
default:
/* Can't tell if column will change. */
break;
}
*tbuf++ = ch;
}
tp->tty_rwords = ubuf - ubufstart;
return(tbuf - (char *) tp->tty_ramqueue);
}
/*==========================================================================*
* rs_start *
*==========================================================================*/
PRIVATE void rs_start(tp)
register struct tty_struct *tp;
{
/* (*devstart)() routine for RS232. */
int count;
if (tp->tty_rwords != 0)
return; /* already going - xon handled at lower level*/
if ( (count = tp->tty_etail - tp->tty_ebuf) > 0) {
/* Do output processing on echo buffer and write result. */
if (tp->tty_mode & RAW)
memcpy((char *) tp->tty_ramqueue, tp->tty_ebuf, count);
else
count = out_process(tp, tp->tty_ebuf, count);
rs_write(tp->tty_line, (char *) tp->tty_ramqueue, count);
tp->tty_etail = tp->tty_ebuf; /* small ebuf all fitted */
tp->tty_rwords = -1; /* kludge echo flag */
} else if ( (count = tp->tty_outleft) != 0) {
/* Do output processing on user buffer and write result. */
if (tp->tty_mode & RAW) {
if (count > sizeof tp->tty_ramqueue)
count = sizeof tp->tty_ramqueue;
phys_copy(tp->tty_phys, tp->tty_outphys, (phys_bytes) count);
tp->tty_rwords = count;
} else {
if (count > sizeof tty_buf) count = sizeof tty_buf;
phys_copy(tp->tty_phys, tty_bphys, (phys_bytes) count);
count = out_process(tp, tty_buf, count);
}
rs_write(tp->tty_line, (char *) tp->tty_ramqueue, count);
}
}
/*==========================================================================*
* tty_icancel *
*==========================================================================*/
PRIVATE void tty_icancel(tp)
register struct tty_struct *tp;
{
/* Discard all data in tty input buffer and driver buffers. */
char *buf;
unsigned char odone;
tp->tty_intail = tp->tty_inhead = tp->tty_inbuf;
if (tp->tty_incount > tp->tty_ilow_water && isrs232(tp))
rs_istart(tp->tty_line);
tp->tty_incount = 0;
tp->tty_lfct = 0;
(*tp->tty_devread)(tp->tty_line, &buf, &odone); /* read fast to discard */
}
/*==========================================================================*
* tty_init *
*==========================================================================*/
PRIVATE void tty_init()
{
/* Initialize tty structure and call driver initialization routines. */
int line;
register struct tty_struct *tp;
for (line = 0; line < NR_CONS + NR_RS_LINES; ++line) {
tp = &tty_struct[line];
tp->tty_line = line - NR_CONS;
p_tty_addr[line] = tp;
tty_bphys = umap(proc_ptr, D, (vir_bytes) tty_buf, sizeof tty_buf);
if (isconsole(tp)) {
tp->tty_inbuf = kb_inbuf[line];
tp->tty_inbufend = tp->tty_inbuf + KB_IN_BYTES;
tp->tty_ihighwater = KB_IN_BYTES;
tp->tty_ilow_water = KB_IN_BYTES;
tp->tty_insize = KB_IN_BYTES;
} else {
tp->tty_inbuf = rs_inbuf[tp->tty_line];
tp->tty_inbufend = tp->tty_inbuf + RS_IN_BYTES;
tp->tty_ihighwater = RS_IN_BYTES - 2 * RS_IBUFSIZE;
tp->tty_ilow_water = (RS_IN_BYTES - 2 * RS_IBUFSIZE) * 7 / 8;
tp->tty_insize = RS_IN_BYTES;
}
tp->tty_inphys = umap(proc_ptr, D, (vir_bytes) tp->tty_inbuf,
tp->tty_insize);
tp->tty_intail = tp->tty_inhead = tp->tty_inbuf;
tp->tty_outphys = umap(proc_ptr, D, (vir_bytes) tp->tty_ramqueue,
sizeof tp->tty_ramqueue);
tp->tty_etail = tp->tty_ebuf;
tp->tty_ebufend = tp->tty_ebuf + sizeof tp->tty_ebuf;
tp->tty_erase = ERASE_CHAR;
tp->tty_kill = KILL_CHAR;
tp->tty_intr = INTR_CHAR;
tp->tty_quit = QUIT_CHAR;
tp->tty_xon = XON_CHAR;
tp->tty_xoff = XOFF_CHAR;
tp->tty_eof = EOT_CHAR;
if (isconsole(tp)) {
tp->tty_devread = kb_read;
tp->tty_devstart = console;
tp->tty_mode = CRMOD | XTABS | ECHO;
tp->tty_makebreak = TWO_INTS;
scr_init(tp->tty_line);
kb_init(tp->tty_line);
} else {
tp->tty_devread = rs_read;
tp->tty_devstart = rs_start;
tp->tty_mode = RAW | BITS8;
tp->tty_makebreak = ONE_INT;
tp->tty_speed = rs_init(tp->tty_line);
rs_setc(tp->tty_line, tp->tty_xoff);
}
}
}
/*==========================================================================*
* tty_ocancel *
*==========================================================================*/
PRIVATE void tty_ocancel(tp)
register struct tty_struct *tp;
{
/* Discard all data in tty output buffer and driver buffers. */
tp->tty_waiting = NOT_WAITING;
tp->tty_outleft = 0;
tp->tty_rwords = 0;
tp->tty_etail = tp->tty_ebuf;
if (isrs232(tp)) rs_ocancel(tp->tty_line);
}
/*==========================================================================*
* tty_wakeup *
*==========================================================================*/
PUBLIC void tty_wakeup()
{
/* Wake up TTY when the threshold is reached, or when there is something to
* do but no new events (slow typist), or after a timeout. The threshold
* dominates for fast terminal input and all keyboard input and output
* completions. The timeout smooths slow terminal input.
*
* Wakeup_timeout and previous_events are probably deadwood (always use
* timeout 1) but tty_awake is probably important to help avoid calling TTY
* too often, apart from its locking function.
*/
#define WAKEUP_TIMEOUT (HZ/60) /* adjust to taste, 1 for fast processor */
static unsigned previous_events;
static unsigned wakeup_timeout = WAKEUP_TIMEOUT;
if (tty_events != 0 && !test_and_set(&tty_awake)) {
if (tty_events >= EVENT_THRESHOLD || tty_events == previous_events ||
--wakeup_timeout == 0) {
wakeup_timeout = WAKEUP_TIMEOUT;
interrupt(TTY);
} else
tty_awake = FALSE;
previous_events = tty_events;
}
}
/*==========================================================================*
* uninhibit *
*==========================================================================*/
PRIVATE void uninhibit(tp)
register struct tty_struct *tp;
{
/* (Re)allow terminal output. */
tp->tty_inhibited = RUNNING;
if (isrs232(tp)) rs_inhibit(tp->tty_line, FALSE);
}