|
|
researchv10 Norman
/*
* 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;
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.