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