V10/lsys/io/kb.c
/*
* simple two-stream kmc datakit driver
* -- assumes it is the first kmc, and only one allowed for now
* (the latter not severe, as 256 channels are permitted)
*/
#include "sys/param.h"
#include "sys/stream.h"
#include "sys/dkio.h"
#include "sys/ubaddr.h"
#include "sys/conf.h"
#include "sys/kb.h"
#include "sys/kmc.h"
#include "sys/dkstat.h"
#include "sys/dkmod.h"
/*
* hardware stuff
*/
struct device {
short sts; /* written by KMC */
short cmd; /* command and transmit chan -- written by host */
short ba; /* low bus address -- written by host */
short bc; /* negative buffer size -- written by host */
};
/*
* status info
*/
#define SCOUNT 0xfff /* receive buffer resid count */
#define SRRDY (020<<8) /* receive ready */
#define SOK (040<<8) /* init ok */
#define SERROR (0100<<8) /* transfer or other error */
#define SXRDY (0200<<8) /* transmit ready */
/*
* commands
*/
#define CGO (01<<8) /* turned off when command read */
#define CRCV (01<<8) /* here is a receive buffer */
#define CSEND (03<<8) /* send this buffer of data */
#define CSCTL (043<<8) /* send this data, first char is ctl */
#define CIE (020<<8) /* interrupt after this command */
#define CXA (014<<8) /* high address bits */
#define XASHIFT 10 /* shift high address bits this much */
#define STALL 10000 /* max time to stall waiting for ready */
/*
* per-channel stuff
*/
extern struct kb kb[];
extern int kbcnt;
extern char kbstate[];
/*
* kb flags
*/
#define DKXCL 01 /* exclusive open */
#define DKXWANT 02 /* output pending this channel */
/*
* per-controller stuff;
* always just one for now
*/
struct kbkmc kbkmc[1];
#define KBNO 0 /* always this kb for now */
/*
* kbkmc flags
*/
#define INIT 01
/*
* illicit linkage to other datakit code
*/
struct dkstat dkstat;
/*
* illicit linkage to kmc driver
*/
extern struct kmc kmc[];
extern struct ubaddr kmcaddr[];
extern int kmccnt;
#define KNO 0 /* kmc0 is ours, by fiat */
#define DKISIZE 1024 /* preferred input buffer size */
#define MAXOBUF 4096 /* max allowed output buffer -- for unibus map */
long kbopen();
int kbclose(), kbput(), kbosrv();
static struct qinit kbrinit = { nodev, NULL, kbopen, kbclose, 0, 0 };
struct qinit kbwinit = { kbput, NULL, kbopen, kbclose, 1500, 600 };
struct streamtab kbinfo = { &kbrinit, &kbwinit };
struct cdevsw kbcdev = cstrinit(&kbinfo);
/*
* open DK channel
*/
long
kbopen(q, dev)
register struct queue *q;
register dev_t dev;
{
register struct kb *dkp;
register struct kbkmc *kk;
register chan;
chan = minor(dev);
if (chan<=0 || chan>=kbcnt)
return(0);
kk = &kbkmc[KBNO];
if ((kk->flags & INIT) == 0) {
if ((kk->modp = dkmodall(dev, 0, kbcnt)) == NULL)
return (0);
kk->modp->dkstate = kbstate;
if (kbinit(kk) == 0)
return (0);
kk->flags |= INIT;
}
dkp = &kb[chan];
if (kbstate[chan] != DKCLOSED) { /* already open */
if (dkp->flag & DKXCL)
return(0);
if (kbstate[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;
WR(q)->flag |= QNOENB|QBIGB;
dkp->flag = DKXCL;
dkp->chan = chan;
kbstate[chan] = DKOPEN;
return(1);
}
/*
* make sure kmc is alive;
* init data structures once
*/
kbinit(kk)
register struct kbkmc *kk;
{
register struct kmc *kp;
register struct device *reg;
register int i;
extern kbintr(), kbreset();
kk->kno = KNO;
kk->ubno = kmcaddr[KNO].ubno; /* cheat */
kp = &kmc[kk->kno];
if ((reg = kp->k_addr) == 0)
return (0);
if ((reg->sts & SOK) == 0)
return (0);
if ((kk->omap = ubmalloc(kk->ubno, MAXOBUF, UBDP)) == 0)
return (0);
kp->k_rint = kbintr;
kp->k_xint = kbintr;
kp->k_reset = kbreset;
kk->addr = reg;
i = spl5();
kbistart(kk);
splx(i);
return (1);
}
/*
* called when, e.g., kmc is reloaded
*/
kbreset(kno)
int kno;
{
register struct kbkmc *kk;
register int s;
kk = &kbkmc[KNO];
if (kk->obuf) {
freeb(kk->obuf);
kk->obuf = NULL;
}
if (kk->imap) {
ubmfree(kk->ubno, kk->imap);
kk->imap = 0;
}
if (kk->ibuf) {
freeb(kk->ibuf);
kk->ibuf = NULL;
}
s = spl5();
kbistart(kk);
splx(s);
}
/*
* close DK channel
*/
kbclose(q)
register struct queue *q;
{
register struct kb *dkp;
register struct kbkmc *kk;
kk = &kbkmc[KBNO];
dkp = (struct kb *)q->ptr;
if (dkp == NULL)
panic("kbclose");
dkp->dkrq = NULL;
dkp->flag = 0;
if (kbstate[dkp->chan] == DKRCLOSE || kk->modp->listnrq==NULL)
kbstate[dkp->chan] = DKCLOSED;
else if (kbstate[dkp->chan] == DKOPEN)
kbstate[dkp->chan] = DKLCLOSE;
if (kk->modp->listnrq)
putctl2(RD(kk->modp->listnrq), M_PRICTL, DKMCLOSE, dkp->chan);
}
/*
* interrupt -- receiver only
* try to short-circuit interrupts;
* data often comes in a large piece, followed by two small ones (URP trailer)
*/
kbintr(dev)
{
register struct kbkmc *kk;
register struct block *bp;
register struct queue *q;
register int i;
kk = &kbkmc[KBNO];
if (kk->addr == NULL)
return;
i = kk->addr->sts;
if ((bp = kk->ibuf) == NULL) { /* shouldn't happen */
kbistart(kk);
return;
}
ubmfree(kk->ubno, kk->imap);
kk->ibuf = NULL;
kbistart(kk);
i |= ~SCOUNT; /* crypto-sign-extend */
if ((i & SCOUNT) == 0)
i = 0; /* overflow just means buffer full */
bp->wptr = bp->lim + i; /* lim - residue */
i = *bp->rptr; /* channel */
bp->rptr++;
if (*bp->rptr) /* ctl flag */
bp->type = M_CTL;
bp->rptr++;
if (i == 0 || i >= kbcnt || (q = kb[i].dkrq) == NULL) {
if (i == 0)
dkstat.pack0++;
else if (i >= kbcnt)
dkstat.packstrange++;
else {
if (kk->modp->listnrq)
putctl2(RD(kk->modp->listnrq), M_PRICTL, DKMCLOSE, i);
dkstat.closepack++;
}
freeb(bp);
return;
}
dkstat.input += bp->wptr - bp->rptr;
if ((q->next->flag & QFULL) == 0)
(*q->next->qinfo->putp)(q->next, bp);
else
freeb(bp);
}
/*
* start the next input buffer
*/
kbistart(kk)
register struct kbkmc *kk;
{
register struct device *reg;
register int sts;
register struct block *bp;
register int n;
register uaddr_t ua;
if ((reg = kk->addr) == NULL)
return;
if (kk->ibuf)
return;
for (sts = 0, n = 0; n < STALL; n++) {
if ((reg->cmd & CGO) == 0
&& (sts = reg->sts) & SRRDY)
break;
/* delay to free up unibus? */
}
if ((sts & SRRDY) == 0) {
printf("kb rcv not ready, sts %o cmd %o\n", reg->sts, reg->cmd);
panic("kbistart");
return;
}
if (sts & SERROR)
printf("kb err, sts %o\n", sts&0177777);
if ((bp = allocb(DKISIZE)) == NULL
|| (kk->imap = ubmblk(kk->ubno, bp, 0)) == 0) {
panic("kbistart can't alloc\n"); /* later printf */
if (bp)
freeb(bp);
return;
}
kk->ibuf = bp;
if ((int)bp->rptr & 01)
bp->rptr++;
kk->iaddr = ua = ubadrptr(kk->ubno, bp, kk->imap);
reg->bc = bp->rptr - bp->lim; /* sic - negative count */
reg->ba = ua;
n = (ua >> 16) << XASHIFT;
n &= CXA;
n |= CIE|CRCV;
reg->cmd = n;
}
/*
* put procedure for output
*/
kbput(q, bp)
struct queue *q;
register struct block *bp;
{
register struct kb *dkp;
register struct kbkmc *kk;
register int n;
register struct device *reg;
register int sts;
int s;
dkp = (struct kb *)q->ptr;
kk = &kbkmc[KBNO];
switch (bp->type) {
case M_IOCTL:
switch (stiocom(bp)) {
case DIOCNXCL:
dkp->flag &=~ DKXCL;
bp->wptr = bp->rptr;
bp->type = M_IOCACK;
break;
case KIOCINIT:
/* eventually, reset things here */
default:
bp->type = M_IOCNAK;
break;
}
qreply(q, bp);
return;
case M_CTL:
case M_DATA:
if (bp->rptr == bp->wptr) {
freeb(bp);
return;
}
dkstat.output += bp->wptr - bp->rptr;
s = spl5();
reg = kk->addr;
for (sts = 0, n = 0; n < STALL; n++) {
if ((reg->cmd & CGO) == 0
&& (sts = reg->sts) & SXRDY)
break;
DELAY(40); /* get off the UNIBUS */
}
if ((sts & SXRDY) == 0) { /* n >= STALL */
printf("kb xmit not ready, sts %o, cmd %o\n", sts, reg->cmd);
splx(s);
freeb(bp);
return;
}
if (sts & SERROR)
printf("kb err, sts %o\n", sts&0177777);
ubmflush(kk->ubno, ubmpath(kk->omap));
if (kk->obuf) {
freeb(kk->obuf);
kk->obuf = NULL;
}
n = bp->wptr - bp->rptr;
kk->oaddr = ubmaddr(kk->ubno, bp->rptr, n, kk->omap);
reg->bc = -n;
reg->ba = kk->oaddr;
n = (kk->oaddr >> 16)<<XASHIFT;
n &= CXA;
n |= dkp->chan;
if (bp->type == M_DATA)
n |= CSEND;
else
n |= CSCTL;
reg->cmd = n;
kk->obuf = bp;
splx(s);
return;
case M_PRICTL:
switch (*bp->rptr) {
case DKMCLOSE:
n = bp->rptr[1];
if (n < kbcnt) {
if (kbstate[n] == DKOPEN) {
kbstate[n] = DKRCLOSE;
putctl(kb[n].dkrq->next, M_HANGUP);
} else if (kbstate[n] == DKLCLOSE)
kbstate[n] = DKCLOSED;
}
freeb(bp);
return;
case DKMXINIT:
n = bp->rptr[1];
if (n < kbcnt && kbstate[n] == DKOPEN)
(*kb[n].dkrq->next->qinfo->putp)(kb[n].dkrq->next, bp);
else
freeb(bp);
return;
default:
freeb(bp);
return;
}
default:
freeb(bp);
return;
}
}