V9/sys/dev.old/dhv.c

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

/*
 * DHV-11 driver for Ninth Edition UNIX
 * by Andrew Hume (loosely based on dh driver from toronto)
 */

#include "dhv.h"
#if NDHV > 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"

#define NPER	8		/* lines per board */
#define	NDHVLINE	(NDHV*NPER)

/* bits in dhv[]->state */
#define	ISOPEN	0x0002
#define	WOPEN	0x0004
#define	TIMEOUT	0x0008
#define	CARRIER	0x0010
#define	DHSTOP	0x0020
#define	HPCL	0x0040
#define	BRKING	0x0080
#define BUSY	0x0100
#define FLUSH	0x0200

#define SSPEED	B9600	/* reasonable default nowadays */
#define DHPRI	30

/* hardware structure */
struct dhvreg
{
	unsigned short csr;
	short rbuf;		/* coincides with txchar (not used) */
	unsigned short lpr;	/* line param */
	unsigned short lstat;	/* line status */
	unsigned short lctl;	/* line control */
	unsigned short addr1;
	unsigned short addr2;
	unsigned short cnt;
};

struct hilo	/* for utterly wretched appalling hardware */
{
	char low;
	char high;
};
#define	LOW(ptr)	((struct hilo *)&(ptr))->low
#define	HIGH(ptr)	((struct hilo *)&(ptr))->high

/* csr bits */
#define	TXACT		0x8000
#define	TXIE		0x4000
#define	DIAGFAIL	0x2000
#define	TXERR		0x1000
#define	TXLINE(csr)	(((csr)>>8)&0xF)
#define	RXAVAIL		0x0080
#define	RXIE		0x0040
#define	MRESET		0x0020
#define	POINT(r, l)	LOW((r)->csr) = (((l)&0xF) | RXIE)

/* rbuf */
#define	VALID		0x8000
#define	OERR		0x4000
#define	FERR		0x2000
#define	PERR		0x1000
#define	LINE(rbuf)	(((rbuf)>>8)&0xF)
#define	ISMODEM(rbuf)	(((rbuf)&0x7000)==0x7000)
#define	DCD		0x10	/* note that it is shifted left 8 bits in lstat */

/* lpr bits */
#define	BITS5		0x00
#define	BITS6		0x08
#define	BITS7		0x10
#define	BITS8		0x18
#define	PENAB		0x20
#define	EPARITY		0x40
#define	STOP2		0x80

/* lctl bits */
#define	TXABORT		0x0001
#define	RXENABLE	0x0004
#define	BREAK		0x0008
#define	MODEM		0x0100
#define	DTR		0x0200
#define	RTS		0x1000

/* addr2 */
#define	TXEN		0x8000
#define	TXSTART		0x0080

char dhvsp[16] =
{
	0, 0, 1, 2, 3, 4 ,0, 5, 6, 7, 8, 10, 11, 13, 14, 0
};


int	dhvprobe(), dhvattach(), dhvrint(), dhvtint();
struct	uba_device *dhvboard[NDHV];
u_short	dhvstd[] = { 0 };
struct	uba_driver dhvdriver =
	{ dhvprobe, 0, dhvattach, 0, dhvstd, "dhv", dhvboard };

/*
 * interface with stream i/o system
 */
int	dhvopen(), dhvclose(), dhvoput(), nodev();
static	struct qinit dhvrinit = { nodev, NULL, dhvopen, dhvclose, 0, 0 };
static	struct qinit dhvwinit = { dhvoput, NULL, dhvopen, dhvclose, 200, 100 };
struct	streamtab dhvinfo = { &dhvrinit, &dhvwinit };

/*
	all stream buffers are mapped into unibus/qbus space.
*/
#define UBACVT(x)	((0x03ffffL&blkubad) + ((long)(caddr_t)(x)-(long)(caddr_t)blkdata))
#define	UBLADDR(x)	(UBACVT(x)&0xffff)	/* low order 16 bits */
#define UBHADDR(x)	((UBACVT(x)>>16)&0x3f)	/* high order 6 bits */

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

/*
 * misc private data
 */
extern long blkubad;
extern u_char blkdata[];
int	dhvoverrun;
int	dhvmiss;			/* chars lost due to q full */

/*ARGSUSED*/
dhvprobe(reg)
	caddr_t reg;
{
	register int br, cvec;		/* these are ``value-result'' */
	register struct dhvreg *regs = (struct dhvreg *)reg;
	int i, c;

#ifdef lint
	br = 0; cvec = br; br = cvec;
	dhvrint(0); dhvtint(0);
#endif
	regs->csr = MRESET;
	DELAY(1500000);		/* 1.5 seconds */
	if((regs->csr&MRESET) || (regs->csr&DIAGFAIL)){
		printf("dhv master reset fail csr=0x%x\n", regs->csr);
		return(0);
	}
	/* all's well after the reset; now allow the interrupt */
	regs->csr = RXIE;
	DELAY(1000);		/* fifo is nonempty so it won't take long */
	printf("dhv reset:");
	for(i = 0; i < 8; i++){
		c = regs->rbuf&0xFF;
		if((c == 0201) || (c == 0203)) continue;	/* fillers */
		if((c&0x80) || ((c&1) == 0)){
			printf("dhv reset silo: bad code 0%o\n", c);
			return(0);
		}
		printf(" rom 0x%x", c);
	}
	printf("\n");
	POINT(regs, 0);	/* init to a known state */
	return 1;
}

/*
	as throughout this driver, we assume uba zero
*/
dhvattach(ui)
	register struct uba_device *ui;
{
	register int i;
	register struct dhv *dhvp;

	for(i = 0, dhvp = &dhv[ui->ui_unit*NPER]; i < NPER; i++, dhvp++){
		dhvp->state = 0;
		dhvp->board = ui->ui_unit;
		dhvp->line  = i;
	}
	return 1;
}

/*
	use tsleep so user can interrupt without wrecking accounting
*/
dhvopen(q, d, flag)
	register struct queue *q;
	dev_t d;
	int flag;
{
	register int dev;
	register struct dhv *dhvp;

	dev = minor(d);
	if(dev >= NDHVLINE)
		return(0);
	dhvp = &dhv[dev];
	q->ptr = (caddr_t)dhvp;
	WR(q)->ptr = (caddr_t)dhvp;
	/*
		If this is first open, initialise tty state to default.
	 */
	if(((dhvp->state&ISOPEN)==0) || ((dhvp->state&CARRIER)==0)){
		register int s = spl5();

		dhvp->flags = ODDP|EPARITY;
		dhvp->ispeed = dhvp->ospeed = SSPEED;
		dhvp->lctl = MODEM|DTR|RTS|RXENABLE;
		dhvp->state = 0;
		dhvparam(dhvp);
		while(!(dhvp->state & CARRIER))
			if(tsleep((caddr_t)dhvp, DHPRI, 0) != TS_OK){
				splx(s);
				return(0);
			}
		dhvp->rdq = q;
		dhvp->oblock = 0;
		dhvp->state |= ISOPEN;
		splx(s);
	}
	return 1;
}

/*
 * Close a DHV11 line.
 */
/*ARGSUSED*/
dhvclose(q, dev)
	dev_t dev;
	register struct queue *q;
{
	register struct dhv *dhvp;
	register struct dhvreg *regs;
	int s = spl5();

	dhvp = (struct dhv *)q->ptr;
	if(dhvp->oblock){
		freeb(dhvp->oblock);
		dhvp->oblock = 0;
	}
	flushq(WR(q), 1);
	dhvp->rdq = NULL;
	regs = ((struct dhvreg *)dhvboard[dhvp->board]->ui_addr);
	POINT(regs, dhvp->line);
	regs->lctl = 0;
	dhvp->state = 0;
	splx(s);
}


/*
 * dhv11 write put routine
 */
dhvoput(q, bp)
	register struct queue *q;
	register struct block *bp;
{
	register struct dhv *dhvp = (struct dhv *)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 TIOCSDEV:
			delaytime = 0;
			if(dhvp->ispeed != sp->ioc3.sb.ispeed)
				delaytime = 20;
			dhvp->ospeed = sp->ioc3.sb.ospeed;
			dhvp->ispeed = sp->ioc3.sb.ispeed;
			dhvp->flags = sp->ioc3.sb.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 dhvparam */
			dhvstart(dhvp);
			return;
		case TIOCGDEV:
			sp->ioc3.sb.ispeed = dhvp->ispeed;
			sp->ioc3.sb.ospeed = dhvp->ospeed;
			sp->ioc3.sb.flags = dhvp->flags;
			sp->ioc3.sb.flags &= (F8BIT | EVENP | ODDP);
			bp->type = M_IOCACK;
			qreply(q, bp);
			return;
		default:
			bp->wptr = bp->rptr;
			bp->type = M_IOCNAK;
			qreply(q, bp);
			return;
		}
	case M_STOP:
		s = spl5();
		dhvp->state |= DHSTOP;
		freeb(bp);
		dhvstop(dhvp);
		splx(s);
		return;
	case M_START:
		dhvp->state &= ~DHSTOP;
		dhvstart(dhvp);
		break;
	case M_FLUSH:
		flushq(q, 1);
		freeb(bp);
		return;
	case M_BREAK:
		qpctl1(q, M_DELAY, 10);
		putq(q, bp);
		qpctl1(q, M_DELAY, 10);
		dhvstart(dhvp);
		return;
	case M_HANGUP:
		dhvp->state &= ~DHSTOP;
	case M_DELAY:
	case M_DATA:
		putq(q, bp);
		dhvstart(dhvp);
		return;
	default:		/* not handled; just toss */
		break;
	}
	freeb(bp);
}

/*
	Set parameters from open or stty into the DH hardware
	registers.
*/
dhvparam(dhvp)
	register struct dhv *dhvp;
{
	register struct dhvreg *regs;
	register int lpar;
	register s;

	regs = (struct dhvreg *)dhvboard[dhvp->board]->ui_addr;
	if(dhvp->ospeed)
		dhvp->lctl |= (DTR|RTS);
	else
		dhvp->lctl &= ~(DTR|RTS);
	lpar = (dhvsp[dhvp->ospeed]<<12) | (dhvsp[dhvp->ispeed]<<8);
	if((dhvp->ospeed) == B134)
		lpar |= BITS6|PENAB;
	else if (dhvp->flags & F8BIT)
		lpar |= BITS8;
	else if(((s = dhvp->flags&(EVENP|ODDP)) == (EVENP|ODDP)) || (s == 0))
		lpar |= BITS8;
	else {
		lpar |= BITS7|PENAB;
		if(dhvp->flags&EVENP)
			lpar |= EPARITY;
	}
	if ((dhvp->ospeed) == B110)	/* 110 baud (ugh!): 2 stop bits */
		lpar |= STOP2;
	dhvp->state &= ~CARRIER;
	POINT(regs, dhvp->line);
	regs->lpr = lpar;
	regs->lctl = dhvp->lctl;
	if(regs->lstat&(DCD<<8))
		dhvp->state |= CARRIER;
}

/*
	DHV11 receiver interrupt.
*/
dhvrint(dev)
	dev_t dev;
{
	register struct block *bp;
	register struct dhvreg *regs;
	register struct dhv *dhvp;
	register int c;
	int hangup;

	if(dev >= NDHV) {
		printf("dhv%d: bad rd intr\n", dev);
		return;
	}
	regs = (struct dhvreg *)dhvboard[dev]->ui_addr;
	/*
		get chars from the silo for this line
	*/
	while((c = regs->rbuf) < 0) { /* char present */
		dhvp = &dhv[dev*NPER + LINE(c)];
		hangup = 0;
		if(ISMODEM(c)){
			dhvp->state &= ~CARRIER;
			if((c&DCD) == 0)
				hangup = 1;
			else {
				dhvp->state |= CARRIER;
				wakeup((caddr_t)dhvp);
				continue;
			}
			c &= ~(OERR|FERR|PERR);
		}
		if(c&OERR){
			++dhvoverrun;
			continue;
		}
		if(dhvp->rdq == NULL)
			continue;
		if(dhvp->rdq->next->flag & QFULL) {
			dhvmiss++;	/* you lose */
			continue;
		}
		if((bp = allocb(16)) == NULL){
			printf("rint: out of space\n");
			continue;	/* out of space - you lose */
		}
		if(hangup)
			bp->type = M_HANGUP;
		else if(c&FERR)		/* frame error == BREAK */
			bp->type = M_BREAK;
		else
			*bp->wptr++ = c;
		(*dhvp->rdq->next->qinfo->putp)(dhvp->rdq->next, bp);
	}
}


/*
	DHV11 transmitter interrupt. Dev is board number.
	Restart each line which used to be active but has
	terminated transmission since the last interrupt.
*/
dhvtint(dev)
	dev_t dev;
{
	register struct dhvreg *regs;
	register struct dhv *dhvp;
	register struct block *bp;
	register struct queue *q;
	dev_t unit;
	short csr;

	regs = (struct dhvreg *)dhvboard[dev]->ui_addr;
	while((csr = regs->csr) < 0){
		unit = TXLINE(csr);
		dhvp = &dhv[unit + dev*NPER];
		dhvp->state &= ~BUSY;
		POINT(regs, unit);
		if((csr&(TXACT|TXERR)) == (TXACT|TXERR)){	/* somebody goofed */
			printf("dhv%d: txerr csr=0x%x addr2=0x%x addr1=0x%x\n",
				dev, csr, regs->addr2, regs->addr1);
			regs->cnt = 0;	/* allow progress */
		}
		q = WR(dhvp->rdq);
		if(bp = dhvp->oblock){
			bp->rptr = bp->wptr - regs->cnt;
			if(bp->rptr == bp->wptr){
				dhvp->oblock = 0;
				freeb(bp);
			}
		}
		dhvstart(dhvp);
	}
}

dhvtimo(dhvp)
	register struct dhv *dhvp;
{
	register struct dhvreg *regs = (struct dhvreg *)dhvboard[dhvp->board]->ui_addr;

	if(dhvp->state&BRKING) {
		int s = spl5();
		POINT(regs, dhvp->line);
		regs->lctl &= ~BREAK;
		splx(s);
	}
	dhvp->state &= ~(TIMEOUT|BRKING);
	dhvstart(dhvp);
}

/*
	Start (restart) transmission on the given DH11 line.
*/
dhvstart(dhvp)
register struct dhv *dhvp;
{
	register struct dhvreg *regs =
		(struct dhvreg *)dhvboard[dhvp->board]->ui_addr;
	register int unit;
	register int s = spl5();
	register struct block *bp;
	caddr_t addr;
	short goo;

	unit = dhvp->line;
	if(dhvp->state & BUSY)
		goto done;
again:
	if(dhvp->state&(TIMEOUT|DHSTOP|BRKING) || (dhvp->rdq == NULL))
		goto done;
	if((bp = dhvp->oblock) == NULL){
		if((bp = getq(WR(dhvp->rdq))) == NULL)
			goto done;
	}
	switch(bp->type)
	{
	case M_DATA:
		if(bp->wptr > bp->rptr){	/* positive length transfer */
			LOW(regs->csr) = RXIE | (unit&0xF);
			while(regs->addr2&TXSTART)
				printf("dhv: start set\n");
			if(regs->lctl&TXABORT)
				regs->lctl &= ~TXABORT;
			HIGH(regs->csr) = TXIE>>8;
			regs->addr1 = UBLADDR(bp->rptr);
			regs->cnt = bp->wptr - bp->rptr;
			HIGH(regs->addr2) = TXEN>>8;
			LOW(regs->addr2) = UBHADDR(bp->rptr) | TXSTART;
			dhvp->state |= BUSY;
			dhvp->oblock = bp;
		} else {
			freeb(bp);
			dhvp->oblock = 0;
		}
		break;
	case M_BREAK:
		dhvp->state |= BRKING|TIMEOUT;
		timeout(dhvtimo, (caddr_t)dhvp, 15);	/* about 250 ms */
		freeb(bp);
		break;
	case M_DELAY:
		dhvp->state |= TIMEOUT;
		timeout(dhvtimo, (caddr_t)dhvp, (int)*bp->rptr + 6);
		freeb(bp);
		break;
	case M_HANGUP:
		dhvp->ispeed = dhvp->ospeed = 0;
		/* fall through */
	case M_CTL:
		freeb(bp);
		dhvparam(dhvp);
		goto again;

	}
done:
	splx(s);
}


/*
	Stop output on a line, e.g. for ^S/^Q or output flush (e.g. ^O).
*/
dhvstop(dhvp)
	register struct dhv *dhvp;
{
	register struct dhvreg *regs = 
		(struct dhvreg *)dhvboard[dhvp->board]->ui_addr;
	register int unit, s = spl5();

	if(dhvp->state & BUSY){
		/*
			just stop it, tint looks at the count
		*/
		POINT(regs, dhvp->line);
		regs->lctl |= TXABORT;
	}
	splx(s);
}

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

	for(dhv = 0; dhv < NDHV; dhv++)
		dhvrint(dhv);
	splx(s);
}

/*
	reset driver after a unibus reset
	this code should work but i don't really know
	what it is supposed to do.
*/
dhvreset(uban)
	int uban;
{
	register int i;
	register struct dhv *dhvp;
	register int s = spl5();

	for(i = 0, dhvp = &dhv[uban*NPER]; i < NPER; i++, dhvp++){
		dhvparam(dhvp);
		dhvp->state &= ~BUSY;
		dhvstart(dhvp);
	}
	splx(s);
}

qpr(q)
	register struct queue *q;
{
	printf("Q@0x%x: flag=0%o cnt=%d ptr=0x%x f=0x%x l=0x%x\n", q, q->flag,
		q->count, q->ptr, q->first, q->last);
}