V10/lsys/io/dz.c
/*
* DZ-11 Driver
*/
/* Modified 4/84 by J. Wolitzky to allow dialout operation with smart modems */
#define TRC(c)
#include "sys/param.h"
#include "sys/stream.h"
#include "sys/ttyio.h"
#include "sys/ttyld.h"
#include "sys/ubaddr.h"
#include "sys/dz.h"
#include "sys/conf.h"
#define LPB 8 /* lines per board */
#define BOARD(d) ((d)>>3)
#define LINE(d) ((d)&07)
#define BITS7 020
#define BITS8 030
#define TWOSB 040
#define PENABLE 0100
#define OPAR 0200
#define CLR 020 /* Clear */
#define MSE 040 /* Master Scan Enable */
#define RIE 0100 /* Receiver Interrupt Enable */
#define SAE 010000 /* Silo Alarm Enable */
#define TIE 040000 /* Transmit Interrupt Enable */
#define TRDY 0100000 /* Transmitter ready */
#define DZ_IEN (MSE+RIE+TIE)
#define PERROR 010000
#define FRERROR 020000
#define OVERRUN 040000
#define SSPEED B1200 /* std speed = 1200 baud */
#define ISOPEN 02
#define WOPEN 04
#define TIMEOUT 010
#define CARR_ON 020
#define DZSTOP 040
#define HPCL 0100
#define BRKING 0200
#define DIALOUT 0400 /* set when used as dialout with smart modems */
#define DZPRI 30
struct device {
short dzcsr;
short dzrbuf;
#define dzlpr dzrbuf
char dztcr;
char dzdtr;
char dztbuf;
#define dzrind dztbuf
char dzbrk;
#define dzmsr dzbrk
};
extern struct dz dz[]; /* one per line */
extern struct ubaddr dzaddr[]; /* one per board */
extern int dzcnt; /* number of lines */
int dzoverrun;
char dz_speeds[] = {
0, 020, 021, 022, 023, 024, 0, 025,
026, 027, 030, 032, 034, 036, 037, 0
};
long dzopen();
int dzclose(), dzoput();
static struct qinit dzrinit = { noput, NULL, dzopen, dzclose, 0, 0 };
struct qinit dzwinit = { dzoput, NULL, dzopen, dzclose, 200, 100 };
struct streamtab dzinfo = { &dzrinit, &dzwinit };
struct cdevsw dzcdev = cstrinit(&dzinfo);
int dzmiss;
long
dzopen(q, d)
register struct queue *q;
dev_t d;
{
register dev;
register struct dz *dzp;
register int s;
static int onceinit = 0;
dev = minor(d);
if (dev >= dzcnt)
return(0);
dzp = &dz[dev];
if ((dzp->addr = (struct device *)ubaddr(&dzaddr[BOARD(dev)])) == NULL
|| ubbadaddr(dzaddr[BOARD(dev)].ubno, dzp->addr, sizeof(short))) {
printf("dz%d absent\n", BOARD(dev));
return (0);
}
dz[BOARD(dev)*LPB].addr = dzp->addr; /* see intr code */
if (onceinit == 0) {
onceinit++;
dzscan((caddr_t)0);
}
dzp->board = BOARD(dev);
dzp->line = LINE(dev);
q->ptr = (caddr_t)dzp;
WR(q)->ptr = (caddr_t)dzp;
dzscan((caddr_t)1); /* update CARR_ON */
if ((dzp->state&ISOPEN)==0 || (dzp->state&CARR_ON)==0) {
s = spl5();
for (;;) {
dzp->flags = F8BIT|ODDP|EVENP;
dzp->speed = SSPEED;
dzparam(dzp);
if (dzp->state & CARR_ON)
break;
#if NOTDEF
dzflag = dzboard[BOARD(dev)]->ui_flags;
/* ignore carrier? */
if (dzflag & (1 << (LINE(dev)))) {
dzp->state |= (DIALOUT | CARR_ON);
break;
}
#endif
if (tsleep((caddr_t)dzp, DZPRI, 0) != TS_OK) {
wakeup((caddr_t)dzp);
dzp->speed = 0;
dzparam(dzp);
splx(s);
return(0);
}
}
dzp->rdq = q;
dzp->state |= ISOPEN;
splx(s);
}
TRC('o');
return(1);
}
dzclose(q)
register struct queue *q;
{
register struct dz *dzp;
register s;
dzp = (struct dz *)q->ptr;
s = spl5();
if (dzp->oblock) {
register struct block *bp = dzp->oblock;
dzp->oblock = NULL;
freeb(bp);
}
flushq(WR(q), 1);
dzp->rdq = NULL;
if (dzp->state&HPCL || (dzp->state&CARR_ON)==0) {
dzp->speed = 0;
dzparam(dzp);
}
dzp->state = 0;
splx(s);
TRC('c');
}
/*
* DZ write put routine
*/
dzoput(q, bp)
register struct queue *q;
register struct block *bp;
{
register struct dz *dzp = (struct dz *)q->ptr;
register s;
register struct ttydevb *tb;
register delaytime;
TRC('p');
switch(bp->type) {
case M_IOCTL:
TRC('i');
switch (stiocom(bp)) {
case TIOCSDEV:
tb = (struct ttydevb *)stiodata(bp);
delaytime = 0;
if (dzp->speed != tb->ispeed)
delaytime = 20;
dzp->speed = tb->ispeed;
dzp->flags = tb->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 dzparam */
dzstart(dzp);
return;
case TIOCGDEV:
tb = (struct ttydevb *)stiodata(bp);
tb->ispeed = tb->ospeed = dzp->speed;
tb->flags = dzp->flags;
bp->wptr = bp->rptr + sizeof(struct ttydevb) + STIOCHDR;
bp->type = M_IOCACK;
qreply(q, bp);
return;
case TIOCHPCL:
dzp->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();
dzp->state |= DZSTOP;
freeb(bp);
if (bp = dzp->oblock) {
dzp->oblock = NULL;
putbq(q, bp);
}
splx(s);
return;
case M_START:
dzp->state &= ~DZSTOP;
dzstart(dzp);
break;
case M_FLUSH:
flushq(q, 1);
freeb(bp);
s = spl5();
if (bp = dzp->oblock) {
dzp->oblock = NULL;
freeb(bp);
}
splx(s);
return;
case M_BREAK:
qpctl1(q, M_DELAY, 10);
putq(q, bp);
qpctl1(q, M_DELAY, 10);
dzstart(dzp);
return;
case M_HANGUP:
dzp->state &= ~DZSTOP;
case M_DELAY:
case M_DATA:
putq(q, bp);
TRC('d');
dzstart(dzp);
return;
default: /* not handled; just toss */
break;
}
freeb(bp);
}
/*
* Receive interrupt. Argument is DZ board number
*/
dz0int(dev)
{
register struct dz *dzp;
register struct block *bp;
register struct device *dzaddr;
register int c;
if (dev >= BOARD(dzcnt)) {
printf("dz%d bad rcv int\n", dev);
return;
}
dzaddr = dz[dev*LPB].addr;
while ((c = dzaddr->dzrbuf) < 0) { /* char present */
dzp = &dz[dev*LPB + ((c>>8)&07)];
if (c&(OVERRUN/*|PERROR*/)) {
if (c&OVERRUN)
dzoverrun++;
continue;
}
if (dzp->rdq == NULL)
continue;
if (dzp->rdq->next->flag&QFULL) {
dzmiss++;
continue;
}
if ((bp = allocb(16)) == NULL)
continue; /* no space */
if (c&FRERROR) /* frame err (break) */
bp->type = M_BREAK;
else
*bp->wptr++ = c;
(*dzp->rdq->next->qinfo->putp)(dzp->rdq->next, bp);
}
}
/*
* set device parameters
*/
dzparam(dzp)
register struct dz *dzp;
{
register short lpr;
register struct device *dzaddr;
dzaddr = dzp->addr;
dzaddr->dzcsr = DZ_IEN;
if (dzp->speed == 0) {
dzaddr->dzdtr &= ~(1 << dzp->line); /* hang up */
return;
}
dzaddr->dzdtr |= 1 << dzp->line;
lpr = (dz_speeds[dzp->speed]<<8) | dzp->line;
if (dzp->flags & F8BIT)
lpr |= BITS8;
else
lpr |= (BITS7|PENABLE);
if ((dzp->flags & EVENP) == 0)
lpr |= OPAR;
if (dzp->speed == 3)
lpr |= TWOSB; /* 110 baud: 2 stop bits */
dzaddr->dzlpr = lpr;
}
/*
* Transmitter interrupt. dev is board number.
*/
dz1int(dev)
{
register struct device *dzaddr;
register struct dz *dzp;
register struct block *bp;
register sts;
if (dev >= BOARD(dzcnt)) {
printf("dz%d bad xmt int\n", dev);
return;
}
dzaddr = dz[dev*LPB].addr;
while ((sts = dzaddr->dzcsr) & TRDY) {
dzp = &dz[dev*LPB + ((sts>>8)&07)];
if (bp = dzp->oblock) {
if (bp->rptr < bp->wptr) {
dzaddr->dztbuf = *bp->rptr++;
continue;
}
freeb(bp);
dzp->oblock = NULL;
}
dzaddr->dztcr &= ~(1 << (dzp->line));
dzstart(dzp);
}
}
dztimo(dzp)
register struct dz *dzp;
{
if (dzp->state&BRKING) {
dzp->brking &= ~(1 << dzp->line);
dzp->addr->dzbrk = dzp->brking;
}
dzp->state &= ~(TIMEOUT|BRKING);
dzstart(dzp);
}
dzstart(dzp)
register struct dz *dzp;
{
register s = spl5();
register struct block *bp;
register struct device *dzaddr;
TRC('s');
again:
if (dzp->state & (TIMEOUT|DZSTOP|BRKING) || dzp->oblock) {
TRC('t');
goto out;
}
if (dzp->rdq == NULL)
goto out;
if ((bp = getq(WR(dzp->rdq))) == NULL) {
TRC('n');
goto out;
}
switch (bp->type) {
case M_DATA:
dzp->oblock = bp;
dzaddr = dzp->addr;
dzaddr->dztcr |= 1 << (dzp->line);
break;
case M_BREAK:
dzaddr = dzp->addr;
dzp->brking |= 1 << dzp->line;
dzaddr->dzbrk = dzp->brking;
dzp->state |= BRKING|TIMEOUT;
timeout(dztimo, (caddr_t)dzp, HZ/4); /* about 250 ms */
freeb(bp);
break;
case M_DELAY:
dzp->state |= TIMEOUT;
timeout(dztimo, (caddr_t)dzp, (int)*bp->rptr + 6);
freeb(bp);
break;
case M_HANGUP:
dzp->speed = 0;
case M_CTL:
freeb(bp);
dzparam(dzp);
goto again;
}
out:
splx(s);
}
dzscan(timo)
caddr_t timo;
{
register struct device *dzaddr;
register struct dz *dzp;
register i;
for (i=0, dzp=dz; i<dzcnt; dzp++, i++) {
if (dzp->addr == 0)
continue;
dzaddr = dzp->addr;
if (dzaddr->dzmsr & (1<<dzp->line)) {
/* carrier present */
if ((dzp->state & CARR_ON)==0)
wakeup((caddr_t)dzp);
dzp->state |= CARR_ON;
} else if ((dzp->state & CARR_ON) && !(dzp->state & DIALOUT)) {
/* carrier lost */
if (dzp->state&ISOPEN) {
dzaddr->dzdtr &= ~(1<<dzp->line);
if (dzp->rdq)
putctl(dzp->rdq->next,M_HANGUP);
}
dzp->state &= ~CARR_ON;
}
}
if (timo == 0)
timeout(dzscan, (caddr_t)0, 2*HZ);
}