#
/*
 */

/*
 *	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.
 */

#include "../head/param.h"
#include "../head/conf.h"
#include "../head/user.h"
#include "../head/userx.h"
#include "../head/tty.h"

#define	DHADDR	0160020
#define	DHTYPE	03
#define	NDH11	(1*16)	/* number of lines */
#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
 */
#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	XINT	0100000


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

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;
	register minor, dhaddr;
	extern dhstart();

	if ((minor = dev.d_minor & 0377) >= NDH11) {
		u.u_error = ENXIO;
		return;
	}
	dhaddr = DHADDR + (minor>>4)*16;
	tp = &dh11[minor];
	tp->t_addr = dhstart;
	tp->t_dev = dev;
	tp->t_dtype = DHTYPE;
	tp->t_state =| SSTART;
	if (flag) {
		dhaddr->dhcsr =| IENABLE;
		(*linesw[tp->t_discp].l_open)(dev, tp);
	}
}

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

	tp = &dh11[dev.d_minor & 0377];
	(*linesw[tp->t_discp].l_close)(dev, tp);
}

/*
 * Read from a DH11 line.
 */
dhread(dev)
{
	register struct tty *tp;

	tp = &dh11[dev.d_minor & 0377];
	(*linesw[tp->t_discp].l_read)(tp);
}

/*
 * write on a DH11 line
 */
dhwrite(dev)
{
	register struct tty *tp;

	tp = &dh11[dev.d_minor & 0377];
	(*linesw[tp->t_discp].l_write)(tp);
}

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

	dhaddr = DHADDR + (dev*16);
	while ((c = dhaddr->dhnxch) < 0) {	/* char. present */
		tp = &dh11[((c>>8)&017) + dev*16];
		if (tp >= &dh11[NDH11])
			continue;
		c =& (0377|PERROR|FRERROR);
		(*linesw[tp->t_discp].l_rcvd)(c, tp);
	}
}

/*
 * stty/gtty for DH11
 */
dhsgtty(dev, flag)
{
	register struct tty *tp;
	register minor;

	minor = dev.d_minor & 0377;
	tp = &dh11[minor];
	ttioctl(dev, tp, flag);
}

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

	minor = dev.d_minor & 0377;
	dhaddr = DHADDR + (minor>>4)*16;
	tp = atp;
	sps = PS->integ;
	spl5();
	dhaddr->dhcsr.lobyte = (minor & 017) | IENABLE;
	lpr = (tp->t_speeds.hibyte<<10) | (tp->t_speeds.lobyte<<6);
	if (tp->t_speeds.lobyte == 4)		/* 134.5 baud */
		lpr =| BITS6|PENABLE|HDUPLX;
	else if (tp->t_flags&RAW || (tp->t_flags&(ODDP|EVENP))==(ODDP|EVENP))
		lpr =| BITS8;
	else
		lpr =| BITS7|PENABLE;
	if ((tp->t_flags&EVENP)==0)
		lpr =| OPAR;
	if (tp->t_speeds.lobyte == 3)	/* 110 baud */
		lpr =| TWOSB;
	if(tp->t_flags&HDUP)
		lpr =| HDUPLX;
	dhaddr->dhlpr = lpr;
	PS->integ = sps;
}

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

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

	sps = PS->integ;
	spl5();
	tp = atp;
	dev = tp-dh11;
	dhaddr = DHADDR + (dev>>4)*16;
	/*
	 * 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.
	 */
	if (c = tp->t_char) {
		tp->t_char = 0;
		timeout(ttrstrt, tp, (c&0177)+6);
		tp->t_state =| TIMEOUT;
		goto out;
	}
	cp = dh_clist[dev.d_minor & 0377];
	nch = 0;
	/*
	 * Copy DHNCH characters, or up to a delay indicator,
	 * to the DMA area.
	 */
	while (nch > -DHNCH && (c = (*linesw[tp->t_discp].l_xmtd)(tp))) {
		if (c & CTOUT) {
			tp->t_char = c & 0377;
			break;
		}
		*cp++ = c & 0377;
		nch--;
	}
	/*
	 * 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>>4] =| c;
		tp->t_state =| BUSY;
	} else if (c = tp->t_char) {
		tp->t_char = 0;
		timeout(ttrstrt, tp, (c&0177)+6);
		tp->t_state =| TIMEOUT;
	}
    out:
	PS->integ = sps;
}
