/* * 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" #define TRACE 0 /* turn on to provoke tracing */ /* * hardware stuff */ struct device { char ctl; /* control -- written by host */ unsigned char sts; /* status -- written by device */ short xlo; /* xmit descriptor, low address */ short rlo; /* rcv descriptor, low address */ char xhi, rhi; /* high addresses and flags */ }; /* * control bits */ #define CXIE 01 /* transmit interrupt enable */ #define CRIE 02 /* receive interrupt enable */ /* * status bits */ #define SERROR 0200 /* some error, usually NXM */ /* * flags in xhi and rhi */ #define CNOADDR 0200 /* OK to give another descriptor address */ #define CNODESC 0100 /* no descriptor being processed */ #define CXA 014 /* high address bits */ #define CXSHIFT 2 /* shift high bits this much */ /* * descriptor flags */ #define DVALID 0200 /* valid descriptor for device */ #define DCHAIN 020 /* `buffer' is a fresh descriptor address */ #define DXA 014 /* high buffer address bits */ #define DSCTL 01 /* first char of buffer is a control char */ #define DXSHIFT 2 /* shift high addr bits this much */ /* * per-channel stuff */ struct kb kb[]; extern int kbcnt; 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 RBSIZE 1024 /* desired receive buffer size */ #define MINRBYTES 4096 /* desired total receive buffer */ #define XNEXT(i) ((i)>=XRING-1?0:(i)+1) #define RNEXT(i) ((i)>=RRING-1?0:(i)+1) 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; struct device *reg; uaddr_t ua; 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 ((kk->ringmap = ubmalloc(kk->ubno, sizeof(kk->rings), 0)) == 0) { printf("kb init no map\n"); return (0); } ua = ubmaddr(kk->ubno, (caddr_t)&kk->rings, sizeof(kk->rings), kk->ringmap); kk->xrua = ua + ((caddr_t)kk->xring - (caddr_t)&kk->rings); kk->rrua = ua + ((caddr_t)kk->rring - (caddr_t)&kk->rings); kp->k_rint = kbintr; kp->k_xint = kbintr; kp->k_reset = kbreset; kk->addr = reg; kbreset(KNO); return (1); } /* * called when, e.g., kmc is reloaded */ kbreset(kno) int kno; { register struct kbkmc *kk; register int i; register struct device *reg; kk = &kbkmc[kno]; bzero((caddr_t)&kk->rings, sizeof(kk->rings)); kk->rrp = 0; kk->rwp = 0; kk->xrp = 0; kk->xwp = 0; for (i = 0; i < XRING; i++) { if (kk->xmap[i]) { ubmfree(kk->ubno, kk->xmap[i]); kk->xmap[i] = 0; } if (kk->xblock[i]) { freeb(kk->xblock[i]); kk->xblock[i] = 0; } } kk->xring[XRING].loaddr = kk->xrua; kk->xring[XRING].flag = DVALID|DCHAIN|((kk->xrua>>16)<<DXSHIFT); for (i = 0; i < RRING; i++) { if (kk->rmap[i]) { ubmfree(kk->ubno, kk->rmap[i]); kk->rmap[i] = 0; } if (kk->rblock[i]) { freeb(kk->rblock[i]); kk->rblock[i] = 0; } } kk->rring[RRING].loaddr = kk->rrua; kk->rring[RRING].flag = DVALID|DCHAIN|((kk->rrua>>16)<<DXSHIFT); kk->rbytes = 0; reg = kk->addr; reg->ctl = CRIE; i = spl5(); kbibufs(kk); reg->rlo = kk->rrua; /* kk->rring[0] */ reg->rhi = (kk->rrua>>16)<<CXSHIFT; splx(i); } /* * 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 * receive any blocks that have arrived; * reload receive buffers; * clean up finished transmit buffers * restart receiver or transmitter if needed */ int kbxrst; /* debugging count */ int kbxnst; /* debugging count */ kbintr(dev) { register struct kbkmc *kk; register struct device *reg; register int i; register uaddr_t ua; kk = &kbkmc[dev]; if ((reg = kk->addr) == NULL) return; #if NOTDEF /* straighten out protocol */ i = reg->sts; if (i & SERROR) { reg->sts = 0; printf("kb sts %o\n", i); } #endif for (i = kk->rrp; i != kk->rwp; i = RNEXT(i)) { if (kk->rring[i].flag & DVALID) break; kbrcv(kk, i); } if (kk->rbytes < MINRBYTES) kbibufs(kk); if ((reg->rhi & (CNOADDR|CNODESC)) == (CNOADDR|CNODESC)) { /* * receiver has stopped, but may have finished * another block or two since the loop above */ for (; i != kk->rwp; i = RNEXT(i)) { if (kk->rring[i].flag & DVALID) break; kbrcv(kk, i); } if (kk->rbytes < MINRBYTES) kbibufs(kk); if (kk->rwp != i) { /* safety check */ ua = kk->rrua + i * sizeof(struct kbdesc); reg->rlo = ua; reg->rhi = (ua>>16)<<CXSHIFT; } } kk->rrp = i; i = reg->xhi; if (kk->xrp != kk->xwp) kbxscan(kk); if ((i & (CNOADDR|CNODESC)) == (CNOADDR|CNODESC) && kk->xrp != kk->xwp) { ua = kk->xrua + kk->xrp * sizeof(struct kbdesc); reg->xlo = ua; reg->xhi = (ua>>16)<<CXSHIFT; kbxrst++; } } /* * receive a block */ kbrcv(kk, i) register struct kbkmc *kk; register int i; { register struct kbdesc *dxp; register struct queue *q; register int chan; register struct block *bp; struct block *xbp; dxp = &kk->rring[i]; #if TRACE trace('i', (i<<24)|(dxp->chan<<16)|dxp->flag, -dxp->len, *(int *)(kk->rblock[i]->rptr)); #endif if (kk->rmap[i] == 0) panic("kbrcv"); ubmfree(kk->ubno, kk->rmap[i]); kk->rmap[i] = 0; bp = kk->rblock[i]; kk->rblock[i] = NULL; kk->rbytes -= bp->lim - bp->wptr; bp->wptr = bp->lim + dxp->len; /* negative residual length */ if (dxp->flag & DSCTL) { /* * special hack to save big blocks */ if (bp->wptr == bp->rptr + 1 && (xbp = allocb(1)) != NULL) { *xbp->wptr++ = *bp->rptr; freeb(bp); bp = xbp; } bp->type = M_CTL; } if (bp->wptr <= bp->base || bp->wptr > bp->lim) { printf("kb ch%d bad len %d\n", dxp->chan, -dxp->len); freeb(bp); return; } chan = dxp->chan; if (chan == 0 || chan >= kbcnt || (q = kb[chan].dkrq) == NULL) { if (chan == 0) dkstat.pack0++; else if (chan >= kbcnt) dkstat.packstrange++; else 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); } /* * refresh our supply of input buffers */ kbibufs(kk) register struct kbkmc *kk; { register struct block *bp; register int i; register struct kbdesc *dxp; register uaddr_t ua; i = kk->rwp; while (kk->rbytes < MINRBYTES) { if (RNEXT(i) == kk->rrp) /* all descriptors used */ break; if ((bp = allocb(RBSIZE)) == NULL) break; if ((kk->rmap[i] = ubmblk(kk->ubno, bp, 0)) == 0) { printf("kb no rcv map\n"); freeb(bp); break; } kk->rblock[i] = bp; ua = ubadrptr(kk->ubno, bp, kk->rmap[i]); dxp = &kk->rring[i]; dxp->loaddr = ua; dxp->len = bp->wptr - bp->lim; /* negative length */ dxp->flag = ((ua>>16)<<DXSHIFT)|DVALID; kk->rbytes += bp->lim - bp->wptr; i = RNEXT(i); } kk->rwp = i; } /* * put procedure for output */ kbput(q, bp) struct queue *q; register struct block *bp; { register struct kb *dkp; register struct kbkmc *kk; register int i; register struct device *reg; register struct kbdesc *dxp; uaddr_t ua; 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; } s = spl5(); i = kk->xwp - kk->xrp; if (i < 0) i += XRING; if (i > XRING/2) kbxscan(kk); if (XNEXT(kk->xwp) == kk->xrp) { /* no desc available? */ splx(s); printf("kb xmt ch%d lost block\n", dkp->chan); freeb(bp); return; } i = kk->xwp; kk->xwp = XNEXT(i); if ((kk->xmap[i] = ubmblk(kk->ubno, bp, 0)) == 0) { splx(s); printf("kb xmt ch%d no map\n", dkp->chan); freeb(bp); return; } #if TRACE trace('x', (i<<24)|(dkp->chan<<16)|bp->type, bp->wptr - bp->rptr, *(int *)bp->rptr); #endif kk->xblock[i] = bp; ua = ubadrptr(kk->ubno, bp, kk->xmap[i]); dxp = &kk->xring[i]; dxp->loaddr = ua; dxp->len = bp->rptr - bp->wptr; /* negative length */ dxp->chan = dkp->chan; dxp->flag = (ua>>16)<<DXSHIFT; if (bp->type != M_DATA) dxp->flag |= DSCTL; reg = kk->addr; dxp->flag |= DVALID; if ((reg->xhi & (CNOADDR|CNODESC)) == (CNOADDR|CNODESC)) { if (i != kk->xrp) kbxscan(kk); if (kk->xwp != kk->xrp) { /* maybe it finished it off -- unlikely */ ua = kk->xrua + kk->xrp * sizeof(struct kbdesc); reg->xlo = ua; reg->xhi = ((ua>>16)<<CXSHIFT); } } else /* debug */ kbxnst++; /* debug */ dkstat.output += bp->wptr - bp->rptr; splx(s); return; case M_PRICTL: switch (*bp->rptr) { case DKMCLOSE: i = bp->rptr[1]; if (i < kbcnt) { if (kbstate[i] == DKOPEN) { kbstate[i] = DKRCLOSE; putctl(kb[i].dkrq->next, M_HANGUP); } else if (kbstate[i] == DKLCLOSE) kbstate[i] = DKCLOSED; } freeb(bp); return; case DKMXINIT: i = bp->rptr[1]; if (i < kbcnt && kbstate[i] == DKOPEN) (*kb[i].dkrq->next->qinfo->putp)(kb[i].dkrq->next, bp); else freeb(bp); return; default: freeb(bp); return; } default: freeb(bp); return; } } /* * clean up finished transmit buffers * call at spl5 */ kbxscan(kk) register struct kbkmc *kk; { register int i; for (i = kk->xrp; i != kk->xwp; i = XNEXT(i)) { if (kk->xring[i].flag & DVALID) break; if (kk->xblock[i] == NULL) panic("kbxscan"); ubmfree(kk->ubno, kk->xmap[i]); kk->xmap[i] = 0; freeb(kk->xblock[i]); kk->xblock[i] = NULL; } kk->xrp = i; }