AUSAM/sys106/dmr/tty.c

Compare this file to the similar file:
Show the results in this format:

#
/*
 * general TTY subroutines
 */

#include "../defines.h"
#include "../param.h"
#ifdef	AUSAML
#include "../lnode.h"
#endif	AUSAML
#include "../systm.h"
#include "../file.h"
#include "../user.h"
#include "../tty.h"
#include "../proc.h"
#include "../inode.h"
#include "../reg.h"
#include "../conf.h"

/*
 * Input mapping table-- if an entry is non-zero, when the
 * corresponding character is typed preceded by "\" the escape
 * sequence is replaced by the table value.  Mostly used for
 * upper-case only terminals.
 */
char	maptab[]
{
	000,000,000,000,004,000,000,000,
	000,000,000,000,000,000,000,000,
	000,000,000,000,000,000,000,000,
	000,000,000,000,000,000,000,000,
#ifdef SLOSHED
	000,'|',000,000,000,000,000,'`',
#endif
#ifndef SLOSHED
	000,'|',000,'#',000,000,000,'`',
#endif
	'{','}',000,000,000,000,000,000,
	000,000,000,000,000,000,000,000,
	000,000,000,000,000,000,000,000,
#ifdef SLOSHED
	000,000,000,000,000,000,000,000,
#endif
#ifndef SLOSHED
	'@',000,000,000,000,000,000,000,
#endif
	000,000,000,000,000,000,000,000,
	000,000,000,000,000,000,000,000,
#ifdef SLOSHED
	000,000,000,000,'\\',000,'~',000,
#endif
#ifndef SLOSHED
	000,000,000,000,000,000,'~',000,
#endif
	000,'A','B','C','D','E','F','G',
	'H','I','J','K','L','M','N','O',
	'P','Q','R','S','T','U','V','W',
	'X','Y','Z',000,000,000,000,000,
};

#ifndef	ONCE
#include "../cinit.h"
#endif

/*
 * structure of device registers for KL, DL, and DC
 * interfaces-- more particularly, those for which the
 * SSTART bit is off and can be treated by general routines
 * (that is, not DH).
 */
struct {
	int ttrcsr;
	int ttrbuf;
	int tttcsr;
	int tttbuf;
};

/*
 * The routine implementing the gtty system call.
 * Just call lower level routine and pass back values.
 */
gtty()
{
	int v[3];
	register *up, *vp;

	vp = v;
	sgtty(vp);
	if (u.u_error)
		return;
	up = u.u_arg[0];
	suword(up, *vp++);
	suword(++up, *vp++);
	suword(++up, *vp++);
}

/*
 * The routine implementing the stty system call.
 * Read in values and call lower level.
 */
stty()
{
	register int *up;

	up = u.u_arg[0];
	u.u_arg[0] = fuword(up);
	u.u_arg[1] = fuword(++up);
	u.u_arg[2] = fuword(++up);
	if(u.u_error)			/* fix037 */
		return;

	sgtty(0);
}

/*
 * Stuff common to stty and gtty.
 * Check legality and switch out to individual
 * device routine.
 * v  is 0 for stty; the parameters are taken from u.u_arg[].
 * c  is non-zero for gtty and is the place in which the device
 * routines place their information.
 */
sgtty(v)
int *v;
{
	register struct file *fp;
	register struct inode *ip;

	if ((fp = getf(u.u_ar0[R0])) == NULL)
		return;
	ip = fp->f_inode;
	if ((ip->i_mode&IFMT) != IFCHR) {
		u.u_error = ENOTTY;
		return;
	}
#ifdef	TTY_SUSER
	if (u.u_uid != guid(ip) && ! suser())
		return;
#endif
	(*cdevsw[ip->i_addr[0].d_major].d_sgtty)(ip->i_addr[0], v);
}

/*
 * Wait for output to drain, then flush input waiting.
 */
wflushtty(tp)
register struct tty *tp;	/* fix000 */
{
	spl5();
	while (tp->t_outq.c_cc) {
#		ifdef	TTY_MULTIPLEXED_PORTS
		ttstart( tp );
#		endif	TTY_MULTIPLEXED_PORTS
		tp->t_state =| ASLEEP;
		sleep(&tp->t_outq, TTOPRI);
	}
	flushtty(tp);
	spl0();
}

/*
 * flush all TTY queues
 */
flushtty(tp)
register struct tty *tp;	/* fix000 */
{
	register int sps;
#ifdef	TTY_FLOW_CONTROL
	register *state = &tp->t_state;
#endif

	while (getc(&tp->t_canq) >= 0);
#ifndef	TTY_FLOW_CONTROL
	while (getc(&tp->t_outq) >= 0);
#endif
#ifdef	TTY_FLOW_CONTROL
	while ( getc(&tp->t_outq) >= 0 )
		tp->t_count--;
#endif
	wakeup(&tp->t_rawq);
	wakeup(&tp->t_outq);
	sps = PS->integ;
	spl5();
#ifdef	TTY_FLOW_CONTROL
	if ( *state & ACKFLAG )  {
		*state =| FCINTFL;
		if ( *state & FCSLEEP )
			wakeup( state );
		*state =& ~(ACKFLAG|FCSLEEP);
	}
#endif
	while (getc(&tp->t_rawq) >= 0);
	tp->t_delct = 0;
	PS->integ = sps;
}

/*
 * transfer raw input list to canonical list,
 * doing erase-kill processing and handling escapes.
 * It waits until a full line has been typed in cooked mode,
 * or until any character has been typed in raw mode.
 */
canon(tp)
register struct tty *tp;	/* fix000 */
{
	register char *bp;
	char *bp1;
#ifdef SLOSHED
	int slosh;
	register char *c;
#endif
#ifndef	SLOSHED
	register int c;
#endif

	spl5();
	while (tp->t_delct==0) {
		if ((tp->t_state&CARR_ON)==0)
			return(0);
		sleep(&tp->t_rawq, TTIPRI);
	}
	spl0();
#ifndef SLOSHED
loop:
	bp = &canonb[2];
#endif
#ifdef SLOSHED
	bp = canonb;
	slosh = 0;
#endif
	while ((c=getc(&tp->t_rawq)) >= 0) {
		if (c==0377) {
			tp->t_delct--;
			break;
		}
#ifdef SLOSHED
#ifndef	TTY_TRUE_RAW
		if ((tp->t_flags&RAW)==0) {
#endif
			if( slosh)
			{
				slosh = 0;
				if(maptab[c] &&
					(tp->t_flags & LCASE) )
				{
					c = maptab[c];
					bp--;
				}
				else
				{
					if(c == tp->t_erase || c == tp->t_kill)
						bp--;
				}
			}
			else
			{
				if( c == tp->t_erase)
				{
					if(bp > canonb) bp--;
					continue;
				}
				if( c == tp->t_kill) c = 0377;
				else if( c == CEOT) continue;
				if( c == '\\') slosh = 1;
			}
#ifndef	TTY_TRUE_RAW
		}
#endif
		*bp++ = c;
		if(bp >= canonb+TTYHOG) break;
	}
	c = canonb; bp1 = c;
	while( c < bp)
	{
	/**** 177777 below should read 377 but  *c++ causes a 'movb' and ensuing sign-bit propagation screws it */
		if( *c++ == 0177777) bp1 = c;
	}
	c = bp1;
	tp = &tp->t_canq;
	while(c < bp)
	{
		putc( *c, tp);
		*c++ = 0;	/* so that passwords won't appear in core dumps */
	}
	return(1);
#endif
#ifndef SLOSHED
#ifndef	TTY_TRUE_RAW
		if ((tp->t_flags&RAW)==0) {
#endif
			if (bp[-1]!='\\') {
				if (c==tp->t_erase) {
					if (bp > &canonb[2])
						bp--;
					continue;
				}
				if (c==tp->t_kill)
					goto loop;
				if (c==CEOT)
					continue;
			} else
			if (maptab[c] && (maptab[c]==c || (tp->t_flags&LCASE))) {
				if (bp[-2] != '\\')
					c = maptab[c];
				bp--;
			}
#ifndef	TTY_TRUE_RAW
		}
#endif
		*bp++ = c;
		if (bp>=canonb+TTYHOG)
			break;
	}
	bp1 = bp;
	bp = &canonb[2];
	c = &tp->t_canq;
	while (bp<bp1)
		putc(*bp++, c);
	return(1);
#endif
}

/*
 * Place a character on raw TTY input queue, putting in delimiters
 * and waking up top half as needed.
 * Also echo if required.
 * The arguments are the character and the appropriate
 * tty structure.
 */
ttyinput(ac, tp)
register struct tty *tp;	/* fix000 */
{
	register c;
	register int t_flags;

	c = ac & 0177;
#ifdef	TTY_CONNECT
	if(tp->t_rtype)
	{
		/*
		 * redirection of this tty port in progress
		 */
#		ifdef	TTY_MULTIPLEXED_PORTS
		if ( tp->t_rtype == TMULTIPLEX )
		{
			/** This line is being multiplexed **/
			struct { int (*func)(); };

			(*tp->t_redirect.func)( ac, tp );
			return;
		}
#		endif	TTY_MULTIPLEXED_PORTS
		if(c == tp->t_cchar && tp->t_rtype&TMASTER)
		{
			/*
			 * master terminate received - stop slave&master
			 */
			tp = tp->t_redirect;
			tp->t_rtype = tp->t_cchar = 0;
			tp = tp->t_redirect;
			tp->t_rtype = tp->t_cchar = 0;
			tp->t_redirect = (tp->t_redirect)->t_redirect = 0;
			wakeup(&tp->t_rtype);
			return;
		}
		switch(tp->t_rtype)
		{
	case TMASTER+TCONLOG:
	case	     TCONLOG:
			tp = tp->t_redirect;
			if(tp->t_outq.c_cc < TTYHOG)
			{
				putc(c, &tp->t_outq);
				ttstart(tp);
			}
			return;
#ifdef	TTY_SPECIAL_POWERS
	case TMASTER+TCONSHARE:
	case TMASTER+TCONGRAB:
			tp = tp->t_redirect;
			break;
	case	     TCONGRAB:
			return;
#endif	TTY_SPECIAL_POWERS
		}
	}
#endif	TTY_CONNECT
	t_flags = tp->t_flags;
	if(c == '\r')
	{
		if (t_flags&CRMOD)
			c = '\n';
	}
#ifdef	TTY_FLOW_CONTROL
	else if(c == ACK && (t_flags & (FLOWCNTRL|RAW)) == FLOWCNTRL)
	{
		if ( tp->t_state & FCSLEEP )
			wakeup( &tp->t_state );
		tp->t_state =& ~(ACKFLAG|FCINTFL|FCSLEEP);
		return;
	}
#endif	TTY_FLOW_CONTROL
	else if((c == CQUIT || c == CINTR) && (t_flags&RAW) == 0)
	{
		signal(tp, c==CINTR? SIGINT:SIGQIT);
		flushtty(tp);
		return;
	}

	if (tp->t_rawq.c_cc>=TTYHOG-1)  {	/* fix041 */
		flushtty(tp);
	}
	else					/* fix041 */
	{
#ifdef	TTY_INVCASE
		if ( t_flags&INVCASE && c>='A' )  {
			if ( c <= 'Z' )  c =+ 'a'-'A';
			else  if ( c >= 'a' && c <= 'z' )  c =- 'a'-'A';
		}else
#endif	TTY_INVCASE
		if (t_flags&LCASE && c>='A' && c<='Z')
			c =+ 'a'-'A';
#ifdef	TTY_TRUE_RAW
		if ( t_flags & RAW )  {
			if ( tp->t_state & RAWSLEEP )  {
				tp->t_state =& ~RAWSLEEP;
				wakeup( &tp->t_rawq );
			}
			if ( t_flags == RAW )
				c = ac;
			putc(c, &tp->t_rawq);
		}
		else
		{
			putc(c, &tp->t_rawq);
			if(c == '\n' || c == 004)
			{
				wakeup(&tp->t_rawq);
				if (putc(0377, &tp->t_rawq)==0)
					tp->t_delct++;
			}
		}
#else
		putc(c, &tp->t_rawq);
		if(t_flags&RAW || c=='\n' || c==004)
		{
			wakeup(&tp->t_rawq);
			if (putc(0377, &tp->t_rawq)==0)
				tp->t_delct++;
		}
#endif	TTY_TRUE_RAW
	}					/* fix041 */
	if(t_flags&ECHO)
	{
		ttyoutput(c, tp);
		if( c == '\b' && tp->t_erase == '\b')	/* fix033 */
		{					/* fix033 */
			ttyoutput(' ', tp);		/* fix033 */
			ttyoutput(c, tp);		/* fix033 */
		}					/* fix033 */
		ttstart(tp);
	}
#ifdef	TTY_SPECIAL_POWERS
	else if(tp->t_rtype == TCONVIEW || tp->t_rtype == TCONSHARE)
	{
		ttyoutput(c, tp->t_redirect);
		ttstart(tp->t_redirect);
	}
#endif	TTY_SPECIAL_POWERS
}

/*
 * put character on TTY output queue, adding delays,
 * expanding tabs, and handling the CR/NL bit.
 * It is called both from the top half for output, and from
 * interrupt level for echoing.
 * The arguments are the character and the tty structure.
 */
ttyoutput(c, tp)
register char c;			/* fix000 */
register struct tty *tp;		/* fix000 */
{
	register union { char *colp; int flags; } i;	/* fasterfix */
	int ctype;

#ifdef	TTY_SPECIAL_POWERS
	if(tp->t_rtype)
	{
		switch(tp->t_rtype)
		{
	case TCONVIEW:
	case TCONSHARE:
			ttyoutput(c, tp->t_redirect);
			ttstart(tp->t_redirect);
			break;
	case TCONGRAB:
			ttyoutput(c, tp->t_redirect);
			ttstart(tp->t_redirect);
			return;
		}
	}
#endif	TTY_SPECIAL_POWERS
	i.flags = tp->t_flags;	/* fasterfix */

#ifdef	TTY_FLOW_CONTROL
	if ( i.flags & FLOWCNTRL && tp->t_state & ESCFL )
		return;	/* don't interrupt escape sequence */
#endif	TTY_FLOW_CONTROL

#ifdef	TTY_TRUE_RAW
	if ( ( i.flags & RAW ) == 0 )
#endif
		c =& 0177;
	/*
	 * Ignore EOT in normal mode to avoid hanging up
	 * certain terminals.
	 */
	if (c==004 && (i.flags&RAW)==0)
		return;
	/*
	 * Turn tabs to spaces as required
	 */
	if (c=='\t' && i.flags&XTABS) {
		c = 8 - (tp->t_col&07);	/* fix022 */
		do
			putc(' ', &tp->t_outq);
		while ( --c );		/* fix022 */
		tp->t_col = (tp->t_col+8) & ~07;
	/*	while (tp->t_col&07);	/* fix022 */
		return;
	}
	/*
	 * for upper-case-only terminals,
	 * generate escapes.
	 */
	if (i.flags&LCASE) {
		i.colp = "({)}!|^~'`";
		while(*i.colp++)
			if(c == *i.colp++) {
				putc('\\', &tp->t_outq);
				tp->t_col++;
				c = i.colp[-2];
				break;
			}
		if ('a'<=c && c<='z')
			c =+ 'A' - 'a';
		i.flags = tp->t_flags;
	}
	/*
	 * turn <nl> to <cr><lf> if desired.
	 */
	if (c=='\n' && i.flags&CRMOD)
		ttyoutput('\r', tp);

	if (putc(c, &tp->t_outq))
		return;

#ifdef	TTY_TRUE_RAW
	if ( i.flags == RAW )  return;	/* true RAW only */
#endif
#ifdef	TTY_FLOW_CONTROL
	tp->t_count++;
#endif

	/*
	 * Calculate delays.
	 * The numbers here represent clock ticks
	 * and are not necessarily optimal for all terminals.
	 * The delays are indicated by characters above 0200,
	 * thus (unfortunately) restricting the transmission
	 * path to 7 bits.
	 */
	i.colp = &tp->t_col;
	ctype = partab[c];
	c = 0;
	switch (ctype&077) {

	/* ordinary */
	case 0:
		(*i.colp)++;

	/* non-printing */
	case 1:
		break;

	/* backspace */
	case 2:
		if (*i.colp)
			(*i.colp)--;
		break;

	/* newline */
	case 3:
		ctype = (tp->t_flags >> 8) & 03;
		if(ctype == 1) { /* tty 37 */
			if (*i.colp)
				c = max((*i.colp>>4) + 3, 6);
		} else
		if(ctype == 2) { /* vt05 */
			c = 6;
		}
		*i.colp = 0;
		break;

	/* tab */
	case 4:
		ctype = (tp->t_flags >> 10) & 03;
		if(ctype == 1) { /* tty 37 */
			c = 1 - (*i.colp | ~07);
			if(c < 5)
				c = 0;
		}
		*i.colp =| 07;
		(*i.colp)++;
		break;

	/* vertical motion */
	case 5:
		if(tp->t_flags & VTDELAY) /* tty 37 */
			   c = 0177;
		break;

	/* carriage return */
	case 6:
		ctype = (tp->t_flags >> 12) & 03;
		if(ctype == 1) { /* tn 300 */
			c = 5;
		} else
		if(ctype == 2) { /* ti 700 */
			c = 10;
		}else
		if ( ctype == 3 )  {	/* NCR260 */
			/* need 1 tic delay for every 8 cols */
			c = (*i.colp>>3);
		}
		*i.colp = 0;
	}

	if(c)
		putc(c|0200, &tp->t_outq);
}

/*
 * Restart typewriter output following a delay
 * timeout.
 * The name of the routine is passed to the timeout
 * subroutine and it is called during a clock interrupt.
 */
ttrstrt(tp)
register struct tty *tp;
{
	tp->t_state =& ~TIMEOUT;
	ttstart(tp);
}

/*
 * Start output on the typewriter. It is used from the top half
 * after some characters have been put on the output queue,
 * from the interrupt routine to transmit the next
 * character, and after a timeout has finished.
 * If the SSTART bit is off for the tty the work is done here,
 * using the protocol of the single-line interfaces (KL, DL, DC);
 * otherwise the address word of the tty structure is
 * taken to be the name of the device-dependent startup routine.
 *
 * Always assumed to be called at level five or greater
 *
 */
ttstart(tp)
register struct tty *tp;	/* fix000 */
{
	register int *addr, c;
	struct { int (*func)(); };

	if ( tp->t_state & TIMEOUT )	/* fix008 */
		return;

	addr = tp->t_addr;
	if (tp->t_state&SSTART) {
		(*addr.func)(tp);
		return;
	}
	if ((addr->tttcsr&DONE)==0)	/* fix008 */
		return;
	if ((c=getc(&tp->t_outq)) >= 0) {
#ifdef	TTY_TRUE_RAW
		if ( tp->t_flags == RAW )  addr->tttbuf = c;
		else
#endif
		if (c<=0177)
			addr->tttbuf = c | (partab[c]&0200);
		else {
			timeout(ttrstrt, tp, c&0177);
			tp->t_state =| TIMEOUT;
		}
	}
}

/*
 * Called from device's read routine after it has
 * calculated the tty-structure given as argument.
 */
ttread(tp)
register struct tty *tp;	/* fix000 */
{

#ifdef	TTY_TRUE_RAW
	if ( tp->t_flags & RAW )  {
		do
		{
			spl5();
			while ( tp->t_rawq.c_cc == 0 )
				if ( tp->t_state & CARR_ON )  {
					tp->t_state =| RAWSLEEP;
					sleep( &tp->t_rawq , TTIPRI );
				}else  {
					spl0();
					return;
				}
			spl0();
		}
		while
			( passc( getc( &tp->t_rawq ) ) >= 0 );
	}else
#endif
#ifndef	TTY_TRUE_RAW
	if ((tp->t_state&CARR_ON)==0)
		return;
#endif
	if (tp->t_canq.c_cc || canon(tp))
		while (tp->t_canq.c_cc && passc(getc(&tp->t_canq))>=0);
}

/*
 * Called from the device's write routine after it has
 * calculated the tty-structure given as argument.
 */
ttwrite(tp)
register struct tty *tp;	/* fix000 */
{
	register int *state, c;
#ifdef	TTY_FLOW_CONTROL
	extern	int	ttackfix();
#endif

	state = &tp->t_state;
	if ((*state&CARR_ON)==0)
		return;
	while ((c=cpass())>=0) {
		spl5();
#ifdef	TTY_HISPEED
		while ( (tp->t_outq.c_cc>TTHIWAT) && ( ((tp->t_speeds&017)<=B1200) || (tp->t_outq.c_cc>TTHSHIWAT) ) )  {
#endif
#ifndef	TTY_HISPEED
		while (tp->t_outq.c_cc > TTHIWAT) {
#endif
			ttstart(tp);
			*state =| ASLEEP;
			sleep(&tp->t_outq, TTOPRI);
		}
#ifdef	TTY_FLOW_CONTROL
		if ( (tp->t_flags & FLOWCNTRL) && (tp->t_count >= FCBUFZ - 1) && ((*state & ESCFL) == 0) )  {
			while ( putc( ETX , &tp->t_outq ) )
				delay( 2 );
			while ( *state & ACKFLAG )  {
				if ( !tp->t_timep )
					tp->t_timep = timeout( ttackfix, tp, FCTIMEOUT );
				*state =| FCSLEEP;
				sleep( state , TTOPRI );
			}
			untimeout( tp->t_timep, ttackfix, tp );
			tp->t_timep = 0;
			*state =| ACKFLAG;
			tp->t_count =- FCBUFZ-1;
		}
#endif
		spl0();
#ifdef	TTY_FLOW_CONTROL
		if ( tp->t_flags & FLOWCNTRL )
		if ( (*state & ESCFL) == 0 )  {  if ( (c & 0177) == ESC )  {  *state =| ESCFL;  goto put; }  }
		else
		{
			if ( *state & ESC2FL )  *state =& ~(ESCFL|ESC2FL);
			else
				switch ( c & 0177 )  {
				 default:	*state =& ~ESCFL;  break;
				 case VT: case HT: case RS: case US:  *state =| ESC2FL;	/* per DIABLO HYTERM */
				}

put:
			while ( putc( c, &tp->t_outq ) )
				delay( 2 );
			tp->t_count++;
			continue;
		}
#endif
		ttyoutput(c, tp);
	}
	spl5(); ttstart(tp); spl0();	/* fix014 */
}

/*
 * Common code for gtty and stty functions on typewriters.
 * If v is non-zero then gtty is being done and information is
 * passed back therein;
 * if it is zero stty is being done and the input information is in the
 * u_arg array.
 */
ttystty(tp, v)
register int *v;		/* fix000 */
register struct tty *tp;	/* fix000 */
{

	if( v ) {
		*v++ = tp->t_speeds;
		v->lobyte = tp->t_erase;
		v->hibyte = tp->t_kill;
		v[1] = tp->t_flags;
		return(1);
	}

	wflushtty(tp);
	v = u.u_arg;

#ifdef	TTY_SUSER
	{
		register struct lnode *lp;

	if( (v->hibyte>tp->t_speeds.hibyte || v->lobyte>tp->t_speeds.lobyte)
	 && (v->hibyte > TTY_MAXSPD || v->lobyte > TTY_MAXSPD)
	 && (lp = u.u_procp->p_lnode) && (lp->l_flags & TTYOPTS) == 0)
		*v = tp->t_speeds;	/* don't allow `bad' speeds */
	}
#endif	TTY_SUSER
	tp->t_speeds = *v++;
	tp->t_erase = v->lobyte;
	tp->t_kill = v->hibyte;
	tp->t_flags = v[1];
	return(0);
}

#ifdef	TTY_FLOW_CONTROL
ttackfix( tp )
  register struct tty *tp;
{
  tp->t_timep = 0;
  if ( tp->t_state & FCSLEEP )
	wakeup( &tp->t_state );
  tp->t_state =& ~(FCSLEEP|ACKFLAG);
}
#endif	TTY_FLOW_CONTROL

#ifdef	TTY_CONNECT
struct tty *ttyget(arg)
int arg;
{
	register struct tty *tp;
	register struct inode *ip;
	register struct file *fp;
	int dev;

	if((fp = getf(arg)) == NULL)
	{
		return 0;	/* illegal fd passed */
	}
	if((fp->f_flag&(FREAD|FWRITE)) != (FREAD|FWRITE))
	{
		u.u_error = EACCES;	/* no permission */
		return 0;
	}
	ip = fp->f_inode;
	if((ip->i_mode&IFMT) != IFCHR)
	{
		u.u_error = ENOTTY;
		return 0;
	}
	dev = ip->i_addr[0];
	if((tp = cdevsw[dev.d_major].d_tty) == 0)
	{
		u.u_error = ENOREDIRECT; /* not capable */
		return 0;
	}
	return tp + dev.d_minor;
}

ttyconnect()
{
	register struct tty *mtp, *stp;
	register int rtype;

	if(!suser())
		return;
	rtype = u.u_arg[0];	/* master control character */
	if((mtp = ttyget(u.u_ar0[R0])) == 0) return;
	if((stp = ttyget(u.u_ar0[R1])) == 0) return;

	spl5();

	switch(rtype)
	{
    case 0:	/* disconnect */
		if(mtp != stp || mtp->t_rtype == 0)
		{
			u.u_error = EREDIRECT;
		}
		else
		{
			stp = mtp->t_redirect;
			if(stp->t_redirect != mtp)
			{
				u.u_error = EREDIRECT;
			}
			else
			{
				/*
				 * wakeup master tty after
				 * determining which it is
				 */
				if(mtp->t_rtype&TMASTER)
					wakeup(&mtp->t_rtype);
				else
					wakeup(&stp->t_rtype);
				mtp->t_rtype = stp->t_rtype = 0;
				mtp->t_cchar = stp->t_cchar = 0;
				mtp->t_redirect = stp->t_redirect = 0;
			}
		}
		break;
    case TCONLOG:
#ifdef	TTY_SPECIAL_POWERS
    case TCONVIEW:
    case TCONGRAB:
    case TCONSHARE:
#endif	TTY_SPECIAL_POWERS
		if(mtp == stp || mtp->t_rtype || stp->t_rtype )
		{
			/* already redirected */
			u.u_error = EREDIRECT;
		}
		else
		{
			mtp->t_rtype = TMASTER+rtype;
			stp->t_rtype = rtype;
			mtp->t_cchar = u.u_arg[1];
			mtp->t_redirect = stp;
			stp->t_redirect = mtp;
			while(mtp->t_rtype)
				sleep(&mtp->t_rtype, PRIREDIRECT);
		}
		break;
    default:
		u.u_error = EINVAL;
		break;
	}

	spl0();
}
#endif	TTY_CONNECT