[comp.os.minix] Yet another TTY driver, part 3 of 3

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