/* * DEQNA Ethernet Communications Controller interface, * vaguely derived from the driver in Ultrix. * Each DEQNA has eight minor devices starting at N*8; * the different channels may be caused to receive different * Ethernet protocols via ENIOTYPE. * Packets read or written have the Ethernet header in front. */ #include "sys/param.h" #include "sys/buf.h" #include "sys/stream.h" #include "sys/ubaddr.h" #include "sys/conf.h" #include "sys/enio.h" #include "sys/deqna.h" #include "sys/ethernet.h" struct qedevice { unsigned short qe_sta_addr[2]; /* ethernet addr; 6 shorts, overlaid */ unsigned short qe_rcvlist_lo; /* rcv list addr */ unsigned short qe_rcvlist_hi; unsigned short qe_xmtlist_lo; /* xmt list addr */ unsigned short qe_xmtlist_hi; unsigned short qe_vector; unsigned short qe_csr; }; /* * qe_csr */ #define QE_RCV_ENABLE 0x0001 /* receiver enable */ #define QE_RESET 0x0002 /* controller reset */ #define QE_NEX_MEM_INT 0x0004 /* non-existent memory */ #define QE_XL_INVALID 0x0010 /* nothing left to transmit */ #define QE_RL_INVALID 0x0020 /* no buffers left to receive */ #define QE_INT_ENABLE 0x0040 /* interrupt enable */ #define QE_XMIT_INT 0x0080 /* intr because xmit finished */ #define QE_ILOOP 0x0100 /* internal loopback disable */ #define QE_ELOOP 0x0200 /* external loopback enable */ #define QE_RCV_INT 0x8000 /* intr because rcv finished */ /* * bits to shove into qe_csr for normal operation */ int qebits = QE_RCV_ENABLE|QE_INT_ENABLE|QE_ILOOP; /* * interrupt request bits: * after an interrupt, write the ones that are set * to allow the next interrupt */ #define INTFLAGS (QE_XMIT_INT|QE_RCV_INT) /* * buffer descriptor * flag is who owns it; * the rest is where the data is, and how big * they may be a list, or a chain. * we just use a ring; the DEQNA thinks it's a list, * but the chain flag is used to sew the ring together that the end */ struct qe_ring { short flag; short hiaddr; /* and descriptor type flags */ short loaddr; short len; /* negative length */ unsigned short status1; unsigned short status2; }; /* * flags: just descriptor ownership */ #define FREADY 0x8000 /* descriptor ready, port not using yet */ /* * descriptor type flags, in hiaddr */ #define DVALID 0x8000 /* valid descriptor */ #define DCHAIN 0x4000 /* chain: address is that of next descriptor */ #define DEND 0x2000 /* end of message; push output packet */ #define DSETUP 0x1000 /* this is a setup packet */ #define DENDODD 0x80 /* buffer ends on odd address */ #define DBEGODD 0x40 /* buffer begins on odd address */ /* * status1 when we write it */ #define NOTYET 0x8000 /* descriptor ready, but DEQNA hasn't taken it */ /* * status1 when DEQNA is through */ #define NOTLAST 0x8000 /* rcv: not the last descriptor of a packet */ #define ERROR 0x4000 /* either: some error happened */ #define ESETUP 0x2000 /* rcv: setup packet acknowledgement */ #define RHILEN 0x0700 /* rcv: high bits of received packet length */ /* * status2 when we write it */ #define S2MAGIC 1 /* write this there to make descriptor ready. why? */ /* * status2 when DEQNA is through */ #define RLOLEN 0x00ff /* rcv: low bits of received length */ #define RMINLEN 60 /* add this to get real received packet length */ /* * operating condition set-up bits */ #define ALLM 0x1 /* match all multicast addresses */ #define PROM 0x2 /* match all addresses (promiscuous mode) */ /* * setup packet: * lists the addresses we may receive * each column (sic) of the array contains an address * e.g. setup[1][0..5] * setup[0][*] and setup[*][6..7] are junk */ struct setup { char d[16][8]; }; /* * data accessed directly by the DEQNA: * the buffer descriptors and the setup packet */ struct qeio { struct qe_ring rring[QENRCV+1]; struct qe_ring xring[QENXMT+1]; struct setup setup; }; /* * This constant should really be 60 because the qna adds 4 bytes of crc. * However when set to 60 our packets are ignored by deuna's , 3coms are * okay ?????????????????????????????????????????? */ #define ETHERMINTU 64 #define ETHERMAXTU 1564 #define MAX_RCV_BYTES (3*ETHERMAXTU) /* maximum bytes in receive ring */ int nRCV = QENRCV; int nXMT = QENXMT; struct qechan *qefindchan(); extern int qecnt; extern struct qe qe[]; extern struct ubaddr qeaddr[]; #define NEXTCH(c) (((c)+1)%QENCHAN) /* next channel */ /* flag settings */ #define ATTACHED 0x4 #define SETUPQD 0x8 /* handy macros for ring management */ #define NFREE(s) ((s)->xindex>=(s)->oxindex?(QENXMT-((s)->xindex-(s)->oxindex)-1):\ ((s)->oxindex-(s)->xindex-1)) #define NEXTRCV(c) (((c)+1)%QENRCV) #define NEXTXMT(c) (((c)+1)%QENXMT) long qeopen(); int qeclose(), qeput(); static struct qinit qerinit = { noput, NULL, qeopen, qeclose, 0, 0 }; static struct qinit qewinit = { qeput, NULL, qeopen, qeclose, 4*ETHERMAXTU, 64 }; static struct streamtab qesinfo = { &qerinit, &qewinit }; struct cdevsw qecdev = cstrinit(&qesinfo); /* open a channel */ long qeopen(q, dev) register struct queue *q; register dev_t dev; { register struct qechan *cp; register struct qe *qp; int unit, chan; dev = minor(dev); chan = dev % QENCHAN; unit = dev / QENCHAN; if(unit >= qecnt) return(0); if (qeinit(unit) == 0) return (0); qp = &qe[unit]; cp = &qp->chan[chan]; if(cp->rq) return(0); cp->unit = unit; cp->type = 0; q->ptr = (caddr_t)cp; WR(q)->ptr = (caddr_t)cp; WR(q)->flag |= QDELIM|QBIGB; q->flag |= QDELIM; cp->rq = q; return(1); } qeclose(q) register struct queue *q; { register struct qechan *cp; cp = (struct qechan *)q->ptr; cp->rq = 0; cp->type = 0; } /* * init a controller-- * grab and map a buffer for qeio * reset the device and send it a setup packet */ qeinit(unit) int unit; { register struct qedevice *addr; register struct qe *qp; register uaddr_t ua; qp = &qe[unit]; if (qp->addr) return (1); if ((addr = (struct qedevice *)ubaddr(&qeaddr[unit])) == 0 || badaddr(&addr->qe_sta_addr[0], sizeof(short))) { printf("deqna %d absent\n", unit); return (0); } qp->intvec = qeaddr[unit].vec; qp->ubno = qeaddr[unit].ubno; if (qp->iobuf == NULL) { qp->iobuf = geteblk(); qp->iobm = ubmbuf(qp->ubno, qp->iobuf, 0); ua = ubadbuf(qp->ubno, qp->iobuf, qp->iobm); qp->rring = ((struct qeio *)(qp->iobuf->b_un.b_addr))->rring; qp->xring = ((struct qeio *)(qp->iobuf->b_un.b_addr))->xring; qp->setup = &((struct qeio *)(qp->iobuf->b_un.b_addr))->setup; qp->raddr = (uaddr_t)((struct qeio *)ua)->rring; qp->xaddr = (uaddr_t)((struct qeio *)ua)->xring; qp->setupaddr = (uaddr_t)&((struct qeio *)ua)->setup; } qp->addr = addr; addr->qe_csr = QE_RESET; addr->qe_csr = 0; /* clear RESET to make reset happen */ qemkrings(qp); qemksetup(qp); qeenable(qp); return (1); } /* * make the setup packet * receive from our address, and from the broadcast address */ qemksetup(qp) register struct qe *qp; { register struct setup *sp; register int i, j; sp = qp->setup; /* * fetch our Ethernet address into column 1 * toss the broadcast address into column 2 * copy our address into the rest of the first half * duplicate first half into second * columns 0 and 8 are junk */ for (i = 0; i < ETHERALEN; i++) sp->d[i][1] = qp->addr->qe_sta_addr[i]; #ifndef NOTDEF for (i = 0; i < ETHERALEN; i++) sp->d[i][2] = 0xff; /* the broadcast addr */ for (i = 0; i < ETHERALEN; i++) for (j = 3; j < 8; j++) sp->d[i][j] = sp->d[i][1]; #else for (i = 0; i < ETHERALEN; i++) for (j = 2; j < 8; j++) sp->d[i][j] = sp->d[i][1]; #endif bcopy(&sp->d[0][0], &sp->d[8][0], 8*8); } /* * output put routine * treasure up data till a DELIM comes, * then send it all as one packet */ qeput(q, bp) register struct queue *q; struct block *bp; { register struct qechan *cp=(struct qechan *)q->ptr; int s; switch (bp->type) { case M_IOCTL: qeioctl(q, bp); return; case M_DATA: putq(q, bp); if (bp->class&S_DELIM) break; return; default: freeb(bp); return; } s = spl6(); qestagepacket(cp); while (qexmtpacket(&qe[cp->unit])==0) ; splx(s); } qeioctl(q, bp) register struct queue *q; register struct block *bp; { register struct qechan *cp; int i; char *ap; cp = (struct qechan *)q->ptr; bp->type = M_IOCACK; switch(stiocom(bp)){ case ENIOTYPE: cp->type = *((int *)stiodata(bp)); break; case ENIOPROM: qexmtsetup(&qe[cp->unit], PROM, 1); break; case ENIOADDR: for (ap=stiodata(bp), i=0; i<ETHERALEN; i++) *ap++ = qe[cp->unit].addr->qe_sta_addr[i]; bp->wptr = bp->rptr + ETHERALEN + STIOCHDR; break; default: bp->type = M_IOCNAK; break; } qreply(q, bp); } /* * primitives for sending/receiving packets */ /* * make a descriptor invalid */ qeinvaldesc(rp) register struct qe_ring *rp; { bzero((caddr_t)rp, sizeof(struct qe_ring)); } /* * set up a buffer descriptor * tflags are the ones in hiaddr, * like `setup packet' */ qefilldesc(rp, addr, len, tflags) register struct qe_ring *rp; uaddr_t addr; int len; int tflags; { rp->hiaddr = tflags; if (addr & 1) { len++; addr--; rp->hiaddr |= DBEGODD; } if (len & 1) { len++; rp->hiaddr |= DENDODD; } rp->len = -(len/2); rp->loaddr = addr; rp->hiaddr |= addr >> 16; rp->status2 = S2MAGIC; /* something meaningless */ rp->status1 = NOTYET; rp->flag = FREADY; rp->hiaddr |= DVALID; } /* * allocate a receive buffer * and give it to the DEQNA * if we're out of descriptors or are using too much memory, * return quietly */ qercvblock(qp) register struct qe *qp; { register uaddr_t a; register int len; register struct block *bp; if (qp->rbytes>MAX_RCV_BYTES || NEXTRCV(qp->rindex)==qp->orindex) return -1; if ((bp = allocb(ETHERMAXTU))==NULL) panic("qercvblock"); qp->rbp[qp->rindex] = bp; a = ubadwptr(qp->ubno, bp, ubmblk(qe->ubno, bp, 0)); len = bp->lim - bp->wptr; qefilldesc(&qp->rring[qp->rindex], a, len, 0); qp->rbytes += len; if ((qp->addr->qe_csr&QE_RL_INVALID) && qp->rring[qp->rindex].status1 == NOTYET) { /* wake up device */ a = qp->raddr + qp->rindex*sizeof(struct qe_ring); qp->addr->qe_rcvlist_lo = a; qp->addr->qe_rcvlist_hi = a >> 16; } qp->rindex = NEXTRCV(qp->rindex); return 0; } /* * send a block */ qexmtblock(qp, bp, tflags) register struct qe *qp; struct block *bp; int tflags; { register uaddr_t a; if (qp->xring[qp->xindex].status1 == NOTYET) panic("qexmitbp"); qp->xbp[qp->xindex] = bp; a = ubadrptr(qp->ubno, bp, ubmblk(qe->ubno, bp, 0)); qefilldesc(&qp->xring[qp->xindex], a, bp->wptr - bp->rptr, tflags); if ((qp->addr->qe_csr&QE_XL_INVALID) && qp->xring[qp->xindex].status1==NOTYET) { /* wake up device */ a = qp->xaddr + qp->xindex*sizeof(struct qe_ring); qp->addr->qe_xmtlist_lo = (short)a; qp->addr->qe_xmtlist_hi = (short)(a >> 16); } qp->xindex = NEXTXMT(qp->xindex); } /* * send the setup packet * -- knows it's the first packet ever sent */ qexmtsetup(qp, cmd, docmd) register struct qe *qp; int cmd; int docmd; { register uaddr_t a; qefilldesc(&qp->xring[qp->xindex], qp->setupaddr, docmd ? (128+cmd) : sizeof(struct setup), DSETUP|DEND); if ((qp->addr->qe_csr&QE_XL_INVALID) && qp->xring[qp->xindex].status1==NOTYET) { /* presumably always */ a = qp->xaddr + qp->xindex*sizeof(struct qe_ring); qp->addr->qe_xmtlist_lo = a; qp->addr->qe_xmtlist_hi = a >> 16; } qp->xindex = NEXTXMT(qp->xindex); } /* * init the descriptor rings: * clear out any stale blocks * zap all the descriptors * point the last descriptor at the first, * forming the ring */ qemkrings(qp) register struct qe *qp; { register int i, ch; for (i=0; i<QENRCV; i++) if (qp->rbp[i]) { freeb(qp->rbp[i]); qp->rbp[i] = NULL; } for (i=0; i<QENXMT; i++) if (qp->xbp[i]) { freeb(qp->xbp[i]); qp->xbp[i] = NULL; } for (ch=0; ch<QENCHAN; ch++) { qp->chan[ch].nxmt = 0; for (i=0; i<QENXMT; i++) if (qp->chan[ch].xmt[i]) { freeb(qp->chan[ch].xmt[i]); qp->chan[ch].xmt[i] = NULL; } } for (i = 0 ; i < QENRCV ; i++) qeinvaldesc(&qp->rring[i]); qefilldesc(&qp->rring[QENRCV], qp->raddr, 0, DCHAIN); for (i = 0 ; i < QENXMT ; i++) qeinvaldesc(&qp->xring[i]); qefilldesc(&qp->xring[QENRCV], qp->xaddr, 0, DCHAIN); qp->lastch = qp->orindex = qp->oxindex = qp->xindex = qp->rindex = 0; qp->rbytes = 0; } /* * turn the receiver on: * enable the hardware, set up receive buffers * send a setup packet */ qeenable(qp) register struct qe *qp; { qp->addr->qe_vector = qp->intvec; qp->addr->qe_csr = qebits; while (qercvblock(qp)==0) ; qexmtsetup(qp, 0, 0); /*printf("qe csr after enable: %x\n", qp->addr->qe_csr);*/ } /* * find a staged packet and send it */ qexmtpacket(qp) register struct qe *qp; { register int i; register struct qechan *cp; /* Look for a channel that has a packet that will fit */ for(i=NEXTCH(qp->lastch); ; i=NEXTCH(i)) { cp = &qp->chan[i]; if (cp->nxmt && cp->nxmt<=NFREE(qp)) break; if (i==qp->lastch) return -1; } qp->lastch = i; qedebug(cp->xmt[0], 1, cp->nbytes); for (i=0; i<cp->nxmt-1; i++) { /* send any partial pieces */ qexmtblock(qp, cp->xmt[i], 0); cp->xmt[i] = NULL; } qexmtblock(qp, cp->xmt[i], DEND); /* send the last piece */ cp->xmt[i] = NULL; cp->nxmt = 0; qestagepacket(cp); /* stage the next packet for this channel */ return 0; } /* * Here on any interrupt */ qe0int(unit) int unit; { register struct qe *qp = &qe[unit]; register int csr; csr = qp->addr->qe_csr; qp->addr->qe_csr = qebits|(csr&INTFLAGS); if( csr & QE_RCV_INT ) qerint(qp); if( csr & QE_XMIT_INT ) qexint(qp); if( csr & QE_NEX_MEM_INT ) printf("deqna %d: nxm\n", unit); } /* * transmit interrupt: * free up descriptors and blocks * DEQNA doesn't hear its own broadcasts, * so loop them around by hand here */ qexint(qp) register struct qe *qp; { register int last; /* last block of packet */ register int first; /* first block of packet */ register struct qechan *cp; /* loop once per packet */ for(;;) { /* loop once per block */ first = qp->oxindex; for (last=first; ; last=NEXTXMT(last)) { if (last==qp->xindex || qp->xring[last].status1==NOTYET){ /* * nothing left */ while (qexmtpacket(qp)==0) ; return; } if (qp->xring[last].hiaddr & DEND) break; } /* * first -> first descriptor for some packet * last -> last descriptor for the same packet */ if ((qp->xring[last].status1&ERROR) == 0) qp->opackets++; else { printf("deqna %d: xmt err st1 %x st2 %x\n", qp - qe, qp->xring[last].status1, qp->xring[last].status2); qp->oerrors++; } if (qp->xring[first].hiaddr & DSETUP) { /* skip over setup packet */ qeinvaldesc(&qp->xring[first]); /* just one desc */ qp->oxindex = NEXTXMT(last); continue; } if (qebits&QE_ELOOP) /* debuggery */ cp = NULL; else cp = qefindchan(qp, qp->xbp[first], 1); /* loop once per block to free (or loop back) packet */ for(last=NEXTXMT(last); first!=last; first=NEXTXMT(first)) { /*printf("qexint: removing %d to %x\n", first, cp);*/ if (cp) (*cp->rq->next->qinfo->putp)(cp->rq->next,qp->xbp[first]); else freeb(qp->xbp[first]); qeinvaldesc(&qp->xring[first]); qp->xbp[first] = NULL; } qp->oxindex = last; } } /* * receiver interrupt: * hand packet to the channel * that wants that protocol */ qerint(qp) register struct qe *qp; { register int last; /* last block of packet */ register int first; /* first block of packet */ register struct qechan *cp; struct qe_ring *rp; struct block *bp; int len; /* loop once per packet */ for(;;) { /* loop once per block */ first = qp->orindex; for (last=first; ; last=NEXTRCV(last)) { if (last==qp->rindex || qp->rring[last].status1==NOTYET) { /* * nothing left */ while(qercvblock(qp)==0) ; return; } if ((qp->rring[last].status1&NOTLAST) == 0) break; } /* * first -> first block of received packet * last -> last block of that packet */ if (qp->rring[last].status1&ERROR) { #if NOTDEF /* usually boring */ printf("deqna %d: rcv err st1 %x st2 %x\n", qp - qe, qp->rring[last].status1, qp->rring[last].status2); #endif cp = NULL; qp->ierrors++; } else if (qp->rring[last].status1&ESETUP) { /* printf("qerint: setup\n");*/ cp = NULL; } else { cp = qefindchan(qp, qp->rbp[first], 0); qp->ipackets++; } /* loop once per block to receive packet */ if (cp) { rp = &qp->rring[last]; len = ((rp->status1&RHILEN)|(rp->status2&RLOLEN))+RMINLEN; qedebug(qp->rbp[first], 0, len); } /* install delimiter */ if (qp->rbp[last] == NULL) panic("qerint"); qp->rbp[last]->class |= S_DELIM; for(last=NEXTRCV(last); first!=last; first=NEXTRCV(first)) { /*printf("qerint: received %d to %x\n", first, cp);*/ bp = qp->rbp[first]; qp->rbytes -= bp->lim - bp->wptr; if (cp) { rp = &qp->rring[first]; if (rp->status1&NOTLAST) { len -= bp->lim-bp->wptr; bp->wptr = bp->lim; } else { bp->wptr += len; if (bp->wptr > bp->lim) panic("qerint: length"); } bp = cramb(bp); if(cp->rq->next->flag & QFULL) freeb(bp); else (*cp->rq->next->qinfo->putp)(cp->rq->next, bp); } else freeb(bp); qeinvaldesc(&qp->rring[first]); qp->rbp[first] = NULL; } qp->orindex = last; } } /* * find the channel for this input packet * checklocal means check that the packet was * addressed to us; used for loopback */ struct qechan * qefindchan(qp, bp, checklocal) register struct qe *qp; struct block *bp; int checklocal; { register int i; register struct etherpup *ep=(struct etherpup *)bp->rptr; register struct qechan *ch; /*printf("qefindchan: %x,%x,%x,%x,%x,%x:%d\n", ep->dhost[0], ep->dhost[1], ep->dhost[2], ep->dhost[3], ep->dhost[4], ep->dhost[5], ep->type);*/ if (checklocal) { for (i=0; i<ETHERALEN; i++) { if (ep->dhost[i]!=qp->setup->d[i][1] && ep->dhost[i]!=0xff) /* broadcast */ return NULL; } } for (i=0, ch=qp->chan; i<QENCHAN; i++, ch++) if (ch->rq!=NULL && ch->type==ep->type) return (ch); return NULL; } /* * stage the next packet for this channel: * gather the blocks, make sure they look sensible * printfs are a bit silly */ qestagepacket(cp) register struct qechan *cp; { register struct block *bp; register int blk; int nbytes; register struct etherpup *ep; register struct setup *sp; if (cp->nxmt) /* already have one staged */ return; blk = 0; nbytes = 0; while ((bp = getq(WR(cp->rq))) != NULL) { if (bp->type != M_DATA) panic("qestagepkt"); if (blk >= QENXMT-1) { blk = qepullup(cp); if (blk >= QENXMT-1) { freeb(bp); /* wrong! but too hard for now */ continue; } } cp->xmt[blk++] = bp; nbytes += bp->wptr-bp->rptr; if ((bp->class & S_DELIM) == 0) continue; /* * have a whole packet */ if (nbytes > ETHERMAXTU) { printf("deqna %d: packet too long\n", cp->unit); break; } if (cp->xmt[0]->wptr-cp->xmt[0]->rptr < sizeof(struct etherpup)) { printf("deqna %d: too fragmented\n", cp->unit); break; } if (nbytes < ETHERMINTU) /* wrong, but do it anyway */ cp->xmt[blk-1]->wptr += ETHERMINTU-nbytes; /* * force our address into the packet * why doesn't the hardware do this? */ ep = (struct etherpup *)cp->xmt[0]->rptr; ep->type = cp->type; cp->nxmt = blk; sp = qe[cp->unit].setup; for(blk=0; blk < ETHERALEN; blk++) ep->shost[blk] = sp->d[blk][1]; /*printf("qestagepacket: staged %d\n", cp->nxmt);*/ cp->nbytes = nbytes; return; } /* * if we come here, something went wrong * any useful packet has already been removed from the queue */ if (bp) freeb(bp); else if (blk!=0) printf("deqna %d: stage no delim\n", cp->unit); while (--blk >= 0) freeb(cp->xmt[blk]); cp->nxmt = 0; } /* * transmit packet came in too many pieces * to fit in staging area * allocate a big block, * and try to squeeze some pieces into it * returns the first slot now available for a block, * QENXMT-1 if no space could be made * * assertion: no block we replace is S_DELIM */ int qepullup(cp) register struct qechan *cp; { register struct block *newbp, *bp; register int blk, rep, space; register int n; if ((newbp = allocb(ETHERMAXTU/2)) == NULL) return (QENXMT-1); space = newbp->lim - newbp->wptr; for (blk = 0; blk < QENXMT-1; blk++) { bp = cp->xmt[blk]; if (bp->wptr - bp->rptr < space) break; } if (blk >= QENXMT - 1) { /* no block small enough to fit */ freeb(newbp); return (blk); } /* * pack as much as possible into newbp */ rep = blk; for (; blk < QENXMT-1; blk++) { bp = cp->xmt[blk]; n = bp->wptr - bp->rptr; if (n > space) break; bcopy(bp->rptr, newbp->wptr, n); freeb(bp); newbp->wptr += n; space -= n; } /* * slide remaining blocks up */ cp->xmt[rep++] = newbp; for (; blk < QENXMT-1; blk++, rep++) cp->xmt[rep] = cp->xmt[blk]; return (rep); } /* * tracing stuff */ #include "sys/systm.h" /* for time */ #define QEDEBSIZE 64 struct { time_t time; unsigned short code; struct etherpup pup; short len; } qed[QEDEBSIZE]; int qei = 0; qedebug(bp, code, len) register struct block *bp; { qed[qei].time = time; qed[qei].code = code; qed[qei].len = len; bcopy(bp->rptr, &qed[qei].pup, sizeof(qed[qei].pup)); qei = (qei + 1) % QEDEBSIZE; }