[net.sources] DHU-11 driver for 4.2BSD

adrian@cs.reading.UUCP (Adrian Pell) (09/03/85)

Here's the first version of the promised driver for the DHU-11.
This version runs happily on an 11/750 under 4.2, and should (probably)
run on other VAXen and under ULTRIX, although none of this has been
tested.

The principal limitation of this release is that it does not attempt
to handle the sophisticated modem facilities provided by the DHU-11.
This will come later, time permitting.

In the meantime, use this and let me know of any problems encountered.


Adrian Pell		({mcvax,edcaad}!ukc!reading!A.R.Pell)
Computer Science Dept.
University of Reading
UK

---- cut here and feed to /bin/sh ------
: to unbundle, sh this file
echo x - dhureg.h 1>&2
cat >dhureg.h <<'End of dhureg.h'
/*	dhureg.h	1.1	02/09/85	*/

/* 
 * DHU-11 device register definitions.
 */
struct dhudevice {
	union {
		short	dhucsr;		/* control-status register */
		char	dhucsrl;	/* low byte for line select */
	} un;
	short	dhurbuf;		/* receive buffer register */
	short	dhulpr;			/* line parameter register */
	union {
		short dhufdata;		/* tx fifo data reg */
		struct {
			char dhufsize;	/* tx fifo size */
			char dhulstat;	/* line status register */
		}
	} un2;
	short	dhulctrl;		/* line control register */
	u_short	dhutbf1;		/* transmisster buffer 1 */
	short	dhutbf2;		/* tx buffer 2 */
	u_short	dhutbfct;		/* tx buffer count */
};

/* Bits in dhucsr */
#define	DHU_TI	0100000		/* transmit interrupt */
#define	DHU_TIE	0040000		/* transmit interrupt enable */
#define	DHU_DFL	0020000		/* diagnostic fail */
#define	DHU_TDE	0010000		/* tx dma error */
#define	DHU_TXL	0007400		/* transmit line */
#define	DHU_RI	0000200		/* receiver interrupt */
#define	DHU_RIE	0000100		/* receiver interrupt enable */
#define	DHU_MR	0000040		/* master reset */
#define	DHU_SK	0000020		/* skip self test */

/* Bits in dhulpr */
#define	BITS6	010
#define	BITS7	020
#define	BITS8	030
#define	TWOSB	0200
#define	PENABLE	040
#define	EPAR	0100

#define	DHU_IE	(DHU_TIE|DHU_RIE)

/* Bits in dhurbuf */
#define	DHU_PE		0010000		/* parity error */
#define	DHU_FE		0020000		/* framing error */
#define	DHU_DO		0040000		/* data overrun */

/* Bits in dhulctrl */

#define	TXABORT		001		/* abort dma in progress */
#define	RXENABLE	004		/* receiver enable */
#define	TXBREAK		010		/* transmit break */

/* Bits in dhutbf2 */

#define	TXDMAST		0200		/* start dma operation */
#define	TXENABLE	0100000		/* enable transmitter */

End of dhureg.h
echo x - dhu.c 1>&2
cat >dhu.c <<'End of dhu.c'
/*	dhu.c	1.1	02/09/85	*/

#include "dhu.h"
#if NDHU > 0
/*
 * DHU-11 driver
 */
#include "../machine/pte.h"

#include "bk.h"
#include "../h/param.h"
#include "../h/conf.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/proc.h"
#include "../h/ioctl.h"
#include "../h/tty.h"
#include "../h/map.h"
#include "../h/buf.h"
#include "../h/vm.h"

#include "../vaxuba/ubareg.h"
#include "../vaxuba/ubavar.h"
#include "../vaxuba/dhureg.h"

#include "../h/bk.h"
#include "../h/clist.h"
#include "../h/file.h"
#include "../h/uio.h"

/*
 * Definition of the driver for the auto-configuration program.
 */
int	dhuprobe(), dhuattach(), dhurint(), dhuxint();
struct	uba_device *dhuinfo[NDHU];
u_short	dhustd[] = { 0 };
struct	uba_driver dhudriver =
	{ dhuprobe, 0, dhuattach, 0, dhustd, "dhu", dhuinfo };

#define	ISPEED	B9600
#define	IFLAGS	(EVENP|ODDP)

/*
 * Local variables for the driver
 */

short	dhusoftCAR[NDHU];

struct	tty dhu11[NDHU*16];
int	ndhu11	= NDHU*16;
int	dhuact;				/* mask of active dhu's */
int	dhustart(), ttrstrt();

short dhu_speed[] = {
	0,	/* 0 baud */
	0,	/* 50 baud */
	0,	/* 75 baud */
	2,	/* 110 baud */
	3,	/* 134.5 baud */
	0,	/* 150 baud */
	0,	/* 200 baud */
	5,	/* 300 baud */
	6,	/* 600 baud */
	7,	/* 1200 baud */
	0,	/* 1800 baud */
	10,	/* 2400 baud */
	11,	/* 4800 baud */
	13,	/* 9600 baud */
	0,	/* 19200 baud (EXTA) */
	15,	/* 38400 baud (EXTB) */
};


/*
 * The clist space is mapped by the driver onto each UNIBUS.
 * The UBACVT macro converts a clist space address for unibus uban
 * into an i/o space address for the DMA routine.
 */
int	dhu_ubinfo[MAXNUBA];		/* info about allocated unibus map */
int	cbase[MAXNUBA];			/* base address in unibus map */
#define	UBACVT(x, uban)		(cbase[uban] + ((x)-(char *)cfree))

/*
 * Routine for configuration to force a dhu to interrupt.
 * Set to transmit at 9600 baud, and cause a transmitter interrupt.
 */
/*ARGSUSED*/
dhuprobe(reg)
	caddr_t reg;
{
	register int br, cvec;		/* these are ``value-result'' */
	register struct dhudevice *dhuaddr = (struct dhudevice *)reg;

#ifdef lint
	br = 0; cvec = br; br = cvec;
	if (ndhu11 == 0) ndhu11 = 1;
	dhurint(0); dhuxint(0);
#endif
	dhuaddr->un.dhucsr = DHU_MR|DHU_SK;
	DELAY(100);
	dhuaddr->un.dhucsr = DHU_RIE;
	DELAY(30000);
	dhuaddr->un.dhucsr = 0;
	return (sizeof (struct dhudevice));
}

/*
 * Routine called to attach a dhu.
 */
dhuattach(ui)
	struct uba_device *ui;
{

	dhusoftCAR[ui->ui_unit] = ui->ui_flags;
}

/*
 * Open a DHU11 line, mapping the clist onto the uba if this
 * is the first dhu on this uba.  Turn on this dhu if this is
 * the first use of it.  Also do a dmopen to wait for carrier.
 */
/*ARGSUSED*/
dhuopen(dev, flag)
	dev_t dev;
{
	register struct tty *tp;
	register int unit, dhu;
	register struct dhudevice *addr;
	register struct uba_device *ui;
	int s;

	unit = minor(dev);
	dhu = unit >> 4;
	if (unit >= NDHU*16 || (ui = dhuinfo[dhu])== 0 || ui->ui_alive == 0)
		return (ENXIO);
	tp = &dhu11[unit];
	if (tp->t_state&TS_XCLUDE && u.u_uid!=0)
		return (EBUSY);
	addr = (struct dhudevice *)ui->ui_addr;
	tp->t_addr = (caddr_t)addr;
	tp->t_oproc = dhustart;
	tp->t_state |= TS_WOPEN;
	/*
	 * While setting up state for this uba and this dhu,
	 * block uba resets which can clear the state.
	 */
	s = spl5();
	if (dhu_ubinfo[ui->ui_ubanum] == 0) {
		/* 512+ is a kludge to try to get around a hardware problem */
		dhu_ubinfo[ui->ui_ubanum] =
		    uballoc(ui->ui_ubanum, (caddr_t)cfree,
			512+nclist*sizeof(struct cblock), 0);
		cbase[ui->ui_ubanum] = dhu_ubinfo[ui->ui_ubanum]&0x3ffff;
	}
	if ((dhuact&(1<<dhu)) == 0) {
		addr->un.dhucsr = DHU_IE;
		dhuact |= (1<<dhu);
	}
	addr->un.dhucsrl = DHU_RIE|(unit&017);
	addr->dhulctrl |= RXENABLE;
	splx(s);
	/*
	 * If this is first open, initialze tty state to default.
	 */
	if ((tp->t_state&TS_ISOPEN) == 0) {
		ttychars(tp);
		if (tp->t_ispeed == 0) {
			tp->t_ispeed = ISPEED;
			tp->t_ospeed = ISPEED;
			tp->t_flags = IFLAGS;
		}
		dhuparam(unit);
	}
	/*
	 * fake carrier (until modem control properly in)
	 */
	tp->t_state |= TS_CARR_ON;
	return ((*linesw[tp->t_line].l_open)(dev, tp));
}

/*
 * Close a DHU11 line
 */
/*ARGSUSED*/
dhuclose(dev, flag)
	dev_t dev;
	int flag;
{
	register struct tty *tp;
	register unit;
	int s;
	register struct dhudevice *addr;

	unit = minor(dev);
	tp = &dhu11[unit];
	addr = (struct dhudevice *)tp->t_addr;
	(*linesw[tp->t_line].l_close)(tp);
	s = spl5();
	addr->un.dhucsrl = DHU_RIE|(unit&017);
	addr->dhulctrl &= ~(RXENABLE|TXBREAK);
	splx(s);
	ttyclose(tp);
}

dhuread(dev, uio)
	dev_t dev;
	struct uio *uio;
{
	register struct tty *tp = &dhu11[minor(dev)];

	return ((*linesw[tp->t_line].l_read)(tp, uio));
}

dhuwrite(dev, uio)
	dev_t dev;
	struct uio *uio;
{
	register struct tty *tp = &dhu11[minor(dev)];

	return ((*linesw[tp->t_line].l_write)(tp, uio));
}

/*
 * DHU11 receiver interrupt.
 */
dhurint(dhu)
	int dhu;
{
	register struct tty *tp;
	register c;
	register struct dhudevice *addr;
	register struct tty *tp0;
	register struct uba_device *ui;
	int overrun = 0;

	ui = dhuinfo[dhu];
	if (ui == 0 || ui->ui_alive == 0)
		return;
	addr = (struct dhudevice *)ui->ui_addr;
	tp0 = &dhu11[dhu<<4];
	/*
	 * Loop fetching characters from the silo for this
	 * dhu until there are no more in the silo.
	 */
	while ((c = addr->dhurbuf) < 0) {
		if ((c&070000) == 070000)
			continue;	/* diagnostic or modem */
		tp = tp0 + ((c>>8)&0xf);
		if ((tp->t_state&TS_ISOPEN)==0) {
			wakeup((caddr_t)tp);
			continue;
		}
		if (c & DHU_PE)
			if ((tp->t_flags&(EVENP|ODDP))==EVENP
			 || (tp->t_flags&(EVENP|ODDP))==ODDP )
				continue;
		if ((c & DHU_DO) && overrun == 0) {
			printf("dhu%d: silo overflow\n", dhu);
			overrun = 1;
		}
		if (c & DHU_FE)
			/*
			 * At framing error (break) generate
			 * a null (in raw mode, for getty), or a
			 * interrupt (in cooked/cbreak mode).
			 */
			if (tp->t_flags&RAW)
				c = 0;
			else
				c = tp->t_intrc;
#if NBK > 0
		if (tp->t_line == NETLDISC) {
			c &= 0177;
			BKINPUT(c, tp);
		} else
#endif
			(*linesw[tp->t_line].l_rint)(c, tp);
	}
}

/*
 * Ioctl for DHU11.
 */
/*ARGSUSED*/
dhuioctl(dev, cmd, data, flag)
	caddr_t data;
{
	register struct tty *tp;
	register int unit = minor(dev);
	int error;
	register struct dhudevice *addr;
	int s;

	tp = &dhu11[unit];
	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag);
	addr = (struct dhudevice *)tp->t_addr;
	if (error >= 0)
		return (error);
	error = ttioctl(tp, cmd, data, flag);
	if (error >= 0) {
		if (cmd == TIOCSETP || cmd == TIOCSETN)
			dhuparam(unit);
		return (error);
	}
	switch (cmd) {

	case TIOCSBRK:
		s = spl5();
		addr->un.dhucsrl = DHU_RIE|(unit&017);
		addr->dhulctrl |= TXBREAK;
		splx(s);
		break;

	case TIOCCBRK:
		s = spl5();
		addr->un.dhucsrl = DHU_RIE|(unit&017);
		addr->dhulctrl &= ~TXBREAK;
		splx(s);
		break;

	case TIOCSDTR:
		break;

	case TIOCCDTR:
		break;

	default:
		return (ENOTTY);
	}
	return (0);
}

/*
 * Set parameters from open or stty into the DHU hardware
 * registers.
 */
dhuparam(unit)
	register int unit;
{
	register struct tty *tp;
	register struct dhudevice *addr;
	register int lpar;
	int s;

	tp = &dhu11[unit];
	addr = (struct dhudevice *)tp->t_addr;
	/*
	 * Block interrupts so parameters will be set
	 * before the line interrupts.
	 */
	s = spl5();
	addr->un.dhucsrl = (unit&0xf) | DHU_IE;
	if ((tp->t_ispeed)==0) {
		tp->t_state |= TS_HUPCLS;
		return;
	}
	lpar = ((dhu_speed[tp->t_ospeed])<<12) | ((dhu_speed[tp->t_ispeed])<<8);
	if ((tp->t_ispeed) == B134)
		lpar |= BITS6;
	else if ((tp->t_flags & (RAW|LITOUT)) || (tp->t_flags&ANYP) == ANYP)
		lpar |= BITS8;
	else {
		lpar |= BITS7|PENABLE;
		if (tp->t_flags & EVENP)
			lpar |= EPAR;
	}
	if ((tp->t_ospeed) == B110)
		lpar |= TWOSB;
	addr->dhulpr = lpar;
	splx(s);
}

/*
 * DHU11 transmitter interrupt.
 * Restart each line which used to be active but has
 * terminated transmission since the last interrupt.
 */
dhuxint(dhu)
	int dhu;
{
	register struct tty *tp;
	register struct dhudevice *addr;
	register struct uba_device *ui;
	register int unit;
	int cntr;
	short csr;

	ui = dhuinfo[dhu];
	addr = (struct dhudevice *)ui->ui_addr;
	while ((csr = addr->un.dhucsr) < 0) {
		unit = (csr>>8)&0xf;
		addr->un.dhucsrl = DHU_RIE|unit;
		tp = &dhu11[dhu*16+unit];
		if (csr & DHU_TDE) {
			printf("dhu%d: TDE\n", dhu);
			addr->dhutbfct = 0;
		}
		tp->t_state &= ~TS_BUSY;
		if (tp->t_state & TS_FLUSH) {
			tp->t_state &= ~TS_FLUSH;
			addr->dhutbfct = 0;
		}
		else {
			cntr = ((addr->dhutbf2 & 0x3) << 16)
			       + addr->dhutbf1
			       - UBACVT(tp->t_outq.c_cf, ui->ui_ubanum);
			ndflush(&tp->t_outq, (int)cntr);
		}
		if (tp->t_line)
			(*linesw[tp->t_line].l_start)(tp);
		else
			dhustart(tp);
	}
}

/*
 * Start (restart) transmission on the given DHU11 line.
 */
dhustart(tp)
	register struct tty *tp;
{
	register struct dhudevice *addr;
	register int car, dhu, unit, nch;
	int s;

	unit = minor(tp->t_dev);
	dhu = unit >> 4;
	unit &= 0xf;
	addr = (struct dhudevice *)tp->t_addr;

	/*
	 * Must hold interrupts in following code to prevent
	 * state of the tp from changing.
	 */
	s = spl5();
	/*
	 * If it's currently active, or delaying, no need to do anything.
	 */
	if (tp->t_state&(TS_TIMEOUT|TS_BUSY|TS_TTSTOP)) {
		goto out;
	}
	/*
	 * If there are sleepers, and output has drained below low
	 * water mark, wake up the sleepers.
	 */
	if (tp->t_outq.c_cc<=TTLOWAT(tp)) {
		if (tp->t_state&TS_ASLEEP) {
			tp->t_state &= ~TS_ASLEEP;
			wakeup((caddr_t)&tp->t_outq);
		}
		if (tp->t_wsel) {
			selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL);
			tp->t_wsel = 0;
			tp->t_state &= ~TS_WCOLL;
		}
	}
	/*
	 * Now restart transmission unless the output queue is
	 * empty.
	 */
	if (tp->t_outq.c_cc == 0) {
		goto out;
	}
	addr->un.dhucsrl = DHU_RIE|unit;
	if (addr->dhulctrl & TXABORT)
		addr->dhulctrl &= ~TXABORT;
	if (tp->t_flags & (RAW|LITOUT))
		nch = ndqb(&tp->t_outq, 0);
	else {
		nch = ndqb(&tp->t_outq, 0200);
		/*
		 * If first thing on queue is a delay process it.
		 */
		if (nch == 0) {
			nch = getc(&tp->t_outq);
			timeout(ttrstrt, (caddr_t)tp, (nch&0x7f)+6);
			tp->t_state |= TS_TIMEOUT;
			goto out;
		}
	}
	/*
	 * If characters to transmit, restart transmission.
	 */
	if (nch) {
		car = UBACVT(tp->t_outq.c_cf, dhuinfo[dhu]->ui_ubanum);
		addr->un.dhucsrl = unit|DHU_RIE;
		addr->dhutbf1 = car & 0xffff;
		addr->dhutbf2 = (car>>16) & 3;
		addr->dhutbfct = nch;
		addr->dhutbf2 |= TXDMAST|TXENABLE;
		tp->t_state |= TS_BUSY;
	}
out:
	splx(s);
}

/*
 * Stop output on a line, e.g. for ^S/^Q or output flush.
 */
/*ARGSUSED*/
dhustop(tp, flag)
	register struct tty *tp;
{
	register struct dhudevice *addr;
	register int unit, s;

	addr = (struct dhudevice *)tp->t_addr;
	/*
	 * Block input/output interrupts while messing with state.
	 */
	s = spl5();
	if (tp->t_state & TS_BUSY) {
		/*
		 * Device is transmitting; stop output
		 */
		unit = minor(tp->t_dev);
		addr->un.dhucsrl = (unit&017) | DHU_RIE;
		if ((tp->t_state&TS_TTSTOP)==0)
			tp->t_state |= TS_FLUSH;
		addr->dhulctrl |= TXABORT;
	}
	splx(s);
}

/*
 * Reset state of driver if UBA reset was necessary.
 * Reset the csrl and lpr registers on open lines, and
 * restart transmitters.
 */
dhureset(uban)
	int uban;
{
	register int dhu, unit;
	register struct tty *tp;
	register struct uba_device *ui;
	int i;

	if (dhu_ubinfo[uban] == 0)
		return;
	dhu_ubinfo[uban] = uballoc(uban, (caddr_t)cfree,
	    512+nclist*sizeof (struct cblock), 0);
	cbase[uban] = dhu_ubinfo[uban]&0x3ffff;
	dhu = 0;
	for (dhu = 0; dhu < NDHU; dhu++) {
		ui = dhuinfo[dhu];
		if (ui == 0 || ui->ui_alive == 0 || ui->ui_ubanum != uban)
			continue;
		printf(" dhu%d", dhu);
		((struct dhudevice *)ui->ui_addr)->un.dhucsr = DHU_IE;
		unit = dhu * 16;
		for (i = 0; i < 16; i++) {
			tp = &dhu11[unit];
			if (tp->t_state & (TS_ISOPEN|TS_WOPEN)) {
				dhuparam(unit);
				tp->t_state &= ~TS_BUSY;
				dhustart(tp);
			}
			unit++;
		}
	}
	dhutimer();
}

/*
 * At software clock interrupt time or after a UNIBUS reset
 * empty all the dhu silos.
 */
dhutimer()
{
	register int dhu;
	register int s = spl5();

	for (dhu = 0; dhu < NDHU; dhu++)
		dhurint(dhu);
	splx(s);
}
#endif DHU

End of dhu.c
exit 0
-- 
Adrian Pell		({mcvax,edcaad}!ukc!reading!A.R.Pell)
Computer Science Dept.
University of Reading
UK