File:  [MW Coherent from dump] / coherent / b / kernel / io.386 / tty.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Wed May 29 04:56:37 2019 UTC (7 years ago) by root
Branches: MarkWilliams, MAIN
CVS tags: relic, HEAD
coherent

/*
 * File:	$USRSRC/ttydrv/tty.c
 *
 * Purpose:	COHERENT line discipline module.
 *	This is the common part of typewriter service. It handles all device-
 *	independent aspects of a typewriter, including tandem flow control,
 *	erase and kill, stop and start, and common ioctl functions.
 *
 */

/*
 * About STOP flag bits:
 *	T_ISTOP is set when the tty module's input queue is in danger of
 *		overflow.  It is up to the device driver to check this flag
 *		and do something about it.  If ttin() is called with a
 *		character from the device while T_ISTOP is set, the  character
 *		is discarded.  T_ISTOP is cleared when the input queue is
 *		sufficiently empty.  The device driver can monitor this bit for
 *		hardware flow control.
 *	T_TSTOP is the "Tandem" flow control flag for input.  If TANDEM is set
 *		and the input queue is in danger of overflow, t_stopc is sent
 *		and T_TSTOP is set.  When the input queue is empty enough,
 *		t_startc is sent and T_TSTOP is cleared.
 *	T_STOP is the flow control bit for output.  No output will be
 *		written to the output queue while this bit is true.
 *		Except for initialization of flags in the TTY struct, by
 *		ttopen(), this bit is not written by tty.c.
 *	91/09/15 - hal
 */
/*
 * About VMIN and VTIME:
 * These parameters only apply when ICANON is zero.
 * If VMIN > 0 and VTIME = 0, block until VMIN characters are received.
 * If VMIN > 0 and VTIME > 0, block until the first character is received,
 *   then return after VMIN characters are received or VTIME/10 seconds
 *   have elapsed since last character, whichever comes first.
 * If VMIN = 0, return after first character or after VTIME/10 seconds.
 *   (may return with read count of zero - but will return one character
 *   if it is available, even if VTIME is zero).
 */

/*
 * Includes.
 */
#include <sys/coherent.h>
#include <sys/clist.h>
#include <sys/con.h>
#include <sys/deftty.h>
#include <sys/io.h>
#include <sys/proc.h>
#include <sys/sched.h>
#include <sys/stat.h>
#include <sys/tty.h>
#include <errno.h>
#ifdef _I386
#include <termio.h>
#include <sys/inode.h>
#include <sys/ascii.h>
#else
#define kucopyS(k, u, n)	kucopy(k, u, n)
#define ukcopyS(u, k, n)	ukcopy(u, k, n)
#endif

#ifdef TRACER
#define DUMPSGTTY(sp)	dumpsgtty(sp)

static void dumpsgtty(sp)
struct sgttyb * sp;
{
	T_HAL(2, printf("S:%x:%x ", sp->sg_ispeed, sp->sg_flags));
}
#else
#define DUMPSGTTY(sp)
#endif

/*
 * Definitions.
 *	Constants.
 *	Macros with argument lists.
 *	Typedefs.
 *	Enums.
 */

#define	SGTTY_CPY_LEN	(sizeof (struct sgttyb))

/* NEAR_OR_FAR_CALL is for invoking t_param and t_start */
#ifdef _I386
#define	 NEAR_OR_FAR_CALL(tp_fn)  { (*tp->tp_fn)(tp); }
#define SET_HPCL { \
	if (tp->t_termio.c_cflag & HUPCL) \
		tp->t_flags |= T_HPCL; \
	else \
		tp->t_flags &= ~T_HPCL; }
#else
#define	 NEAR_OR_FAR_CALL(tp_fn)  {\
	if (tp->t_cs_sel) \
		ld_call(tp->t_cs_sel, tp->tp_fn, tp); \
	else \
		(*tp->tp_fn)(tp); }
#define SET_HPCL
#endif

/*
 * Functions.
 *	Import Functions.
 *	Export Functions.
 *	Local Functions.
 */
void ttclose();
void ttflush();
void tthup();
void ttin();
void ttinflush();
int  ttinp();
void ttioctl();
void ttopen();
int  ttout();
void ttoutflush();
int  ttoutp();
int  ttpoll();
void ttread();
void ttread0();
void ttsetgrp();
void ttsignal();
void ttstart();
void ttwrite();
void ttwrite0();

static void ttstash();
static void ttrtp();

#ifdef _I386
static void make_termio();
static void make_sg();
#else
#define make_termio(a1,a2,a3)
#define make_sg(a1,a2,a3)
#endif

/*
 * Global Data.
 *	Import Variables.
 *	Export Variables.
 *	Local Variables.
 */
extern	int	wakeup();
extern	void	pollwake();

/*
 * ttopen()
 *
 *	Called by driver on first open.
 *	Set up defaults.
 */
void
ttopen(tp)
register TTY *tp;
{
	tp->t_escape = 0;
	tp->t_sgttyb.sg_ispeed = tp->t_dispeed;
	tp->t_sgttyb.sg_ospeed = tp->t_dospeed;
	tp->t_sgttyb.sg_erase  = DEF_SG_ERASE;
	tp->t_sgttyb.sg_kill   = DEF_SG_KILL;
	tp->t_sgttyb.sg_flags  = DEF_SG_FLAGS;
	tp->t_tchars.t_intrc   = DEF_T_INTRC;
	tp->t_tchars.t_quitc   = DEF_T_QUITC;
	tp->t_tchars.t_startc  = DEF_T_STARTC;
	tp->t_tchars.t_stopc   = DEF_T_STOPC;
	tp->t_tchars.t_eofc    = DEF_T_EOFC;
	tp->t_tchars.t_brkc    = DEF_T_BRKC;

#ifdef _I386
	tp->t_termio.c_lflag |= ICANON;
	tp->t_termio.c_cc[VEOL] = A_NL;
	tp->t_termio.c_cc[VEOF] = A_EOT;
	make_termio(&tp->t_sgttyb, &tp->t_tchars, &tp->t_termio);
	if (tp->t_flags & T_HPCL)
		tp->t_termio.c_cflag |= HUPCL;
	else
		tp->t_termio.c_cflag &= ~HUPCL;
	tp->t_termio.c_cflag |= (CS8|CREAD);
#endif

	if (tp->t_param)
		NEAR_OR_FAR_CALL(t_param)
}

/*
 * ttsetgrp()
 *
 *	If process is a group leader without a control terminal,
 *	make its control terminal this device.
 *
 *	If process is a group leader and this device does not have
 *	a process group, give it the group of the current process.
 */
void ttsetgrp(tp, ctdev, mode)
register TTY *tp;
dev_t ctdev;
int mode;
{
	register PROC *pp;

	pp = SELF;
#ifdef _I386
	if (pp->p_group == pp->p_pid && 0 == (mode & IPNOCTTY)) {
#else
	if (pp->p_group == pp->p_pid) {
#endif
		if (pp->p_ttdev == NODEV)
			pp->p_ttdev = ctdev;
		if (tp->t_group == 0)
			tp->t_group = pp->p_pid;
	}
}

/*
 * ttyclose()
 *
 *	Called by driver on the last close.
 *	Wait for all pending output to go out.
 *	Kill input.
 */
void ttclose(tp)
register TTY *tp;
{
	register int s;

	while (tp->t_oq.cq_cc) {
		s = sphi();
		if (tp->t_oq.cq_cc) {
			tp->t_flags |= T_DRAIN;
#ifdef _I386
			x_sleep((char *)&tp->t_oq, pritty, slpriSigCatch,
			  "ttydrain");
#else
			v_sleep((char *)&tp->t_oq, CVTTOUT, IVTTOUT, SVTTOUT,
			  "ttydrain");
#endif
			/* The line discipline is waiting for the tty to drain.  */
		}
		spl(s);
		if (SELF->p_ssig && nondsig())
			break;
	}
	ttflush(tp);
	tp->t_flags = tp->t_group = 0;
}

/*
 * ttread()
 *
 *	The read routine for a tty device driver will call this function.
 *
 *	Move data from tp->t_iq to io segment iop.
 *	Number of characters to copy is in iop->ioc.
 *
 *	In cooked mode, copy up to the first newline or break character, or
 *	until the count runs out.
 *	In CBREAK or RAW modes, return when count runs out or when input clist
 *	is empty and we're returning at least one byte.
 */

void ttread(tp, iop)
register TTY *tp;
register IO *iop;
{
	ttread0(tp, iop, 0, 0, 0, 0);
}

/* VTIME value is in 10ths of a second */
/* VTICKS is number of cpu ticks per VTIME unit */
#define VTICKS	(HZ/10)

/*
 * ttread0()
 *
 *	Move data from user (in IO struct) to clists.
 *	Do wakeups on functions supplied when read is blocked or completed.
 */
void ttread0(tp, iop, func1, arg1, func2, arg2)
register TTY *tp;
register IO *iop;
int (*func1)(), arg1, (*func2)(), arg2;
{
	register int c;
	int o;
	int sioc = iop->io_ioc;  /* number of bytes to read */

#ifdef _I386
	int time0 = lbolt;
	int timing = 0;		/* a boolean flag */
	int got_ch = 0;		/* a boolean flag */
	unsigned char vtime = tp->t_termio.c_cc[VTIME];
	unsigned char vmin = tp->t_termio.c_cc[VMIN];
#endif

	while (iop->io_ioc) {
#ifdef _I386
		/*
		 * Start VTIME timer if we got a character or vmin is zero.
		 */
		if (ISBBYB && vtime) {
			if (got_ch || vmin == 0) {
				timing = 1;
				time0 = lbolt;
				timeout(&tp->t_vtime, vtime*VTICKS, wakeup,
				  &tp->t_iq);
			}
		}
#endif

		o = sphi();
		while ((c = cltgetq(&tp->t_iq)) < 0) {
			if ((tp->t_flags & T_CARR) == 0) {
			   u.u_error = EIO;  /* error since no carrier */
				spl(o);
				goto read_done;
			}

#ifdef _I386
			/*
			 * [T_BRD handling in COH 286 is replaced by
			 * VMIN/VTIME handling in COH 386.]
			 *
			 * If vmin is nonzero, and at least vmin characters
			 * have been received, return.
			 *
			 * If vmin is zero and vtime is zero, return
			 * whether characters have been received or not.
			 *
			 * If vmin is zero, and we got a char, return.
			 *
			 * If vtime is nonzero, and vtime 10th seconds have
			 * elapsed, return.
			 *
			 * Otherwise, go to sleep until more input arrives.
			 */
			if (ISBBYB) {
				if (vmin) {
					/* received vmin or more characters? */
					if ((sioc - iop->io_ioc) >= vmin) {
						spl(o);
						goto read_done;
					}
				} else {
					if (got_ch || vtime == 0) {
						spl(o);
						goto read_done;
					}
				}
			}
			if (timing && ((lbolt - time0)/VTICKS) >= vtime) {
				spl(o);
				goto read_done;
			}
#else
			/* If we're in CBREAK or RAW mode, and we don't */
			/* have the special "blocking read" bit set for */
			/* these modes, and we read at least one byte   */
		        /* of input, return immediately, since we have  */
			/* run out of characters from the clist.	*/

			if (ISBBYB && ((tp->t_flags & T_BRD) == 0)
			   && iop->io_ioc < sioc) {
				spl(o);
				goto read_done;
			}
#endif
			/*
			 * Non-blocking reads.
			 * Tell user process to try again later.
			 */
			if (iop->io_flag & IONDLY) {
				u.u_error = EAGAIN;
				spl(o);
				goto read_done;
			}

			tp->t_flags |= T_INPUT;  /* wait for more data */
			if (func1)
				(*func1)(arg1);
			if (func2)
				(*func2)(arg2);
#ifdef _I386
			x_sleep((char *)&tp->t_iq, pritty, slpriSigLjmp,
			  "ttywait");
#else
			v_sleep((char *)&tp->t_iq, CVTTIN, IVTTIN, SVTTIN,
			  "ttywait");
#endif
			/* The line discipline is waiting for more data.  */

			if (SELF->p_ssig && nondsig()) {
				if (iop->io_ioc == sioc)
					u.u_error = EINTR;
				spl(o);
				goto read_done;
			}
		}

		/* Got a character "c" from the input queue. */
		got_ch = 1;

		/*
		 * Flow control - can we turn on input from the driver yet?
		 */
		if (tp->t_iq.cq_cc <= ILOLIM) {
			if (tp->t_flags & T_ISTOP)
				tp->t_flags &= ~T_ISTOP;
			if (ISTAND && (tp->t_flags&T_TSTOP)) {
				tp->t_flags &= ~T_TSTOP;
				while (cltputq(&tp->t_oq, startc) < 0) {
					ttstart(tp);
					waitq();
				}
				ttstart(tp);
			}
		}
		spl(o);
		if (!ISBBYB && ISEOF)
			goto read_done;
		if (ioputc(c, iop) < 0)
			goto read_done;
		if (!ISBBYB && (c=='\n' || ISBRK))
			goto read_done;
#ifdef _I386
		if (ISBBYB && vtime)
			timing = 1;
#endif
	}

read_done:

#ifdef _I386
	if (timing)	/* turn off the VTIME timer */
		timeout(&tp->t_vtime, 0, 0, 0);
#endif

	if (func1)
		(*func1)(arg1);
	if (func2)
		(*func2)(arg2);
	return;
}

/*
 * ttwrite()
 *
 *	Write routine.
 */
void
ttwrite(tp, iop)
register TTY *tp;
register IO *iop;
{
	ttwrite0(tp, iop, 0, 0, 0, 0);
}

/*
 * ttwrite0()
 *
 *	Move data from user (in IO struct) to clists.
 *	Do wakeups on functions supplied when write is blocked or completed.
 */
void
ttwrite0(tp, iop, func1, arg1, func2, arg2)
register TTY *tp;
register IO *iop;
int (*func1)(), arg1, (*func2)(), arg2;
{
	register int c;
	int o;

	/*
	 * Non-blocking writes which can fit.
	 * NOTE: exhaustion of clists can still cause blocking writes.
	 */
	if ((iop->io_flag & IONDLY) && (OHILIM >= iop->io_ioc)) {

		/*
		 * No room.
		 */
		if (tp->t_oq.cq_cc >= OHILIM-iop->io_ioc) {
			u.u_error = EAGAIN;
			return;
		}
	}

	while ((c = iogetc(iop)) >= 0) {
		if ((tp->t_flags & T_CARR) == 0) {
			u.u_error = EIO;  /* error since no carrier */
			return;
		}
		o = sphi();
		while (tp->t_oq.cq_cc >= OHILIM) {
			ttstart(tp);
			if (tp->t_oq.cq_cc < OHILIM)
				break;
			tp->t_flags |= T_HILIM;
			if (func1)
				(*func1)(arg1);
			if (func2)
				(*func2)(arg2);
#ifdef _I386
			x_sleep((char *)&tp->t_oq, pritty, slpriSigCatch, "ttyoq");
#else
			v_sleep((char *)&tp->t_oq, CVTTOUT, IVTTOUT, SVTTOUT,
				"ttyoq");
#endif
			/*
			 * The line discipline is waiting for an output
			 * queue to drain.
			 */
			if (SELF->p_ssig && nondsig()) {
				u.u_error = EINTR;
				spl(o);
				return;
			}
		}
		while (cltputq(&tp->t_oq, c) < 0) {
			ttstart(tp);
			waitq();
		}
		spl(o);
	}
	o = sphi();
	ttstart(tp);
	spl(o);
	if (func1)
		(*func1)(arg1);
	if (func2)
		(*func2)(arg2);
}

/*
 * ttioctl()
 *
 *	This routine handles common typewriter ioctl functions.
 *	Note that flushing the stream now means drain the output
 *	and clear the input.
 */
void
ttioctl(tp, com, vec)
register TTY *tp;
int com;
register struct sgttyb *vec;
{
	register int	outDrain = 0;
	int s;
	int rload = 0;
	int was_bbyb;
	int inFlush = 0, outFlush = 0;

	/*
	 * Keep sgttyb, t_chars, AND termio structs for each tty device.
	 *
	 * TCSET* writes a new termio and converts so as to update
	 * sgttyb and t_chars as well.
	 *
	 * TIOCSET[NP] writes new sgttyb and converts so as to update termio.
	 *
	 * TIOCSETC writes new t_chars and converts so as to update termio.
	 */
	switch (com) {
#ifdef _I386
	case TCGETA:
		if (!kucopyS(&tp->t_termio, vec, sizeof(struct termio)))
			return;
		break;
	case TCSETA:
		was_bbyb = ISBBYB;	/* previous mode */
		if(!ukcopyS(vec, &tp->t_termio, sizeof(struct termio)))
			return;
		make_sg(vec, &tp->t_sgttyb, &tp->t_tchars);
		SET_HPCL;
		++rload;
		if (!was_bbyb && ISBBYB)
			ttrtp(tp);
		break;
	case TCSETAW:
		was_bbyb = ISBBYB;	/* previous mode */
		if(!ukcopyS(vec, &tp->t_termio, sizeof(struct termio)))
			return;
		make_sg(vec, &tp->t_sgttyb, &tp->t_tchars);
		SET_HPCL;
		++outDrain;	/* delay for output */
		++rload;
		if (!was_bbyb && ISBBYB)
			ttrtp(tp);
		break;
	case TCSETAF:
		if(!ukcopyS(vec, &tp->t_termio, sizeof(struct termio)))
			return;
		make_sg(vec, &tp->t_sgttyb, &tp->t_tchars);
		SET_HPCL;
	        ++inFlush;	/* flush input */
		++outDrain;	/* delay for output */
		++rload;
		break;
#endif
	case TIOCQUERY:
		kucopyS(&tp->t_iq.cq_cc, vec, sizeof(int));
		break;
	case TIOCGETP:
		if (XMODE_386 && !useracc(vec, sizeof(struct sgttyb), 1)) {
			u.u_error = EFAULT;
			return;
		}
		kucopy(&tp->t_sgttyb, vec, SGTTY_CPY_LEN);
		break;
	case TIOCSETP:
		if (XMODE_386 && !useracc(vec, sizeof(struct sgttyb), 0)) {
			u.u_error = EFAULT;
			return;
		}
		DUMPSGTTY(&tp->t_sgttyb);
	        ++inFlush;	/* flush input */
		++outDrain;	/* delay for output */
		++rload;
		ukcopy(vec, &tp->t_sgttyb, SGTTY_CPY_LEN);
		make_termio(&tp->t_sgttyb, &tp->t_tchars, &tp->t_termio);
		break;
	case TIOCSETN:
		was_bbyb = ISBBYB;	/* previous mode */
		DUMPSGTTY(&tp->t_sgttyb);
		++rload;
		if (XMODE_386 && !useracc(vec, sizeof(struct sgttyb), 0)) {
			u.u_error = EFAULT;
			return;
		}
		ukcopy(vec, &tp->t_sgttyb, SGTTY_CPY_LEN);
		make_termio(&tp->t_sgttyb, &tp->t_tchars, &tp->t_termio);
		if (!was_bbyb && ISBBYB)
			ttrtp(tp);
		break;
	case TIOCGETC:
		kucopyS(&tp->t_tchars, vec, sizeof (struct tchars));
		break;
	case TIOCSETC:
		++rload;
		++outDrain;
		if(!ukcopyS(vec, &tp->t_tchars, sizeof (struct tchars)))
			return;
		make_termio(&tp->t_sgttyb, &tp->t_tchars, &tp->t_termio);
		break;
	case TIOCEXCL:
		s = sphi();
		tp->t_flags |= T_EXCL;
		spl(s);
		break;
	case TIOCNXCL:
		s = sphi();
		tp->t_flags &= ~T_EXCL;
		spl(s);
		break;
	case TIOCHPCL:		/* set hangup on last close */
		s = sphi();
		tp->t_flags |= T_HPCL;
		spl(s);
#ifdef _I386
		tp->t_termio.c_cflag |= HUPCL;
#endif
		break;
	case TIOCCHPCL:		/* don't hangup on last close */
		if (!super())   /* only superuser may do this */
			u.u_error = EPERM;        /* not su */
		else {
			s = sphi();
			tp->t_flags &= ~T_HPCL;   /* turn off hangup bit */
			spl(s);
#ifdef _I386
			tp->t_termio.c_cflag &= ~HUPCL;
#endif
		}
		break;
	case TIOCGETTF:		/* get tty flag word */
		kucopyS(&tp->t_flags, (unsigned *) vec, sizeof(unsigned));
		break;
#ifdef _I386
	case TCFLSH:
		switch ((int)vec) {
		case 0:  inFlush++;  break;
		case 1:  outFlush++;  break;
		case 2:  inFlush++; outFlush++;  break;
		default: u.u_error = EINVAL;
		}
		break;
	case TCSBRK:
		++outDrain;
		break;
#endif
	case TIOCFLUSH:
		++inFlush;	/* flush both input and output */
		++outFlush;
/*		++outDrain;	Why? - hws - 91/11/22	*/
		break;
#ifndef _I386
	case TIOCBREAD:		/* blocking read for CBREAK/RAW mode */
		s = sphi();
		tp->t_flags |= T_BRD;
		spl(s);
		break;
	case TIOCCBREAD:	/* turn off CBREAK/RAW blocking read mode */
		s = sphi();
		tp->t_flags &= ~T_BRD;
		spl(s);
		break;
#endif
	/*
	 * The following is a hack so that the process group for /dev/console
	 * contains the current login shell running on it.
	 * Only expect /etc/init to use this ugliness.
	 */
	case TIOCSETG:
		if (super())
			tp->t_group = SELF->p_group;
		break;
	default:
		u.u_error = EINVAL;
	}

	/*
	 * T_STOP is set under two conditions:
	 * - a modem control device is awaiting carrier
	 * - a stopc (usually Ctrl-S) character was received.
	 *
	 * If ioctl just put device into RAWIN mode, make sure device
	 * is not still waiting for startc.
	 */
#if _I386
	/* Is XON/XOFF flow control off *and* we are waiting for startc? */
	if ((!ISIXON) && (tp->t_flags & T_XSTOP)) {
		s = sphi();
		tp->t_flags &= ~(T_STOP | T_XSTOP);
		ttstart(tp);
		spl(s);
	}
#else
	if ((!ISIXON) && (tp->t_flags & T_STOP) && !(tp->t_flags & T_HOPEN)) {
		s = sphi();
		tp->t_flags &= ~T_STOP;
		ttstart(tp);
		spl(s);
	}
#endif

	/*
	 * Wait for output to drain, or signal to arrive.
	 *
	 * NOTE:  This stuff is properly done within the device driver,
	 * *before* ttioctl() is called.  This paragraph should disappear
	 * from tty.c, with maybe an entry point added for the driver to
	 * drain the queue while draining the peripheral device. -hws-
	 */
	if (outDrain) {
		while (tp->t_oq.cq_cc) {
			s = sphi();
			tp->t_flags |= T_DRAIN;
			spl(s);
#ifdef _I386
			x_sleep((char *)&tp->t_oq, pritty, slpriSigCatch,
			  "ttyiodrn");
#else
			v_sleep((char *)&tp->t_oq, CVTTOUT, IVTTOUT, SVTTOUT,
			  "ttyiodrn");
#endif
			/* A TIOC has asked for tty output to drain.  */
			if (SELF->p_ssig && nondsig())
				break;
		}
	}

	/*
	 * Flush.
	 */
	if (inFlush)
		ttinflush(tp);
	if (outFlush)
		ttoutflush(tp);

	/*
	 * Re-initialize hardware.
	 */
	if (rload) {
		if (tp->t_param)
			NEAR_OR_FAR_CALL(t_param)
	}
}

/*
 * ttpoll()
 *
 *	Polling routine.
 *	[System V.3 Compatible]
 */
int ttpoll(tp, ev, msec)
register TTY * tp;
int ev;
int msec;
{
	/*
	 * Priority polls not supported.
	 */
	ev &= ~POLLPRI;

	/*
	 * Input poll with no data present.
	 */
	if ((ev & POLLIN) && (tp->t_iq.cq_cc == 0)) {

		/*
		 * Blocking input poll.
		 */
		if (msec)
			pollopen(&tp->t_ipolls);

		/*
		 * Second look to avoid interrupt race.
		 */
		if (tp->t_iq.cq_cc == 0)
			ev &= ~POLLIN;
	}

	/*
	 * Output poll with no space.
	 */
	if ((ev & POLLOUT) && (tp->t_oq.cq_cc >= OLOLIM)) {

		/*
		 * Blocking output poll.
		 */
		if (msec)
			pollopen(&tp->t_opolls);

		/*
		 * Second look to avoid interrupt race.
		 */
		if (tp->t_oq.cq_cc >= OLOLIM)
			ev &= ~POLLOUT;
	}

	if (((ev & POLLIN) == 0) && ((tp->t_flags & T_CARR) == 0))
		ev |= POLLHUP;

	return ev;
}

/*
 * ttout()
 *
 *	Pull a character from the output queues of the typewriter.
 *	Doing fills, newline insert, tab expansion, etc.
 *
 *	If the stream is empty return a -1.
 *	Called at high priority.
 */
int ttout(tp)
register TTY *tp;
{
	register int c;

	if (tp->t_nfill) {
		--tp->t_nfill;
		c = tp->t_fillb;
	} else if (tp->t_flags & T_INL) {
		tp->t_flags &= ~T_INL;
		c = '\n';
	} else {
		if ((c=cltgetq(&tp->t_oq)) < 0)
			return -1;
		if (!ISROUT) {
			if (c=='\n' && ISONLCR) {
				tp->t_flags |= T_INL;
				c = '\r';
			} else if (c=='\r' && ISOCRNL) {
				c = '\n';
			} else if (c=='\t' && ISXTABS) {
				tp->t_nfill = ~(tp->t_hpos|~07);
				tp->t_fillb = ' ';
				c = ' ';
			}
		}
	}
	if (!ISROUT) {
		if (c == '\b') {
			if (tp->t_hpos)
				--tp->t_hpos;
		} else if (c == '\r')
			tp->t_hpos = 0;
		else if (c == '\t')
			tp->t_hpos = (tp->t_hpos|07) + 1;
#if NOT_8_BIT
		else if (c >= ' ' && c <= '~')
#else
		else if ((c >= ' ' && c <= '~') || (c >= 0200 && c <= 0376))
#endif
			++tp->t_hpos;
	}
	return c;
}

/*
 * ttin()
 *
 *	Pass a character to the device independent typewriter routines.
 *	Handle erase and kill, tandem flow control, and other magic.
 *	Often called at high priority from the driver's interrupt routine.
 */
void
ttin(tp, c)
register TTY *tp;
register int c;
{
	int dc, i, n;
	int s;

	if (ISISTRIP)
		c &= 0x7F;

	if (ISISIG && ISQUIT) {
		ttsignal(tp, SIGQUIT);
		goto ttin_ret;
	}

	if (ISISIG && ISINTR) {
		ttsignal(tp, SIGINT);
		goto ttin_ret;
	}

	if (tp->t_flags & T_ISTOP)
		goto ttin_ret;

	if (ISICRNL && !ISIGNCR) {
		if (c=='\r')
			c = '\n';
	}

	if (!ISRIN) {
		if (tp->t_escape) {
			if (c == ESC)
				++tp->t_escape;
			else {
				if (ISERASE || ISKILL) {
					c |= 0200;
					--tp->t_escape;
				}
				while (tp->t_escape && tp->t_ibx<NCIB-1) {
					tp->t_ib[tp->t_ibx++] = ESC;
					--tp->t_escape;
				}
				ttstash(tp, c);
			}
			if (ISECHO) {
#if NOT_8_BIT
				cltputq(&tp->t_oq, c&0177);
#else
				cltputq(&tp->t_oq, c); /* no strip for 8-bit */
#endif
				ttstart(tp);
			}
			goto ttin_ret;
		}
		if (ISERASE && !ISCBRK) {
			while (tp->t_escape && tp->t_ibx<NCIB-1) {
				tp->t_ib[tp->t_ibx++] = ESC;
				--tp->t_escape;
			}
			if (tp->t_ibx == 0)
				goto ttin_ret;
			dc = tp->t_ib[--tp->t_ibx];
			if (ISECHO) {
				if (!ISCRT)
					cltputq(&tp->t_oq, c);
				/* don't erase for bell, null, or rubout */
#if NOT_8_BIT
				else if (((c = dc&0177) == '\007')
					|| c == 0 || c == 0177)
#else
				else if (((c = dc) == '\007')
					|| c == 0 || c == 0177 || c == 0377)
#endif
				        goto ttin_ret;
				else if (c != '\b' && c != '\t') {
					cltputq(&tp->t_oq, '\b');
					cltputq(&tp->t_oq,  ' ');
					cltputq(&tp->t_oq, '\b');
				} else if (c == '\t') {
					n = tp->t_opos + tp->t_escape;
					for (i=0; i<tp->t_ibx; ++i) {
						c = tp->t_ib[i];
#if NOT_8_BIT
						if (c & 0200) {
							++n;
							c &= 0177;
						}
#endif
						if (c == '\b')
							--n;
						else {
							if (c == '\t')
								n |= 07;
							++n;
						}
					}
					while (n++ < tp->t_hpos)
						cltputq(&tp->t_oq, '\b');
				}
#if NOT_8_BIT
				if (dc & 0200) {
					if ((dc&0177) != '\b')
						cltputq(&tp->t_oq, '\b');
					cltputq(&tp->t_oq,  ' ');
					cltputq(&tp->t_oq, '\b');
				}
#endif
				ttstart(tp);
			}
			goto ttin_ret;
		}
		if (ISKILL && !ISCBRK) {
			tp->t_ibx = 0;
			tp->t_escape = 0;
			if (ISECHO) {
				if (c < 0x20) {
					cltputq(&tp->t_oq, '^');
					c += 0x40;
				}
				cltputq(&tp->t_oq, c);
				cltputq(&tp->t_oq, '\n');
				ttstart(tp);
			}
			goto ttin_ret;
		}
	}
	if (ISBBYB) {
		cltputq(&tp->t_iq, c);
		if (tp->t_flags & T_INPUT) {
			s = sphi();
			tp->t_flags &= ~T_INPUT;
			spl(s);
			wakeup(&tp->t_iq);
		}
		if (tp->t_ipolls.e_procp) {
			tp->t_ipolls.e_procp = 0;
			pollwake((char *) &tp->t_ipolls);
		}
	} else {
		if (tp->t_ibx == 0)
			tp->t_opos = tp->t_hpos;
		if (c == ESC)
			++tp->t_escape;
		else
			ttstash(tp, c);
	}
	if (ISECHO) {
		if (ISRIN || !ISEOF) {
			cltputq(&tp->t_oq, c);
			ttstart(tp);
		}
	}
	if ((n=tp->t_iq.cq_cc)>=IHILIM) {
		s = sphi();
		tp->t_flags |= T_ISTOP;
		spl(s);
	} else if (ISTAND && (tp->t_flags&T_TSTOP)==0 && n>=ITSLIM) {
		s = sphi();
		tp->t_flags |= T_TSTOP;
		spl(s);
		cltputq(&tp->t_oq, stopc);
		ttstart(tp);
	}

ttin_ret:
	return;
}

/*
 * ttstash()
 *
 *	Cooked mode.
 *	Put character in the buffer and check for end of line.
 *	Only a legal end of line can take the last character position.
 *
 *	Only called from ttin(), and ttin() is called at high priority.
 */
static void ttstash(tp, c)
register TTY *tp;
{
	register char *p1, *p2;

	if (c=='\n' || ISEOF || ISBRK) {
		p1 = &tp->t_ib[0];
		p2 = &tp->t_ib[tp->t_ibx];
		*p2++ = c;			/* Always room */
		while (p1 < p2)
#if NOT_8_BIT
			cltputq(&tp->t_iq, (*p1++)&0177);
#else
			cltputq(&tp->t_iq, (*p1++));
#endif
		tp->t_ibx = 0;
		tp->t_escape = 0;

		if (tp->t_flags & T_INPUT) {
			tp->t_flags &= ~T_INPUT;
			wakeup(&tp->t_iq);
		}

		if (tp->t_ipolls.e_procp) {
			tp->t_ipolls.e_procp = 0;
			pollwake((char *) &tp->t_ipolls);
		}

	} else if (tp->t_ibx < NCIB-1)
		tp->t_ib[tp->t_ibx++] = c;
}

/*
 * ttstart()
 *
 *	Start output on a tty.
 *	Duck out if stopped.  Do wakeups.
 */
void ttstart(tp)
register TTY *tp;
{
	register int n;
	int s;

	n = tp->t_flags;
	if (n & T_STOP)
		goto stdone;

	if ((n&T_DRAIN) && tp->t_oq.cq_cc==0
	   && (n&T_INL)==0 && tp->t_nfill==0) {
		s = sphi();
		tp->t_flags &= ~T_DRAIN;
		spl(s);
		wakeup(&tp->t_oq);
		goto stdone;
	}

	NEAR_OR_FAR_CALL(t_start)

	if (tp->t_oq.cq_cc > OLOLIM)
		goto stdone;

	if (n & T_HILIM) {
		s = sphi();
	   	tp->t_flags &= ~T_HILIM;
		spl(s);
		wakeup(&tp->t_oq);
	}

	if (tp->t_opolls.e_procp) {
		tp->t_opolls.e_procp = 0;
		pollwake((char *) &tp->t_opolls);
	}
stdone:
	return;
}

/*
 * ttflush()
 *
 *	Flush tty input and output queues.
 */
void
ttflush(tp)
register TTY *tp;
{
	ttinflush(tp);
	ttoutflush(tp);
}

/*
 * ttinflush()
 *
 *	Flush input queues.
 */
void
ttinflush(tp)
register TTY *tp;
{
	clrq(&tp->t_iq);

	if (tp->t_flags & T_INPUT) {
		wakeup(&tp->t_iq);
	}

	if (tp->t_ipolls.e_procp) {
		tp->t_ipolls.e_procp = 0;
		pollwake((char *) &tp->t_ipolls);
	}

	tp->t_ibx = 0;
	tp->t_escape = 0;
}

/*
 * ttoutflush()
 *
 *	Flush tty output queues.
 */
void
ttoutflush(tp)
register TTY *tp;
{
	clrq(&tp->t_oq);

	if (tp->t_flags & (T_DRAIN|T_HILIM)) {
		wakeup(&tp->t_oq);
	}

	if (tp->t_opolls.e_procp) {
		tp->t_opolls.e_procp = 0;
		pollwake((char *) &tp->t_opolls);
	}
}

/*
 * ttsignal()
 *
 *	Send a signal to every process in the given process group.
 */
void
ttsignal(tp, sig)
TTY *tp;
int sig;
{
	register int g;
	register PROC *pp;

	g = tp->t_group;
	if (g == 0)
		goto sigdone;
	ttflush(tp);
	pp = &procq;
	while ((pp=pp->p_nforw) != &procq)
		if (pp->p_group == g) {
			sendsig(sig, pp);
		}
sigdone:
	return;
}

/*
 * tthup()
 *
 *	Flag hangup internally to force errors on tty read/write, flush tty,
 *	then send hangup signal.
 */
void tthup(tp)
register TTY *tp;
{
	ttflush(tp);
	ttsignal(tp, SIGHUP);
}

#ifdef _I386
/*
 * Convert from sgttyb and tchars structs to termio.
 */
static void
make_termio(sgp, tcp, trp)
struct sgttyb * sgp;
struct tchars * tcp;
struct termio * trp;
{
	char	vmin = 1, vtime = 0;
	char	veof = 4, veol = 10; /* default to ^D, ^J */

	/*
	 * If VMIN/VTIME are active, save now for possible restore.
	 */
	if ((trp->c_lflag & ICANON) == 0) {
		vmin = trp->c_cc[VMIN];
		vtime = trp->c_cc[VTIME];
	} else {
		veol = trp->c_cc[VEOL];
	}

	trp->c_cc[VINTR] = tcp->t_intrc;
	trp->c_cc[VQUIT] = tcp->t_quitc;
	veof = tcp->t_eofc;
	trp->c_cc[VERASE] = sgp->sg_erase;
	trp->c_cc[VKILL ] = sgp->sg_kill;

	trp->c_iflag = BRKINT | IXON | IGNPAR | INPCK;
	trp->c_oflag = OPOST;
	trp->c_cflag &= (CSIZE|HUPCL|CLOCAL|CREAD);
	trp->c_lflag = ICANON | ISIG | ECHONL | ECHOK;

	if (sgp->sg_flags & TANDEM)
		trp->c_iflag |= IXOFF;

	if (sgp->sg_flags & CRMOD) {
		trp->c_iflag |= ICRNL;
		trp->c_oflag |= ONLCR;
	}

	if (sgp->sg_flags & LCASE) {
		trp->c_lflag |= XCASE;
		trp->c_iflag |= IUCLC;
		trp->c_oflag |= OLCUC;
	}

	if (sgp->sg_flags & (RAW|RAWIN)) {
		trp->c_iflag = 0;
		trp->c_cflag &= ~(PARODD|PARENB);
		trp->c_cflag |= (CS8|CREAD);
		trp->c_lflag &= ~(ECHONL|ISIG|ICANON);
	}

	if (sgp->sg_flags & (RAW|RAWOUT)) {
		trp->c_oflag &= ~OPOST;
		trp->c_lflag &= ~(XCASE);
	}

	if (sgp->sg_flags & XTABS)
		trp->c_oflag |= TAB3;

	if (sgp->sg_flags & (EVENP|ODDP)) {
		trp->c_cflag |= PARENB;
		if (sgp->sg_flags & ODDP)
			trp->c_cflag |= PARODD;
	}
	trp->c_cflag |= sgp->sg_ispeed;

	if (sgp->sg_flags & CRT)
		trp->c_lflag |= ECHOE;

	if (sgp->sg_flags & CBREAK)
		trp->c_lflag &= ~ICANON;

	if (sgp->sg_flags & ECHO)
		trp->c_lflag |= ECHO;

	/*
	 * If not doing canonical processing, set VMIN/VTIME.
	 */
	if ((trp->c_lflag & ICANON) == 0) {
		trp->c_cc[VMIN] = vmin;
		trp->c_cc[VTIME] = vtime;
	} else {
		trp->c_cc[VEOF] = veof;
		trp->c_cc[VEOL] = veol;
	}
}

/*
 * Convert from termio struct to sgttyb and tchars.
 */
static void
make_sg(trp, sgp, tcp)
struct termio * trp;
struct sgttyb * sgp;
struct tchars * tcp;
{
	T_HAL(1, { printf("T:%x:%x:%x:%x ", trp->c_iflag, trp->c_oflag, \
	  trp->c_cflag, trp->c_lflag);});
	tcp->t_intrc = trp->c_cc[VINTR];
	tcp->t_quitc = trp->c_cc[VQUIT];
	tcp->t_startc= '\021';		/* Ctrl-Q */
	tcp->t_stopc = '\023';		/* Ctrl-S */
	tcp->t_eofc  = trp->c_cc[VEOF];
	tcp->t_brkc  = -1;

	sgp->sg_erase  = trp->c_cc[VERASE];
	sgp->sg_kill   = trp->c_cc[VKILL ];
	sgp->sg_ispeed = trp->c_cflag & CBAUD;
	sgp->sg_ospeed = sgp->sg_ispeed;
	sgp->sg_flags  = RAWIN | RAWOUT | CBREAK;

	if (trp->c_lflag & ECHO)
		sgp->sg_flags |= ECHO;

	if (trp->c_lflag & ECHOE)
		sgp->sg_flags |= CRT;

	if ( (trp->c_lflag & XCASE)
	  || (trp->c_oflag & OLCUC)
	  || (trp->c_iflag & IUCLC))
		sgp->sg_flags |= LCASE;

	if (trp->c_iflag & IXOFF)
		sgp->sg_flags |= TANDEM;

	if (trp->c_iflag & ICRNL)
		sgp->sg_flags |= CRMOD;

	if (trp->c_oflag & ONLCR)
		sgp->sg_flags |= CRMOD;

	if (trp->c_oflag & OPOST)
		sgp->sg_flags &= ~RAWOUT;

	if ((trp->c_oflag & TABDLY) == TAB3)
		sgp->sg_flags |= XTABS;

	if (trp->c_cflag & PARENB) {
		if (trp->c_cflag & PARODD)
			sgp->sg_flags |= ODDP;
		else
			sgp->sg_flags |= EVENP;
	}

	if (trp->c_lflag & ISIG)
		sgp->sg_flags &= ~RAWIN;

	if (trp->c_lflag & ICANON)
		sgp->sg_flags &= ~CBREAK;
}
#endif

/*
 * ttrtp()
 *
 * Recover contents of typeahead when changing modes.
 * Called for ioctls of TIOCSETP and TCSETA,
 * when going from not byte-by-byte input to BBYB.
 */
static void
ttrtp(tp)
TTY * tp;
{
	register char	*p1, *p2;

	if (tp->t_ibx) {
		p1 = &tp->t_ib[0];
		p2 = &tp->t_ib[tp->t_ibx];
		while (p1 < p2) {
#if NOT_8_BIT
			cltputq(&tp->t_iq, (*p1++) & 0177);
#else
			cltputq(&tp->t_iq, (*p1++));
#endif
		}
		tp->t_ibx = 0;
	}
}

/*
 * ttinp()
 *
 * Return nonzero if ttin() may be called to send data for pickup by ttread(),
 * or 0 if not.
 */
int
ttinp(tp)
TTY * tp;
{
	return ((tp->t_flags&T_ISTOP) == 0);
}

/*
 * ttoutp()
 *
 * Return nonzero if ttout() may be called to fetch data stored by ttwrite(),
 * or 0 if not.
 */
int
ttoutp(tp)
TTY * tp;
{
	return (tp->t_nfill || (tp->t_flags&T_INL) || tp->t_oq.cq_cc);
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.