V10/lsys/io/dk.c
/*
* Datakit driver
*/
#include "sys/param.h"
#include "sys/systm.h"
#include "sys/stream.h"
#include "sys/dkio.h"
#include "sys/ubaddr.h"
#include "sys/conf.h"
#include "sys/dk.h"
#include "sys/dkstat.h"
#include "sys/dkmod.h"
#define SAFETY 1 /* turn off doubtful speed hack */
struct device {
unsigned short csr;
unsigned short dko;
unsigned short dki;
};
struct device *DKADDR; /* assume one addr for now */
extern struct dk dk[];
extern int dkcnt;
extern char dkstate[];
#define DKXCL 01
int dkalive;
struct dkstat dkstat;
extern struct dkmodule dkmod[];
struct dkmodule *dkmodp;
/*
* Hardware control bits
*/
#define DKTENAB 0100
#define DKRENAB 040
#define ENABS (DKRENAB)
#define DKTDONE 0200
#define DKRDONE 0100000
#define D_OSEQ 0
#define D_READ 01
#define D_WRITE 02
#define D_XPACK 03
#define DKMARK 01000
#define DKDATA 0400
#define DKPARITY 0100000
#define DKOPKT 16 /* biggest output packet */
#define DKIPKT 16 /* biggest input packet */
#define DKISML 16 /* smallest interesting allocation */
#define DKITHRES 200
int dkclose(), dkput();
long dkopen();
static struct qinit dkrinit = { noput, NULL, dkopen, dkclose, 0, 0 };
struct qinit dkwinit = { dkput, NULL, dkopen, dkclose, 0, 0 };
struct streamtab dkinfo = { &dkrinit, &dkwinit };
struct cdevsw dkcdev = cstrinit(&dkinfo);
extern struct ubaddr dkaddr[];
/*
* open DK channel
*/
long
dkopen(q, dev)
register struct queue *q;
register dev_t dev;
{
register struct dk *dkp;
static opened, badtime;
register chan;
chan = minor(dev);
if (chan<=0 || chan>=dkcnt)
return(0);
if (!opened) {
register i;
if ((DKADDR = (struct device *)ubaddr(&dkaddr[0])) == NULL
|| badaddr(DKADDR, 2)) {
printf("dk0 absent\n");
return (0);
}
DKADDR->csr = D_OSEQ;
DKADDR->dko = 0; /* Clear fifo's */
i = 256; /* sanity count */
DELAY(1000); /* wait for board to reset */
do {
if (DKADDR->csr & DKTDONE) {
dkalive = 0;
if (time > badtime + 300) {
badtime = time;
printf("DK interface bad\n");
}
return(0);
}
} while (--i);
if ((dkmodp = dkmodall(dev, 0, dkcnt)) == NULL)
return (0);
dkmodp->dkstate = dkstate;
dkalive = 1;
for (dkp = dk, i = 0; i < dkcnt; dkp++, i++)
dkp->chan = i;
DKADDR->csr = ENABS;
opened++;
dkrecover(chan);
}
dkp = &dk[chan];
if (dkstate[chan] != DKCLOSED) { /* already open */
if (dkp->flag & DKXCL)
return(0);
if (dkstate[chan] != DKOPEN)
return(0); /* closing channels can't reopen */
return(1);
}
dkp->dkrq = q;
q->ptr = (caddr_t)dkp;
WR(q)->ptr = (caddr_t)dkp;
dkp->flag = DKXCL;
dkp->icnt = 0;
dkp->isize = 0;
dkp->ibuf = 0;
dkp->ialloc = DKISML;
dkstate[chan] = DKOPEN;
return(1);
}
/*
* Timer to recover from lost interrupt condition.
*/
dkrecover(dev)
dev_t dev;
{
register int ps = spl5();
if (DKADDR->csr & DKRDONE)
dk1int(dev);
splx(ps);
timeout(dkrecover, (caddr_t)dev, HZ/2);
}
/*
* close DK channel
*/
dkclose(q)
register struct queue *q;
{
register struct dk *dkp;
register struct block *bp;
dkp = (struct dk *)q->ptr;
dkp->dkrq = NULL;
dkp->flag = 0;
if (dkstate[dkp->chan] == DKRCLOSE || dkmodp->listnrq==NULL)
dkstate[dkp->chan] = DKCLOSED;
else if (dkstate[dkp->chan] == DKOPEN)
dkstate[dkp->chan] = DKLCLOSE;
if (dkmodp->listnrq)
putctl2(RD(dkmodp->listnrq), M_PRICTL, DKMCLOSE, dkp->chan);
if (dkp->ibuf)
freeb(dkp->ibuf);
while ((bp = dkp->ofirst) != NULL) {
dkp->ofirst = bp->next;
freeb(bp);
}
dkp->ibuf = 0;
}
/*
* DK receiver interrupt.
*/
#define PUTIN(bp, q) {\
dkstat.input += (bp)->wptr - (bp)->rptr;\
if (((q)->next->flag & QFULL) == 0)\
(*(q)->next->qinfo->putp)(q->next, bp);\
else\
freeb(bp);\
}
#define DKSIZUP(dkp) {\
if (dkp->isize > DKITHRES) {\
dkp->ialloc = dkp->isize;\
dkp->icnt = 3;\
} else if (--dkp->icnt <= 0) {\
dkp->icnt = 0;\
dkp->ialloc = DKISML;\
}\
dkp->isize = 0;\
}
dk1int(dev)
{
register struct queue *q;
register struct block *bp;
register struct dk *dkp;
register struct device *reg;
register c, nbytes;
register sane, chan;
register struct block **bpp;
struct block *blist[DKIPKT+2];
if ((reg = DKADDR) == NULL)
return;
c = 0;
dkp = NULL;
while (reg->csr & DKRDONE) {
reg->csr = D_READ|ENABS;
if ((c & DKMARK) == 0) {
/* Search for channel number */
sane = 256;
do {
c = reg->dki;
if (c & DKMARK || --sane==0)
break;
} while (reg->csr & DKRDONE);
}
if ((c & DKMARK) == 0) {
dkstat.markflt++;
continue;
}
if ((c & DKPARITY) == 0) {
dkstat.markparity++;
c = 0;
continue;
}
/* check channel */
chan = c & 0777;
if (dkp && dkp->chan != chan) {
if ((bp = dkp->ibuf) != NULL)
PUTIN(bp, q);
DKSIZUP(dkp);
dkp->ibuf = NULL;
}
if (chan == 0 || chan >= dkcnt || (q = dk[chan].dkrq)==NULL) {
if (chan>0 && chan<dkcnt) {
if (dkmodp->listnrq)
putctl2(RD(dkmodp->listnrq),
M_PRICTL, DKMCLOSE, chan);
dkstat.closepack++;
} else if (chan == 0)
dkstat.pack0++;
else
dkstat.packstrange++;
for (nbytes=0; nbytes<DKIPKT; nbytes++) {
c = reg->dki;
if (c & DKMARK)
break;
}
continue;
}
dkp = (struct dk *)q->ptr;
if ((bp = dkp->ibuf) == NULL
&& (bp = allocb(dkp->ialloc)) == NULL)
return;
bpp = blist;
for (nbytes=0; nbytes<DKIPKT; nbytes++) {
#ifdef SAFETY
if ((reg->csr&DKRDONE) == 0)
break;
#endif
c = reg->dki;
if ((c&(DKPARITY|DKMARK|DKDATA)) == (DKPARITY|DKDATA)) {
if (bp->wptr >= bp->lim) {
*bpp++ = bp;
if ((bp = allocb(DKISML)) == NULL)
break;
}
*bp->wptr++ = c;
dkp->isize++;
continue;
}
/*
* control or error
*/
if (c & DKMARK) {
dkstat.shortpack++;
break;
}
if ((c & DKPARITY) == 0) {
dkstat.parity++;
break;
}
if ((c & 0377) == 0) /* control-null */
continue;
if (bp->wptr > bp->rptr) {
*bpp++ = bp;
if ((bp = allocb(DKISML)) == NULL)
break;
}
bp->type = M_CTL;
*bp->wptr++ = c;
}
if (bpp == blist && bp->type == M_DATA) {
dkp->ibuf = bp;
continue;
}
*bpp++ = bp;
*bpp = NULL;
bpp = blist;
while ((bp = *bpp++) != NULL)
PUTIN(bp, q);
DKSIZUP(dkp);
dkp->ibuf = NULL;
}
if (dkp && (bp = dkp->ibuf) != NULL) {
PUTIN(bp, q);
DKSIZUP(dkp);
dkp->ibuf = NULL;
}
}
/*
* DK put procedure
*/
dkput(q, bp)
register struct queue *q;
register struct block *bp;
{
register n;
register bn;
int s;
register struct device *reg = DKADDR;
register struct dk *dkp;
dkp = (struct dk *)q->ptr;
if (dkp == NULL) {
freeb(bp);
return;
}
switch (bp->type) {
case M_IOCTL:
bp->type = M_IOCACK;
switch (stiocom(bp)) {
case DIOCNXCL:
dkp->flag &=~ DKXCL;
bp->wptr = bp->rptr;
bp->type = M_IOCACK;
break;
default:
bp->type = M_IOCNAK;
break;
}
qreply(q, bp);
return;
case M_CTL:
case M_DATA:
s = spl6();
bp->next = NULL;
if (dkp->ofirst == NULL)
dkp->ofirst = bp;
else
dkp->olast->next = bp;
dkp->olast = bp;
if ((bp->class&S_DELIM)==0 || dkalive == 0) {
splx(s);
return;
}
/*
* S_DELIM:
* push out data
*/
n = DKOPKT;
reg->csr = D_WRITE + ENABS;
reg->dko = DKMARK + dkp->chan;
while ((bp = dkp->ofirst) != NULL) {
dkp->ofirst = bp->next;
if (bp->wptr == bp->rptr) {
freeb(bp);
continue;
}
dkstat.output += bp->wptr - bp->rptr;
if (n <= 0) {
reg->csr = D_XPACK + ENABS;
reg->dko = 0;
n = DKOPKT;
reg->csr = D_WRITE + ENABS;
reg->dko = DKMARK + dkp->chan;
}
--n;
reg->dko = *bp->rptr++ | ((bp->type==M_DATA)?DKDATA:0);
while ((bn = bp->wptr - bp->rptr) > 0) {
if (bn > n)
bn = n;
n -= bn;
while (--bn >= 0)
reg->dko = *bp->rptr++ | DKDATA;
if (n <= 0) {
reg->csr = D_XPACK + ENABS;
reg->dko = 0;
n = DKOPKT;
reg->csr = D_WRITE + ENABS;
reg->dko = DKMARK + dkp->chan;
}
}
freeb(bp);
}
reg->csr = D_XPACK + ENABS;
reg->dko = 0;
splx(s);
return;
case M_PRICTL:
switch (*bp->rptr) {
case DKMCLOSE:
n = bp->rptr[1];
if (n < dkcnt) {
if (dkstate[n] == DKOPEN) {
dkstate[n] = DKRCLOSE;
putctl(dk[n].dkrq->next, M_HANGUP);
} else if (dkstate[n] == DKLCLOSE)
dkstate[n] = DKCLOSED;
}
freeb(bp);
return;
case DKMXINIT:
n = bp->rptr[1];
if (n < dkcnt && dkstate[n] == DKOPEN)
(*dk[n].dkrq->next->qinfo->putp)(dk[n].dkrq->next, bp);
else
freeb(bp);
return;
default:
freeb(bp);
return;
}
default:
freeb(bp);
return;
}
}
/*
* transmit interrupt
*/
dk0int(dev)
int dev;
{
printf("dk%d xmit stray\n", dev);
}
/*
* unibus reset
*/
dkreset()
{
if (DKADDR)
DKADDR->csr = ENABS;
}