SRI-NOSC/dmr/misc/dh.c.ill

#

/*
 *	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.
*	5/76: modified to use "line doubler"
*	i.e. bit 8 selects a terminal thus doubling the
*	effective number of (7-bit) lines from 16 to 32.
 */

#include "/usr/sys/h/param.h"
#include "/usr/sys/h/conf.h"
#include "/usr/sys/h/user.h"
#include "/usr/sys/h/tty1.h"

#define	DHADDR	0160020
#define	NDH11	16	/* number of lines */
#define NDBL	16	/* number of doubled lines */
#define	DHNCH	16	/* max number of DMA chars */

struct	tty dh11[NDH11+NDBL];


/*
 * ones in dbl signify doubled lines
 * ones in dial signify doubled dialin lines
 */
char dbl[NDH11] {0,0,0,0,0,0,0,0,1,1,1,1,0,0,1,0};
char dial[NDH11]{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
/*
 * 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
 */
#define	BITS6	01
#define	BITS7	02
#define	BITS8	03
#define	TWOSB	04
#define	PENABLE	020
/* DEC manuals incorrectly say this bit causes generation of even parity. */
#define	OPAR	040
#define	HDUPLX	040000

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

/*
 * Software copy of last dhbar
 */
int	dhsar;

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

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

	if (dev.d_minor >= NDH11+NDBL) {
		u.u_error = ENXIO;
		return;
	}
	tp = &dh11[dev.d_minor];
	tp->t_addr = dhstart;
	tp->t_state =| WOPEN|SSTART;
	DHADDR->dhcsr =| IENABLE;
	if ((tp->t_state&ISOPEN) == 0) {
		tp->t_erase = CERASE;
		tp->t_kill = CKILL;
		tp->t_speeds = SSPEED | (SSPEED << 8);
		tp->t_flags = ODDP|EVENP|ECHO;
		dhparam(dev);
	}
	dmopen(dev);
	ttyopen(dev, tp);
}

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

	tp = &dh11[dev.d_minor];
	dmclose(dev);
	tp->t_state =& (CARR_ON|SSTART);
	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()
{
	register struct tty *tp;
	register int c;
	register int chan;

	while ((c = DHADDR->dhnxch) < 0) {	/* char. present */
		chan = (c>>8)&017;
		if (dbl[chan] && (c & 0200)) chan =+ 16;
		tp = &dh11[chan];
		if (tp >= &dh11[NDH11+NDBL])
			continue;
		/*
		 * It was necessary to pervert break processing
		 * to handle the ATTN key on the ibm 2741.
		 * This is because pressing the ATTN key will
		 * cause a break if the 2741 keyboard is locked.
		 * Otherwise a CRC will be sent (arrgh).
		 * Therefore all breaks at 134.5 baud are passed
		 * thru as 0177's and ttyinput interprets them.
		 */
		if (c&FRERROR) {		/* break */
			if (tp->t_flags&RAW
#ifdef	IBM
			&& (tp->t_speeds&017)!=IBM
#endif	IBM
			)
				c = 0;		/* null (for getty) */
			else
				c = 0177;	/* DEL (intr) */
		}
		if((tp->t_state&ISOPEN)==0 || (c&PERROR)) {
			wakeup(tp);
			continue;
		}
		/*
		 * Some ibm terminals may send synchronous idle codes.
		 * It is better to throw them away here rather than to
		 * let them get through ttyinput.
		 */
#ifdef	IBM
		if ((tp->t_speeds&017)==IBM && (c== 075)) continue;
#endif	IBM
		ttyinput(c, tp);
	}
}

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

	tp = &dh11[dev.d_minor];
	if (ttystty(tp, av))
		return;
	dhparam(dev);
}

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

	tp = &dh11[dev.d_minor];
	spl5();
	if (dev.d_minor >= NDH11) return;
	DHADDR->dhcsr.lobyte = dev.d_minor | IENABLE;
	/*
	 * Hang up line?
	 */
	if (tp->t_speeds.lobyte==0) {
		tp->t_flags =| HUPCL;
		dmclose(dev);
		return;
	}
	lpr = ((tp->t_speeds.hibyte & 017)<<10) |
		((tp->t_speeds.lobyte & 017)<<6);
	if ((tp->t_speeds.lobyte&017) == 4)
		lpr =| BITS6|OPAR|PENABLE; else
		if (tp->t_flags&EVENP)
			if (tp->t_flags&ODDP)
				lpr =| BITS8; else
				lpr =| BITS7|PENABLE; else
			lpr =| BITS7|OPAR|PENABLE;
	if (tp->t_speeds.lobyte == 3)	/* 110 baud */
		lpr =| TWOSB;
	DHADDR->dhlpr = lpr;
	spl0();
}

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

	bar = dhsar & ~DHADDR->dhbar;
	DHADDR->dhcsr =& ~XINT;
	ttybit = 1;
	for (tp = dh11; bar; tp++) {
		if(bar&ttybit) {
			dhsar =& ~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();
	register c, nch;
	register struct tty *tp;
	struct tty *tp1,*tp2;
	int sps, dev;
	char *cp;
	int	dhnch;

	sps = PS->integ;
	spl5();
	tp = atp;
	dev = tp-dh11;
	/*
	 * Detect doubled lines and set atp and dev
	 * to the real dh line.
	 */
	if (dev >= NDH11) {
		atp =- 16; dev =- 16;
	}
	/*
	 * clear break register after
	 * timing out a space signal
	 */
	if (tp->t_state&BREAK && !(tp->t_state&TIMEOUT)) {
	   nch=1; nch=<< dev;
	   DHADDR->dhbreak =& ~nch;
	   tp->t_state =& ~BREAK;
	}
	/*
	 * Arbitrate doubled lines:
	 */
	if (dbl[dev]) {
		if (atp->t_state & BUSY) goto out;
		tp2 = delay(atp+16);
		tp1 = delay(atp);
		if (!tp1 && !tp2) goto out;
		nch = 0;
		cp = dh_clist[dev];
		while (nch > -DHNCH) {
			if (tp1) {
				c = getc(&tp1->t_outq);
				if (c>=0200) {
					tp1->t_char = c;
					tp1 = 0;
				} else {
					*cp++ = c;
					nch--;
				}
				if (tp1->t_outq.c_cc == 0) tp1 = 0;
			}
			if (tp2) {
				c = getc(&tp2->t_outq);
				if (c>=0200) {
					tp2->t_char = c;
					tp2 = 0;
				} else {
					*cp++ = c | 0200;
					nch--;
				}
				if (tp2->t_outq.c_cc == 0) tp2 = 0;
			}
			if (!tp1 && !tp2) {
				break;
			}
		}
		trywake(atp+16,nch);
		trywake(atp,nch);
		goto startio;
	}
	/*
	 * If it's currently active, or delaying,
	 * no need to do anything.
	 */
	if (tp->t_state&(TIMEOUT|BUSY))
		goto out;
	/*
	 * t_char is a delay indicator which may have been
	 * left over from the last start.
	 * Arrange for the delay.
	 * a delay > 60 will be interpreted as a request for
	 * a break signal of (delay-60) ticks.
	 */
	if (c = tp->t_char) {
		c =& 0377;
		tp->t_char = 0;
ibmdelay:
		nch = c-0200;
		if (nch>60) {
			nch=- 60;
			c=1; c=<< dev;
			DHADDR->dhbreak =| c;
			tp->t_state =| BREAK;
			c = nch-6;
		   }
		timeout(ttrstrt, tp, (c&0177)+6);
		tp->t_state =| TIMEOUT;
		goto out;
	}
copy:
	cp = dh_clist[dev.d_minor];
	nch = 0;
	/*
	 * Copy DHNCH characters, or up to a delay indicator,
	 * to the DMA area.
	 */
	dhnch = -DHNCH;
	while (nch > dhnch && (c = getc(&tp->t_outq))>=0) {
		if (c >= 0200) {
		   if (!(tp->t_speeds&BINARY)
#ifdef	IBM
		   || (tp->t_speeds&017)==IBM
#endif	IBM
		   ) {
			tp->t_char = c;
			break;
		   }
		}
		*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.
	 */
startio:
	if (nch) {
		DHADDR->dhcsr.lobyte = dev.d_minor | IENABLE;
		DHADDR->dhcar = cp+nch;
		DHADDR->dhbcr = nch;
		c = 1<<dev.d_minor;
		DHADDR->dhbar =| c;
		dhsar =| c;
		atp->t_state =| BUSY;
		goto out;
	} else if (c = tp->t_char) {
		c =& 0377;
		tp->t_char = 0;
		if (c >= 0200+60)
			goto ibmdelay;
		timeout(ttrstrt, tp, (c&0177)+6);
		tp->t_state =| TIMEOUT;
		goto out;
	}
	ttstop(tp);
    out:
	PS->integ = sps;
}
delay(atp)
struct tty *atp;
{
register struct tty *tp;
register int c;
extern ttrstrt();
	tp = atp;
	if (c = tp->t_char) {
		tp->t_char = 0;
		timeout(ttrstrt, tp, (c&0177)+6);
		tp->t_state =| TIMEOUT;
		return(0);
	}
	if (tp->t_state & TIMEOUT) {
		return(0);
	}
	if (tp->t_outq.c_cc) {
		return(tp);
	}
	return(0);
}
trywake(atp,nch)
{
register struct tty *tp;
	tp = atp;
	if (tp->t_outq.c_cc<=TTLOWAT && tp->t_state&ASLEEP) {
		tp->t_state =& ~ASLEEP;
		wakeup(&tp->t_outq);
		return;
	}
	if (tp->t_char && nch==0) {
		delay(tp);
	}
}