/* * first cut at NI port in DEBNA * slower than it need be, because of all the copying; * disallows use of the storage port */ #include "sys/param.h" #include "sys/biaddr.h" #include "sys/conf.h" #include "sys/user.h" #include "sys/buf.h" #include "sys/stream.h" #include "sys/biic.h" #include "sys/ethernet.h" #include "sys/enio.h" #include "sys/pte.h" #include "sys/bvp.h" #include "sys/debna.h" caddr_t remqhi(); #define PCMDQ 0 /* just one command queue */ #define PFREQ 0 /* just one free queue for now */ /* * internal ideas about packets */ /* * bounds of an Ethernet packet * min is probably uninteresting * max is constrained by the port, which will fuss and shut down * if handed a buffer bigger than ETHERMAXTU+ETHERCKSUM */ #define ETHERMINTU 60 #define ETHERMAXTU 1514 #define ETHERCKSUM 4 #define NIRCVBUF (ETHERMAXTU+ETHERCKSUM) struct dgi { struct bvpdg h; /* header */ char d[NIRCVBUF]; }; struct mst { struct bvpmsg h; struct stptdb p; }; /* * config stuff */ extern struct bnactl bna[]; extern struct bnabuf bnabuf[]; extern struct biaddr bnaaddr[]; extern int bnacnt; long bnaopen(); int bnaclose(), bnaput(); static struct qinit bnarinit = { noput, NULL, bnaopen, bnaclose, 0, 0 }; static struct qinit bnawinit = { bnaput, NULL, bnaopen, bnaclose, 4*ETHERMAXTU, 64 }; static struct streamtab bnasinfo = { &bnarinit, &bnawinit }; struct cdevsw bnacdev = cstrinit(&bnasinfo); long bnaopen(q, dev) register struct queue *q; dev_t dev; { register int unit, chan; register struct bnachan *bc; unit = minor(dev); chan = unit % BNACHAN; unit /= BNACHAN; if (unit >= bnacnt) return (0); if (bnainit(unit) == 0) return (0); bc = &bna[unit].chan[chan]; if (bc->rq) return (0); bc->unit = unit; bc->proto = 0; q->ptr = (caddr_t)bc; q->flag |= QDELIM; WR(q)->ptr = (caddr_t)bc; WR(q)->flag |= QDELIM|QBIGB; bc->rq = q; return (1); } /* * init the bvp port registers and pqb * set up our list of free transmit buffers, * and the free queue for the port * -- only one free queue for now */ bnainit(unit) register int unit; { register char *cp; register struct bnactl *bn; register int i; bn = &bna[unit]; if (bvpinit(&bn->bvp, &bnaaddr[unit], NIREGS) == 0) { printf("debna %d: can't init\n", unit); return (0); } if (bn->bvp.d->f[PFREQ].f_size == 0) { /* somewhat sleazy init flag */ bn->bvp.d->f[PFREQ].f_size = BNABSIZE-BVPHSIZE; cp = bnabuf[unit].rbuf; cp = (char *)(((int)cp + 7) & ~07); /* quad-align */ for (i = 0; i < BNARBUF; i++, cp += BNABSIZE) { if (((int)cp&PGOFSET) > (((int)cp+sizeof(struct bvpdg))&PGOFSET)) cp = (char *)(((int)cp+PGOFSET)&~PGOFSET); bnarbuf(bn, (struct dgi *)cp); } cp = bnabuf[unit].xbuf; cp = (char *)(((int)cp + 7) & ~07); /* quad-align */ for (i = 0; i < BNAXBUF; i++, cp += BNABSIZE) { if (((int)cp&PGOFSET) > (((int)cp+sizeof(struct bvpdg))&PGOFSET)) cp = (char *)(((int)cp+PGOFSET)&~PGOFSET); bnaxbuf(bn, (struct dgi *)cp); } } bcopy(bn->bvp.r->addr, bn->myaddr, sizeof(bn->myaddr)); return (1); } bnaclose(q) register struct queue *q; { register struct bnachan *bc; bc = (struct bnachan *)q->ptr; /* turn off PTDB? */ bc->rq = NULL; bc->proto = 0; } /* * output put */ bnaput(q, bp) register struct queue *q; register struct block *bp; { register struct bnachan *bc; register int s; bc = (struct bnachan *)q->ptr; switch (bp->type) { case M_IOCTL: bnaioctl(q, bp); return; case M_DATA: putq(q, bp); if (bp->class & S_DELIM) break; return; default: freeb(bp); return; } /* * at least a whole packet on the queue */ bc->dcnt++; s = spl6(); while (bnasend(bc->unit)) ; splx(s); } bnaioctl(q, bp) register struct queue *q; register struct block *bp; { register struct bnachan *bc; register struct bnactl *bn; bc = (struct bnachan *)q->ptr; bn = &bna[bc->unit]; bp->type = M_IOCACK; switch (stiocom(bp)) { case ENIOTYPE: /* set proto */ bc->proto = *((int *)stiodata(bp)); bnastptdb(bn, bc); freeb(bp); /* IOCACK when the port finishes */ return; case ENIOADDR: bcopy(bn->myaddr, stiodata(bp), sizeof(bn->myaddr)); bp->wptr = bp->rptr + sizeof(bn->myaddr) + STIOCHDR; break; default: bp->type = M_IOCNAK; break; } qreply(q, bp); } bnastptdb(bn, bc) register struct bnactl *bn; register struct bnachan *bc; { register struct mst *dp; register int s; s = spl6(); if ((dp = (struct mst *)bn->xfree) == NULL) { bc->needst = 1; bn->needst = 1; splx(s); return; } bn->xfree = (struct dgi *)(dp->h.q.head); bc->needst = 0; splx(s); bzero((caddr_t)dp, sizeof(*dp)); dp->h.bd_opc = BVPSNDMSG; dp->h.bd_flag = BVPRSP; dp->h.bm_len = sizeof(*dp) - ((char *)&dp->h.bm_opc - (char *)dp); dp->h.bm_opc = NISTPTDB; dp->p.pt_proto = bc->proto; dp->p.pt_fqi = PFREQ; dp->p.pt_flag = PTABM|PTAAM; dp->p.pt_id = bc - bn->chan; if (insqti((caddr_t)dp, &bn->bvp.d->p.p_cmdq[PCMDQ])) bvpcomm(bn->bvp.r, PCOWN|PCCMDQ|(PCMDQ<<PCDS)); } /* * here on an interrupt */ bna0int(unit) int unit; { register struct bvpdata *b; register struct bnactl *bn; register struct dgi *dp; register struct bnabuf *bf; /* just for safety check */ register int i; if ((unsigned)unit >= bnacnt) panic("bna0int"); bn = &bna[unit]; if (bn->bvp.r == NULL) { printf("debna %d stray intr\n", unit); return; } bn->bvp.rsave = *bn->bvp.r; /* copy all, for debugging */ if (bn->bvp.rsave.stat & PSOWN) bn->bvp.r->stat &=~ PSOWN; /* allow fresh status */ if ((bn->bvp.rsave.stat & PSSTAT) != SENAB) { printf("debna %d: ps %x pe %x pd %x\n", unit, bn->bvp.rsave.stat, bn->bvp.rsave.err, bn->bvp.rsave.data); return; } b = bn->bvp.d; bf = &bnabuf[unit]; while ((dp = (struct dgi *)remqhi(&b->p.p_rspq)) != NULL) { if (dp->h.bd_sts != BVPSUC) printf("debna %d opc %x sts %x\n", unit, dp->h.bd_opc, dp->h.bd_sts); switch (dp->h.bd_opc) { case BVPSNDDGI: /* datagram sent */ for (i = ETHERALEN-1; i >= 0; --i) if (((struct etherpup *)dp->d)->dhost[i] != 0xff && ((struct etherpup *)dp->d)->dhost[i] != bn->myaddr[i]) goto notecho; /* * packet sent to ourselves; * port won't echo it back */ dp->h.bd_len += ETHERCKSUM; /* banrdg will trim */ bcopy(bn->myaddr, ((struct etherpup *)dp->d)->shost, sizeof(bn->myaddr)); bnardg(bn, dp); notecho: /* fall through */ case BVPSNDMSG: /* message sent */ if ((char *)dp < bf->xbuf || (char *)dp >= &bf->xbuf[sizeof(bf->xbuf)]) panic("bna0int sent"); bnaxbuf(bn, dp); continue; case BVPRCVDGI: /* datagram received */ if ((char *)dp < bf->rbuf || (char *)dp >= &bf->rbuf[sizeof(bf->rbuf)]) panic("bna0int rcv dg"); bnardg(bn, dp); bnarbuf(bn, dp); continue; case BVPRCVMSG: /* message received */ if ((char *)dp < bf->rbuf || (char *)dp >= &bf->rbuf[sizeof(bf->rbuf)]) panic("bna0int rcv msg"); bnarmsg(bn, (struct bvpmsg *)dp); bnarbuf(bn, dp); continue; default: printf("debna %d: %x: opc %x\n", unit, dp, dp->h.bd_opc); continue; /* leaving buffer tied up */ } } while (bnasend(unit)) ; } /* * here is a received datagram; * find the right channel, * and send it up the queue */ bnardg(bn, dp) register struct bnactl *bn; register struct dgi *dp; { register int n; register struct block *bp; register char *p; register struct queue *q; int len; short proto; if (dp->h.bd_dgsts != NISUC) { printf("debna %d dg sts %x\n", bn - bna, dp->h.bd_dgsts); return; } len = (dp->h.bd_len+BVPHSIZE)-sizeof(struct bvpdg)-ETHERCKSUM; proto = (short)((struct etherpup *)dp->d)->type; for (n = 0; n < BNACHAN; n++) if (bn->chan[n].proto == proto) break; if (n >= BNACHAN) return; /* unexpected proto */ q = bn->chan[n].rq; if (q == NULL) return; /* snh */ if (q->next->flag & QFULL) return; /* no room */ p = dp->d; while (len > 0) { if ((bp = allocb(len)) == NULL) return; /* wrong, but hard */ n = bp->lim - bp->wptr; if (len < n) n = len; bcopy(p, bp->wptr, n); bp->wptr += n; p += n; len -= n; if (len <= 0) bp->class |= S_DELIM; (*q->next->qinfo->putp)(q->next, bp); } } /* * here is a message */ bnarmsg(bn, dp) register struct bnactl *bn; register struct bvpmsg *dp; { register struct bnachan *bc; register struct mst *sdp; switch (dp->bm_opc) { case NISTPTDB: /* ptdb set, sent ioctl back */ sdp = (struct mst *)dp; if (sdp->p.pt_id < 0 || sdp->p.pt_id >= BNACHAN) panic("bnastptdb"); bc = &bn->chan[sdp->p.pt_id]; if (bc->rq == NULL || bc->proto != sdp->p.pt_proto) return; /* early close? */ if (sdp->h.bm_nists != NISUC) printf("debna %d %d stptdb sts %x\n", bn - bna, sdp->p.pt_id, sdp->h.bm_nists); putctl(bc->rq->next, M_IOCACK); return; default: printf("debna %d msg opc %x sts %x\n", dp->bm_opc, dp->bm_nists); return; } } /* * find a channel with a packet, * find a buffer, * and send */ bnasend(unit) int unit; { register struct bnactl *bn; register struct bnachan *bc; register struct dgi *dp; register char *p; register struct block *bp; register int n; char *ep; bn = &bna[unit]; n = bn->lastx; while (bn->chan[n].dcnt == 0) { if (++n >= BNACHAN) n = 0; if (n == bn->lastx) return (0); /* nothing to send */ } bn->lastx = n; bc = &bn->chan[n]; if ((dp = bn->xfree) == NULL) return (0); /* no buffer */ bn->xfree = (struct dgi *)dp->h.q.head; bzero((caddr_t)&dp->h, sizeof(dp->h)); p = dp->d; ep = p + sizeof(dp->d); while ((bp = getq(WR(bc->rq))) != NULL) { n = bp->wptr - bp->rptr; if (p + n > ep) n = ep - p; /* quietly truncate */ if (n) { bcopy(bp->rptr, p, n); p += n; } if (bp->class & S_DELIM) { bc->dcnt--; freeb(bp); break; } freeb(bp); } if (bp == NULL) { printf("debna %d no delim\n", unit); bc->dcnt = 0; dp->h.q.head = (quadque *)bn->xfree; bn->xfree = dp; return (1); /* ok to try another channel */ } if (p - dp->d < ETHERMINTU) p = dp->d + ETHERMINTU; /* quietly extend */ dp->h.bd_opc = BVPSNDDGI; dp->h.bd_flag = BVPRSP; /* so it will appear on rsp queue */ dp->h.bd_len = p - &dp->h.bd_dgsts; dp->h.bd_ptdb = unit; /* some valid number */ /* probably should zero some other fields */ if (insqti((caddr_t)dp, &bn->bvp.d->p.p_cmdq[PCMDQ])) bvpcomm(bn->bvp.r, PCOWN|PCCMDQ|(PCMDQ<<PCDS)); return (1); } /* * free a buffer for the port to fill in * -- port fusses if buffer bigger than an Ethernet packet + BVP header */ bnarbuf(bn, dp) register struct bnactl *bn; register struct dgi *dp; { dp->h.bd_sts = 0; /* just in case */ dp->h.bd_opc = BVPRCVDGI; dp->h.bd_len = sizeof(struct bvpdg) + NIRCVBUF - BVPHSIZE; if (insqti((caddr_t)dp, &bn->bvp.d->f[PFREQ].f_q)) bvpcomm(bn->bvp.r, PCOWN|PCFREQ|(PFREQ<<PCDS)); } bnaxbuf(bn, dp) register struct bnactl *bn; register struct dgi *dp; { register int i; dp->h.q.head = (quadque *)bn->xfree; bn->xfree = dp; if (bn->needst) { bn->needst = 0; for (i = 0; i < BNACHAN; i++) if (bn->chan[i].needst) bnastptdb(bn, &bn->chan[i]); } } /* * (fairly) general BVP routines */ bvpinit(bv, ap, roff) register struct bvp *bv; struct biaddr *ap; int roff; { register struct bvpregs *r; register struct biic *rb; register long ps; /* most recent status */ if ((rb = bv->rb) == NULL) { if ((rb = (struct biic *)biaddr(ap)) == NULL || badaddr(rb, sizeof(long))) { printf("bvp absent\n"); return (0); } bv->rb = rb; } if (bv->r == NULL) { r = (struct bvpregs *)((char *)rb + roff); if (badaddr(r, sizeof(long))) { printf("bvp off %x absent\n", roff); return (0); } bv->r = r; } if (bv->dbuf == NULL) { bv->dbuf = geteblk(); if (bv->dbuf->b_bcount < sizeof(struct bvpdata)) { printf("bcount too small\n"); brelse(bv->dbuf); return (0); } clrbuf(bv->dbuf); bv->d = (struct bvpdata *)bv->dbuf->b_un.b_addr; } r = bv->r; ps = r->stat; if ((ps & PSSTAT) == SUNDEF) { biinit(ap, 0); bvpdatinit(bv->d); bv->d->p.p_nodmsk = rb->biintr; bv->d->p.p_vector = (ap->vec<<2) | 5; /* vec, br */ bvpcomm(r, physadr(&bv->d->p)|PCOWN|PCINIT); ps = bvpstat(r); } if ((ps & PSSTAT) == SINIT) { bvpcomm(r, PCOWN|PCENAB); ps = bvpstat(r); } if ((ps & PSSTAT) != SENAB) { printf("ps %x pe %x pd %x\n", r->stat, r->err, r->data); return (0); } return (1); } bvpdatinit(b) register struct bvpdata *b; { bzero((char *)b, sizeof(*b)); b->p.p_nfreeq = NFREEQ; b->p.p_fqb = b->f; b->p.p_bvplvl = 1; b->p.p_pqb = &b->p; b->p.p_bdt = b->b; b->p.p_bdtlen = NBDT; /* -1? */ b->p.p_spt = (struct pte *)physadr(Sysmap); b->p.p_sptlen = 0x100000; /* huge */ b->p.p_gpt = b->p.p_spt; b->p.p_gptlen = b->p.p_sptlen; } /* * send a port command */ #define COMMTIME 1000000 bvpcomm(r, c) register struct bvpregs *r; long c; { register int i; i = COMMTIME; while (r->ctrl & PCOWN) if (--i <= 0) { printf("bvpcomm timeout c %x\n", c); return; /* what else to do? */ } r->stat &=~ PSOWN; r->ctrl = c; } /* * wait for status in init */ #define STATTIME 1000000 int bvpstat(r) register struct bvpregs *r; { register long ps; register int i; i = STATTIME; while (((ps = r->stat) & PSOWN) == 0) if (--i <= 0) { printf("bvpstat timeout, ps %x\n", ps); return (0); } return (ps); } /* * VAX queue primitives * * -- to set up empty queue, just zero both longwords */ /* * remove entry from head; * return entry, or 0 if none */ caddr_t remqhi(q) quadque *q; { asm("0: remqhi *4(ap),r0"); asm("bcs 0b"); /* couldn't interlock; try again */ asm("bvc 1f"); /* br if removed something */ asm("clrl r0"); asm("1:"); } /* * insert entry to tail * return 1 if this was the first entry */ insqti(e, q) caddr_t e; quadque *q; { asm("clrl r0"); asm("0: insqti *4(ap),*8(ap)"); asm("bcs 0b"); asm("bneq 1f"); asm("incl r0"); asm("1:"); }