SRI-NOSC/dmr/ffille

etty.cQH#
/* Revision - New handshake handler */

/*
 * general TTY subroutines
 */
#include "param.h"
#include "systm.h"
#include "user.h"
/*#include "userx.h"*/
#include "tty.h"
#include "proc.h"
/*#include "procx.h"*/
#include "inode.h"
/*#include "inodex.h"*/
#include "file.h"
/*#include "filex.h"*/
#include "reg.h"
#include "conf.h"

#define	DLDELAY	4

/*
 * The actual structure of a clist block manipulated by
 * getc and putc (mch.s)
 */
struct cblock {
	struct cblock *c_next;
	char info[6];
};
/* Structure to select one char out of queue */
struct {
	char	q_char;
};

/* The character lists-- space for 6*NCLIST characters */
struct cblock cfree[NCLIST];
/* List head for unused character blocks. */
struct cblock *cfreelist;

/*
 * 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;
};


/*
 * routine called on first teletype open.
 * establishes a process group for distribution
 * of quits and interrupts from the tty.
 */
ttyopen(dev, atp)
struct tty *atp;
{
	register struct proc *pp;
	register struct tty *tp;

	pp = u.u_procp;
	tp = atp;
	if(pp->p_pgrp == 0) {
		pp->p_pgrp = pp->p_pid;
		u.u_ttyp = tp;
		u.u_ttyd = dev;
		tp->t_pgrp = pp->p_pid;
	}

	if ((tp->t_state & ISOPEN) == 0)
	    {
		tp->t_asize = TTACK;
		tp->t_enq = CENQ;
		tp->t_ack = CACK;
		tp->t_esc = CESC;
		tp->t_cntla = CSPEC;
		tp->t_freeze = CFREEZE;
		tp->t_thaw = CTHAW;
		tp->t_hold = CHOLD;
		tp->t_release = CREL;
		tp->t_retype = CRETYPE;
		tp->t_clear = CCLEAR;
		tp->t_silent = CSILENT;
		tp->t_atime = 60;
		tp->t_alwat = (tp->t_asize&0377) >> 1;
	    }

	tp->t_state =& ~WOPEN;
	tp->t_state =| ISOPEN;

}


/*
 * 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);
	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;
	}
	(*cdevsw[ip->i_addr[0].d_major].d_sgtty)(ip->i_addr[0], v);
}


/*
 * Wait for output to drain, then flush input waiting.
 */
wflushtty(atp)
struct tty *atp;
{
	register struct tty *tp;

	tp = atp;
	spl5();
	while (tp->t_outq.c_cc) {
		tp->t_state =| ASLEEP;
		sleep(&tp->t_outq, TTOPRI);
	}
	flushtty(tp);
	spl0();
}


/*
 * Initialize clist by freeing all character blocks, then count
 * number of character devices. (Once-only routine)
 */
cinit()
{
	register int ccp;
	register struct cblock *cp;
	register struct cdevsw *cdp;

	ccp = cfree;
	for (cp=(ccp+07)&~07; cp <= &cfree[NCLIST-1]; cp++) {
		cp->c_next = cfreelist;
		cfreelist = cp;
	}
	ccp = 0;
	for(cdp = cdevsw; cdp->d_open; cdp++)
		ccp++;
	nchrdev = ccp;
}


/*
 * flush all TTY queues
 */
flushtty(atp)
struct tty *atp;
{
	register struct tty *tp;
	register int sps;

	sps = PS->integ;
	spl5();
	tp = atp;
	while (getc(&tp->t_canq) >= 0);
	while (getc(&tp->t_outq) >= 0);
	wakeup(&tp->t_canq);
	wakeup(&tp->t_outq);
	PS->integ = sps;
}



/*
 * Place a character on can 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.
 */

/* Echo places a character on the appropriate tty's output queue, and starts
 * it up. Also, some processing to output certain control characters is done */

echo(ac,atp)
register struct tty *atp;
{
	if (ac < 0 || ((atp->t_flags & ECHO) == 0)) return;
	if (ac <= 037 && (ac < 07 || ac > 012))
	    {
		echo ('^',atp);
		ac =+ 0100;
	    }
	ttyoutput(ac,atp);
	ttstart(atp);
}

echostr(s,tp)
register char *s;
register struct tty *tp;
{
	if ((tp->t_flags & ECHO) == 0) return;
	while(*s) putc(*s++,&tp->t_outq);
	ttstart(tp);
}

ttcursor(x,y,tp)
register struct tty *tp;
{
	if ((tp->t_flags & ECHO) == 0) return;
	echostr("\033Y",tp);		/* escape sequence */
	putc(x+040,&tp->t_outq);	/* row */
	putc(y+040,&tp->t_outq);	/* col */
	putc(0201,&tp->t_outq);		/* vt52 delay */
	ttstart(tp);
	tp->t_col = y;
}

ttretype(tp)
register struct tty *tp;
{
	register cptr;

	cptr = tp->t_llf;		/* start from end of last line */

/*	if ((cptr = tp->t_llf) == 0)
		cptr = tp->t_canq.c_cf; */

	if (tp->t_canq.c_cc)
		while (cptr != tp->t_canq.c_cl) {
			if ((cptr & 07) == 0)
				if ( (cptr = &((cptr - 010)->c_next->info[0]))
					== tp->t_canq.c_cl )
					break;
			echo(cptr->q_char,tp);
			cptr++;
		}
}

char *tterase "\b \b";
char *ttclear "\033H\033J";
char *ttforw "\033C";
char *ttkill "\033K";

ttyinput(ac,atp)
struct tty *atp;
{
	register int t_flags, c;
	register struct tty *tp;
	int	cptr;
	int	cano;			/* type of terminal */

/*	tk_nin =+ 1;*/
	tp = atp; c=ac&0377; t_flags = tp->t_flags;	/* Load registers */

	if ((tp->t_flag2 & LITIN) == 0) c =& 0177;

	if (tp->t_flag2 & (RACK | WACK))
	  {	if ((tp->t_flag2 & WACK) && (c == (tp->t_ack & 0377)))
		    {
			tp->t_acnt = tp->t_asize;
			wakeup(&tp->t_acnt);
			return;
		    }

		if ((tp->t_flag2 & RACK) && (c == (tp->t_enq & 0377)))
		    {
			if ((tp->t_canq.c_cc <= tp->t_alwat) || /* ack if possible */
			  (tp->t_llf == tp->t_canq.c_cf))
			    {
				putc(tp->t_ack,&tp->t_outq);
				ttstart(tp);
			    }
			else
				tp->t_state =| ENQRCV;
			return;
		    }

		if (tp->t_state & ESCRCV)
		    {
			tp->t_state =& ~ESCRCV;
			switch(c)
			    {
				case CESC1:	c = tp->t_enq; break;
				case CESC2:	c = tp->t_ack; break;
			    }
		    }
		else if (c == (tp->t_esc & 0377))
		    {
			tp->t_state =| ESCRCV;
			return;
		    }
	  }
	if ((tp->t_flag2 & WHOLD) && !(tp->t_state & HELD)
		&& (tp->t_canq.c_cc >= tp->t_asize))
	    {						/* send a HOLD character */
		tp->t_state =| HELD;
		putc(tp->t_hold, &tp->t_outq);
		ttstart(tp);
	    }
	if (tp->t_flag2 & LITIN)			/* Literal input */
	    {
		if (tp->t_canq.c_cc >= TTYHOG) return;
		putc(c,&tp->t_canq);
		tp->t_llf = tp->t_canq.c_cl;		/* set llf */
		wakeup(&tp->t_canq);			/* wake up!! */
		return;
	    }

	if (t_flags & CRMOD)
		if (c == '\r') c = '\n';
/*	following misfeature flushed 5/15/79 --KLH
		else if (c == '\n') c = '\r'	*/

	if (((c==tp->t_intr) || (c==tp->t_quit) || (c==tp->t_cntla))
		&& ((tp->t_flag2 & EEI) || ((t_flags & RAW) == 0)))
	    {
		if (c == tp->t_intr)
			signal (tp->t_pgrp,SIGINT);
		else
			signal (tp->t_pgrp, c==tp->t_quit?SIGQIT:SIGSPC);
		flushtty (tp);
		tp->t_state =& ~FROZEN;			/* signals thaw tty */
		echo(c,tp);
		echo('\n',tp);
		return;
	    }

	if (tp->t_canq.c_cc >= TTYHOG) {
		if (t_flags & RAW) return;
		if ((c != tp->t_erase) && (c != tp->t_kill)) return;
	}

	switch(tp->t_cano) {
		case TC_GT40:
		case TC_TERM:
		case TC_VT52:
			cano = 1;
			break;
		default:
			cano = 0;
			break;
	}
	if (t_flags & LCASE && c >= 'A' && c <= 'Z')	/* Convert to lower case if necessary */
		c =+ 'a' - 'A' ;
	if (t_flags & RAW)
	    {
		putc(c,&tp->t_canq);
	    }
	else if (c == tp->t_erase)
	    {
		c = -1;
		if ((tp->t_llf != tp->t_canq.c_cl ) &&
		    ((c = unputc(&tp->t_canq)) >= 0))
			if (cano) {	/* special terminals */
				switch (partab[c] & 077) {
				case 1:		/* non - printing */
					if (c <= 012 && c >= 07) break;
				case 5:
				case 6:	
					echostr(tterase,tp);
					tp->t_col--;
				case 0:		/* normal */
					echostr(tterase,tp);
					tp->t_col--;
					break;
				case 2:
					echostr(ttforw,tp);
					tp->t_col++;
					break;
				case 4:
					ttcursor(0136,tp->t_delpos,tp);
					ttretype(tp);
					break;
				}
			c = -1;
					
			/* normal printing terminal delete */
			} else
				if ((tp->t_state & ERMODE) == 0)
				    {
					echo('\\',tp);
					tp->t_state =| ERMODE;
				    }
	    }
	else
	    {
		if (tp->t_state & ERMODE)		/* If in erase mode, */
		    {
			echo('\\',tp);			/* echo '\' to indicate out of erase mode */
			tp->t_state =& ~ERMODE;		/* and turn it off */
		    }
		if (c == tp->t_retype)
		    {
			if (cano) ttcursor(0136,tp->t_delpos,tp);
			else {
				echo(c,tp);
				echo('\n',tp);
			}
			ttretype(tp);
			c = -1;
		    }
		else if (c == tp->t_kill)
		    {
			if (cano) {
				ttcursor(0136,tp->t_delpos,tp);
				echostr(ttkill,tp);
			} else {
				echo (c,atp);
				echo('\n',tp);
			}
			while ((tp->t_llf != tp->t_canq.c_cl) &&
				(unputc(&tp->t_canq) > 0)) ;	/* flush till empty or end-of-line */
			c = -1;
		    }
		else if (c == tp->t_EOT)		/* End of file indicator */
		    {
			putc(c,&tp->t_canq);
			c = -2;
		    }
		else if (c == tp->t_silent)
		    {
			flushtty(tp);
			echo (c,tp); echo ('\n',tp); echo ('\n',tp);
			if (tp->t_flag2 & SILENT)
				tp->t_flag2 =& ~SILENT;
			else tp->t_flag2 =| SILENT;
		    }
		else if (c == tp->t_clear)
		    {
			if (cano) echostr(ttclear,tp);
			else {
				echo(c,tp);
				echo('\n',tp);
			}
			ttretype(tp);
			tp->t_delpos = 0;
			c = -1;
		    }
		else if (c == tp->t_freeze)
		  {	tp->t_state =| FROZEN;		/* freeze the screen */
			return;
		  }
		else if (c == tp->t_thaw)
		  {	tp->t_state =& ~FROZEN;		/* wakeup output   */
			wakeup(&tp->t_outq);
			return;
		  }
		else
			putc(c,&tp->t_canq);		/* Anything else goes straight to can queue */
	    }

	echo(c,tp);					/* Echo the character */
	if (tp->t_canq.c_cc == 1 || (tp->t_llf&07) == 0)
		tp->t_llf = tp->t_canq.c_cf;		/* Initialize t_llf */

	if (t_flags & RAW || c == '\n' || c == -2)	/* If line delimiter, wake up canan() */
	    {
		tp->t_delpos = 0;			/* for kill processing */
		tp->t_llf = tp->t_canq.c_cl;
		wakeup(&tp->t_canq);
	    }
	return;						/* (Implied return anyway) */
}

/*
 * 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(ac, tp)
struct tty *tp;
{
	register int c;
	register struct tty *rtp;
	register char *colp;
	int ctype,sps;

/*	tk_nout =+ 1;*/
	rtp = tp;
	c = ac;
	if (rtp->t_flag2 & SILENT) return(0);

	if ((rtp->t_flag2 & (RACK | WACK))
		&& ((c == tp->t_enq) || (c == tp->t_ack) ||
		  (c == tp->t_esc)))
		    {
			if (putc(rtp->t_esc,&rtp->t_outq)) return(1);
			if (c == rtp->t_enq) c = CESC1;
			else if (c == rtp->t_ack) c = CESC2;
		    }
			

	if (rtp->t_flag2 & LITOUT)			/* Literal output */
	    {
		if (putc(c, &rtp->t_outq))
			return(1);
		else
		     {
			if (rtp->t_acnt) rtp->t_acnt--;
			return(0);
		     }
	    }
	c =& 0177;
	/*
	 * Ignore EOT in normal mode to avoid hanging up
	 * certain terminals.
	 */
	if (c==004 && (rtp->t_flags&(RAW|WACK|RACK))==0)
		return(0);
	/*
	 * Turn tabs to spaces as required
	 */
	if (c=='\t' && rtp->t_flags&XTABS) {
		do
			if (ttyoutput(' ', rtp)) return(1);
		while (rtp->t_col&07);
		return(0);
	}
	/*
	 * for upper-case-only terminals,
	 * generate escapes.
	 */
	if (rtp->t_flags&LCASE) {
		colp = "({)}!|^~'`";
		while(*colp++)
			if(c == *colp++) {
				if (ttyoutput('\\', rtp)) return(1);
				c = colp[-2];
				break;
			}
		if ('a'<=c && c<='z')
			c =+ 'A' - 'a';
	}
	/*
	 * turn <nl> to <cr><lf> if desired.
	 */
	if (c=='\n') if (ttyoutput('\r', rtp)) return(1);

	if (putc(c, &rtp->t_outq)) return(1);	/* put char in buffer */
	if (rtp->t_acnt) rtp->t_acnt--;

	/*
	 * 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.
	 */
	colp = &rtp->t_col;
	ctype = partab[c];
	c = 0;
	switch (ctype&077) {

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

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

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

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

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

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

	/* carriage return */
	case 6:
		ctype = (rtp->t_flags >> 12) & 03;
		if(ctype == 1) { /* tn 300 */
			c = 5;
		} else
		if(ctype == 2) { /* ti 700 */
			c = 10;
		}
		*colp = 0;
	}
	if(c) putc(c|0200, &rtp->t_outq);  /* put in delay unless zero */
	return(0);	/* we always return 0 here because if inserting
				the delay char fails, we can just ignore. */
}



/*
 * 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(atp)
{
	register struct tty *tp;

	tp = atp;
	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.
 */
ttstart(atp)
struct tty *atp;
{
	register int *addr, c;
	int sps;
	register struct tty *tp;
	struct { int (*func)(); };

	tp = atp;
	addr = tp->t_addr;
	if (tp->t_state&SSTART) {
		(*addr.func)(tp);
		return;
	}
	sps = PS->integ;
	spl5();
	if ((addr->tttcsr&DONE)==0 || tp->t_state&TIMEOUT)
		return;
	if ((c=getc(&tp->t_outq)) >= 0) {
		if (tp->t_flag2 & LITOUT)			/* Literal output */
			addr->tttbuf = c;
		else if (c<=0177)
			addr->tttbuf = c | (partab[c]&0200);
		else {
			timeout(ttrstrt, tp, (c&0177) + DLDELAY);
			tp->t_state =| TIMEOUT;
		}
	}
	PS->integ = sps;
}


/*
 * Called from device's read routine after it has
 * calculated the tty-structure given as argument.
 * The pc is backed up for the duration of this call.
 * In case of a caught interrupt, an RTI will re-execute.
 */
ttread(atp)
struct tty *atp;
{
	register struct tty *tp;
	register int c;

	tp = atp;
	tp->t_flag2 =& ~SILENT;
	spl5();
	while ((tp->t_canq.c_cc == 0) || (tp->t_llf == tp->t_canq.c_cf))
	    {
		if ((tp->t_state & CARR_ON) == 0)
			return;
		sleep(&tp->t_canq,TTIPRI);
	    }
	spl0();
	while ((tp->t_canq.c_cf) && (tp->t_llf != tp->t_canq.c_cf))
	    {
		c = getc (&tp->t_canq);
		if (tp->t_canq.c_cc <= tp->t_alwat)
		    {
			if ((tp->t_flag2 & RACK) && (tp->t_state & ENQRCV))
			    {
				tp->t_state =& ~ENQRCV;
				while (putc(tp->t_ack, &tp->t_outq)) sleep(&lbolt,22);
				ttstart(tp);
			    }

			if ((tp->t_flag2 & WHOLD) && (tp->t_state & HELD))
			    {
				tp ->t_state =& ~HELD;
				while (putc(tp->t_release, &tp->t_outq)) sleep(&lbolt,22);
				ttstart(tp);
			    }
		    }
		if (tp->t_flags & RAW)
		    {
			passc(c);
			break;
		    }
		if ((c == tp->t_EOT) || passc(c) < 0 || c == '\n')
			break;
	    }
}

/*
 * wakes up process waiting for an acknowledge if the
 * ACKWAIT bit in t_state is still set.
 */
ttywakeup(atp)
register struct tty *atp;
{
	if (atp->t_state & ACKWAIT)
	     {
		atp->t_state =& ~ACKWAIT;
		wakeup(&atp->t_acnt);
	     }
}

/*
 * Called from the device's write routine after it has
 * calculated the tty-structure given as argument.
 */
ttwrite(atp)
struct tty *atp;
{
	register struct tty *tp;
	register int c;

	tp = atp;
	if ((tp->t_state&CARR_ON)==0)
		return;
	while ((c=cpass())>=0) {
		spl5();
		while(tp->t_state & FROZEN)
			sleep(&tp->t_outq, TTOPRI);	/* wait for thaw  */
		while (tp->t_outq.c_cc > TTHIWAT) {
			ttstart(tp);
			tp->t_state =| ASLEEP;
			sleep(&tp->t_outq, TTOPRI);
		}
		while((tp->t_flag2 & WACK) && (tp->t_acnt == 0))
		     {
			while (putc(tp->t_enq, &tp->t_outq)) sleep(&lbolt,22);
			ttstart(tp);
			if ((tp->t_state & ACKWAIT) == 0)
			     {
				timeout(ttywakeup,tp,tp->t_atime);
				tp->t_state =| ACKWAIT;
			     }
			sleep(&tp->t_acnt,TTOPRI);
		     }
		spl0();

		while (ttyoutput(c, tp)) sleep(&lbolt, 21);
	}
	tp->t_delpos = tp->t_col;
	ttstart(tp);
}


/*
 * 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(atp, av)
int *atp, *av;
{
	register  *tp, *v;

	tp = atp;
	if(v = av) {
		*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;
	tp->t_speeds = *v++;
	tp->t_erase = v->lobyte;
	tp->t_kill = v->hibyte;
	tp->t_flags = v[1];
	return(0);
}


/* common code for ttymode handlers */

ttmode(atp,acp)
int *acp;
char atp[];

/* Rather than considering atp as a pointer to a tty structure, it is treated
 * as an array of bytes, so that the individual bytes can be referenced as an
 * offset from the base, rather than being individually selected via ->.
 */

{
	register bn,bits;
	register int *cp;

	cp = acp;
	bits = (*cp) & TBITS;			/* extract argument bits
*/
	if ((bn = (((*cp)&TBYTNUM)>>8)) > TMAXBN)	/* extract byte number, and check for validity */
	    {
		u.u_error = EINVAL;		/* invalid argument */
		return(1);
	    }

	if ((*cp) & TFLUSH) wflushtty(atp);	/* request to flush buffer first*/

	*cp = ((*cp) & 0177400) + (atp[TFIXOFS+bn] & 0377);	/* current value placed in *cp */
	switch((*cp) & TCNTL)				/* control bits */
	    {
		case TCLEAR:				/* bit clear */
			atp[TFIXOFS + bn] =& ~bits;
			break;
		case TSET:				/* bit set */
			atp[TFIXOFS + bn] =| bits;
			break;
		case TREPLAC:
			atp[TFIXOFS + bn] = bits;	/* replace */
		case TGET: ;				/* just get it. (done anyway) */
	    }
	if (((*cp)&TCNTL != TGET) && (bn<6))		/* if speeds or mode changed, return 0. (for dhttymd) */
		return(0);
	return(1);
}
)dh.cC#
/*
 *	DH-11 driver
 *	This driver calls on the DHDM driver.
 *	If the DH has no DM11-BB, then the latter will
 *	be fake. To insure loading of the correct DM code,
 *	lib2 should have dhdm.o, dh.o and dhfdm.o in that order.
 *
 *      This driver was modified to allow  the  use  of  multiple
 *      DH11s.   To avail yourself of this feature (or to disable
 *      it) you should do the following:
 *
 *      In this module: Initialize the global array dhaddrs  with
 *          the  base addresses for your dh11s.  Initialize NDH11
 *          to the total number of dh11  ports  in  your  system.
 *          Dimension dhsar to the number of dh11s you have.
 *
 *      In l.s: When you initialize  the  interrupt  vectors  for
 *          your  dh11s, add to each interrupt PS a dh11 index (0
 *          for the first dh11,  1  for  the  second  etc).  This
 *          number   will  be  used  to  index  into  dhaddrs  to
 *          determine  which  set   of   registers   caused   the
 *          interrupt.  It allows all sets of dh registers to use
 *          the same interrupt routines.
 *
 *      globals:
 *
 *      dhaddrs   contains base addresses for the various dh11s in the
 *                system.
 *
 *      dhsar     contains (for each dh11) a copy of the bar as it
 *                was last known to be.
 *
 *      history:
 *
 *      Initially a Release six driver.
 *      Modified by Dennis L. Mumaugh for multiple DH/DM support
 *              15 April 1977
 *      Thanks to Mark Kampe (UCLA) for assistance
 */

#include "param.h"
#include "conf.h"
#include "user.h"
/*#include "userx.h"*/
#include "tty.h"
#include "proc.h"
#ifdef DNTTY
#include "baudot.h"	/* DN: file containing baudot mapping tables */
#define VRTMO 20+20	/* 60'ths of sec to wait for dropping RTS */
#define IDLTMO (15*60*60)&077777 /* 60'ths of sec to sleep between idle chks */
#endif DNTTY

#ifndef NDH11
#define	NDH11	32	/* number of lines */
#endif NDH11
#define	DHNCH	8	/* max number of DMA chars */

struct	tty dh11[NDH11];
/*
 * Place from which to do DMA on output
 */
char	dh_clist[NDH11][DHNCH];

/*
 * Used to communicate the number of lines to the DM
 */
int	ndh11	NDH11;

/*
 * Hardware control bits
 */
#ifdef DNTTY
#define BITS5	00	/* DN: for Baudot terms */
#endif DNTTY
#define	BITS6	01
#define	BITS7	02
#define	BITS8	03
#define	TWOSB	04
#define	PENABLE	020
#define	OPAR	040	/* Old DEC manuals wrongly say this sets EVEN parity.*/
#define	HDUPLX	040000

#define	IENABLE	030100
#define	PERROR	010000
#define	FRERROR	020000
#define	XINT	0100000
#define	SSPEED	7	/* standard speed: 300 baud */

/*
 * DM control bits
 */
#define	TURNON	03	/* CD lead + line enable */
#define	TURNOFF	01	/* line enable */
#define	RQS	04	/* request to send */

#define	XCLUDE	0200	/* exclusive-use flag against open */

/*
 * Software copy of last dhbar
 */
int 	dhsar[2];

struct dhregs {
	int dhcsr;
	int dhnxch;
	int dhlpr;
	int dhcar;
	int dhbcr;
	int dhbar;
	int dhbreak;
	int dhsilo;
};

int dhaddrs[2] { 0760020, 0760040 };

#ifdef DNTTY
int dnidlflg;	/* Set when idle timeout rtn activated */
#endif DNTTY

/*
 * Open a DH11 line.
 */
dhopen(dev, flag)
{
	register struct tty *tp;
	register struct dhregs *dhaddr;
	extern dhstart();

#ifdef DNTTY
	extern dnidltmo();
	if(dnidlflg == 0) {
		dnidlflg++;
		timeout(dnidltmo, 0, IDLTMO);
		}
#endif DNTTY

	if (dev.d_minor >= NDH11) {
		u.u_error = ENXIO;
		return;
	}
	dhaddr = dhaddrs[ dev.d_minor >> 4 ];
	tp = &dh11[dev.d_minor];
	tp->t_addr = dhstart;
	tp->t_dev = dev;
	dhaddr->dhcsr =| IENABLE;
	tp->t_state =| WOPEN|SSTART;
	if ((tp->t_state&ISOPEN) == 0) {
		tp->t_erase = CERASE;
		tp->t_kill = CKILL;
		tp->t_quit = CQUIT;	/* I'm not sure this is the best */
		tp->t_intr = CINTR;	/* place to set these up but... */
		tp->t_EOT =  CEOT;
		tp->t_speeds = SSPEED | (SSPEED<<8);
		tp->t_flags = ODDP|EVENP|ECHO;
#ifdef DNTTY
		tp->t_dnflgs = 0;
		tp->t_ioct = 1;
		/* DN: following line is complete crock to turn off modem
		 * control for the first DH.  It would be unnecessary if
		 * there was some decent way to set TTY parameters BEFORE
		 * the open attempt, or if system itself knew more about
		 * its hardwired line parameters.  Can just use null modems,
		 * but it would sure be nice to have things set right from
		 * the start.
		 */
/*		if(dev.d_minor>>4 == 0) tp->t_flags =| DUMC; */
		/* another big crock */
		if((dev.d_minor&017) < 5) tp->t_flags =| DUMC;
#endif DNTTY
		dhparam(tp);
	} else if (tp->t_state&XCLUDE && u.u_uid!=0) {
		u.u_error = EBUSY;
		return;
	}
	dmopen(dev);
	ttyopen(dev, tp);
}

/*
 * Close a DH11 line.
 */
dhclose(dev)
{
	register struct tty *tp;

	tp = &dh11[dev.d_minor];
	if (tp->t_flags&HUPCL)
		dmctl(dev, TURNOFF);
	tp->t_state =& (CARR_ON|SSTART);
#ifdef DNTTY
	tp->t_dnflgs =& ~(TMOCTS|TMORTS);
#endif DNTTY
	wflushtty(tp);
}

/*
 * Read from a DH11 line.
 */
dhread(dev)
{
	ttread(&dh11[dev.d_minor]);
}

/*
 * write on a DH11 line
 */
dhwrite(dev)
{
	ttwrite(&dh11[dev.d_minor]);
}

/*
 * DH11 receiver interrupt.
 */
dhrint(dev)
int dev;
{
	register struct tty *tp;
	register int c;
	register struct dhregs *dhaddr;

	dhaddr = dhaddrs[dev];
	while ((c = dhaddr->dhnxch) < 0) {	/* char. present */
		tp = &dh11[ (dev<<4) | ((c>>8) & 017) ];
		if (tp >= &dh11[NDH11])
			continue;
		if((tp->t_state&ISOPEN)==0) {
			wakeup(tp);
			continue;
		}
		if (c&PERROR)
			if ((tp->t_flags&(EVENP|ODDP))==EVENP
			 || (tp->t_flags&(EVENP|ODDP))==ODDP )
				continue;
		if (c&FRERROR)		/* break */
			if (tp->t_flags&RAW)
				c = 0;		/* null (for getty) */
			else
				c = tp->t_intr;	/* intr */
#ifdef DNTTY
		if (tp->t_dnflgs&BAUDOT){
			tp->t_ioct++;		/* Bump to mark non-idle */
			c = bditab[(c&037)|(tp->t_dnflgs&FIGS ? 040 : 0)]&0377;
			/* printf("(I:%o,%o)",ac&0177,c);	/* Debug */
			switch(c) {
				case FIGSCHR: tp->t_dnflgs =| FIGS; continue;
				case LETSCHR: tp->t_dnflgs =& ~FIGS; continue;
				case 015:
					if(tp->t_dnflgs&LICLF){c = 012; break;}
					else {tp->t_dnflgs=| LICCR; continue;}
				case 012:
					if(tp->t_dnflgs&LICCR) break;
					else {tp->t_dnflgs=| LICLF; continue;}
				}
			tp->t_dnflgs =& ~(LICCR | LICLF);
			}
#endif DNTTY
		ttyinput(c, tp);
	}
}

/*
 * stty/gtty for DH11
 */
dhsgtty(dev, av)
int *av;
{
	register struct tty *tp;
	register struct dhregs *dhaddr;
	register int *mod;

	tp = &dh11[dev.d_minor];
	/*
	 * Special weirdness.
	 * On stty, if the input speed is 15 (EXT B)
	 * then the output speed selects a special function.
	 * The stored modes are not affected.
	 */
	if (av==0 && (mod=u.u_arg)[0].lobyte==15) {
		dhaddr = dhaddrs[dev.d_minor>>4];
		switch (mod[0].hibyte) {

		/*
		 * Set break
		 */
		case 1:
			dhaddr->dhbreak =| 1<<(dev.d_minor&017);
			return;

		/*
		 * Clear break
		 */
		case 2:
			dhaddr->dhbreak =& ~(1<<(dev.d_minor&017));
			return;

		/*
		 * Turn on request to send
		 */
		case 3:
			dmctl(dev, TURNON|RQS);
			return;

		/*
		 * Turn off request to send
		 */
		case 4:
			dmctl(dev, TURNON);
			return;

		/*
		 * Prevent opens on channel
		 */
		case 5:
			tp->t_state =| XCLUDE;
			return;
		}
		return;
	}
	if (ttystty(tp, av))
		return;
	dhparam(tp);
}

/*
 * Set parameters from open or stty into the DH hardware
 * registers.
 */
dhparam(atp)
struct tty *atp;
{
	register struct tty *tp;
	register int lpr;
	register struct dhregs *dhaddr;

	tp = atp;
	spl5();
	dhaddr = dhaddrs[ tp->t_dev.d_minor >> 4 ];
	dhaddr->dhcsr.lobyte = (tp->t_dev.d_minor&017) | IENABLE;
	/*
	 * Hang up line?
	 */
	if (tp->t_speeds.lobyte==0) {
		tp->t_flags =| HUPCL;
		dmctl(tp->t_dev, TURNOFF);
		return;
	}
	lpr = (tp->t_speeds.hibyte<<10) | (tp->t_speeds.lobyte<<6);
	switch (tp->t_speeds.lobyte) {
#ifdef DNTTY
		case BEXTA:		/* DN: Crock - Ext A implies baudot */
			lpr =| BITS5|TWOSB;	/* 5 bits with 1.5 stop bits */
			tp->t_dnflgs =| BAUDOT;	/* State of FIGS undefined */
			break;
#endif DNTTY
		case B134:		/* 134.5 baud */
			lpr =| BITS6|PENABLE|HDUPLX;
			break;
		case B110:		/* 110 baud */
			lpr =| TWOSB;	/* fall thru to continue */
		default:
			if (tp->t_flags&EVENP)
				if (tp->t_flags&ODDP) lpr =| BITS8;
						else lpr =| BITS7|PENABLE;
				else lpr =| BITS7|OPAR|PENABLE;
		}
	dhaddr->dhlpr = lpr;
	spl0();
}

/*
 * DH11 transmitter interrupt.
 * Restart each line which used to be active but has
 * terminated transmission since the last interrupt.
 */
dhxint(dev)
int dev;
{
	register struct tty *tp;
	register ttybit, bar;
	struct dhregs *dhaddr;

	dhaddr = dhaddrs[dev];
	bar = dhsar[dev] & ~dhaddr->dhbar;
	dhaddr->dhcsr =& ~XINT;
	ttybit = 1;
	for (tp = &dh11[dev<<4]; bar; tp++) {
		if(bar&ttybit) {
			dhsar[dev] =& ~ttybit;
			bar =& ~ttybit;
			tp->t_state =& ~BUSY;
			dhstart(tp);
		}
		ttybit =<< 1;
	}
}

/*
 * Start (restart) transmission on the given DH11 line.
 */
dhstart(atp)
struct tty *atp;
{
	extern ttrstrt();
#ifdef DNTTY
	extern dnrstrt(),dnrtsoff();	/* timeout routines */
#endif DNTTY
	register c, nch;
	register struct tty *tp;
	int sps, dev;
	struct dhregs *dhaddr;
	char *cp;

	sps = PS->integ;
	spl5();
	tp = atp;
	dev = tp-dh11;
	dhaddr = dhaddrs[ dev.d_minor >> 4 ];
	/*
	 * If it's currently active, or delaying,
	 * no need to do anything.
	 */
	if (tp->t_state&(TIMEOUT|BUSY))
		goto out;

	/* Commented out; too MIT specific.
	 * It has been cvted for multi DH/DM tho. --KLH 5/15/79
	 */
#ifdef COMMENT
	/*
	 * This routine hacks our line printer.  Namely it checks
	 * if the line printer is busy (dmsecrcv) and if it is,
	 * goes away for a while.
	 */
	if ((tp->t_flag2 & SECRX) && dmsecrcv(dev)) {
		timeout(ttrstrt, tp, 60);
		tp->t_state =| TIMEOUT;
		goto out;
	}
#endif COMMENT

	/*
	 * t_char is a delay indicator which may have been
	 * left over from the last start.
	 * Arrange for the delay.
	 */
	if ((c = tp->t_char)
#ifdef DNTTY
		&& ((c >= 0200) || (!(tp->t_dnflgs&BAUDOT)))
#endif DNTTY
		) {
		tp->t_char = 0;
		timeout(ttrstrt, tp, (c&0177)+6);
		tp->t_state =| TIMEOUT;
		goto out;
	}
#ifdef DNTTY
redo:
#endif DNTTY
	cp = dh_clist[dev.d_minor];
	nch = 0;
#ifdef DNTTY
	if (tp->t_dnflgs&BAUDOT) {
		if (!(tp->t_dnflgs&CTS)) {	/* If can't send, */
			if (dmaskcts(dev)) {	/* ask politely */
				tp->t_dnflgs =| CTS;	/* Won! */
				tp->t_dnflgs =& ~TMOCTS; /*may be pending*/
				printf(">");	/* Debug crock */
				}
			else {			/* Barf, wait a second. */
				if((tp->t_dnflgs&TMOCTS) == 0) {
					tp->t_dnflgs =| TMOCTS;
					timeout(dnrstrt, tp, 60);
					}
				goto out;
				}
			}
		if (c = tp->t_char) {	/* Holds extra output char */
			tp->t_char = 0;
			*cp++ = c;
			nch--;
			}
		}
#endif DNTTY
	/*
	 * Copy DHNCH characters, or up to a delay indicator,
	 * to the DMA area.
	 */
	while (nch > -DHNCH && (c = getc(&tp->t_outq))>=0) {
		if (c >= 0200 && (tp->t_flag2 & LITOUT)==0) {
			tp->t_char = c;
			break;
			}
#ifdef DNTTY
		if (tp->t_dnflgs&BAUDOT) {
			if ((c = bdtcvt(c,tp)) < 0)
				continue;	/* May want to flush char */
			*cp++ = c;
			if ((--nch > -DHNCH) && tp->t_char) {
				c = tp->t_char;
				tp->t_char = 0;
				}		/* Drop thru to store */
			else continue;	/* Already stored char so skip */
			}
#endif DNTTY
		*cp++ = c;
		nch--;
	}
	/*
	 * If the writer was sleeping on output overflow,
	 * wake him when low tide is reached.
	 */
	if (tp->t_outq.c_cc<=TTLOWAT && tp->t_state&ASLEEP) {
		tp->t_state =& ~ASLEEP;
		wakeup(&tp->t_outq);
	}
	/*
	 * If any characters were set up, start transmission;
	 * otherwise, check for possible delay.
	 */
	if (nch) {
		dhaddr->dhcsr.lobyte = (dev.d_minor & 017) | IENABLE;
		dhaddr->dhcar = cp+nch;
		dhaddr->dhbcr = nch;
		c = 1 << (dev.d_minor & 017);
		dhaddr->dhbar =| c;
		dhsar[dev.d_minor>>4] =|c;
		tp->t_state =| BUSY;
#ifdef DNTTY
		tp->t_dnflgs =& ~(TMOCTS);
		tp->t_ioct++;		/* Bump IO cnt to mark non-idle */
#endif DNTTY
	} else if ((c = tp->t_char)
#ifdef DNTTY
		&& ((c >= 0200) || (!(tp->t_dnflgs&BAUDOT)))
#endif DNTTY
		) {
		tp->t_char = 0;
		timeout(ttrstrt, tp, (c&0177)+6);
		tp->t_state =| TIMEOUT;
	}
#ifdef DNTTY
	/* This code contrives to turn off DN modem's RTS lead after
	 * the last char has left the DH.  We cannot turn it off after
	 * the output-complete interrupt because 2 chars are still in the
	 * UART, and we can't pad out the delay because baudot terms
	 * have no padding character... so set up a demon to check things
	 * after 2 char times (2*(1/6 sec)) have passed.
	 */
	/* New additional crock: try to ensure that when output
	 * is done, tty is left in LETS mode rather than FIGS.  No
	 * attempt at extra buffering or optimization is done here,
	 * because the speeds are so slow (6cps) it is highly probable
	 * that when the output queue becomes empty, the user program
	 * really has nothing more to send.  We'll see.
	 */
	else if (tp->t_dnflgs&BAUDOT) {
		if(tp->t_dnflgs&FIGS){		/* Crock */
			tp->t_char = LETSCHR;	/* " */
			tp->t_dnflgs =& ~FIGS;	/* " */
			goto redo;		/* " */
			}			/* " */
		/* Now arrange to turn off RTS */
		if(tp->t_dnflgs&TMORTS)
			outtime(dnrtsoff, tp);	/* Remove existing timeout*/
		else tp->t_dnflgs =| TMORTS;
		timeout(dnrtsoff, tp, VRTMO);	/* after last char leaves DH */
		}
#endif DNTTY
    out:
	PS->integ = sps;
}


/* ttymode handler for dh11 */

dhttymd(dev,acp)
int *acp;
{
	register struct tty *tp;
	tp = &dh11[dev.d_minor];
	if (ttmode(tp, acp)) return;
	dhparam(tp);
}

/*

* DN Baudot TTY support - output conversion
*/
#ifdef DNTTY

	/* All ASCII transformations now done -- Hack baudot here
	 * if necessary (note LCASE should have done some work too)
	 */
bdtcvt(ac,atp)
int ac;
struct tty *atp;
{
	register struct tty *tp;
	register int c;
	tp = atp;
	c = ac;
	if (c >= 0140) c =& 0137; /* At this point, be merciless about case. */
	if(c < 040)		/* If a control char, check special cases */
		 switch (c) {
			case '\r': c = 0210; break;
			case '\n': c = 0202; break;
			case 007:  c = 053;  break;	/* ^G - bell */
			case 006:  c = FIGSCHR; break;	/* ^F - force FIGS */
			case 014:  c = LETSCHR; break;	/* ^L - force LETS */
			default: return(-1);
			}
	else c = bdotab[c-040]&0377;
/*	printf("(O:%o,%o)\n",ac,c);	/* Debug */
	if (c&0200) {
		if(c==NOCH) c = 04;	/* for now, cvt randoms to SP */
		switch (c) {
			case FIGSCHR: tp->t_dnflgs=|FIGS; break;
			case LETSCHR: tp->t_dnflgs=& ~FIGS; break;
			case 0202:	/* Special crock for LF */
				tp->t_char = LETSCHR&037;/* Follow with LETS */
				tp->t_dnflgs=& ~FIGS;
				break;
			}
		return(c&037);
		}
	if(c&040 && !(tp->t_dnflgs&FIGS)) { /* Wants FIGS and not in? */
		tp->t_char = c&037;		/* Store char */
		tp->t_dnflgs =| FIGS;
		return(FIGSCHR&037);
		}
	if(!(c&040) && tp->t_dnflgs&FIGS) { /* Wants LETS and not in? */
		tp->t_char = c&037;		/* Store char */
		tp->t_dnflgs =& ~FIGS;
		return(LETSCHR&037);
		}
	return(c&037);
}

#endif DNTTY
/*

* DN Baudot TTY support - timeout routines
*/

/* Both of these routines are called with spl = 5 from the clock
 * interrupt routine, so need not worry about DH or DM interrupts.
 * Likewise, they won't be executed if already at some int level,
 * so DH/DM int level rtns don't need to worry about these either.
 */

#ifdef DNTTY
dnrstrt(atp)
struct tty *atp;
{
	extern dnrstrt();
	register struct tty *tp;
	tp = atp;
			printf("X");		/* Debug hack */
	if (tp->t_dnflgs&TMOCTS)	/* Still need CTS check? */
		if((tp->t_state&ISOPEN) == 0) {	/* For redundant safety */
			tp->t_dnflgs =& ~TMOCTS;
			return;
			}
		if(tp->t_state&BUSY || dmaskcts(tp->t_dev)) {
			tp->t_dnflgs =& ~TMOCTS;
			tp->t_dnflgs =| CTS;
			dhstart(tp);
			}
		else timeout(dnrstrt, tp, 60);
}

dnrtsoff(atp)
struct tty *atp;
{
	register struct tty *tp;
	tp = atp;
	if (tp->t_dnflgs&TMORTS) {
		tp->t_dnflgs =& ~TMORTS;
		if (!(tp->t_state&BUSY)) {
			dmrtsoff(tp->t_dev);
			tp->t_dnflgs =& ~CTS;
			printf("O");		/* Debug hack */
			}
		}
}
#endif DNTTY
/*
*	"outtime" - DN addition.
*	The reverse of the "timeout" kernel routine; removes
*	an entry from the clock queue.
*	Called with the same params as timeout, except the third
*	argument isn't furnished.  If outtime finds an entry with
*	the same argument and function, it will remove it.
*	It is even safe to call this while the clock routine is
*	hacking the queue; it checks for imminent execution and
*	substitutes a null routine if so.
*	It doesn't look for more than one entry, although it could.
*	(remove the break and ensure p1 is preserved during entry flush).
*/

#ifdef DNTTY
#include "systm.h"

outtime(fun, arg)
{
	extern nullrtn();
	register struct callo *p1, *p2;
	register a;
	int s;
	a = arg;
	p1 = &callout[0];
	s = PS->integ;
	spl7();
	while(p1->c_func != 0) {
		if(p1->c_arg == a && p1->c_func == fun) {
			if(p1->c_time <= 0) p1->c_func = &nullrtn;
			else {
				p2 = p1++;
				while(p2->c_func = p1->c_func) {
					p2->c_time = p1->c_time;
					p2->c_arg = p1->c_arg;
					p1++; p2++;
					}
				}
			break;
			}
		p1++;
		}
	PS->integ = s;
}
nullrtn(dummy)
{
}
#endif DNTTY
/**/
/*	Idle timeout routine -- called every IDLTMO ticks
 * to see if any lines have been inactive too long & need hangup.
 */

#ifdef DNTTY

dnidltmo()
{	register i;
	register struct tty *tp;
	for (tp = &dh11[0]; tp < &dh11[NDH11]; tp++)
		if(tp->t_dnflgs&BAUDOT)
		{	if ((tp->t_state&CARR_ON) && (tp->t_ioct == 0))
				dhclose(tp->t_dev);
			tp->t_ioct = 0;
		}
	timeout(dnidltmo, 0, IDLTMO);
}
#endif DNTTY

dhdm.c%3#
/*
 *	DM-BB driver
 *
 *      This driver has been modified to support multiple  DM11s,
 *      in  order  to  avail  yourself  of  it  you  must  do the
 *      following.
 *
 *      In this module:
 *           Initialize the array dmaddrs to contain the  base
 *           addresses of your DM11s.
 *
 *      In l.s:
 *           When you set up the  interrupt  vectors  for  the
 *           DM11s,  you should add a DM11 index number to the
 *           interrupt PS for each vector  (0  for  the  first
 *           one,  1  for the second, etc.) This index will be
 *           passed to the interrupt routine so  that  it  can
 *           tell  which  DM11  caused  the interrupt. This is
 *           done so that one interrupt  routine  can  service
 *           the interrupts from all of the DM11s.
 *
 *      globals:
 *         dmaddrs      array of pointers to the sets of dm11 registers.
 *
 *      history:
 *
 *      Started out as a standard release 6 DM11 driver.
 *      Modified for Multi DM support by Dennis L. Mumaugh
 *              15 April 1977
 *      Thanks to Mark Kampe (UCLA) for assistance.
 */

#include "param.h"
#include "tty.h"
#include "conf.h"

int dmaddrs[2] {0170500, 0170510};

struct	tty dh11[];
int	ndh11;		/* Set by dh.c to number of lines */

#define	BUSY	020	/* for CSR, not line status reg */
#define SECRX	020
#define	DONE	0200
#define	SCENABL	040
#define	CLSCAN	01000
#define	TURNON	03	/* CD lead, line enable */
#define	TURNOFF	1	/* line enable only */
#define	CARRIER	0100
#ifdef DNTTY
#define	DMRTS	04
#define DMCTS	040
#endif DNTTY

struct dmregs {
	int	dmcsr;
	int	dmlstat;
};

/*
 * Turn on the line associated with the (DH) device dev.
 */
dmopen(dev)
{
	register struct tty *tp;
	register struct dmregs *dmaddr;

	dmaddr = dmaddrs[dev.d_minor >> 4];
	tp = &dh11[dev.d_minor];
	dmaddr->dmcsr = dev.d_minor&017;
	if (tp->t_flags&DUMC){		/* KLH: if don't want modem ctl, */
		dmaddr->dmlstat = 0;	/* turn it off for line */
		tp->t_state =| CARR_ON;	/* pretend carrier's on */
		dmaddr->dmcsr = IENABLE|SCENABL;
		return;
		}
	dmaddr->dmlstat = TURNON;
	if (dmaddr->dmlstat&CARRIER)
		tp->t_state =| CARR_ON;
	dmaddr->dmcsr = IENABLE|SCENABL;
	spl5();
	while ((tp->t_state&CARR_ON)==0)
		sleep(&tp->t_canq, TTIPRI);
	spl0();
}

/*
 * If a DH line has the HUPCL mode,
 * turn off carrier when it is closed.
 */
/* This routine is obsoleted by existence of DMCTL. --KLH 5/15/79
 * dmclose(dev)
 * {
 *	register struct tty *tp;
 *	register struct dmregs *dmaddr;
 *
 *	dmaddr = dmaddrs[dev.d_minor>>4];
 *	tp = &dh11[dev.d_minor];
 *	if (tp->t_flags&HUPCL) {
 *		dmaddr->dmcsr = dev.d_minor&017;
 *		dmaddr->dmlstat = TURNOFF;
 *		dmaddr->dmcsr = IENABLE|SCENABL;
 *	}
 * }
 */

/*
 * Dump control bits into the DM registers.
 */
dmctl(dev, bits)
{
	register struct dmregs *dmaddr;
	dmaddr = dmaddrs[dev.d_minor>>4];
	dmaddr->dmcsr = dev.d_minor&017;
	dmaddr->dmlstat = bits;
	dmaddr->dmcsr = IENABLE|SCENABL;
}

/*
 * DM11 interrupt.
 * Mainly, deal with carrier transitions.
 */
dmint(dev)
int dev;
{
	register struct tty *tp;
	register struct dmregs *dmaddr;

	dmaddr = dmaddrs[dev];
	if (dmaddr->dmcsr&DONE) {
		tp = &dh11[ (dev<<4) | (dmaddr->dmcsr&017) ];
		if (tp < &dh11[ndh11]) {
#ifdef DNTTY
			if (dmaddr->dmlstat&DMCTS) tp->t_dnflgs =| CTS;
			else tp->t_dnflgs =& ~CTS;
#endif DNTTY
			wakeup(tp);
			if ((dmaddr->dmlstat&CARRIER)==0) {
				if ((tp->t_state&WOPEN)==0) {
					signal(tp->t_pgrp, SIGHUP);
					dmaddr->dmlstat = 0;
					flushtty(tp);
				}
				tp->t_state =& ~CARR_ON;
			} else
				tp->t_state =| CARR_ON;
		}
		dmaddr->dmcsr = IENABLE|SCENABL;
	}
}

/* This is the MIT routine "dmsecrcv" which isn't needed, hence
 * commented out.  It has been converted for multiple DH/DM's however.
 * --KLH 5/15/79
 */
#ifdef COMMENT
dmsecrcv(dev)
int dev;
{	register int ans;
	register struct dmregs *dmaddr;

	dmaddr = dmaddrs[dev.d_minor>>4];
	dmaddr->dmcsr =& ~SCENABL;
	while (dmaddr->dmcsr & BUSY);
	dmaddr->dmcsr =& ~017;
	dmaddr->dmcsr =| dev.d_minor&017;
	dmaddr->dmlstat =| TURNOFF;
	ans = dmaddr->dmlstat & SECRX;
	dmaddr->dmcsr =| SCENABL;
	return(ans);
}
#endif COMMENT
/*

* DN Modem support
*/

#ifdef DNTTY

/* Assert RTS for line and return state of CTS lead after suitable
 * delay for modem signal propagation.
 * This is called only from dhstart at spl 5, so needn't worry about
 * DH or timeout interrupts.
 */

dmaskcts(dev)
int dev;
{
	register struct dmregs *dmaddr;
	register int flag;

	dmaddr = dmaddrs[dev.d_minor>>4];
	dmaddr->dmcsr = dev.d_minor&017;
	dmaddr->dmlstat =| DMRTS;
	flag = dmaddr->dmlstat&DMCTS;
	dmaddr->dmcsr = IENABLE | SCENABL;
	return(flag);
}

/* Turn off RTS for specified line */

dmrtsoff(dev)
int dev;
{
	register struct dmregs *dmaddr;

	dmaddr = dmaddrs[dev.d_minor>>4];
	dmaddr->dmcsr = dev.d_minor&017;
	dmaddr->dmlstat =& ~DMRTS;
	dmaddr->dmcsr = IENABLE | SCENABL;
}

#endif DNTTY

*dhfdm.cd#
/*
 */

/*
 *	DM-BB fake driver
 */
#include "tty.h"
#include "conf.h"

struct	tty	dh11[];

dmopen(dev)
{
	register struct tty *tp;

	tp = &dh11[dev.d_minor];
	tp->t_state =| CARR_ON;
}

dmclose(dev)
{
}