1*23305Smckusick /* 2*23305Smckusick * Copyright (c) 1982 Regents of the University of California. 3*23305Smckusick * All rights reserved. The Berkeley software License Agreement 4*23305Smckusick * specifies the terms and conditions for redistribution. 5*23305Smckusick * 6*23305Smckusick * @(#)if_vv.c 6.9 (Berkeley) 06/08/85 7*23305Smckusick */ 87024Ssam 99799Ssam #include "vv.h" 1011192Ssam 117024Ssam /* 1220997Skarels * Proteon proNET-10 and proNET-80 token ring driver. 1320997Skarels * The name of this device driver derives from the old MIT 1420997Skarels * name of V2LNI for the proNET hardware, would would abbreviate 1520997Skarels * to "v2", but this won't work right. Thus the name is "vv". 1611192Ssam * 1720997Skarels * This driver is compatible with the proNET 10 meagbit and 1820997Skarels * 80 megabit token ring interfaces (models p1000 and p1080). 1920997Skarels * 2020997Skarels * TRAILERS: You must turn off trailers via ifconfig if you want to share 2120997Skarels * a ring with software using the following protocol types: 2220997Skarels * 3: Address Resolution Protocol 2320997Skarels * 4: HDLC (old Proteon drivers) 2420997Skarels * 5: VAX Debugging Protocol (never used) 2520997Skarels * This is because the protocol type values chosen for trailers 2620997Skarels * conflict with these protocols. It's too late to change either now. 2720997Skarels * 2820997Skarels * HARDWARE COMPATABILITY: This driver requires that the HSBU (p1001) 2920997Skarels * have a serial number >= 040, which is about March, 1982. Older 3020997Skarels * HSBUs do not carry across 64kbyte boundaries. The old warning 3120997Skarels * about use without Wire Centers applies only to CTL (p1002) cards with 3220997Skarels * serial <= 057, which have not received ECO 176-743, which was 3320997Skarels * implemented in March, 1982. Most such CTLs have received this ECO, 3420997Skarels * but they are only compatible with the old HSBUs (<=039) anyways. 357024Ssam */ 3611209Ssam #include "../machine/pte.h" 379799Ssam 3817117Sbloom #include "param.h" 3917117Sbloom #include "systm.h" 4017117Sbloom #include "mbuf.h" 4117117Sbloom #include "buf.h" 4217117Sbloom #include "protosw.h" 4317117Sbloom #include "socket.h" 4417117Sbloom #include "vmmac.h" 4517117Sbloom #include "errno.h" 4617117Sbloom #include "ioctl.h" 478465Sroot 488465Sroot #include "../net/if.h" 4911209Ssam #include "../net/netisr.h" 508465Sroot #include "../net/route.h" 518421Swnj #include "../netinet/in.h" 528421Swnj #include "../netinet/in_systm.h" 5321779Skarels #include "../netinet/in_var.h" 548421Swnj #include "../netinet/ip.h" 558421Swnj #include "../netinet/ip_var.h" 568465Sroot 5715794Sleres #include "../vax/cpu.h" 5811209Ssam #include "../vax/mtpr.h" 5917117Sbloom #include "if_vv.h" 6017117Sbloom #include "if_uba.h" 618465Sroot #include "../vaxuba/ubareg.h" 628465Sroot #include "../vaxuba/ubavar.h" 637024Ssam 647024Ssam /* 6520997Skarels * 80 megabit configuration 6620997Skarels * Uncomment the next line if you are using the 80 megabit system. The 6720997Skarels * only change is the disposition of packets with parity/link_data_error 6820997Skarels * indication. 697024Ssam */ 7020997Skarels /* #define PRONET80 */ 717024Ssam 7220997Skarels /* 7320997Skarels * maximum transmission unit definition -- 7420997Skarels * you can set VVMTU at anything from 576 to 2024. 7520997Skarels * 1536 is a popular "large" value, because it is a multiple 7620997Skarels * of 512, which the trailer scheme likes. 7720997Skarels * The absolute maximum size is 2024, which is enforced. 7820997Skarels */ 7920997Skarels 8020997Skarels #define VVMTU (1024) 8120997Skarels 8220997Skarels #define VVMRU (VVMTU + 16) 8320997Skarels #define VVBUFSIZE (VVMRU + sizeof(struct vv_header)) 8420997Skarels #if VVMTU>2024 8520997Skarels #undef VVMTU 8620997Skarels #undef VVMRU 8720997Skarels #undef VVBUFSIZE 8820997Skarels #define VVBUFSIZE (2046) 8920997Skarels #define VVMRU (VVBUFSIZE - sizeof (struct vv_header)) 9020997Skarels #define VVMTU (VVMRU - 16) 917024Ssam #endif 927024Ssam 9320997Skarels /* 9420997Skarels * debugging and tracing stuff 9520997Skarels */ 9616581Skarels int vv_tracehdr = 0; /* 1 => trace headers (slowly!!) */ 977640Ssam 9820997Skarels #define vvtracehdr if (vv_tracehdr) vvprt_hdr 9921779Skarels #define vvprintf if (vs->vs_if.if_flags & IFF_DEBUG) printf 10011192Ssam 10120997Skarels /* 10220997Skarels * externals, types, etc. 10320997Skarels */ 10416581Skarels int vvprobe(), vvattach(), vvreset(), vvinit(); 10516581Skarels int vvidentify(), vvstart(), vvxint(), vvwatchdog(); 10616581Skarels int vvrint(), vvoutput(), vvioctl(), vvsetaddr(); 1077024Ssam struct uba_device *vvinfo[NVV]; 1087024Ssam u_short vvstd[] = { 0 }; 1097024Ssam struct uba_driver vvdriver = 1107024Ssam { vvprobe, 0, vvattach, 0, vvstd, "vv", vvinfo }; 1117024Ssam #define VVUNIT(x) minor(x) 1127024Ssam 11320997Skarels #define LOOPBACK /* use loopback for packets meant for us */ 11420997Skarels #ifdef LOOPBACK 11520997Skarels extern struct ifnet loif; 11620997Skarels #endif 11720997Skarels 1187024Ssam /* 1197024Ssam * Software status of each interface. 1207024Ssam * 1217024Ssam * Each interface is referenced by a network interface structure, 1227024Ssam * vs_if, which the routing code uses to locate the interface. 1237024Ssam * This structure contains the output queue for the interface, its address, ... 1247024Ssam * We also have, for each interface, a UBA interface structure, which 1257024Ssam * contains information about the UNIBUS resources held by the interface: 1267024Ssam * map registers, buffered data paths, etc. Information is cached in this 1277024Ssam * structure for use by the if_uba.c routines in running the interface 1287024Ssam * efficiently. 1297024Ssam */ 1307024Ssam struct vv_softc { 1317024Ssam struct ifnet vs_if; /* network-visible interface */ 1327024Ssam struct ifuba vs_ifuba; /* UNIBUS resources */ 13321779Skarels int vs_host; 13411192Ssam short vs_oactive; /* is output active */ 1357024Ssam short vs_olen; /* length of last output */ 13615794Sleres u_short vs_lastx; /* address of last packet sent */ 13715794Sleres u_short vs_lastr; /* address of last packet received */ 13811192Ssam short vs_tries; /* transmit current retry count */ 1397024Ssam short vs_init; /* number of ring inits */ 14020997Skarels short vs_refused; /* number of packets refused */ 14115794Sleres short vs_timeouts; /* number of transmit timeouts */ 14220997Skarels short vs_otimeout; /* number of output timeouts */ 14320997Skarels short vs_ibadf; /* number of input bad formats */ 14420997Skarels short vs_parity; /* number of parity errors on 10 meg, */ 14520997Skarels /* link data errors on 80 meg */ 1467024Ssam } vv_softc[NVV]; 1477024Ssam 14820997Skarels /* 14920997Skarels * probe the interface to see that the registers exist, and then 15020997Skarels * cause an interrupt to find its vector 15120997Skarels */ 1527024Ssam vvprobe(reg) 1537024Ssam caddr_t reg; 1547024Ssam { 1557024Ssam register int br, cvec; 15616581Skarels register struct vvreg *addr; 1577024Ssam 1587024Ssam #ifdef lint 15915764Sleres br = 0; cvec = br; br = cvec; 1607024Ssam #endif 16116581Skarels addr = (struct vvreg *)reg; 16220997Skarels 1637024Ssam /* reset interface, enable, and wait till dust settles */ 1647024Ssam addr->vvicsr = VV_RST; 1657024Ssam addr->vvocsr = VV_RST; 16615764Sleres DELAY(100000); 16720997Skarels 1687024Ssam /* generate interrupt by doing 1 word DMA from 0 in uba space!! */ 1697024Ssam addr->vvoba = 0; /* low 16 bits */ 1707024Ssam addr->vvoea = 0; /* extended bits */ 1717024Ssam addr->vvowc = -1; /* for 1 word */ 17220997Skarels addr->vvocsr = VV_IEN | VV_DEN; /* start the DMA, with interrupt */ 1737024Ssam DELAY(100000); 17420997Skarels addr->vvocsr = VV_RST; /* clear out the CSR */ 1757024Ssam if (cvec && cvec != 0x200) 17615764Sleres cvec -= 4; /* backup so vector => receive */ 1777024Ssam return(1); 1787024Ssam } 1797024Ssam 1807024Ssam /* 1817024Ssam * Interface exists: make available by filling in network interface 1827024Ssam * record. System will initialize the interface when it is ready 1837024Ssam * to accept packets. 1847024Ssam */ 1857024Ssam vvattach(ui) 1867024Ssam struct uba_device *ui; 1877024Ssam { 18815764Sleres register struct vv_softc *vs; 1897024Ssam 19015764Sleres vs = &vv_softc[ui->ui_unit]; 1917024Ssam vs->vs_if.if_unit = ui->ui_unit; 1927024Ssam vs->vs_if.if_name = "vv"; 1937024Ssam vs->vs_if.if_mtu = VVMTU; 19421779Skarels vs->vs_if.if_flags = IFF_BROADCAST; 1957024Ssam vs->vs_if.if_init = vvinit; 19613057Ssam vs->vs_if.if_ioctl = vvioctl; 1977024Ssam vs->vs_if.if_output = vvoutput; 19811209Ssam vs->vs_if.if_reset = vvreset; 19915794Sleres vs->vs_if.if_timer = 0; 20015794Sleres vs->vs_if.if_watchdog = vvwatchdog; 2017640Ssam vs->vs_ifuba.ifu_flags = UBA_CANTWAIT | UBA_NEEDBDP | UBA_NEED16; 20212354Smo #if defined(VAX750) 20312354Smo /* don't chew up 750 bdp's */ 20412354Smo if (cpu == VAX_750 && ui->ui_unit > 0) 20512354Smo vs->vs_ifuba.ifu_flags &= ~UBA_NEEDBDP; 20612354Smo #endif 2077024Ssam if_attach(&vs->vs_if); 2087024Ssam } 2097024Ssam 2107024Ssam /* 2117024Ssam * Reset of interface after UNIBUS reset. 2127024Ssam * If interface is on specified uba, reset its state. 2137024Ssam */ 2147024Ssam vvreset(unit, uban) 2157024Ssam int unit, uban; 2167024Ssam { 2177024Ssam register struct uba_device *ui; 2187024Ssam 2197024Ssam if (unit >= NVV || (ui = vvinfo[unit]) == 0 || ui->ui_alive == 0 || 2207024Ssam ui->ui_ubanum != uban) 2217024Ssam return; 2227024Ssam printf(" vv%d", unit); 2237024Ssam vvinit(unit); 2247024Ssam } 2257024Ssam 2267024Ssam /* 2277024Ssam * Initialization of interface; clear recorded pending 2287024Ssam * operations, and reinitialize UNIBUS usage. 2297024Ssam */ 2307024Ssam vvinit(unit) 2317024Ssam int unit; 2327024Ssam { 23315764Sleres register struct vv_softc *vs; 23415764Sleres register struct uba_device *ui; 2357024Ssam register struct vvreg *addr; 23616581Skarels register int ubainfo, s; 2377024Ssam 23815764Sleres vs = &vv_softc[unit]; 23915764Sleres ui = vvinfo[unit]; 24020997Skarels 24121779Skarels if (vs->vs_if.if_addrlist == (struct ifaddr *)0) 24213057Ssam return; 24320997Skarels 2447640Ssam addr = (struct vvreg *)ui->ui_addr; 2457024Ssam if (if_ubainit(&vs->vs_ifuba, ui->ui_ubanum, 24615764Sleres sizeof (struct vv_header), (int)btoc(VVMTU)) == 0) { 24715794Sleres printf("vv%d: can't initialize, if_ubainit() failed\n", unit); 2487640Ssam vs->vs_if.if_flags &= ~IFF_UP; 2497024Ssam return; 2507024Ssam } 25120997Skarels 2527024Ssam /* 25315764Sleres * Now that the uba is set up, figure out our address and 25415764Sleres * update complete our host address. 2557640Ssam */ 25621779Skarels if ((vs->vs_host = vvidentify(unit)) == -1) { 25715794Sleres vs->vs_if.if_flags &= ~IFF_UP; 25815794Sleres return; 25915794Sleres } 26021779Skarels printf("vv%d: host %d\n", unit, vs->vs_host); 26120997Skarels 2627640Ssam /* 26320997Skarels * Reset the interface, and stay in the ring 2647640Ssam */ 26520997Skarels addr->vvocsr = VV_RST; /* take over output */ 26620997Skarels addr->vvocsr = VV_CPB; /* clear packet buffer */ 26720997Skarels addr->vvicsr = VV_RST | VV_HEN; /* take over input, */ 26820997Skarels /* keep relay closed */ 26912351Smo DELAY(500000); /* let contacts settle */ 27020997Skarels 27120997Skarels vs->vs_init = 0; /* clear counters, etc. */ 27220997Skarels vs->vs_refused = 0; 27315794Sleres vs->vs_timeouts = 0; 27420997Skarels vs->vs_otimeout = 0; 27520997Skarels vs->vs_ibadf = 0; 27620997Skarels vs->vs_parity = 0; 27715794Sleres vs->vs_lastx = 256; /* an invalid address */ 27815794Sleres vs->vs_lastr = 256; /* an invalid address */ 27920997Skarels 2807640Ssam /* 2817640Ssam * Hang a receive and start any 2827640Ssam * pending writes by faking a transmit complete. 2837640Ssam */ 2847640Ssam s = splimp(); 2857640Ssam ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; 28613057Ssam addr->vviba = (u_short)ubainfo; 28713057Ssam addr->vviea = (u_short)(ubainfo >> 16); 28820997Skarels addr->vviwc = -(VVBUFSIZE) >> 1; 28920997Skarels addr->vvicsr = VV_IEN | VV_HEN | VV_DEN | VV_ENB; 2907640Ssam vs->vs_oactive = 1; 29121779Skarels vs->vs_if.if_flags |= IFF_RUNNING; 2927640Ssam vvxint(unit); 2937640Ssam splx(s); 2947640Ssam } 2957640Ssam 2967640Ssam /* 29720997Skarels * Do a moderately thorough self-test in all three modes. Mostly 29820997Skarels * to keeps defective nodes off the ring, rather than to be especially 29920997Skarels * thorough. The key issue is to detect any cable breaks before joining 30020997Skarels * the ring. Return our node address on success, return -1 on failure. 30120997Skarels * 3027640Ssam */ 30320997Skarels 30420997Skarels /* the three self-test modes */ 30520997Skarels static u_short vv_modes[] = { 30620997Skarels VV_STE|VV_LPB, /* digital loopback */ 30720997Skarels VV_STE, /* analog loopback */ 30820997Skarels VV_HEN /* network mode */ 30920997Skarels }; 31020997Skarels 31111209Ssam vvidentify(unit) 31213057Ssam int unit; 31311209Ssam { 31415764Sleres register struct vv_softc *vs; 31515764Sleres register struct uba_device *ui; 3167640Ssam register struct vvreg *addr; 31716581Skarels register struct mbuf *m; 31816581Skarels register struct vv_header *v; 31920997Skarels register int ubainfo; 32020997Skarels register int i, successes, failures, waitcount; 32120997Skarels u_short shost = -1; 3227640Ssam 32320997Skarels vs = &vv_softc[unit]; 32420997Skarels ui = vvinfo[unit]; 32520997Skarels addr = (struct vvreg *)ui->ui_addr; 32620997Skarels 3277640Ssam /* 3287024Ssam * Build a multicast message to identify our address 32920997Skarels * We need do this only once, since nobody else is about to use 33020997Skarels * the intermediate transmit buffer (ifu_w.ifrw_addr) that 33120997Skarels * if_ubainit() aquired for us. 3327024Ssam */ 33311209Ssam m = m_get(M_DONTWAIT, MT_HEADER); 33415794Sleres if (m == NULL) { 33515794Sleres printf("vv%d: can't initialize, m_get() failed\n", unit); 33613057Ssam return (0); 33715794Sleres } 33811192Ssam m->m_next = 0; 3397024Ssam m->m_off = MMINOFF; 3407024Ssam m->m_len = sizeof(struct vv_header); 3417024Ssam v = mtod(m, struct vv_header *); 34211192Ssam v->vh_dhost = VV_BROADCAST; /* multicast destination address */ 3437024Ssam v->vh_shost = 0; /* will be overwritten with ours */ 3447024Ssam v->vh_version = RING_VERSION; 34520997Skarels v->vh_type = RING_DIAGNOSTICS; 3467024Ssam v->vh_info = 0; 34720997Skarels /* map xmit message into uba, copying to intermediate buffer */ 34820997Skarels vs->vs_olen = if_wubaput(&vs->vs_ifuba, m); 34920997Skarels 3507024Ssam /* 35120997Skarels * For each of the modes (digital, analog, network), go through 35220997Skarels * a self-test that requires me to send VVIDENTSUCC good packets 35320997Skarels * in VVIDENTRETRY attempts. Use broadcast destination to find out 35420997Skarels * who I am, then use this as my address to check my address match 35520997Skarels * logic. Only data checked is the vh_type field. 3567024Ssam */ 3577640Ssam 35820997Skarels for (i = 0; i < 3; i++) { 35920997Skarels successes = 0; /* clear successes for this mode */ 36020997Skarels failures = 0; /* and clear failures, too */ 3617640Ssam 36220997Skarels /* take over device, and leave ring */ 36320997Skarels addr->vvicsr = VV_RST; 36420997Skarels addr->vvocsr = VV_RST; 36520997Skarels addr->vvicsr = vv_modes[i]; /* test mode */ 36620997Skarels 36720997Skarels /* 36820997Skarels * let the flag and token timers pop so that the init ring bit 36920997Skarels * will be allowed to work, by waiting about 1 second 37020997Skarels */ 37120997Skarels DELAY(1000000L); 37220997Skarels 37320997Skarels /* 37420997Skarels * retry loop 37520997Skarels */ 37620997Skarels while ((successes < VVIDENTSUCC) && (failures < VVIDENTRETRY)) 37720997Skarels { 37820997Skarels /* start a receive */ 37920997Skarels ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; 38020997Skarels addr->vvicsr = VV_RST | vv_modes[i]; /* abort last */ 38120997Skarels addr->vviba = (u_short) ubainfo; 38220997Skarels addr->vviea = (u_short) (ubainfo >> 16); 38320997Skarels addr->vviwc = -(VVBUFSIZE) >> 1; 38420997Skarels addr->vvicsr = vv_modes[i] | VV_DEN | VV_ENB; 38520997Skarels 38620997Skarels /* purge stale data from BDP */ 38720997Skarels if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 38820997Skarels UBAPURGE(vs->vs_ifuba.ifu_uba, 38920997Skarels vs->vs_ifuba.ifu_w.ifrw_bdp); 39020997Skarels 39120997Skarels /* do a transmit */ 39220997Skarels ubainfo = vs->vs_ifuba.ifu_w.ifrw_info; 39320997Skarels addr->vvocsr = VV_RST; /* abort last try */ 39420997Skarels addr->vvoba = (u_short) ubainfo; 39520997Skarels addr->vvoea = (u_short) (ubainfo >> 16); 39620997Skarels addr->vvowc = -((vs->vs_olen + 1) >> 1); 39720997Skarels addr->vvocsr = VV_CPB | VV_DEN | VV_INR | VV_ENB; 39820997Skarels 39920997Skarels /* poll receive side for completion */ 40020997Skarels DELAY(10000); /* give it a chance */ 40120997Skarels for (waitcount = 0; waitcount < 10; waitcount++) { 40220997Skarels if (addr->vvicsr & VV_RDY) 40320997Skarels goto gotit; 40420997Skarels DELAY(1000); 40520997Skarels } 40620997Skarels failures++; /* no luck */ 40711209Ssam continue; 40820997Skarels 40920997Skarels gotit: /* we got something--is it any good? */ 41020997Skarels if ((addr->vvicsr & (VVRERR|VV_LDE)) || 41121779Skarels (addr->vvocsr & (VVXERR|VV_RFS))) { 41220997Skarels failures++; 41320997Skarels continue; 41420997Skarels } 41520997Skarels 41620997Skarels /* Purge BDP before looking at received packet */ 41720997Skarels if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 41820997Skarels UBAPURGE(vs->vs_ifuba.ifu_uba, 41920997Skarels vs->vs_ifuba.ifu_r.ifrw_bdp); 42020997Skarels m = if_rubaget(&vs->vs_ifuba, 42120997Skarels sizeof(struct vv_header), 0); 42220997Skarels if (m != NULL) 42320997Skarels m_freem(m); 42420997Skarels 42520997Skarels v = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr); 42620997Skarels 42720997Skarels /* check message type, catch our node address */ 42820997Skarels if ((v->vh_type & 0xff) == RING_DIAGNOSTICS) { 42920997Skarels if (shost == -1) { 43020997Skarels shost = v->vh_shost & 0xff; 43120997Skarels /* send to ourself now */ 43220997Skarels ((struct vv_header *) 43320997Skarels (vs->vs_ifuba.ifu_r.ifrw_addr)) 43420997Skarels ->vh_dhost = shost; 43520997Skarels } 43620997Skarels successes++; 43720997Skarels v->vh_type = 0; /* clear to check again */ 43820997Skarels } 43911192Ssam } 44020997Skarels 44120997Skarels if (failures >= VVIDENTRETRY) 44220997Skarels { 44320997Skarels printf("vv%d: failed self-test after %d tries \ 44420997Skarels in %s mode\n", 44520997Skarels unit, VVIDENTRETRY, i == 0 ? "digital loopback" : 44620997Skarels (i == 1 ? "analog loopback" : "network")); 44720997Skarels printf("vv%d: icsr = %b, ocsr = %b\n", 44820997Skarels unit, 0xffff & addr->vvicsr, VV_IBITS, 44920997Skarels 0xffff & addr->vvocsr, VV_OBITS); 45020997Skarels addr->vvicsr = VV_RST; /* kill the sick board */ 45120997Skarels addr->vvocsr = VV_RST; 45220997Skarels shost = -1; 45320997Skarels goto done; 45420997Skarels } 45511192Ssam } 45620997Skarels 45720997Skarels done: 45820997Skarels /* deallocate mbuf used for send packet (won't be one, anyways) */ 45915794Sleres if (vs->vs_ifuba.ifu_xtofree) { 4607024Ssam m_freem(vs->vs_ifuba.ifu_xtofree); 46115794Sleres vs->vs_ifuba.ifu_xtofree = 0; 46215794Sleres } 46320997Skarels 46420997Skarels return(shost); 4657024Ssam } 4667024Ssam 4677024Ssam /* 4687024Ssam * Start or restart output on interface. 46911192Ssam * If interface is active, this is a retransmit, so just 47011192Ssam * restuff registers and go. 4717024Ssam * If interface is not already active, get another datagram 4727024Ssam * to send off of the interface queue, and map it to the interface 4737024Ssam * before starting the output. 4747024Ssam */ 4757024Ssam vvstart(dev) 4767024Ssam dev_t dev; 4777024Ssam { 47816581Skarels register struct uba_device *ui; 47915764Sleres register struct vv_softc *vs; 4807024Ssam register struct vvreg *addr; 48116581Skarels register struct mbuf *m; 48216581Skarels register int unit, ubainfo, dest, s; 4837024Ssam 48416581Skarels unit = VVUNIT(dev); 48515764Sleres ui = vvinfo[unit]; 48615764Sleres vs = &vv_softc[unit]; 4877024Ssam if (vs->vs_oactive) 4887024Ssam goto restart; 4897024Ssam /* 4907024Ssam * Not already active: dequeue another request 4917024Ssam * and map it to the UNIBUS. If no more requests, 4927024Ssam * just return. 4937024Ssam */ 49415794Sleres s = splimp(); 4957024Ssam IF_DEQUEUE(&vs->vs_if.if_snd, m); 49615794Sleres splx(s); 49711209Ssam if (m == NULL) { 4987024Ssam vs->vs_oactive = 0; 4997024Ssam return; 5007024Ssam } 5017024Ssam dest = mtod(m, struct vv_header *)->vh_dhost; 5027024Ssam vs->vs_olen = if_wubaput(&vs->vs_ifuba, m); 5037024Ssam vs->vs_lastx = dest; 5047024Ssam restart: 5057024Ssam /* 5067024Ssam * Have request mapped to UNIBUS for transmission. 50715794Sleres * Purge any stale data from this BDP, and start the output. 50815794Sleres * 50915794Sleres * Make sure this packet will fit in the interface. 5107024Ssam */ 51120997Skarels if (vs->vs_olen > VVBUFSIZE) { 51220997Skarels printf("vv%d vs_olen: %d > VVBUFSIZE\n", unit, vs->vs_olen); 51311192Ssam panic("vvdriver vs_olen botch"); 51411192Ssam } 51520997Skarels 51620997Skarels vs->vs_if.if_timer = VVTIMEOUT; 51720997Skarels vs->vs_oactive = 1; 51820997Skarels 51920997Skarels /* ship it */ 5207024Ssam if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 5217024Ssam UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp); 5227024Ssam addr = (struct vvreg *)ui->ui_addr; 5237024Ssam ubainfo = vs->vs_ifuba.ifu_w.ifrw_info; 5247024Ssam addr->vvoba = (u_short) ubainfo; 5257024Ssam addr->vvoea = (u_short) (ubainfo >> 16); 5267024Ssam addr->vvowc = -((vs->vs_olen + 1) >> 1); 52720997Skarels addr->vvowc = -((vs->vs_olen + 1) >> 1); /* extra byte is garbage */ 52820997Skarels if (addr->vvocsr & VV_NOK) 52920997Skarels vs->vs_init++; /* count ring inits */ 5307024Ssam addr->vvocsr = VV_IEN | VV_CPB | VV_DEN | VV_INR | VV_ENB; 53115794Sleres vs->vs_if.if_timer = VVTIMEOUT; 5327024Ssam vs->vs_oactive = 1; 5337024Ssam } 5347024Ssam 5357024Ssam /* 53620997Skarels * proNET transmit interrupt 5377024Ssam * Start another output if more data to send. 5387024Ssam */ 5397024Ssam vvxint(unit) 5407024Ssam int unit; 5417024Ssam { 54215764Sleres register struct uba_device *ui; 54315764Sleres register struct vv_softc *vs; 5447024Ssam register struct vvreg *addr; 5457024Ssam register int oc; 5467024Ssam 54715764Sleres ui = vvinfo[unit]; 54815764Sleres vs = &vv_softc[unit]; 54915794Sleres vs->vs_if.if_timer = 0; 5507024Ssam addr = (struct vvreg *)ui->ui_addr; 5517024Ssam oc = 0xffff & (addr->vvocsr); 5527024Ssam if (vs->vs_oactive == 0) { 55320997Skarels vvprintf("vv%d: stray interrupt vvocsr = %b\n", unit, 55415764Sleres oc, VV_OBITS); 5557024Ssam return; 5567024Ssam } 55720997Skarels 55820997Skarels /* 55920997Skarels * we retransmit on soft error 56020997Skarels * TODO: sort retransmits to end of queue if possible! 56120997Skarels */ 56220997Skarels if (oc & (VV_OPT | VV_RFS)) { 56311192Ssam if (vs->vs_tries++ < VVRETRY) { 5647024Ssam if (oc & VV_OPT) 56520997Skarels vs->vs_otimeout++; 56620997Skarels if (oc & VV_RFS) { 56720997Skarels vs->vs_if.if_collisions++; 56820997Skarels vs->vs_refused++; 56920997Skarels } 57011192Ssam vvstart(unit); /* restart this message */ 5717024Ssam return; 5727024Ssam } 5737024Ssam } 5747024Ssam vs->vs_if.if_opackets++; 5757024Ssam vs->vs_oactive = 0; 5767024Ssam vs->vs_tries = 0; 57720997Skarels 5787024Ssam if (oc & VVXERR) { 5797024Ssam vs->vs_if.if_oerrors++; 58020997Skarels vvprintf("vv%d: error vvocsr = %b\n", unit, 0xffff & oc, 58115764Sleres VV_OBITS); 5827024Ssam } 5837024Ssam if (vs->vs_ifuba.ifu_xtofree) { 5847024Ssam m_freem(vs->vs_ifuba.ifu_xtofree); 5857024Ssam vs->vs_ifuba.ifu_xtofree = 0; 5867024Ssam } 5877024Ssam vvstart(unit); 5887024Ssam } 5897024Ssam 5907024Ssam /* 59115794Sleres * Transmit watchdog timer routine. 59215794Sleres * This routine gets called when we lose a transmit interrupt. 59315794Sleres * The best we can do is try to restart output. 59415794Sleres */ 59515794Sleres vvwatchdog(unit) 59615794Sleres int unit; 59715794Sleres { 59815794Sleres register struct vv_softc *vs; 59915794Sleres register int s; 60015794Sleres 60115794Sleres vs = &vv_softc[unit]; 60220997Skarels vvprintf("vv%d: lost a transmit interrupt.\n", unit); 60315794Sleres vs->vs_timeouts++; 60415794Sleres s = splimp(); 60515794Sleres vvstart(unit); 60615794Sleres splx(s); 60715794Sleres } 60815794Sleres 60915794Sleres /* 61020997Skarels * proNET interface receiver interrupt. 6117024Ssam * If input error just drop packet. 61215764Sleres * Otherwise purge input buffered data path and examine 6137024Ssam * packet to determine type. If can't determine length 61415764Sleres * from type, then have to drop packet. Otherwise decapsulate 6157024Ssam * packet based on type and pass to type specific higher-level 6167024Ssam * input routine. 6177024Ssam */ 6187024Ssam vvrint(unit) 6197024Ssam int unit; 6207024Ssam { 62115764Sleres register struct vv_softc *vs; 62216581Skarels register struct vvreg *addr; 6237024Ssam register struct vv_header *vv; 6247024Ssam register struct ifqueue *inq; 62516581Skarels register struct mbuf *m; 62615794Sleres int ubainfo, len, off, s; 6277640Ssam short resid; 6287024Ssam 62915764Sleres vs = &vv_softc[unit]; 63016581Skarels vs->vs_if.if_ipackets++; 63115764Sleres addr = (struct vvreg *)vvinfo[unit]->ui_addr; 63220997Skarels 6337024Ssam /* 6347024Ssam * Purge BDP; drop if input error indicated. 6357024Ssam */ 6367024Ssam if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 6377024Ssam UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_r.ifrw_bdp); 63820997Skarels 63920997Skarels /* 64020997Skarels * receive errors? 64120997Skarels */ 6427024Ssam if (addr->vvicsr & VVRERR) { 64320997Skarels vvprintf("vv%d: receive error, vvicsr = %b\n", unit, 64420997Skarels 0xffff&(addr->vvicsr), VV_IBITS); 64520997Skarels if (addr->vvicsr & VV_BDF) 64620997Skarels vs->vs_ibadf++; 6477640Ssam goto dropit; 6487024Ssam } 6497640Ssam 6507024Ssam /* 65120997Skarels * parity errors? 65220997Skarels */ 65320997Skarels if (addr->vvicsr & VV_LDE) { 65420997Skarels /* we don't have to clear it because the receive command */ 65520997Skarels /* writes 0 to parity bit */ 65620997Skarels vs->vs_parity++; 65720997Skarels #ifndef PRONET80 65820997Skarels /* 65920997Skarels * only on 10 megabit proNET is VV_LDE an end-to-end parity 66020997Skarels * bit. On 80 megabit, it returns to the intended use of 66120997Skarels * node-to-node parity. End-to-end parity errors on 80 megabit 66220997Skarels * give VV_BDF. 66320997Skarels */ 66420997Skarels goto dropit; 66520997Skarels #endif 66620997Skarels } 66720997Skarels 66820997Skarels /* 66920997Skarels * Get packet length from residual word count 6707640Ssam * 6717640Ssam * Compute header offset if trailer protocol 6727640Ssam * 6737640Ssam * Pull packet off interface. Off is nonzero if packet 6747640Ssam * has trailing header; if_rubaget will then force this header 6757640Ssam * information to be at the front. The vh_info field 6767640Ssam * carries the offset to the trailer data in trailer 6777640Ssam * format packets. 6787024Ssam */ 6797640Ssam vv = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr); 68011192Ssam vvtracehdr("vi", vv); 68120997Skarels resid = addr->vviwc & 01777; /* only low 10 bits valid */ 6827640Ssam if (resid) 68320997Skarels resid |= 0176000; /* high 6 bits are undefined */ 68420997Skarels len = ((VVBUFSIZE >> 1) + resid) << 1; 6857640Ssam len -= sizeof(struct vv_header); 68620997Skarels 68720997Skarels if ((addr->vvicsr & VV_DPR) || len > VVMRU || len <= 0) { 68820997Skarels vvprintf("vv%d: len too long or short, \ 68920997Skarels len = %d, vvicsr = %b\n", 69015794Sleres unit, len, 0xffff&(addr->vvicsr), VV_IBITS); 6917640Ssam goto dropit; 69215794Sleres } 69320997Skarels 69420997Skarels /* check the protocol header version */ 69520997Skarels if (vv->vh_version != RING_VERSION) { 69620997Skarels vvprintf("vv%d: bad protocol header version %d\n", 69720997Skarels unit, vv->vh_version & 0xff); 69820997Skarels goto dropit; 69920997Skarels } 70020997Skarels 7017640Ssam #define vvdataaddr(vv, off, type) ((type)(((caddr_t)((vv)+1)+(off)))) 70213057Ssam if (vv->vh_type >= RING_IPTrailer && 70313057Ssam vv->vh_type < RING_IPTrailer+RING_IPNTrailer) { 7047640Ssam off = (vv->vh_type - RING_IPTrailer) * 512; 70515794Sleres if (off > VVMTU) { 70620997Skarels vvprintf("vv%d: off > VVMTU, off = %d, vvicsr = %b\n", 70715794Sleres unit, off, 0xffff&(addr->vvicsr), VV_IBITS); 7087640Ssam goto dropit; 70915794Sleres } 7107640Ssam vv->vh_type = *vvdataaddr(vv, off, u_short *); 7117640Ssam resid = *(vvdataaddr(vv, off+2, u_short *)); 71215794Sleres if (off + resid > len) { 71320997Skarels vvprintf("vv%d: trailer packet too short\n", unit); 71420997Skarels vvprintf("vv%d: off = %d, resid = %d, vvicsr = %b\n", 71515794Sleres unit, off, resid, 71615794Sleres 0xffff&(addr->vvicsr), VV_IBITS); 7177640Ssam goto dropit; 71815794Sleres } 7197640Ssam len = off + resid; 72011209Ssam } else 7217640Ssam off = 0; 72220997Skarels 72315794Sleres if (len == 0) { 72420997Skarels vvprintf("vv%d: len is zero, vvicsr = %b\n", unit, 72515794Sleres 0xffff&(addr->vvicsr), VV_IBITS); 7267640Ssam goto dropit; 72715794Sleres } 72820997Skarels 7297640Ssam m = if_rubaget(&vs->vs_ifuba, len, off); 73015794Sleres if (m == NULL) { 73120997Skarels vvprintf("vv%d: if_rubaget() failed, vvicsr = %b\n", unit, 73215794Sleres 0xffff&(addr->vvicsr), VV_IBITS); 7337640Ssam goto dropit; 73415794Sleres } 7357640Ssam if (off) { 7367640Ssam m->m_off += 2 * sizeof(u_short); 7377640Ssam m->m_len -= 2 * sizeof(u_short); 7387640Ssam } 73911192Ssam 74015794Sleres /* Keep track of source address of this packet */ 74115794Sleres vs->vs_lastr = vv->vh_shost; 74220997Skarels 74311192Ssam /* 74415764Sleres * Demultiplex on packet type 74511192Ssam */ 7467024Ssam switch (vv->vh_type) { 74711192Ssam 7487024Ssam #ifdef INET 7497024Ssam case RING_IP: 7507024Ssam schednetisr(NETISR_IP); 7517024Ssam inq = &ipintrq; 7527024Ssam break; 7537024Ssam #endif 7547024Ssam default: 75520997Skarels vvprintf("vv%d: unknown pkt type 0x%x\n", unit, vv->vh_type); 7567640Ssam m_freem(m); 7577024Ssam goto setup; 7587024Ssam } 75915794Sleres s = splimp(); 7607640Ssam if (IF_QFULL(inq)) { 7617640Ssam IF_DROP(inq); 7627640Ssam m_freem(m); 76311209Ssam } else 7647640Ssam IF_ENQUEUE(inq, m); 76515764Sleres 76615794Sleres splx(s); 7677024Ssam /* 76815764Sleres * Reset for the next packet. 7697024Ssam */ 77015764Sleres setup: 77115764Sleres ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; 77215764Sleres addr->vviba = (u_short) ubainfo; 77315764Sleres addr->vviea = (u_short) (ubainfo >> 16); 77420997Skarels addr->vviwc = -(VVBUFSIZE) >> 1; 77520997Skarels addr->vvicsr = VV_HEN | VV_IEN | VV_DEN | VV_ENB; 77615764Sleres return; 77711192Ssam 77811192Ssam /* 77911209Ssam * Drop packet on floor -- count them!! 78011192Ssam */ 7817640Ssam dropit: 7827640Ssam vs->vs_if.if_ierrors++; 7837640Ssam goto setup; 7847024Ssam } 7857024Ssam 7867024Ssam /* 78720997Skarels * proNET output routine. 7887024Ssam * Encapsulate a packet of type family for the local net. 7897024Ssam * Use trailer local net encapsulation if enough data in first 7907024Ssam * packet leaves a multiple of 512 bytes of data in remainder. 7917024Ssam */ 7927024Ssam vvoutput(ifp, m0, dst) 7937024Ssam struct ifnet *ifp; 7947024Ssam struct mbuf *m0; 7957024Ssam struct sockaddr *dst; 7967024Ssam { 79716581Skarels register struct mbuf *m; 7987024Ssam register struct vv_header *vv; 7997640Ssam register int off; 80016581Skarels register int unit; 80116581Skarels register struct vvreg *addr; 80216581Skarels register struct vv_softc *vs; 80316581Skarels register int s; 80416581Skarels int type, dest, error; 8057024Ssam 80616581Skarels m = m0; 80716581Skarels unit = ifp->if_unit; 80816581Skarels addr = (struct vvreg *)vvinfo[unit]->ui_addr; 80916581Skarels vs = &vv_softc[unit]; 81020997Skarels 81116581Skarels /* 81220997Skarels * Check to see if the input side has wedged due the UBA 81320997Skarels * vectoring through 0. 81416581Skarels * 81516581Skarels * We are lower than device ipl when we enter this routine, 81616581Skarels * so if the interface is ready with an input packet then 81716581Skarels * an input interrupt must have slipped through the cracks. 81816581Skarels * 81916581Skarels * Avoid the race with an input interrupt by watching to see 82016581Skarels * if any packets come in. 82116581Skarels */ 82216581Skarels s = vs->vs_if.if_ipackets; 82316581Skarels if (addr->vvicsr & VV_RDY && s == vs->vs_if.if_ipackets) { 82420997Skarels vvprintf("vv%d: lost a receive interrupt, icsr = %b\n", 82516581Skarels unit, 0xffff&(addr->vvicsr), VV_IBITS); 82616581Skarels s = splimp(); 82716581Skarels vvrint(unit); 82816581Skarels splx(s); 82916581Skarels } 83016581Skarels 8317024Ssam switch (dst->sa_family) { 83211192Ssam 8337024Ssam #ifdef INET 83415764Sleres case AF_INET: 83521779Skarels if (in_broadcast(((struct sockaddr_in *)dst)->sin_addr)) 83621779Skarels dest = VV_BROADCAST; 83721779Skarels else 83821779Skarels dest = in_lnaof(((struct sockaddr_in *)dst)->sin_addr); 83920997Skarels #ifdef LOOPBACK 84021779Skarels if (dest == vs->vs_host && (loif.if_flags & IFF_UP)) 84121779Skarels return (looutput(&loif, m0, dst)); 84220997Skarels #endif LOOPBACK 84321779Skarels if (dest >= 0x100) { 8447640Ssam error = EPERM; 8457640Ssam goto bad; 8467640Ssam } 8477640Ssam off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; 84820997Skarels /* 84920997Skarels * Trailerize, if the configuration allows it. 85020997Skarels * TODO: Need per host negotiation. 85120997Skarels */ 85213090Ssam if ((ifp->if_flags & IFF_NOTRAILERS) == 0) 85313090Ssam if (off > 0 && (off & 0x1ff) == 0 && 8547640Ssam m->m_off >= MMINOFF + 2 * sizeof (u_short)) { 8557640Ssam type = RING_IPTrailer + (off>>9); 8567640Ssam m->m_off -= 2 * sizeof (u_short); 8577640Ssam m->m_len += 2 * sizeof (u_short); 8587640Ssam *mtod(m, u_short *) = RING_IP; 8597640Ssam *(mtod(m, u_short *) + 1) = m->m_len; 8607640Ssam goto gottrailertype; 8617640Ssam } 8627024Ssam type = RING_IP; 8637024Ssam off = 0; 8647024Ssam goto gottype; 8657024Ssam #endif 8667024Ssam default: 86716581Skarels printf("vv%d: can't handle af%d\n", unit, dst->sa_family); 8687640Ssam error = EAFNOSUPPORT; 8697640Ssam goto bad; 8707024Ssam } 8717024Ssam 8727024Ssam gottrailertype: 8737024Ssam /* 8747024Ssam * Packet to be sent as trailer: move first packet 8757024Ssam * (control information) to end of chain. 8767024Ssam */ 8777024Ssam while (m->m_next) 8787024Ssam m = m->m_next; 8797024Ssam m->m_next = m0; 8807024Ssam m = m0->m_next; 8817024Ssam m0->m_next = 0; 8827024Ssam m0 = m; 8837024Ssam gottype: 8847024Ssam /* 8857024Ssam * Add local net header. If no space in first mbuf, 8867024Ssam * allocate another. 8877024Ssam */ 8887024Ssam if (m->m_off > MMAXOFF || 8897024Ssam MMINOFF + sizeof (struct vv_header) > m->m_off) { 89011209Ssam m = m_get(M_DONTWAIT, MT_HEADER); 89111209Ssam if (m == NULL) { 8927640Ssam error = ENOBUFS; 8937640Ssam goto bad; 8947024Ssam } 8957024Ssam m->m_next = m0; 8967024Ssam m->m_off = MMINOFF; 8977024Ssam m->m_len = sizeof (struct vv_header); 8987024Ssam } else { 8997024Ssam m->m_off -= sizeof (struct vv_header); 9007024Ssam m->m_len += sizeof (struct vv_header); 9017024Ssam } 9027024Ssam vv = mtod(m, struct vv_header *); 90321779Skarels vv->vh_shost = vs->vs_host; 90421779Skarels vv->vh_dhost = dest; 9057024Ssam vv->vh_version = RING_VERSION; 9067024Ssam vv->vh_type = type; 9077640Ssam vv->vh_info = off; 90811192Ssam vvtracehdr("vo", vv); 9097024Ssam 9107024Ssam /* 9117024Ssam * Queue message on interface, and start output if interface 9127024Ssam * not yet active. 9137024Ssam */ 9147024Ssam s = splimp(); 9157640Ssam if (IF_QFULL(&ifp->if_snd)) { 9167640Ssam IF_DROP(&ifp->if_snd); 9177640Ssam error = ENOBUFS; 9187640Ssam goto qfull; 9197640Ssam } 9207024Ssam IF_ENQUEUE(&ifp->if_snd, m); 92116581Skarels if (vs->vs_oactive == 0) 92216581Skarels vvstart(unit); 9237024Ssam splx(s); 9247640Ssam return (0); 9257640Ssam qfull: 9267640Ssam m0 = m; 9277640Ssam splx(s); 9287640Ssam bad: 9297640Ssam m_freem(m0); 9307640Ssam return(error); 9317024Ssam } 9327024Ssam 9337024Ssam /* 93413057Ssam * Process an ioctl request. 93513057Ssam */ 93613057Ssam vvioctl(ifp, cmd, data) 93713057Ssam register struct ifnet *ifp; 93813057Ssam int cmd; 93913057Ssam caddr_t data; 94013057Ssam { 94121779Skarels struct ifaddr *ifa = (struct ifaddr *) data; 94221779Skarels int s = splimp(), error = 0; 94313057Ssam 94413057Ssam switch (cmd) { 94513057Ssam 94613057Ssam case SIOCSIFADDR: 94721779Skarels if ((ifp->if_flags & IFF_RUNNING) == 0) 94813057Ssam vvinit(ifp->if_unit); 94921779Skarels /* 95021779Skarels * Attempt to check agreement of protocol address 95121779Skarels * and board address. 95221779Skarels */ 95321779Skarels switch (ifa->ifa_addr.sa_family) { 95421779Skarels case AF_INET: 95521779Skarels if (in_lnaof(IA_SIN(ifa)->sin_addr) != 95621779Skarels vv_softc[ifp->if_unit].vs_host) 95721779Skarels return (EADDRNOTAVAIL); 95821779Skarels break; 95921779Skarels } 96021779Skarels ifp->if_flags |= IFF_UP; 96113057Ssam break; 96213057Ssam 96313057Ssam default: 96413057Ssam error = EINVAL; 96513057Ssam } 96613057Ssam splx(s); 96721779Skarels return (error); 96813057Ssam } 969