V9/sys/dev.old/dh.c

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

/*
 * DH-11/DM-11 driver for Eighth Edition UNIX
 * Synthesised by Ian Darwin and Geoff Collyer,
 * University of Toronto, January 1986.
 */

#include "dh.h"
#include "dm.h"
#if NDH > 0
#include "../h/param.h"
#include "../h/stream.h"
#include "../h/ioctl.h"
#include "../h/ttyld.h"
#include "../h/pte.h"
#include "../h/map.h"
#include "../h/buf.h"
#include "../h/systm.h"
#include "../h/ubareg.h"
#include "../h/ubavar.h"
#include "../h/conf.h"
#include "sparam.h"

#include "../h/dhreg.h"		/* half of hardware */
#include "../h/dmreg.h"		/* half of hardware */
#include "../h/mdmreg.h"	/* modem command stuff */


#define NPER	16		/* lines per board */
#define	NDHLINE	(NDH*NPER)

/* bits in dh[]->state */
#define	EXISTS	01
#define	ISOPEN	02
#define	WOPEN	04
#define	TIMEOUT	010
#define	CARR_ON	020
#define	DHSTOP	040
#define	HPCL	0100
#define	BRKING	0200
#define	DIALOUT	0400	/* used as dialout with smart modems */
#define BUSY	01000
#define FLUSH	02000

#define SSPEED	B4800	/* half reasonable default nowadays */
#define DHPRI	30

/*
 * interface to auto-configuration.
 * There is one definition for the dh and one for the dm.
 */
int	dhprobe(), dhattach(), dhrint(), dhtint();
struct	uba_device *dhboard[NDH];
u_short	dhstd[] = { 0 };
struct	uba_driver dhdriver =
	{ dhprobe, 0, dhattach, 0, dhstd, "dh", dhboard };

int	dmprobe(), dmattach(), dmintr();
#define	NDM	1
struct	uba_device *dmboard[NDM];
u_short	dmstd[] = { 0 };
struct	uba_driver dmdriver =
	{ dmprobe, 0, dmattach, 0, dmstd, "dm", dmboard };

/*
 * interface with stream i/o system
 */
int	dhopen(), dhclose(), dhoput(), nodev();
static	struct qinit dhrinit = { nodev, NULL, dhopen, dhclose, 0, 0 };
static	struct qinit dhwinit = { dhoput, NULL, dhopen, dhclose, 200, 100 };
struct	streamtab dhinfo = { &dhrinit, &dhwinit };

/*
 * private data for the driver
 */

/*
 * Well, we can't map the clist space onto each Unibus,
 * since we ain't got no more clists (rah!).
 * Fortunately, we can get the same effect by mapping all stream
 * buffers onto the Unibus space  (as does qinit()).
 */
#define UBADMASK 0x3ffff		/* 18 bits of address */
#ifdef MULTI_UBA
#define UBACVT(x, uba)  (dhblkubad[uba] + ((caddr_t)(x)-(caddr_t)blkdata))
#else
#define UBACVT(x, uba)  ((blkubad&UBADMASK) + ((caddr_t)(x)-(caddr_t)blkdata))
#endif
#define	UBADDR(x, uba)  (UBACVT((x), (uba))&0xffff)	/* low order 16 bits */
#define UBXADDR(x, uba) ((UBACVT((x), (uba))>>16)&0x3)	/* high order 2 bits */

/*
 * One structure per line
 */
struct	dh {
	short	state;
	short	flags;
	struct	block	*oblock;
	struct	queue	*rdq;
	char	board;
	char	line;
	char	ispeed, ospeed;
	short	brking;
} dh[NDHLINE];

/*
 * misc private data
 */
#ifdef MULTI_UBA
long	dhblkubad[MAXNUBA];		/* uba allocation */
#else
extern long blkubad;
extern u_char blkdata[];
#endif
int	dhoverrun;

short	dhsar[NDH];		/* software copy of last bar */
int	dhmiss;			/* chars lost due to q full */

/*
 * routines for top half hardware -- the dh11
 */
int	dhstart();

/*
 * Routine for configuration to force a dh to interrupt.
 * Set to transmit at 9600 baud, and cause a transmitter interrupt.
 *
 * From 4BSD.
 */
/*ARGSUSED*/
dhprobe(reg)
caddr_t reg;
{
	register int br, cvec;		/* these are ``value-result'' */
	register struct dhdevice *dhaddr = (struct dhdevice *)reg;

#ifdef lint
	br = 0; cvec = br; br = cvec;
	dhrint(0); dhtint(0);
#endif
	dhaddr->un.dhcsr = DH_RIE|DH_MM|DH_RI;
	DELAY(1000);
	dhaddr->un.dhcsr &= ~DH_RI;
	dhaddr->un.dhcsr = 0;
	return 1;
}

/*
 * Routine to attach a dh.
 * Map the stream buffers onto the unibus (qinit tries to do this,
 * but only does it for unibus zero; the dh might be on any unibus).
 */
dhattach(ui)
register struct uba_device *ui;
{
	register int i;
#ifdef MULTI_UBA
	/* this defn must match that in dev/stream.c: */
	extern u_char blkdata[1024*NBLKBIG + 64*NBLK64 + NBLK16*16 + NBLK4*4];
	extern long blkubad;		/* wanton licentiousness w/ streamld */
#endif

	for (i = 0; i < NPER; i++) {
		dh[ui->ui_unit*NPER+i].state = EXISTS;
		dh[ui->ui_unit*NPER+i].board = ui->ui_unit;
		dh[ui->ui_unit*NPER+i].line  = i;
	}
#ifdef MULTI_UBA
#if 0
	if (ui->ui_ubanum == 0)			/* uba 0 */
		/* won't work because blkubad isn't initialised yet */
		dhblkubad[ui->ui_ubanum] = blkubad;
	else
#endif
		/* adding 512 is from 4.2, allegedly a hardware kludge */
		dhblkubad[ui->ui_ubanum] = uballoc(ui->ui_ubanum,
			(caddr_t)blkdata, 512 + sizeof blkdata, 0);
	if (dhblkubad[ui->ui_ubanum] == 0) {
		printf("dh%d: can't map blkdata\n", ui->ui_unit);
		return 0;
	}
	dhblkubad[ui->ui_ubanum] &= UBADMASK;
#endif
	return 1;
}

/*
 * Open a DH11 line.
 * Do not monkey with uba mapping.
 * Turn on dh if this is its first open.
 * Call dmopen to wait for carrier.
 */
/*ARGSUSED*/
dhopen(q, d, flag)
register struct queue *q;
dev_t d;
int flag;
{
	register int dev;
	register struct dh *dhp;

	dev = minor(d);
	dhp = &dh[dev];
	if (dev >= NDHLINE || !(dhp->state & EXISTS))
		return (0);

	q->ptr = (caddr_t)dhp;
	WR(q)->ptr = (caddr_t)dhp;
	/*
	 * If this is first open, initialise tty state to default.
	 */
	if ((dhp->state&ISOPEN)==0 || (dhp->state&CARR_ON)==0) {
		register int s = spl5();

		dhp->flags = ODDP|EVENP;
		dhp->ispeed = dhp->ospeed = SSPEED;
		dhp->state |= WOPEN;
		dhparam(dhp);
		dmopen(dev);	/* wait for carrier */
		if (!(dhp->state & CARR_ON)) {
			wakeup((caddr_t)dhp);
			dhp->ispeed = dhp->ospeed = 0;
			dhparam(dhp);
			splx(s);
			return 0;
		}
		dhp->rdq = q;
		dhp->state |= ISOPEN;
		dhp->state &= ~WOPEN;
		splx(s);
	}
	return 1;
}

/*
 * Close a DH11 line, turning off the DM11.
 */
/*ARGSUSED*/
dhclose(q, d)
dev_t d;
register struct queue *q;
{
	register struct dh *dhp;
	int s = spl5();

	dhp = (struct dh *)q->ptr;
	if (dhp->oblock != NULL) {
		register struct block *bp = dhp->oblock;

		dhp->oblock = NULL;
		freeb(bp);
	}
	flushq(WR(q), 1);
	dhp->rdq = NULL;
	((struct dhdevice *)dhboard[dhp->board]->ui_addr)->dhbreak &=
		~(1<<(minor(d) & 017));
	if (dhp->state&HPCL || (dhp->state&CARR_ON)==0) {
		dhp->ispeed = dhp->ospeed = 0;
		dhparam(dhp);
	}
	dhp->state &= EXISTS;
	
	/* there is no dmclose() to call */
	splx(s);
}


/*
 * dh11 write put routine
 */
dhoput(q, bp)
register struct queue *q;
register struct block *bp;
{
	register struct dh *dhp = (struct dh *)q->ptr;
	register union stmsg *sp;
	register int s;
	int delaytime;

	switch (bp->type) {

	case M_IOCTL:
		sp = (union stmsg *)bp->rptr;
		switch (sp->ioc0.com) {

		case TIOCSETP:
		case TIOCSETN:
			delaytime = 0;
			if (dhp->ispeed != sp->ioc1.sb.sg_ispeed)
				delaytime = 20;
			dhp->ispeed = sp->ioc1.sb.sg_ispeed;
			dhp->flags = sp->ioc1.sb.sg_flags;
			bp->type = M_IOCACK;
			bp->wptr = bp->rptr;
			qreply(q, bp);
			qpctl1(q, M_DELAY, delaytime);	/* wait a bit */
			qpctl(q, M_CTL);		/* means do dhparam */
			dhstart(dhp);
			return;

		case TIOCGETP:
			sp->ioc1.sb.sg_ispeed = dhp->ispeed;
			sp->ioc1.sb.sg_ospeed = dhp->ospeed;
			bp->type = M_IOCACK;
			qreply(q, bp);
			return;

		case TIOCHPCL:
			dhp->state |= HPCL;
			bp->type = M_IOCACK;
			bp->wptr = bp->rptr;
			qreply(q, bp);
			return;

		default:
			bp->wptr = bp->rptr;
			bp->type = M_IOCNAK;
			qreply(q, bp);
			return;
		}

	case M_STOP:
		s = spl5();
		dhp->state |= DHSTOP;
		freeb(bp);
		if ((bp = dhp->oblock) != NULL) {
			dhp->oblock = NULL;
			putbq(q, bp);
		}
		dhstop(dhp);
		splx(s);
		return;

	case M_START:
		dhp->state &= ~DHSTOP;
		dhstart(dhp);
		break;

	case M_FLUSH:
		flushq(q, 1);
		freeb(bp);
		s = spl5();
		if ((bp = dhp->oblock) != NULL) {
			dhp->oblock = NULL;
			freeb(bp);
		}
		splx(s);
		return;

	case M_BREAK:
		qpctl1(q, M_DELAY, 10);
		putq(q, bp);
		qpctl1(q, M_DELAY, 10);
		dhstart(dhp);
		return;

	case M_HANGUP:
		dhp->state &= ~DHSTOP;
	case M_DELAY:
	case M_DATA:
		putq(q, bp);
		dhstart(dhp);
		return;

	default:		/* not handled; just toss */
		break;
	}
	freeb(bp);
}


/*
 * Set parameters from open or stty into the DH hardware
 * registers.
 */
dhparam(dhp)
register struct dh *dhp;
{
	register struct dhdevice *addr;
	register int lpar;
	int unit = dhp->line;

	addr = (struct dhdevice *)dhboard[dhp->board]->ui_addr;
	addr->un.dhcsr = (unit&017) | DH_IE;
	if ((dhp->ispeed)==0) {
		dhp->state |= HPCL;
		dmctl(unit, DML_OFF, DMSET);		/* hang up */
		return;
	}
	lpar = ((dhp->ospeed)<<10) | ((dhp->ispeed)<<6);
	if ((dhp->ispeed) == B134)
		lpar |= BITS6|PENABLE|HDUPLX;
	else if (dhp->flags & RAW)
		lpar |= BITS8;
	else
		lpar |= BITS7|PENABLE;
	if ((dhp->flags&EVENP) == 0)
		lpar |= OPAR;
	if ((dhp->ospeed) == B110)	/* 110 baud (ugh!): 2 stop bits */
		lpar |= TWOSB;
	addr->dhlpr = lpar;
}

/*
 * DH11 receiver interrupt.
 */
dhrint(dev)
int dev;
{
	register struct block *bp;
	register struct dhdevice *dhaddr;
	register struct dh *dhp;
	register int c;

	if (dev >= NDH) {
		printf("dh%d: bad rd intr\n", dev);
		return;
	}
	dhaddr = (struct dhdevice *)dhboard[dev]->ui_addr;
	/*
	 * get chars from the silo for this line
	 */
	while ((c = dhaddr->dhrcr) < 0) { /* char present */
		dhp = &dh[dev*NPER + ((c>>8) & 017)];
		if (c&DH_DO) {
			++dhoverrun;
			continue;
		}
		if (dhp->rdq == NULL)
			continue;
		if (dhp->rdq->next->flag & QFULL) {
			dhmiss++;	/* you lose */
			continue;
		}
		if ((bp = allocb(16)) == NULL)
			continue;	/* out of space - you lose */
		if (c&DH_FE)		/* frame error == BREAK */
			bp->type = M_BREAK;
		else
			*bp->wptr++ = c;
		(*dhp->rdq->next->qinfo->putp)(dhp->rdq->next, bp);
	}
}


/*
 * DH11 transmitter interrupt. Dev is board number.
 * Restart each line which used to be active but has
 * terminated transmission since the last interrupt.
 */
dhtint(dev)
int dev;
{
	register struct dhdevice *dhaddr = (struct dhdevice *)dhboard[dev]->ui_addr;
	register struct dh *dhp;
	register struct block *bp;
	register dev_t unit;
	short bar, *sbar, ttybit;

	if (dhaddr->un.dhcsr & DH_NXM) {	/* somebody goofed */
		dhaddr->un.dhcsr |= DH_CNI;	/* make amends */
		printf("dh%d: NXM\n", dev);
	}
	sbar = &dhsar[dev];
	bar = *sbar & ~dhaddr->dhbar;
	unit = dev * NPER;
	ttybit = 1;
	dhaddr->un.dhcsr &= (short)~DH_TI;	/* no more, please */
	for (; bar; unit++, ttybit <<= 1)
		if (bar & ttybit) {
			*sbar &= ~ttybit;
			bar &= ~ttybit;
			dhp = &dh[unit];
			dhp->state &= ~BUSY;
			if (dhp->state&FLUSH)
				dhp->state &= ~FLUSH;
			else {
				register u_short cntr;
				struct uba_device *ui = dhboard[dev];

				dhaddr->un.dhcsrl = (unit&017)|DH_IE;
				bp = dhp->oblock;
				if (bp != NULL) {
					/*
					 * Do arithmetic in a short to make up
					 * for lost 16&17 address bits.
					 * This is an awful kludge.
					 */
					cntr = dhaddr->dhcar -
						UBACVT(bp->rptr, ui->ui_ubanum);
					/* whole block done? */
					if (cntr == (bp->wptr - bp->rptr)) {
						freeb(bp);
					} else {	/* block interrupted */
						bp->rptr += cntr;
						putbq(dhp->rdq, bp);
					}
					dhp->oblock = NULL;
				}
			}
			dhstart(dhp);
		}
}

dhtimo(dhp)
register struct dh *dhp;
{
	if (dhp->state&BRKING) {
		dhp->brking &= ~(1 << dhp->line);
		((struct dhdevice *)dhboard[dhp->board]->ui_addr)->dhbreak =
		    dhp->brking;
	}
	dhp->state &= ~(TIMEOUT|BRKING);
	dhstart(dhp);
}

/*
 * Start (restart) transmission on the given DH11 line.
 */
dhstart(dhp)
register struct dh *dhp;
{
	register struct dhdevice *dhaddr =
		(struct dhdevice *)dhboard[dhp->board]->ui_addr;
	register int dh, unit;
	register int s = spl5();
	register struct block *bp;

#define	GETOUT	{ splx(s); return; }
	unit = dhp->line;
	dh = unit / NPER;
again:
	if (dhp->state&(TIMEOUT|DHSTOP|BRKING) || dhp->oblock != NULL) {
		GETOUT;
	}
	
	if (dhp->rdq == NULL)
		GETOUT;
	if ((bp = getq(WR(dhp->rdq))) == NULL) {
		GETOUT;
	}

	switch (bp->type) {
	case M_DATA:
		dhp->oblock = bp;
		if (bp->wptr > bp->rptr) {	/* positive length transfer */
			/*
			 * truly hideous, but what can you do when
			 * dealing with a VAX dh?
			 */
			dhaddr->un.dhcsrl = unit | DH_IE |
				UBXADDR(bp->rptr, dhboard[dh]->ui_ubanum) << 4;
			/*
			 * nonsense with `word' is to be sure the dhbar |= word
			 * below is done with an interlocking bisw2 instruction.
			 */
			{
			short word = 1 << unit;
	
			dhsar[dh] |= word;
			dhaddr->dhcar = UBADDR(bp->rptr, dhboard[dh]->ui_ubanum);
			dhaddr->dhbcr = -(bp->wptr - bp->rptr);
			dhaddr->dhbar |= word;
			}
			dhp->state |= BUSY;
		}
		break;

	case M_BREAK:
		dhp->brking |= 1 << dhp->line;
		dhaddr->dhbreak = dhp->brking;
		dhp->state |= BRKING|TIMEOUT;
		timeout(dhtimo, (caddr_t)dhp, 15);	/* about 250 ms */
		freeb(bp);
		break;

	case M_DELAY:
		dhp->state |= TIMEOUT;
		timeout(dhtimo, (caddr_t)dhp, (int)*bp->rptr + 6);
		freeb(bp);
		break;

	case M_HANGUP:
		dhp->ispeed = dhp->ospeed = 0;
	case M_CTL:
		freeb(bp);
		dhparam(dhp);
		goto again;

	}
	splx(s);
}


/*
 * Stop output on a line, e.g. for ^S/^Q or output flush (e.g. ^O).
 */
/*ARGSUSED*/
dhstop(dhp)
register struct dh *dhp;
{
	register struct dhdevice *addr = 
		(struct dhdevice *)dhboard[dhp->board]->ui_addr;
	register int unit, s = spl5();

	if (dhp->state & BUSY) {
		/*
		 * Device is transmitting; stop output
		 * by selecting the line and setting the byte
		 * count to -1.  We will clean up later
		 * by examining the address where the dh stopped.
		 */
		unit = dhp->line;
		addr->un.dhcsrl = (unit&017) | DH_IE;
		dhp = &dh[unit];
		if (!(dhp->state&DHSTOP))	/* not called for ^S */
			dhp->state |= FLUSH;	/* must have been for ^O */
		addr->dhbcr = -1;
	}
	splx(s);
}


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

	for (dh = 0; dh < NDH; dh++)
		dhrint(dh);
	splx(s);
}

/*
 * reset driver after a unibus reset
 */
dhreset(uban)
int uban;
{
	register int dhn;
	register int unit;
	register struct dh *dhp;
	register struct uba_device *ui;
	int i;

	for (dhn = 0; dhn < NDH; dhn++) {
		ui = dhboard[dhn];
		if (ui == 0 || ui->ui_alive == 0 || ui->ui_ubanum != uban)
			continue;
		printf(" dh%d", dhn);
		((struct dhdevice *)ui->ui_addr)->un.dhcsr |= DH_IE;
		((struct dhdevice *)ui->ui_addr)->dhsilo = 16;
		unit = dhn * NPER;
		for (i = 0; i < NPER; i++) {
			dhp = &dh[unit];
			if (dhp->state & (ISOPEN|WOPEN)) {
				dhparam(dhp);
				dmctl(unit, DML_ON, DMSET);
				dhp->state &= ~BUSY;
				dhstart(dhp);
			}
			unit++;
		}
	}
	dhtimer();
}

/*
 * Routines for DM11 half of hardware
 */

/*
 * Configuration routine to cause a dm to interrupt.
 */
dmprobe(reg)
caddr_t reg;
{
	register int br, vec;			/* value-result */
	register struct dmdevice *dmaddr = (struct dmdevice *)reg;

#ifdef lint
	br = 0; vec = br; br = vec;
	dmintr(0);
#endif
	dmaddr->dmcsr = DM_DONE|DM_IE;
	DELAY(20);
	dmaddr->dmcsr = 0;
	return (1);
}

/*ARGSUSED*/
dmattach(ui)
struct uba_device *ui;
{

	/* no local state to set up */
}

/*
 * Turn on the line associated with dh dev.
 *
 * From 4BSD.
 */
dmopen(dev)
{
	register struct dh *dhp;
	register struct dmdevice *addr;
	register struct uba_device *ui;
	register dev_t unit;
	register int dm;
	int s;

	unit = minor(dev);
	dm = unit / NPER;
	dhp = &dh[unit];
	unit &= 0xf;
	if (dm >= NDM || (ui = dmboard[dm]) == 0 || ui->ui_alive == 0 ||
	    ui->ui_flags & (1<<unit)) {
		dhp->state |= CARR_ON|DIALOUT;
		return;
	}
	addr = (struct dmdevice *)ui->ui_addr;
	s = spl5();
	addr->dmcsr &= ~DM_SE;
	while (addr->dmcsr & DM_BUSY)
		/* please do not adjust yr set */ ;
	addr->dmcsr = unit;
	addr->dmlstat = DML_ON;
	if (addr->dmlstat&DML_CAR)
		dhp->state |= CARR_ON;
	addr->dmcsr = DM_IE|DM_SE;
	while ((dhp->state&CARR_ON)==0)
		if (tsleep((caddr_t)dhp, DHPRI, 0) != TS_OK)
			break;
	splx(s);
	printf("dmopen(0x%x) state=0x%x\n", dev, dhp->state);
}

/*
 * Dump control bits into the DM registers.
 */
dmctl(dev, bits, how)
int dev;
int bits, how;
{
	register struct uba_device *ui;
	register struct dmdevice *addr;
	register int unit, s;
	int dm;

	unit = minor(dev);
	dm = unit / NPER;
	if ((ui = dmboard[dm]) == 0 || ui->ui_alive == 0)
		return;
	addr = (struct dmdevice *)ui->ui_addr;
	s = spl5();
	addr->dmcsr &= ~DM_SE;
	while (addr->dmcsr & DM_BUSY)
		/* be patient, I'm only a DH-11 */ ;
	addr->dmcsr = unit & 0xf;
	switch (how) {
	case DMSET:
		addr->dmlstat = bits;
		break;
	case DMBIS:
		addr->dmlstat |= bits;
		break;
	case DMBIC:
		addr->dmlstat &= ~bits;
		break;
	}
	addr->dmcsr = DM_IE|DM_SE;
	splx(s);
}

/*
 * DM11 interrupt; deal with carrier transitions.
 */
dmintr(dm)
register int dm;
{
	register struct uba_device *ui;
	register struct dh *dhp;
	register struct dmdevice *addr;

	ui = dmboard[dm];
	if (ui == 0)
		return;
	addr = (struct dmdevice *)ui->ui_addr;
	if (addr->dmcsr&DM_DONE) {
		if (addr->dmcsr&DM_CF) {
			dhp = &dh[dm * NPER +(addr->dmcsr&0xf)];
			wakeup((caddr_t)dhp);
#ifdef	NOTDEF
			if ((dhp->state&WOPEN) == 0 ) {
				if (addr->dmlstat & DML_CAR) {
					dhp->state &= ~DHSTOP;
					wakeup((caddr_t)dhp);
				} else if ((dhp->state&DHSTOP) == 0) {
					dhp->state |= DHSTOP;
					dhstop(dhp);
				}
			} else 
#endif
			if ((addr->dmlstat&DML_CAR)==0) {
				if (!(dhp->state&WOPEN) && !(dhp->state&DIALOUT)) {
					putctl(dhp->rdq->next, M_HANGUP);
					addr->dmlstat = 0;
				}
				dhp->state &= ~CARR_ON;
			} else
				dhp->state |= CARR_ON;
		}
		addr->dmcsr = DM_IE|DM_SE;
	}
}