1*21779Skarels /* if_vv.c 6.8 85/06/03 */ 27024Ssam 39799Ssam #include "vv.h" 411192Ssam 57024Ssam /* 620997Skarels * Proteon proNET-10 and proNET-80 token ring driver. 720997Skarels * The name of this device driver derives from the old MIT 820997Skarels * name of V2LNI for the proNET hardware, would would abbreviate 920997Skarels * to "v2", but this won't work right. Thus the name is "vv". 1011192Ssam * 1120997Skarels * This driver is compatible with the proNET 10 meagbit and 1220997Skarels * 80 megabit token ring interfaces (models p1000 and p1080). 1320997Skarels * 1420997Skarels * TRAILERS: You must turn off trailers via ifconfig if you want to share 1520997Skarels * a ring with software using the following protocol types: 1620997Skarels * 3: Address Resolution Protocol 1720997Skarels * 4: HDLC (old Proteon drivers) 1820997Skarels * 5: VAX Debugging Protocol (never used) 1920997Skarels * This is because the protocol type values chosen for trailers 2020997Skarels * conflict with these protocols. It's too late to change either now. 2120997Skarels * 2220997Skarels * HARDWARE COMPATABILITY: This driver requires that the HSBU (p1001) 2320997Skarels * have a serial number >= 040, which is about March, 1982. Older 2420997Skarels * HSBUs do not carry across 64kbyte boundaries. The old warning 2520997Skarels * about use without Wire Centers applies only to CTL (p1002) cards with 2620997Skarels * serial <= 057, which have not received ECO 176-743, which was 2720997Skarels * implemented in March, 1982. Most such CTLs have received this ECO, 2820997Skarels * but they are only compatible with the old HSBUs (<=039) anyways. 297024Ssam */ 3011209Ssam #include "../machine/pte.h" 319799Ssam 3217117Sbloom #include "param.h" 3317117Sbloom #include "systm.h" 3417117Sbloom #include "mbuf.h" 3517117Sbloom #include "buf.h" 3617117Sbloom #include "protosw.h" 3717117Sbloom #include "socket.h" 3817117Sbloom #include "vmmac.h" 3917117Sbloom #include "errno.h" 4017117Sbloom #include "ioctl.h" 418465Sroot 428465Sroot #include "../net/if.h" 4311209Ssam #include "../net/netisr.h" 448465Sroot #include "../net/route.h" 458421Swnj #include "../netinet/in.h" 468421Swnj #include "../netinet/in_systm.h" 47*21779Skarels #include "../netinet/in_var.h" 488421Swnj #include "../netinet/ip.h" 498421Swnj #include "../netinet/ip_var.h" 508465Sroot 5115794Sleres #include "../vax/cpu.h" 5211209Ssam #include "../vax/mtpr.h" 5317117Sbloom #include "if_vv.h" 5417117Sbloom #include "if_uba.h" 558465Sroot #include "../vaxuba/ubareg.h" 568465Sroot #include "../vaxuba/ubavar.h" 577024Ssam 587024Ssam /* 5920997Skarels * 80 megabit configuration 6020997Skarels * Uncomment the next line if you are using the 80 megabit system. The 6120997Skarels * only change is the disposition of packets with parity/link_data_error 6220997Skarels * indication. 637024Ssam */ 6420997Skarels /* #define PRONET80 */ 657024Ssam 6620997Skarels /* 6720997Skarels * maximum transmission unit definition -- 6820997Skarels * you can set VVMTU at anything from 576 to 2024. 6920997Skarels * 1536 is a popular "large" value, because it is a multiple 7020997Skarels * of 512, which the trailer scheme likes. 7120997Skarels * The absolute maximum size is 2024, which is enforced. 7220997Skarels */ 7320997Skarels 7420997Skarels #define VVMTU (1024) 7520997Skarels 7620997Skarels #define VVMRU (VVMTU + 16) 7720997Skarels #define VVBUFSIZE (VVMRU + sizeof(struct vv_header)) 7820997Skarels #if VVMTU>2024 7920997Skarels #undef VVMTU 8020997Skarels #undef VVMRU 8120997Skarels #undef VVBUFSIZE 8220997Skarels #define VVBUFSIZE (2046) 8320997Skarels #define VVMRU (VVBUFSIZE - sizeof (struct vv_header)) 8420997Skarels #define VVMTU (VVMRU - 16) 857024Ssam #endif 867024Ssam 8720997Skarels /* 8820997Skarels * debugging and tracing stuff 8920997Skarels */ 9016581Skarels int vv_tracehdr = 0; /* 1 => trace headers (slowly!!) */ 917640Ssam 9220997Skarels #define vvtracehdr if (vv_tracehdr) vvprt_hdr 93*21779Skarels #define vvprintf if (vs->vs_if.if_flags & IFF_DEBUG) printf 9411192Ssam 9520997Skarels /* 9620997Skarels * externals, types, etc. 9720997Skarels */ 9816581Skarels int vvprobe(), vvattach(), vvreset(), vvinit(); 9916581Skarels int vvidentify(), vvstart(), vvxint(), vvwatchdog(); 10016581Skarels int vvrint(), vvoutput(), vvioctl(), vvsetaddr(); 1017024Ssam struct uba_device *vvinfo[NVV]; 1027024Ssam u_short vvstd[] = { 0 }; 1037024Ssam struct uba_driver vvdriver = 1047024Ssam { vvprobe, 0, vvattach, 0, vvstd, "vv", vvinfo }; 1057024Ssam #define VVUNIT(x) minor(x) 1067024Ssam 10720997Skarels #define LOOPBACK /* use loopback for packets meant for us */ 10820997Skarels #ifdef LOOPBACK 10920997Skarels extern struct ifnet loif; 11020997Skarels #endif 11120997Skarels 1127024Ssam /* 1137024Ssam * Software status of each interface. 1147024Ssam * 1157024Ssam * Each interface is referenced by a network interface structure, 1167024Ssam * vs_if, which the routing code uses to locate the interface. 1177024Ssam * This structure contains the output queue for the interface, its address, ... 1187024Ssam * We also have, for each interface, a UBA interface structure, which 1197024Ssam * contains information about the UNIBUS resources held by the interface: 1207024Ssam * map registers, buffered data paths, etc. Information is cached in this 1217024Ssam * structure for use by the if_uba.c routines in running the interface 1227024Ssam * efficiently. 1237024Ssam */ 1247024Ssam struct vv_softc { 1257024Ssam struct ifnet vs_if; /* network-visible interface */ 1267024Ssam struct ifuba vs_ifuba; /* UNIBUS resources */ 127*21779Skarels int vs_host; 12811192Ssam short vs_oactive; /* is output active */ 1297024Ssam short vs_olen; /* length of last output */ 13015794Sleres u_short vs_lastx; /* address of last packet sent */ 13115794Sleres u_short vs_lastr; /* address of last packet received */ 13211192Ssam short vs_tries; /* transmit current retry count */ 1337024Ssam short vs_init; /* number of ring inits */ 13420997Skarels short vs_refused; /* number of packets refused */ 13515794Sleres short vs_timeouts; /* number of transmit timeouts */ 13620997Skarels short vs_otimeout; /* number of output timeouts */ 13720997Skarels short vs_ibadf; /* number of input bad formats */ 13820997Skarels short vs_parity; /* number of parity errors on 10 meg, */ 13920997Skarels /* link data errors on 80 meg */ 1407024Ssam } vv_softc[NVV]; 1417024Ssam 14220997Skarels /* 14320997Skarels * probe the interface to see that the registers exist, and then 14420997Skarels * cause an interrupt to find its vector 14520997Skarels */ 1467024Ssam vvprobe(reg) 1477024Ssam caddr_t reg; 1487024Ssam { 1497024Ssam register int br, cvec; 15016581Skarels register struct vvreg *addr; 1517024Ssam 1527024Ssam #ifdef lint 15315764Sleres br = 0; cvec = br; br = cvec; 1547024Ssam #endif 15516581Skarels addr = (struct vvreg *)reg; 15620997Skarels 1577024Ssam /* reset interface, enable, and wait till dust settles */ 1587024Ssam addr->vvicsr = VV_RST; 1597024Ssam addr->vvocsr = VV_RST; 16015764Sleres DELAY(100000); 16120997Skarels 1627024Ssam /* generate interrupt by doing 1 word DMA from 0 in uba space!! */ 1637024Ssam addr->vvoba = 0; /* low 16 bits */ 1647024Ssam addr->vvoea = 0; /* extended bits */ 1657024Ssam addr->vvowc = -1; /* for 1 word */ 16620997Skarels addr->vvocsr = VV_IEN | VV_DEN; /* start the DMA, with interrupt */ 1677024Ssam DELAY(100000); 16820997Skarels addr->vvocsr = VV_RST; /* clear out the CSR */ 1697024Ssam if (cvec && cvec != 0x200) 17015764Sleres cvec -= 4; /* backup so vector => receive */ 1717024Ssam return(1); 1727024Ssam } 1737024Ssam 1747024Ssam /* 1757024Ssam * Interface exists: make available by filling in network interface 1767024Ssam * record. System will initialize the interface when it is ready 1777024Ssam * to accept packets. 1787024Ssam */ 1797024Ssam vvattach(ui) 1807024Ssam struct uba_device *ui; 1817024Ssam { 18215764Sleres register struct vv_softc *vs; 1837024Ssam 18415764Sleres vs = &vv_softc[ui->ui_unit]; 1857024Ssam vs->vs_if.if_unit = ui->ui_unit; 1867024Ssam vs->vs_if.if_name = "vv"; 1877024Ssam vs->vs_if.if_mtu = VVMTU; 188*21779Skarels vs->vs_if.if_flags = IFF_BROADCAST; 1897024Ssam vs->vs_if.if_init = vvinit; 19013057Ssam vs->vs_if.if_ioctl = vvioctl; 1917024Ssam vs->vs_if.if_output = vvoutput; 19211209Ssam vs->vs_if.if_reset = vvreset; 19315794Sleres vs->vs_if.if_timer = 0; 19415794Sleres vs->vs_if.if_watchdog = vvwatchdog; 1957640Ssam vs->vs_ifuba.ifu_flags = UBA_CANTWAIT | UBA_NEEDBDP | UBA_NEED16; 19612354Smo #if defined(VAX750) 19712354Smo /* don't chew up 750 bdp's */ 19812354Smo if (cpu == VAX_750 && ui->ui_unit > 0) 19912354Smo vs->vs_ifuba.ifu_flags &= ~UBA_NEEDBDP; 20012354Smo #endif 2017024Ssam if_attach(&vs->vs_if); 2027024Ssam } 2037024Ssam 2047024Ssam /* 2057024Ssam * Reset of interface after UNIBUS reset. 2067024Ssam * If interface is on specified uba, reset its state. 2077024Ssam */ 2087024Ssam vvreset(unit, uban) 2097024Ssam int unit, uban; 2107024Ssam { 2117024Ssam register struct uba_device *ui; 2127024Ssam 2137024Ssam if (unit >= NVV || (ui = vvinfo[unit]) == 0 || ui->ui_alive == 0 || 2147024Ssam ui->ui_ubanum != uban) 2157024Ssam return; 2167024Ssam printf(" vv%d", unit); 2177024Ssam vvinit(unit); 2187024Ssam } 2197024Ssam 2207024Ssam /* 2217024Ssam * Initialization of interface; clear recorded pending 2227024Ssam * operations, and reinitialize UNIBUS usage. 2237024Ssam */ 2247024Ssam vvinit(unit) 2257024Ssam int unit; 2267024Ssam { 22715764Sleres register struct vv_softc *vs; 22815764Sleres register struct uba_device *ui; 2297024Ssam register struct vvreg *addr; 23016581Skarels register int ubainfo, s; 2317024Ssam 23215764Sleres vs = &vv_softc[unit]; 23315764Sleres ui = vvinfo[unit]; 23420997Skarels 235*21779Skarels if (vs->vs_if.if_addrlist == (struct ifaddr *)0) 23613057Ssam return; 23720997Skarels 2387640Ssam addr = (struct vvreg *)ui->ui_addr; 2397024Ssam if (if_ubainit(&vs->vs_ifuba, ui->ui_ubanum, 24015764Sleres sizeof (struct vv_header), (int)btoc(VVMTU)) == 0) { 24115794Sleres printf("vv%d: can't initialize, if_ubainit() failed\n", unit); 2427640Ssam vs->vs_if.if_flags &= ~IFF_UP; 2437024Ssam return; 2447024Ssam } 24520997Skarels 2467024Ssam /* 24715764Sleres * Now that the uba is set up, figure out our address and 24815764Sleres * update complete our host address. 2497640Ssam */ 250*21779Skarels if ((vs->vs_host = vvidentify(unit)) == -1) { 25115794Sleres vs->vs_if.if_flags &= ~IFF_UP; 25215794Sleres return; 25315794Sleres } 254*21779Skarels printf("vv%d: host %d\n", unit, vs->vs_host); 25520997Skarels 2567640Ssam /* 25720997Skarels * Reset the interface, and stay in the ring 2587640Ssam */ 25920997Skarels addr->vvocsr = VV_RST; /* take over output */ 26020997Skarels addr->vvocsr = VV_CPB; /* clear packet buffer */ 26120997Skarels addr->vvicsr = VV_RST | VV_HEN; /* take over input, */ 26220997Skarels /* keep relay closed */ 26312351Smo DELAY(500000); /* let contacts settle */ 26420997Skarels 26520997Skarels vs->vs_init = 0; /* clear counters, etc. */ 26620997Skarels vs->vs_refused = 0; 26715794Sleres vs->vs_timeouts = 0; 26820997Skarels vs->vs_otimeout = 0; 26920997Skarels vs->vs_ibadf = 0; 27020997Skarels vs->vs_parity = 0; 27115794Sleres vs->vs_lastx = 256; /* an invalid address */ 27215794Sleres vs->vs_lastr = 256; /* an invalid address */ 27320997Skarels 2747640Ssam /* 2757640Ssam * Hang a receive and start any 2767640Ssam * pending writes by faking a transmit complete. 2777640Ssam */ 2787640Ssam s = splimp(); 2797640Ssam ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; 28013057Ssam addr->vviba = (u_short)ubainfo; 28113057Ssam addr->vviea = (u_short)(ubainfo >> 16); 28220997Skarels addr->vviwc = -(VVBUFSIZE) >> 1; 28320997Skarels addr->vvicsr = VV_IEN | VV_HEN | VV_DEN | VV_ENB; 2847640Ssam vs->vs_oactive = 1; 285*21779Skarels vs->vs_if.if_flags |= IFF_RUNNING; 2867640Ssam vvxint(unit); 2877640Ssam splx(s); 2887640Ssam } 2897640Ssam 2907640Ssam /* 29120997Skarels * Do a moderately thorough self-test in all three modes. Mostly 29220997Skarels * to keeps defective nodes off the ring, rather than to be especially 29320997Skarels * thorough. The key issue is to detect any cable breaks before joining 29420997Skarels * the ring. Return our node address on success, return -1 on failure. 29520997Skarels * 2967640Ssam */ 29720997Skarels 29820997Skarels /* the three self-test modes */ 29920997Skarels static u_short vv_modes[] = { 30020997Skarels VV_STE|VV_LPB, /* digital loopback */ 30120997Skarels VV_STE, /* analog loopback */ 30220997Skarels VV_HEN /* network mode */ 30320997Skarels }; 30420997Skarels 30511209Ssam vvidentify(unit) 30613057Ssam int unit; 30711209Ssam { 30815764Sleres register struct vv_softc *vs; 30915764Sleres register struct uba_device *ui; 3107640Ssam register struct vvreg *addr; 31116581Skarels register struct mbuf *m; 31216581Skarels register struct vv_header *v; 31320997Skarels register int ubainfo; 31420997Skarels register int i, successes, failures, waitcount; 31520997Skarels u_short shost = -1; 3167640Ssam 31720997Skarels vs = &vv_softc[unit]; 31820997Skarels ui = vvinfo[unit]; 31920997Skarels addr = (struct vvreg *)ui->ui_addr; 32020997Skarels 3217640Ssam /* 3227024Ssam * Build a multicast message to identify our address 32320997Skarels * We need do this only once, since nobody else is about to use 32420997Skarels * the intermediate transmit buffer (ifu_w.ifrw_addr) that 32520997Skarels * if_ubainit() aquired for us. 3267024Ssam */ 32711209Ssam m = m_get(M_DONTWAIT, MT_HEADER); 32815794Sleres if (m == NULL) { 32915794Sleres printf("vv%d: can't initialize, m_get() failed\n", unit); 33013057Ssam return (0); 33115794Sleres } 33211192Ssam m->m_next = 0; 3337024Ssam m->m_off = MMINOFF; 3347024Ssam m->m_len = sizeof(struct vv_header); 3357024Ssam v = mtod(m, struct vv_header *); 33611192Ssam v->vh_dhost = VV_BROADCAST; /* multicast destination address */ 3377024Ssam v->vh_shost = 0; /* will be overwritten with ours */ 3387024Ssam v->vh_version = RING_VERSION; 33920997Skarels v->vh_type = RING_DIAGNOSTICS; 3407024Ssam v->vh_info = 0; 34120997Skarels /* map xmit message into uba, copying to intermediate buffer */ 34220997Skarels vs->vs_olen = if_wubaput(&vs->vs_ifuba, m); 34320997Skarels 3447024Ssam /* 34520997Skarels * For each of the modes (digital, analog, network), go through 34620997Skarels * a self-test that requires me to send VVIDENTSUCC good packets 34720997Skarels * in VVIDENTRETRY attempts. Use broadcast destination to find out 34820997Skarels * who I am, then use this as my address to check my address match 34920997Skarels * logic. Only data checked is the vh_type field. 3507024Ssam */ 3517640Ssam 35220997Skarels for (i = 0; i < 3; i++) { 35320997Skarels successes = 0; /* clear successes for this mode */ 35420997Skarels failures = 0; /* and clear failures, too */ 3557640Ssam 35620997Skarels /* take over device, and leave ring */ 35720997Skarels addr->vvicsr = VV_RST; 35820997Skarels addr->vvocsr = VV_RST; 35920997Skarels addr->vvicsr = vv_modes[i]; /* test mode */ 36020997Skarels 36120997Skarels /* 36220997Skarels * let the flag and token timers pop so that the init ring bit 36320997Skarels * will be allowed to work, by waiting about 1 second 36420997Skarels */ 36520997Skarels DELAY(1000000L); 36620997Skarels 36720997Skarels /* 36820997Skarels * retry loop 36920997Skarels */ 37020997Skarels while ((successes < VVIDENTSUCC) && (failures < VVIDENTRETRY)) 37120997Skarels { 37220997Skarels /* start a receive */ 37320997Skarels ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; 37420997Skarels addr->vvicsr = VV_RST | vv_modes[i]; /* abort last */ 37520997Skarels addr->vviba = (u_short) ubainfo; 37620997Skarels addr->vviea = (u_short) (ubainfo >> 16); 37720997Skarels addr->vviwc = -(VVBUFSIZE) >> 1; 37820997Skarels addr->vvicsr = vv_modes[i] | VV_DEN | VV_ENB; 37920997Skarels 38020997Skarels /* purge stale data from BDP */ 38120997Skarels if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 38220997Skarels UBAPURGE(vs->vs_ifuba.ifu_uba, 38320997Skarels vs->vs_ifuba.ifu_w.ifrw_bdp); 38420997Skarels 38520997Skarels /* do a transmit */ 38620997Skarels ubainfo = vs->vs_ifuba.ifu_w.ifrw_info; 38720997Skarels addr->vvocsr = VV_RST; /* abort last try */ 38820997Skarels addr->vvoba = (u_short) ubainfo; 38920997Skarels addr->vvoea = (u_short) (ubainfo >> 16); 39020997Skarels addr->vvowc = -((vs->vs_olen + 1) >> 1); 39120997Skarels addr->vvocsr = VV_CPB | VV_DEN | VV_INR | VV_ENB; 39220997Skarels 39320997Skarels /* poll receive side for completion */ 39420997Skarels DELAY(10000); /* give it a chance */ 39520997Skarels for (waitcount = 0; waitcount < 10; waitcount++) { 39620997Skarels if (addr->vvicsr & VV_RDY) 39720997Skarels goto gotit; 39820997Skarels DELAY(1000); 39920997Skarels } 40020997Skarels failures++; /* no luck */ 40111209Ssam continue; 40220997Skarels 40320997Skarels gotit: /* we got something--is it any good? */ 40420997Skarels if ((addr->vvicsr & (VVRERR|VV_LDE)) || 405*21779Skarels (addr->vvocsr & (VVXERR|VV_RFS))) { 40620997Skarels failures++; 40720997Skarels continue; 40820997Skarels } 40920997Skarels 41020997Skarels /* Purge BDP before looking at received packet */ 41120997Skarels if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 41220997Skarels UBAPURGE(vs->vs_ifuba.ifu_uba, 41320997Skarels vs->vs_ifuba.ifu_r.ifrw_bdp); 41420997Skarels m = if_rubaget(&vs->vs_ifuba, 41520997Skarels sizeof(struct vv_header), 0); 41620997Skarels if (m != NULL) 41720997Skarels m_freem(m); 41820997Skarels 41920997Skarels v = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr); 42020997Skarels 42120997Skarels /* check message type, catch our node address */ 42220997Skarels if ((v->vh_type & 0xff) == RING_DIAGNOSTICS) { 42320997Skarels if (shost == -1) { 42420997Skarels shost = v->vh_shost & 0xff; 42520997Skarels /* send to ourself now */ 42620997Skarels ((struct vv_header *) 42720997Skarels (vs->vs_ifuba.ifu_r.ifrw_addr)) 42820997Skarels ->vh_dhost = shost; 42920997Skarels } 43020997Skarels successes++; 43120997Skarels v->vh_type = 0; /* clear to check again */ 43220997Skarels } 43311192Ssam } 43420997Skarels 43520997Skarels if (failures >= VVIDENTRETRY) 43620997Skarels { 43720997Skarels printf("vv%d: failed self-test after %d tries \ 43820997Skarels in %s mode\n", 43920997Skarels unit, VVIDENTRETRY, i == 0 ? "digital loopback" : 44020997Skarels (i == 1 ? "analog loopback" : "network")); 44120997Skarels printf("vv%d: icsr = %b, ocsr = %b\n", 44220997Skarels unit, 0xffff & addr->vvicsr, VV_IBITS, 44320997Skarels 0xffff & addr->vvocsr, VV_OBITS); 44420997Skarels addr->vvicsr = VV_RST; /* kill the sick board */ 44520997Skarels addr->vvocsr = VV_RST; 44620997Skarels shost = -1; 44720997Skarels goto done; 44820997Skarels } 44911192Ssam } 45020997Skarels 45120997Skarels done: 45220997Skarels /* deallocate mbuf used for send packet (won't be one, anyways) */ 45315794Sleres if (vs->vs_ifuba.ifu_xtofree) { 4547024Ssam m_freem(vs->vs_ifuba.ifu_xtofree); 45515794Sleres vs->vs_ifuba.ifu_xtofree = 0; 45615794Sleres } 45720997Skarels 45820997Skarels return(shost); 4597024Ssam } 4607024Ssam 4617024Ssam /* 4627024Ssam * Start or restart output on interface. 46311192Ssam * If interface is active, this is a retransmit, so just 46411192Ssam * restuff registers and go. 4657024Ssam * If interface is not already active, get another datagram 4667024Ssam * to send off of the interface queue, and map it to the interface 4677024Ssam * before starting the output. 4687024Ssam */ 4697024Ssam vvstart(dev) 4707024Ssam dev_t dev; 4717024Ssam { 47216581Skarels register struct uba_device *ui; 47315764Sleres register struct vv_softc *vs; 4747024Ssam register struct vvreg *addr; 47516581Skarels register struct mbuf *m; 47616581Skarels register int unit, ubainfo, dest, s; 4777024Ssam 47816581Skarels unit = VVUNIT(dev); 47915764Sleres ui = vvinfo[unit]; 48015764Sleres vs = &vv_softc[unit]; 4817024Ssam if (vs->vs_oactive) 4827024Ssam goto restart; 4837024Ssam /* 4847024Ssam * Not already active: dequeue another request 4857024Ssam * and map it to the UNIBUS. If no more requests, 4867024Ssam * just return. 4877024Ssam */ 48815794Sleres s = splimp(); 4897024Ssam IF_DEQUEUE(&vs->vs_if.if_snd, m); 49015794Sleres splx(s); 49111209Ssam if (m == NULL) { 4927024Ssam vs->vs_oactive = 0; 4937024Ssam return; 4947024Ssam } 4957024Ssam dest = mtod(m, struct vv_header *)->vh_dhost; 4967024Ssam vs->vs_olen = if_wubaput(&vs->vs_ifuba, m); 4977024Ssam vs->vs_lastx = dest; 4987024Ssam restart: 4997024Ssam /* 5007024Ssam * Have request mapped to UNIBUS for transmission. 50115794Sleres * Purge any stale data from this BDP, and start the output. 50215794Sleres * 50315794Sleres * Make sure this packet will fit in the interface. 5047024Ssam */ 50520997Skarels if (vs->vs_olen > VVBUFSIZE) { 50620997Skarels printf("vv%d vs_olen: %d > VVBUFSIZE\n", unit, vs->vs_olen); 50711192Ssam panic("vvdriver vs_olen botch"); 50811192Ssam } 50920997Skarels 51020997Skarels vs->vs_if.if_timer = VVTIMEOUT; 51120997Skarels vs->vs_oactive = 1; 51220997Skarels 51320997Skarels /* ship it */ 5147024Ssam if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 5157024Ssam UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp); 5167024Ssam addr = (struct vvreg *)ui->ui_addr; 5177024Ssam ubainfo = vs->vs_ifuba.ifu_w.ifrw_info; 5187024Ssam addr->vvoba = (u_short) ubainfo; 5197024Ssam addr->vvoea = (u_short) (ubainfo >> 16); 5207024Ssam addr->vvowc = -((vs->vs_olen + 1) >> 1); 52120997Skarels addr->vvowc = -((vs->vs_olen + 1) >> 1); /* extra byte is garbage */ 52220997Skarels if (addr->vvocsr & VV_NOK) 52320997Skarels vs->vs_init++; /* count ring inits */ 5247024Ssam addr->vvocsr = VV_IEN | VV_CPB | VV_DEN | VV_INR | VV_ENB; 52515794Sleres vs->vs_if.if_timer = VVTIMEOUT; 5267024Ssam vs->vs_oactive = 1; 5277024Ssam } 5287024Ssam 5297024Ssam /* 53020997Skarels * proNET transmit interrupt 5317024Ssam * Start another output if more data to send. 5327024Ssam */ 5337024Ssam vvxint(unit) 5347024Ssam int unit; 5357024Ssam { 53615764Sleres register struct uba_device *ui; 53715764Sleres register struct vv_softc *vs; 5387024Ssam register struct vvreg *addr; 5397024Ssam register int oc; 5407024Ssam 54115764Sleres ui = vvinfo[unit]; 54215764Sleres vs = &vv_softc[unit]; 54315794Sleres vs->vs_if.if_timer = 0; 5447024Ssam addr = (struct vvreg *)ui->ui_addr; 5457024Ssam oc = 0xffff & (addr->vvocsr); 5467024Ssam if (vs->vs_oactive == 0) { 54720997Skarels vvprintf("vv%d: stray interrupt vvocsr = %b\n", unit, 54815764Sleres oc, VV_OBITS); 5497024Ssam return; 5507024Ssam } 55120997Skarels 55220997Skarels /* 55320997Skarels * we retransmit on soft error 55420997Skarels * TODO: sort retransmits to end of queue if possible! 55520997Skarels */ 55620997Skarels if (oc & (VV_OPT | VV_RFS)) { 55711192Ssam if (vs->vs_tries++ < VVRETRY) { 5587024Ssam if (oc & VV_OPT) 55920997Skarels vs->vs_otimeout++; 56020997Skarels if (oc & VV_RFS) { 56120997Skarels vs->vs_if.if_collisions++; 56220997Skarels vs->vs_refused++; 56320997Skarels } 56411192Ssam vvstart(unit); /* restart this message */ 5657024Ssam return; 5667024Ssam } 5677024Ssam } 5687024Ssam vs->vs_if.if_opackets++; 5697024Ssam vs->vs_oactive = 0; 5707024Ssam vs->vs_tries = 0; 57120997Skarels 5727024Ssam if (oc & VVXERR) { 5737024Ssam vs->vs_if.if_oerrors++; 57420997Skarels vvprintf("vv%d: error vvocsr = %b\n", unit, 0xffff & oc, 57515764Sleres VV_OBITS); 5767024Ssam } 5777024Ssam if (vs->vs_ifuba.ifu_xtofree) { 5787024Ssam m_freem(vs->vs_ifuba.ifu_xtofree); 5797024Ssam vs->vs_ifuba.ifu_xtofree = 0; 5807024Ssam } 5817024Ssam vvstart(unit); 5827024Ssam } 5837024Ssam 5847024Ssam /* 58515794Sleres * Transmit watchdog timer routine. 58615794Sleres * This routine gets called when we lose a transmit interrupt. 58715794Sleres * The best we can do is try to restart output. 58815794Sleres */ 58915794Sleres vvwatchdog(unit) 59015794Sleres int unit; 59115794Sleres { 59215794Sleres register struct vv_softc *vs; 59315794Sleres register int s; 59415794Sleres 59515794Sleres vs = &vv_softc[unit]; 59620997Skarels vvprintf("vv%d: lost a transmit interrupt.\n", unit); 59715794Sleres vs->vs_timeouts++; 59815794Sleres s = splimp(); 59915794Sleres vvstart(unit); 60015794Sleres splx(s); 60115794Sleres } 60215794Sleres 60315794Sleres /* 60420997Skarels * proNET interface receiver interrupt. 6057024Ssam * If input error just drop packet. 60615764Sleres * Otherwise purge input buffered data path and examine 6077024Ssam * packet to determine type. If can't determine length 60815764Sleres * from type, then have to drop packet. Otherwise decapsulate 6097024Ssam * packet based on type and pass to type specific higher-level 6107024Ssam * input routine. 6117024Ssam */ 6127024Ssam vvrint(unit) 6137024Ssam int unit; 6147024Ssam { 61515764Sleres register struct vv_softc *vs; 61616581Skarels register struct vvreg *addr; 6177024Ssam register struct vv_header *vv; 6187024Ssam register struct ifqueue *inq; 61916581Skarels register struct mbuf *m; 62015794Sleres int ubainfo, len, off, s; 6217640Ssam short resid; 6227024Ssam 62315764Sleres vs = &vv_softc[unit]; 62416581Skarels vs->vs_if.if_ipackets++; 62515764Sleres addr = (struct vvreg *)vvinfo[unit]->ui_addr; 62620997Skarels 6277024Ssam /* 6287024Ssam * Purge BDP; drop if input error indicated. 6297024Ssam */ 6307024Ssam if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 6317024Ssam UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_r.ifrw_bdp); 63220997Skarels 63320997Skarels /* 63420997Skarels * receive errors? 63520997Skarels */ 6367024Ssam if (addr->vvicsr & VVRERR) { 63720997Skarels vvprintf("vv%d: receive error, vvicsr = %b\n", unit, 63820997Skarels 0xffff&(addr->vvicsr), VV_IBITS); 63920997Skarels if (addr->vvicsr & VV_BDF) 64020997Skarels vs->vs_ibadf++; 6417640Ssam goto dropit; 6427024Ssam } 6437640Ssam 6447024Ssam /* 64520997Skarels * parity errors? 64620997Skarels */ 64720997Skarels if (addr->vvicsr & VV_LDE) { 64820997Skarels /* we don't have to clear it because the receive command */ 64920997Skarels /* writes 0 to parity bit */ 65020997Skarels vs->vs_parity++; 65120997Skarels #ifndef PRONET80 65220997Skarels /* 65320997Skarels * only on 10 megabit proNET is VV_LDE an end-to-end parity 65420997Skarels * bit. On 80 megabit, it returns to the intended use of 65520997Skarels * node-to-node parity. End-to-end parity errors on 80 megabit 65620997Skarels * give VV_BDF. 65720997Skarels */ 65820997Skarels goto dropit; 65920997Skarels #endif 66020997Skarels } 66120997Skarels 66220997Skarels /* 66320997Skarels * Get packet length from residual word count 6647640Ssam * 6657640Ssam * Compute header offset if trailer protocol 6667640Ssam * 6677640Ssam * Pull packet off interface. Off is nonzero if packet 6687640Ssam * has trailing header; if_rubaget will then force this header 6697640Ssam * information to be at the front. The vh_info field 6707640Ssam * carries the offset to the trailer data in trailer 6717640Ssam * format packets. 6727024Ssam */ 6737640Ssam vv = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr); 67411192Ssam vvtracehdr("vi", vv); 67520997Skarels resid = addr->vviwc & 01777; /* only low 10 bits valid */ 6767640Ssam if (resid) 67720997Skarels resid |= 0176000; /* high 6 bits are undefined */ 67820997Skarels len = ((VVBUFSIZE >> 1) + resid) << 1; 6797640Ssam len -= sizeof(struct vv_header); 68020997Skarels 68120997Skarels if ((addr->vvicsr & VV_DPR) || len > VVMRU || len <= 0) { 68220997Skarels vvprintf("vv%d: len too long or short, \ 68320997Skarels len = %d, vvicsr = %b\n", 68415794Sleres unit, len, 0xffff&(addr->vvicsr), VV_IBITS); 6857640Ssam goto dropit; 68615794Sleres } 68720997Skarels 68820997Skarels /* check the protocol header version */ 68920997Skarels if (vv->vh_version != RING_VERSION) { 69020997Skarels vvprintf("vv%d: bad protocol header version %d\n", 69120997Skarels unit, vv->vh_version & 0xff); 69220997Skarels goto dropit; 69320997Skarels } 69420997Skarels 6957640Ssam #define vvdataaddr(vv, off, type) ((type)(((caddr_t)((vv)+1)+(off)))) 69613057Ssam if (vv->vh_type >= RING_IPTrailer && 69713057Ssam vv->vh_type < RING_IPTrailer+RING_IPNTrailer) { 6987640Ssam off = (vv->vh_type - RING_IPTrailer) * 512; 69915794Sleres if (off > VVMTU) { 70020997Skarels vvprintf("vv%d: off > VVMTU, off = %d, vvicsr = %b\n", 70115794Sleres unit, off, 0xffff&(addr->vvicsr), VV_IBITS); 7027640Ssam goto dropit; 70315794Sleres } 7047640Ssam vv->vh_type = *vvdataaddr(vv, off, u_short *); 7057640Ssam resid = *(vvdataaddr(vv, off+2, u_short *)); 70615794Sleres if (off + resid > len) { 70720997Skarels vvprintf("vv%d: trailer packet too short\n", unit); 70820997Skarels vvprintf("vv%d: off = %d, resid = %d, vvicsr = %b\n", 70915794Sleres unit, off, resid, 71015794Sleres 0xffff&(addr->vvicsr), VV_IBITS); 7117640Ssam goto dropit; 71215794Sleres } 7137640Ssam len = off + resid; 71411209Ssam } else 7157640Ssam off = 0; 71620997Skarels 71715794Sleres if (len == 0) { 71820997Skarels vvprintf("vv%d: len is zero, vvicsr = %b\n", unit, 71915794Sleres 0xffff&(addr->vvicsr), VV_IBITS); 7207640Ssam goto dropit; 72115794Sleres } 72220997Skarels 7237640Ssam m = if_rubaget(&vs->vs_ifuba, len, off); 72415794Sleres if (m == NULL) { 72520997Skarels vvprintf("vv%d: if_rubaget() failed, vvicsr = %b\n", unit, 72615794Sleres 0xffff&(addr->vvicsr), VV_IBITS); 7277640Ssam goto dropit; 72815794Sleres } 7297640Ssam if (off) { 7307640Ssam m->m_off += 2 * sizeof(u_short); 7317640Ssam m->m_len -= 2 * sizeof(u_short); 7327640Ssam } 73311192Ssam 73415794Sleres /* Keep track of source address of this packet */ 73515794Sleres vs->vs_lastr = vv->vh_shost; 73620997Skarels 73711192Ssam /* 73815764Sleres * Demultiplex on packet type 73911192Ssam */ 7407024Ssam switch (vv->vh_type) { 74111192Ssam 7427024Ssam #ifdef INET 7437024Ssam case RING_IP: 7447024Ssam schednetisr(NETISR_IP); 7457024Ssam inq = &ipintrq; 7467024Ssam break; 7477024Ssam #endif 7487024Ssam default: 74920997Skarels vvprintf("vv%d: unknown pkt type 0x%x\n", unit, vv->vh_type); 7507640Ssam m_freem(m); 7517024Ssam goto setup; 7527024Ssam } 75315794Sleres s = splimp(); 7547640Ssam if (IF_QFULL(inq)) { 7557640Ssam IF_DROP(inq); 7567640Ssam m_freem(m); 75711209Ssam } else 7587640Ssam IF_ENQUEUE(inq, m); 75915764Sleres 76015794Sleres splx(s); 7617024Ssam /* 76215764Sleres * Reset for the next packet. 7637024Ssam */ 76415764Sleres setup: 76515764Sleres ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; 76615764Sleres addr->vviba = (u_short) ubainfo; 76715764Sleres addr->vviea = (u_short) (ubainfo >> 16); 76820997Skarels addr->vviwc = -(VVBUFSIZE) >> 1; 76920997Skarels addr->vvicsr = VV_HEN | VV_IEN | VV_DEN | VV_ENB; 77015764Sleres return; 77111192Ssam 77211192Ssam /* 77311209Ssam * Drop packet on floor -- count them!! 77411192Ssam */ 7757640Ssam dropit: 7767640Ssam vs->vs_if.if_ierrors++; 7777640Ssam goto setup; 7787024Ssam } 7797024Ssam 7807024Ssam /* 78120997Skarels * proNET output routine. 7827024Ssam * Encapsulate a packet of type family for the local net. 7837024Ssam * Use trailer local net encapsulation if enough data in first 7847024Ssam * packet leaves a multiple of 512 bytes of data in remainder. 7857024Ssam */ 7867024Ssam vvoutput(ifp, m0, dst) 7877024Ssam struct ifnet *ifp; 7887024Ssam struct mbuf *m0; 7897024Ssam struct sockaddr *dst; 7907024Ssam { 79116581Skarels register struct mbuf *m; 7927024Ssam register struct vv_header *vv; 7937640Ssam register int off; 79416581Skarels register int unit; 79516581Skarels register struct vvreg *addr; 79616581Skarels register struct vv_softc *vs; 79716581Skarels register int s; 79816581Skarels int type, dest, error; 7997024Ssam 80016581Skarels m = m0; 80116581Skarels unit = ifp->if_unit; 80216581Skarels addr = (struct vvreg *)vvinfo[unit]->ui_addr; 80316581Skarels vs = &vv_softc[unit]; 80420997Skarels 80516581Skarels /* 80620997Skarels * Check to see if the input side has wedged due the UBA 80720997Skarels * vectoring through 0. 80816581Skarels * 80916581Skarels * We are lower than device ipl when we enter this routine, 81016581Skarels * so if the interface is ready with an input packet then 81116581Skarels * an input interrupt must have slipped through the cracks. 81216581Skarels * 81316581Skarels * Avoid the race with an input interrupt by watching to see 81416581Skarels * if any packets come in. 81516581Skarels */ 81616581Skarels s = vs->vs_if.if_ipackets; 81716581Skarels if (addr->vvicsr & VV_RDY && s == vs->vs_if.if_ipackets) { 81820997Skarels vvprintf("vv%d: lost a receive interrupt, icsr = %b\n", 81916581Skarels unit, 0xffff&(addr->vvicsr), VV_IBITS); 82016581Skarels s = splimp(); 82116581Skarels vvrint(unit); 82216581Skarels splx(s); 82316581Skarels } 82416581Skarels 8257024Ssam switch (dst->sa_family) { 82611192Ssam 8277024Ssam #ifdef INET 82815764Sleres case AF_INET: 829*21779Skarels if (in_broadcast(((struct sockaddr_in *)dst)->sin_addr)) 830*21779Skarels dest = VV_BROADCAST; 831*21779Skarels else 832*21779Skarels dest = in_lnaof(((struct sockaddr_in *)dst)->sin_addr); 83320997Skarels #ifdef LOOPBACK 834*21779Skarels if (dest == vs->vs_host && (loif.if_flags & IFF_UP)) 835*21779Skarels return (looutput(&loif, m0, dst)); 83620997Skarels #endif LOOPBACK 837*21779Skarels if (dest >= 0x100) { 8387640Ssam error = EPERM; 8397640Ssam goto bad; 8407640Ssam } 8417640Ssam off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; 84220997Skarels /* 84320997Skarels * Trailerize, if the configuration allows it. 84420997Skarels * TODO: Need per host negotiation. 84520997Skarels */ 84613090Ssam if ((ifp->if_flags & IFF_NOTRAILERS) == 0) 84713090Ssam if (off > 0 && (off & 0x1ff) == 0 && 8487640Ssam m->m_off >= MMINOFF + 2 * sizeof (u_short)) { 8497640Ssam type = RING_IPTrailer + (off>>9); 8507640Ssam m->m_off -= 2 * sizeof (u_short); 8517640Ssam m->m_len += 2 * sizeof (u_short); 8527640Ssam *mtod(m, u_short *) = RING_IP; 8537640Ssam *(mtod(m, u_short *) + 1) = m->m_len; 8547640Ssam goto gottrailertype; 8557640Ssam } 8567024Ssam type = RING_IP; 8577024Ssam off = 0; 8587024Ssam goto gottype; 8597024Ssam #endif 8607024Ssam default: 86116581Skarels printf("vv%d: can't handle af%d\n", unit, dst->sa_family); 8627640Ssam error = EAFNOSUPPORT; 8637640Ssam goto bad; 8647024Ssam } 8657024Ssam 8667024Ssam gottrailertype: 8677024Ssam /* 8687024Ssam * Packet to be sent as trailer: move first packet 8697024Ssam * (control information) to end of chain. 8707024Ssam */ 8717024Ssam while (m->m_next) 8727024Ssam m = m->m_next; 8737024Ssam m->m_next = m0; 8747024Ssam m = m0->m_next; 8757024Ssam m0->m_next = 0; 8767024Ssam m0 = m; 8777024Ssam gottype: 8787024Ssam /* 8797024Ssam * Add local net header. If no space in first mbuf, 8807024Ssam * allocate another. 8817024Ssam */ 8827024Ssam if (m->m_off > MMAXOFF || 8837024Ssam MMINOFF + sizeof (struct vv_header) > m->m_off) { 88411209Ssam m = m_get(M_DONTWAIT, MT_HEADER); 88511209Ssam if (m == NULL) { 8867640Ssam error = ENOBUFS; 8877640Ssam goto bad; 8887024Ssam } 8897024Ssam m->m_next = m0; 8907024Ssam m->m_off = MMINOFF; 8917024Ssam m->m_len = sizeof (struct vv_header); 8927024Ssam } else { 8937024Ssam m->m_off -= sizeof (struct vv_header); 8947024Ssam m->m_len += sizeof (struct vv_header); 8957024Ssam } 8967024Ssam vv = mtod(m, struct vv_header *); 897*21779Skarels vv->vh_shost = vs->vs_host; 898*21779Skarels vv->vh_dhost = dest; 8997024Ssam vv->vh_version = RING_VERSION; 9007024Ssam vv->vh_type = type; 9017640Ssam vv->vh_info = off; 90211192Ssam vvtracehdr("vo", vv); 9037024Ssam 9047024Ssam /* 9057024Ssam * Queue message on interface, and start output if interface 9067024Ssam * not yet active. 9077024Ssam */ 9087024Ssam s = splimp(); 9097640Ssam if (IF_QFULL(&ifp->if_snd)) { 9107640Ssam IF_DROP(&ifp->if_snd); 9117640Ssam error = ENOBUFS; 9127640Ssam goto qfull; 9137640Ssam } 9147024Ssam IF_ENQUEUE(&ifp->if_snd, m); 91516581Skarels if (vs->vs_oactive == 0) 91616581Skarels vvstart(unit); 9177024Ssam splx(s); 9187640Ssam return (0); 9197640Ssam qfull: 9207640Ssam m0 = m; 9217640Ssam splx(s); 9227640Ssam bad: 9237640Ssam m_freem(m0); 9247640Ssam return(error); 9257024Ssam } 9267024Ssam 9277024Ssam /* 92813057Ssam * Process an ioctl request. 92913057Ssam */ 93013057Ssam vvioctl(ifp, cmd, data) 93113057Ssam register struct ifnet *ifp; 93213057Ssam int cmd; 93313057Ssam caddr_t data; 93413057Ssam { 935*21779Skarels struct ifaddr *ifa = (struct ifaddr *) data; 936*21779Skarels int s = splimp(), error = 0; 93713057Ssam 93813057Ssam switch (cmd) { 93913057Ssam 94013057Ssam case SIOCSIFADDR: 941*21779Skarels if ((ifp->if_flags & IFF_RUNNING) == 0) 94213057Ssam vvinit(ifp->if_unit); 943*21779Skarels /* 944*21779Skarels * Attempt to check agreement of protocol address 945*21779Skarels * and board address. 946*21779Skarels */ 947*21779Skarels switch (ifa->ifa_addr.sa_family) { 948*21779Skarels case AF_INET: 949*21779Skarels if (in_lnaof(IA_SIN(ifa)->sin_addr) != 950*21779Skarels vv_softc[ifp->if_unit].vs_host) 951*21779Skarels return (EADDRNOTAVAIL); 952*21779Skarels break; 953*21779Skarels } 954*21779Skarels ifp->if_flags |= IFF_UP; 95513057Ssam break; 95613057Ssam 95713057Ssam default: 95813057Ssam error = EINVAL; 95913057Ssam } 96013057Ssam splx(s); 961*21779Skarels return (error); 96213057Ssam } 963