# /* VIILNI Driver * Noel Chiappa MIT-LCS-CSR * * This is an asynchronous device driver for use with a high speed * network device. As such it obeys most of the rules layed down for * the LNI driver. * * The common (portable) kludge of using the v_naddr to point at * the CSR is used extensively, more to bum code space than for speed. */ #include "../h/buf.h" #include "../h/conf.h" #include "../h/param.h" #include "../h/user.h" #include #include #define xmtp() (rw == B_WRITE) #define viircsr(pnt) (*((uns *) (pnt->v_raddr))) #define viixcsr(pnt) (*((uns *) (pnt->v_xaddr))) #define viirbuf(pnt) (pnt->v_rcvb) #define viixbuf(pnt) (pnt->v_xmtb) /* A few constants */ #define VIIXERR (VII_BDF|VII_OPT|VII_OVR|VII_NXM) /* Xmit errs */ #define VIIRERR (VII_BDF|VII_ODB|VII_OVR|VII_NXM) /* Rcv errs */ #define INITR 1 #define NORDYC 2 #define SPINT 3 #define LDE 4 #define NOPTTRY 5 /* Xmit OPT retries */ #define PRINT 1 #define NOPRINT 0 #define DEBUG 1 /* Register structure */ struct viireg { uns viicsr; /* Control and status */ uns viiwcr; /* Word count register */ uns viiarl; /* Address low 16 bits */ uns viiarh; /* Address high 2 bits */ }; /* I/O queue structure and device table */ struct viitb { int v_flags; /* Per device flags */ struct viireg *v_raddr; /* Rcv registers */ struct viireg *v_xaddr; /* Xmt registers */ struct buf *v_rcvb; /* Rcv side buf */ struct buf *v_xmtb; /* Xmit side buf */ }; struct viist { long v_pktin; /* Packets in */ long v_pktout; /* Packets out */ long v_bytin; /* Bytes in */ long v_bytout; /* Bytes out */ short v_initr; /* Ring inits */ short v_rlde; /* Rcv link err */ short v_rrdync; /* Rcv rdy no clr */ short v_rspint; /* Rcv spur int */ short v_rbdf; /* Rcv bad format */ short v_rodb; /* Rcv odd byte cnt */ short v_rovr; /* Rcv overrun */ short v_rnxm; /* Rcv nxm */ short v_xrdync; /* Xmit rdy no clr */ short v_xspint; /* Xmit spur int */ short v_xbdf; /* Xmit bad format */ short v_xopt; /* Xmit timeout */ short v_xovr; /* Xmit overrun */ short v_xnxm; /* Xmit nxm */ }; /* state flags */ #define V_RCVSG 01 /*%%*/ /* Rcv scatter-gather in progress */ #define V_XMTSG 02 /* Xmit scatter-gather in progress */ #ifdef DEBUG extern short neterr[]; #endif /* VIILNI register locations */ uns viiloc = { 0175200 }; /* Actual buffer headers and device tables */ struct viitb viitab; struct viist viistat; short v2errprnt; /* 0 = don't print v2 errors 1 = do print v2 errors */ short nopt; short xnotdone = 0; /* VIILNI open routine - turn on the BUSY bit in the device header * status word, store a pointer to the owning proc, set up the * device address, etc. * Returns ENODEV if its in use, and ENXIO if it ain't there. */ viiinit() { reg struct viitb *viit; reg struct viireg *viir; viit = &viitab; viir = viiloc; viit->v_flags = 0; viit->v_raddr = viir++; /* Kludge */ viit->v_xaddr = viir; viit->v_rcvb = NULL; viit->v_xmtb = NULL; viircsr(viit) = VII_RST|VII_HEN; viixcsr(viit) = VII_RST; viircsr(viit) |= VII_IEN; viixcsr(viit) |= VII_IEN; } netflush() { reg struct viitb *viit; reg struct buf *bp; viit = &viitab; viircsr(viit) = VII_RST|VII_HEN; viixcsr(viit) = VII_RST; viit->v_flags = 0; if ((bp = viit->v_xmtb) != NULL) { viit->v_xmtb = NULL; bp->b_flags = 0; } if ((bp = viit->v_rcvb) != NULL) { viit->v_rcvb = NULL; netr_flush(bp); } viircsr(viit) |= VII_IEN; viixcsr(viit) |= VII_IEN; } /* viixmt, viircv * Start I/O on an VIILNI interface - sets the regs as appropriate and * pokes the device. It should do something better if Ready didn't * clear, but it's not clear what to do, or if it was, how to do it. * Note intricate hackery due to the fact that it won't go over * moby (64k) boundaries. The low order address shifted right * just happens to be the negative of the number of words from there * to the end of the moby! * Viixmt invokes mapalloc to set up UNIBUS map for it (a DMA device). * Viircv does not need to invoke mapalloc because it uses kernel data * space which is permanently set up in the UNIBUS map. * Viixmt is not called at interrupt level so appropriate to * spl down on exit. Also try busy wait for xmit to complete so * when return to physio unlikely for process to sleep. * Viircv is called at interrupt and non-interrupt levels so * calling routine must spl as appropriate. */ viixmt(bp) reg struct buf *bp; { reg struct viitb *viit; reg struct viireg *viir; int i; spl4(); viit = &viitab; viir = viit->v_xaddr; if ((viit->v_xmtb != NULL) || !(viir->viicsr & VII_RDY)) panic("netstart xmt in progress"); viit->v_xmtb = bp; nopt = 0; mapalloc(bp); viir->viiarl = bp->b_addr; viir->viiarh = bp->b_xmem; /*%%*/ if ((bp->b_addr + ((-bp->b_wcount) << 1)) < bp->b_addr) { printf("V2LNI SG1 %o %o\n", bp->b_addr, bp->b_wcount); viit->v_flags =| V_XMTSG; viir->viiwcr = (((int) bp->b_addr) >> 1); viir->viicsr =| (VII_DEN | VII_CPB); } else { viir->viiwcr = bp->b_wcount; viir->viicsr =| (VII_ENB | VII_DEN | VII_CPB); if (viir->viicsr & VII_NOK) { viir->viicsr =| VII_INR; viierrp("init ring", viircsr(viit), viir->viicsr, B_WRITE, INITR, PRINT); } } if (viir->viicsr & VII_RDY) viierrp("no rdy clr", viircsr(viit), viixcsr(viit), B_WRITE, NORDYC, NOPRINT); spl0(); /* Assume 500 byte pkt takes 1 millisecond to write Each loop takes .005 millisecs (5 microsecs) */ i = 200; while ( !(viir->viicsr & VII_RDY) && (--i > 0) ) ; if (!(viir->viicsr & VII_RDY)) xnotdone++; } viircv(bp) reg struct buf *bp; { reg struct viitb *viit; reg struct viireg *viir; spl4(); viit = &viitab; viir = viit->v_raddr; if ((viit->v_rcvb != NULL) || !(viir->viicsr & VII_RDY)) panic("netstart rcv in progress"); viit->v_rcvb = bp; viir->viiarl = bp->b_addr; viir->viiarh = bp->b_xmem; /*%%*/ if ((bp->b_addr + ((-bp->b_wcount) << 1)) < bp->b_addr) { printf("V2LNI SG1 %o %o\n", bp->b_addr, bp->b_wcount); viit->v_flags =| V_RCVSG; viir->viiwcr = (((int) bp->b_addr) >> 1); viir->viicsr =| (VII_DEN | VII_ENB); } else { viir->viiwcr = bp->b_wcount; viir->viicsr =| (VII_ENB | VII_DEN | VII_CPB); } if (viir->viicsr & VII_RDY) viierrp("no rdy clr", viircsr(viit), viixcsr(viit), B_READ, NORDYC, NOPRINT); } /* Actual interrupt routines; simple call common one with flag. */ viiiint(dev) { viiint(dev, B_READ); } viioint(dev) { viiint(dev, B_WRITE); } /* VIILNI interrupt routine - Flush spurious interrupts, and note * errors, etc. If we were doing a scatter gather, queue the * second operation. See the comments for viistrt for some comments * that may help in understanding the code here. Note the kludge use * of a global flag (most efficient way, unfortunately) to tell which * flavor of interrupt. * Iodone will invoke mapfree to release UNIBUS map for this DMA device. */ viiint(dev, rw) { reg struct viitb *viit; reg struct viireg *viir; reg struct buf *bp; reg short err; int nrwcnt; viit = &viitab; if ((bp = (xmtp() ? viixbuf(viit) : viirbuf(viit))) == NULL) { viierrp("spurious int", viircsr(viit), viixcsr(viit), rw, SPINT, PRINT); return; } viir = (xmtp() ? viit->v_xaddr : viit->v_raddr); err = viir->viicsr & (xmtp() ? VIIXERR : VIIRERR); if (err) viierrp(" ", viircsr(viit), viixcsr(viit), rw, 0, PRINT); if (xmtp() && (err == VII_OPT) && (nopt++ < NOPTTRY) ) { viir->viicsr =| (VII_ENB | VII_INR); return; } /*%%*/ if ( viit->v_flags & (xmtp() ? V_XMTSG : V_RCVSG) ) { printf("V2LNI SG2 %o %o\n", bp->b_addr, bp->b_wcount); viit->v_flags =& ~(xmtp() ? V_XMTSG : V_RCVSG); if (err) goto done; nrwcnt = (bp->b_wcount - (((int) bp->b_addr) >> 1)); if (!xmtp() && !(viir->viicsr & VII_DPR)) { viir->viiwcr =+ nrwcnt; goto done; } viir->viiarl = 0; viir->viiarh = (bp->b_xmem + 1); viir->viiwcr = nrwcnt; viir->viicsr =| (xmtp() ? (VII_ENB | VII_DEN) : VII_DEN); if (xmtp() && (viir->viicsr & VII_NOK)) { viir->viicsr =| VII_INR; viierrp("init ring", viircsr(viit), viir->viicsr, B_WRITE, INITR, PRINT); } return; } done: bp->b_resid = viir->viiwcr; bp->b_blkno = viir->viicsr; if (xmtp()) viixint(viit, bp); else viirint(viit, bp); } viixint(viit, bp) reg struct viitb *viit; reg struct buf *bp; { reg struct viist *viis; reg short bcnt; viit->v_xmtb = NULL; viis = &viistat; bcnt = ((-bp->b_wcount) + bp->b_resid) << 1; viis->v_bytout += bcnt; viis->v_pktout++; if ((bp->b_blkno & VIIXERR) & (~VII_RFS)) { viixrst(viit->v_xaddr); bp->b_flags |= B_ERROR; bp->b_error = EIO; } else if (bp->b_blkno & VII_RFS) { bp->b_flags |= B_ERROR; bp->b_error = EACCES; } iodone(bp); } viirint(viit, bp) reg struct viitb *viit; reg struct buf *bp; { reg struct viist *viis; reg struct viireg *viir; reg short bcnt; viit->v_rcvb = NULL; viis = &viistat; bcnt = ((-bp->b_wcount) + bp->b_resid) << 1; viis->v_bytin += bcnt; viis->v_pktin++; viir = viit->v_raddr; if (viir->viicsr & VII_LDE) { viierrp("Link error", viir->viicsr, viixcsr(viit), B_READ, LDE, PRINT); viir->viicsr &= (~VII_LDE); } if (bp->b_blkno & VIIRERR) { viirrst(viir); bp->b_flags |= B_ERROR; } #ifdef DEBUG if (neterr[0] != 0) { spl6(); printf("viirint neterr[0] not 0, rcv csr = %o neterr = %o\n", bp->b_blkno, neterr[0]); return; } #endif netrcv(bp); netr_start(); } /* Reset receive interface */ viirrst(viir) reg struct viireg *viir; { viir->viicsr = VII_RST|VII_HEN; viir->viicsr |= VII_IEN; } /* Reset transmit interface */ viixrst(viir) reg struct viireg *viir; { viir->viicsr = VII_RST; viir->viicsr |= VII_IEN; } viierrp(msg, rcsr, xcsr, rw, ecode, prnt) char *msg; uns rcsr, xcsr; int rw, ecode, prnt; { switch(rw) { case B_WRITE: if (prnt && v2errprnt) printf("Error VIILNI xmit %s %o %o\n", msg, rcsr, xcsr); switch(ecode) { case NORDYC: viistat.v_xrdync++; break; case SPINT: viistat.v_xspint++; break; case INITR: viistat.v_initr++; break; default: if (xcsr & VIIXERR) viixerr( xcsr ); break; } break; case B_READ: if (prnt && v2errprnt) printf("Error VIILNI rcv %s %o %o\n", msg, rcsr, xcsr); switch(ecode) { case NORDYC: viistat.v_rrdync++; break; case SPINT: viistat.v_rspint++; break; case LDE: viistat.v_rlde++; break; default: if (rcsr & VIIRERR) viirerr( rcsr ); break; } break; } } viixerr( csr ) reg short csr; { reg struct viist *viis; viis = &viistat; if (csr & VII_BDF) viis->v_xbdf++; else if (csr & VII_OPT) viis->v_xopt++; else if (csr & VII_OVR) viis->v_xovr++; else if (csr & VII_NXM) viis->v_xnxm++; } viirerr( csr ) reg short csr; { reg struct viist *viis; viis = &viistat; if (csr & VII_BDF) viis->v_rbdf++; else if (csr & VII_ODB) viis->v_rodb++; else if (csr & VII_OVR) viis->v_rovr++; else if (csr & VII_NXM) viis->v_rnxm++; }