123305Smckusick /* 223305Smckusick * Copyright (c) 1982 Regents of the University of California. 323305Smckusick * All rights reserved. The Berkeley software License Agreement 423305Smckusick * specifies the terms and conditions for redistribution. 523305Smckusick * 6*25275Sbloom * @(#)if_vv.c 6.13 (Berkeley) 10/24/85 723305Smckusick */ 87024Ssam 99799Ssam #include "vv.h" 10*25275Sbloom #if NVV > 0 1111192Ssam 127024Ssam /* 1320997Skarels * Proteon proNET-10 and proNET-80 token ring driver. 1420997Skarels * The name of this device driver derives from the old MIT 1520997Skarels * name of V2LNI for the proNET hardware, would would abbreviate 1620997Skarels * to "v2", but this won't work right. Thus the name is "vv". 1711192Ssam * 1820997Skarels * This driver is compatible with the proNET 10 meagbit and 1920997Skarels * 80 megabit token ring interfaces (models p1000 and p1080). 2020997Skarels * 2120997Skarels * TRAILERS: You must turn off trailers via ifconfig if you want to share 2220997Skarels * a ring with software using the following protocol types: 2320997Skarels * 3: Address Resolution Protocol 2420997Skarels * 4: HDLC (old Proteon drivers) 2520997Skarels * 5: VAX Debugging Protocol (never used) 2620997Skarels * This is because the protocol type values chosen for trailers 2720997Skarels * conflict with these protocols. It's too late to change either now. 2820997Skarels * 2920997Skarels * HARDWARE COMPATABILITY: This driver requires that the HSBU (p1001) 3020997Skarels * have a serial number >= 040, which is about March, 1982. Older 3120997Skarels * HSBUs do not carry across 64kbyte boundaries. The old warning 3220997Skarels * about use without Wire Centers applies only to CTL (p1002) cards with 3320997Skarels * serial <= 057, which have not received ECO 176-743, which was 3420997Skarels * implemented in March, 1982. Most such CTLs have received this ECO, 3520997Skarels * but they are only compatible with the old HSBUs (<=039) anyways. 367024Ssam */ 3711209Ssam #include "../machine/pte.h" 389799Ssam 3917117Sbloom #include "param.h" 4017117Sbloom #include "systm.h" 4117117Sbloom #include "mbuf.h" 4217117Sbloom #include "buf.h" 4317117Sbloom #include "protosw.h" 4417117Sbloom #include "socket.h" 4517117Sbloom #include "vmmac.h" 4617117Sbloom #include "errno.h" 4717117Sbloom #include "ioctl.h" 488465Sroot 498465Sroot #include "../net/if.h" 5011209Ssam #include "../net/netisr.h" 518465Sroot #include "../net/route.h" 5224793Skarels 5324793Skarels #ifdef BBNNET 5424793Skarels #define INET 5524793Skarels #endif 5624793Skarels #ifdef INET 578421Swnj #include "../netinet/in.h" 588421Swnj #include "../netinet/in_systm.h" 5921779Skarels #include "../netinet/in_var.h" 608421Swnj #include "../netinet/ip.h" 6124793Skarels #endif 628465Sroot 6315794Sleres #include "../vax/cpu.h" 6411209Ssam #include "../vax/mtpr.h" 6517117Sbloom #include "if_vv.h" 6617117Sbloom #include "if_uba.h" 678465Sroot #include "../vaxuba/ubareg.h" 688465Sroot #include "../vaxuba/ubavar.h" 697024Ssam 707024Ssam /* 7120997Skarels * 80 megabit configuration 7220997Skarels * Uncomment the next line if you are using the 80 megabit system. The 7320997Skarels * only change is the disposition of packets with parity/link_data_error 7420997Skarels * indication. 757024Ssam */ 7620997Skarels /* #define PRONET80 */ 777024Ssam 7820997Skarels /* 7920997Skarels * maximum transmission unit definition -- 8020997Skarels * you can set VVMTU at anything from 576 to 2024. 8120997Skarels * 1536 is a popular "large" value, because it is a multiple 8220997Skarels * of 512, which the trailer scheme likes. 8320997Skarels * The absolute maximum size is 2024, which is enforced. 8420997Skarels */ 8520997Skarels 8624793Skarels #define VVMTU (1536) 8720997Skarels 8820997Skarels #define VVMRU (VVMTU + 16) 8920997Skarels #define VVBUFSIZE (VVMRU + sizeof(struct vv_header)) 9020997Skarels #if VVMTU>2024 9120997Skarels #undef VVMTU 9220997Skarels #undef VVMRU 9320997Skarels #undef VVBUFSIZE 9420997Skarels #define VVBUFSIZE (2046) 9520997Skarels #define VVMRU (VVBUFSIZE - sizeof (struct vv_header)) 9620997Skarels #define VVMTU (VVMRU - 16) 977024Ssam #endif 987024Ssam 9920997Skarels /* 10020997Skarels * debugging and tracing stuff 10120997Skarels */ 10216581Skarels int vv_tracehdr = 0; /* 1 => trace headers (slowly!!) */ 1037640Ssam 10420997Skarels #define vvtracehdr if (vv_tracehdr) vvprt_hdr 10521779Skarels #define vvprintf if (vs->vs_if.if_flags & IFF_DEBUG) printf 10611192Ssam 10720997Skarels /* 10820997Skarels * externals, types, etc. 10920997Skarels */ 11016581Skarels int vvprobe(), vvattach(), vvreset(), vvinit(); 11116581Skarels int vvidentify(), vvstart(), vvxint(), vvwatchdog(); 11216581Skarels int vvrint(), vvoutput(), vvioctl(), vvsetaddr(); 1137024Ssam struct uba_device *vvinfo[NVV]; 1147024Ssam u_short vvstd[] = { 0 }; 1157024Ssam struct uba_driver vvdriver = 1167024Ssam { vvprobe, 0, vvattach, 0, vvstd, "vv", vvinfo }; 1177024Ssam #define VVUNIT(x) minor(x) 1187024Ssam 11920997Skarels #define LOOPBACK /* use loopback for packets meant for us */ 12020997Skarels #ifdef LOOPBACK 12120997Skarels extern struct ifnet loif; 12220997Skarels #endif 12320997Skarels 1247024Ssam /* 1257024Ssam * Software status of each interface. 1267024Ssam * 1277024Ssam * Each interface is referenced by a network interface structure, 1287024Ssam * vs_if, which the routing code uses to locate the interface. 1297024Ssam * This structure contains the output queue for the interface, its address, ... 1307024Ssam * We also have, for each interface, a UBA interface structure, which 1317024Ssam * contains information about the UNIBUS resources held by the interface: 1327024Ssam * map registers, buffered data paths, etc. Information is cached in this 1337024Ssam * structure for use by the if_uba.c routines in running the interface 1347024Ssam * efficiently. 1357024Ssam */ 1367024Ssam struct vv_softc { 1377024Ssam struct ifnet vs_if; /* network-visible interface */ 1387024Ssam struct ifuba vs_ifuba; /* UNIBUS resources */ 13921779Skarels int vs_host; 14011192Ssam short vs_oactive; /* is output active */ 1417024Ssam short vs_olen; /* length of last output */ 14215794Sleres u_short vs_lastx; /* address of last packet sent */ 14315794Sleres u_short vs_lastr; /* address of last packet received */ 14411192Ssam short vs_tries; /* transmit current retry count */ 1457024Ssam short vs_init; /* number of ring inits */ 14620997Skarels short vs_refused; /* number of packets refused */ 14715794Sleres short vs_timeouts; /* number of transmit timeouts */ 14820997Skarels short vs_otimeout; /* number of output timeouts */ 14920997Skarels short vs_ibadf; /* number of input bad formats */ 15020997Skarels short vs_parity; /* number of parity errors on 10 meg, */ 15120997Skarels /* link data errors on 80 meg */ 1527024Ssam } vv_softc[NVV]; 1537024Ssam 15420997Skarels /* 15520997Skarels * probe the interface to see that the registers exist, and then 15620997Skarels * cause an interrupt to find its vector 15720997Skarels */ 1587024Ssam vvprobe(reg) 1597024Ssam caddr_t reg; 1607024Ssam { 1617024Ssam register int br, cvec; 16216581Skarels register struct vvreg *addr; 1637024Ssam 1647024Ssam #ifdef lint 16515764Sleres br = 0; cvec = br; br = cvec; 1667024Ssam #endif 16716581Skarels addr = (struct vvreg *)reg; 16820997Skarels 1697024Ssam /* reset interface, enable, and wait till dust settles */ 1707024Ssam addr->vvicsr = VV_RST; 1717024Ssam addr->vvocsr = VV_RST; 17215764Sleres DELAY(100000); 17320997Skarels 1747024Ssam /* generate interrupt by doing 1 word DMA from 0 in uba space!! */ 1757024Ssam addr->vvoba = 0; /* low 16 bits */ 1767024Ssam addr->vvoea = 0; /* extended bits */ 1777024Ssam addr->vvowc = -1; /* for 1 word */ 17820997Skarels addr->vvocsr = VV_IEN | VV_DEN; /* start the DMA, with interrupt */ 1797024Ssam DELAY(100000); 18020997Skarels addr->vvocsr = VV_RST; /* clear out the CSR */ 1817024Ssam if (cvec && cvec != 0x200) 18215764Sleres cvec -= 4; /* backup so vector => receive */ 1837024Ssam return(1); 1847024Ssam } 1857024Ssam 1867024Ssam /* 1877024Ssam * Interface exists: make available by filling in network interface 1887024Ssam * record. System will initialize the interface when it is ready 1897024Ssam * to accept packets. 1907024Ssam */ 1917024Ssam vvattach(ui) 1927024Ssam struct uba_device *ui; 1937024Ssam { 19415764Sleres register struct vv_softc *vs; 1957024Ssam 19615764Sleres vs = &vv_softc[ui->ui_unit]; 1977024Ssam vs->vs_if.if_unit = ui->ui_unit; 1987024Ssam vs->vs_if.if_name = "vv"; 1997024Ssam vs->vs_if.if_mtu = VVMTU; 20021779Skarels vs->vs_if.if_flags = IFF_BROADCAST; 2017024Ssam vs->vs_if.if_init = vvinit; 20213057Ssam vs->vs_if.if_ioctl = vvioctl; 2037024Ssam vs->vs_if.if_output = vvoutput; 20411209Ssam vs->vs_if.if_reset = vvreset; 20515794Sleres vs->vs_if.if_timer = 0; 20615794Sleres vs->vs_if.if_watchdog = vvwatchdog; 2077640Ssam vs->vs_ifuba.ifu_flags = UBA_CANTWAIT | UBA_NEEDBDP | UBA_NEED16; 20812354Smo #if defined(VAX750) 20912354Smo /* don't chew up 750 bdp's */ 21012354Smo if (cpu == VAX_750 && ui->ui_unit > 0) 21112354Smo vs->vs_ifuba.ifu_flags &= ~UBA_NEEDBDP; 21212354Smo #endif 2137024Ssam if_attach(&vs->vs_if); 2147024Ssam } 2157024Ssam 2167024Ssam /* 2177024Ssam * Reset of interface after UNIBUS reset. 2187024Ssam * If interface is on specified uba, reset its state. 2197024Ssam */ 2207024Ssam vvreset(unit, uban) 2217024Ssam int unit, uban; 2227024Ssam { 2237024Ssam register struct uba_device *ui; 2247024Ssam 2257024Ssam if (unit >= NVV || (ui = vvinfo[unit]) == 0 || ui->ui_alive == 0 || 2267024Ssam ui->ui_ubanum != uban) 2277024Ssam return; 2287024Ssam printf(" vv%d", unit); 2297024Ssam vvinit(unit); 2307024Ssam } 2317024Ssam 2327024Ssam /* 2337024Ssam * Initialization of interface; clear recorded pending 2347024Ssam * operations, and reinitialize UNIBUS usage. 2357024Ssam */ 2367024Ssam vvinit(unit) 2377024Ssam int unit; 2387024Ssam { 23915764Sleres register struct vv_softc *vs; 24015764Sleres register struct uba_device *ui; 2417024Ssam register struct vvreg *addr; 24216581Skarels register int ubainfo, s; 2437024Ssam 24415764Sleres vs = &vv_softc[unit]; 24515764Sleres ui = vvinfo[unit]; 24620997Skarels 24721779Skarels if (vs->vs_if.if_addrlist == (struct ifaddr *)0) 24813057Ssam return; 24920997Skarels 2507640Ssam addr = (struct vvreg *)ui->ui_addr; 2517024Ssam if (if_ubainit(&vs->vs_ifuba, ui->ui_ubanum, 25215764Sleres sizeof (struct vv_header), (int)btoc(VVMTU)) == 0) { 25315794Sleres printf("vv%d: can't initialize, if_ubainit() failed\n", unit); 2547640Ssam vs->vs_if.if_flags &= ~IFF_UP; 2557024Ssam return; 2567024Ssam } 25720997Skarels 2587024Ssam /* 25915764Sleres * Now that the uba is set up, figure out our address and 26015764Sleres * update complete our host address. 2617640Ssam */ 26221779Skarels if ((vs->vs_host = vvidentify(unit)) == -1) { 26315794Sleres vs->vs_if.if_flags &= ~IFF_UP; 26415794Sleres return; 26515794Sleres } 26621779Skarels printf("vv%d: host %d\n", unit, vs->vs_host); 26720997Skarels 2687640Ssam /* 26920997Skarels * Reset the interface, and stay in the ring 2707640Ssam */ 27120997Skarels addr->vvocsr = VV_RST; /* take over output */ 27220997Skarels addr->vvocsr = VV_CPB; /* clear packet buffer */ 27320997Skarels addr->vvicsr = VV_RST | VV_HEN; /* take over input, */ 27420997Skarels /* keep relay closed */ 27512351Smo DELAY(500000); /* let contacts settle */ 27620997Skarels 27720997Skarels vs->vs_init = 0; /* clear counters, etc. */ 27820997Skarels vs->vs_refused = 0; 27915794Sleres vs->vs_timeouts = 0; 28020997Skarels vs->vs_otimeout = 0; 28120997Skarels vs->vs_ibadf = 0; 28220997Skarels vs->vs_parity = 0; 28315794Sleres vs->vs_lastx = 256; /* an invalid address */ 28415794Sleres vs->vs_lastr = 256; /* an invalid address */ 28520997Skarels 2867640Ssam /* 2877640Ssam * Hang a receive and start any 2887640Ssam * pending writes by faking a transmit complete. 2897640Ssam */ 2907640Ssam s = splimp(); 2917640Ssam ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; 29213057Ssam addr->vviba = (u_short)ubainfo; 29313057Ssam addr->vviea = (u_short)(ubainfo >> 16); 29420997Skarels addr->vviwc = -(VVBUFSIZE) >> 1; 29520997Skarels addr->vvicsr = VV_IEN | VV_HEN | VV_DEN | VV_ENB; 2967640Ssam vs->vs_oactive = 1; 29721779Skarels vs->vs_if.if_flags |= IFF_RUNNING; 2987640Ssam vvxint(unit); 2997640Ssam splx(s); 3007640Ssam } 3017640Ssam 3027640Ssam /* 30320997Skarels * Do a moderately thorough self-test in all three modes. Mostly 30420997Skarels * to keeps defective nodes off the ring, rather than to be especially 30520997Skarels * thorough. The key issue is to detect any cable breaks before joining 30620997Skarels * the ring. Return our node address on success, return -1 on failure. 30720997Skarels * 3087640Ssam */ 30920997Skarels 31020997Skarels /* the three self-test modes */ 31120997Skarels static u_short vv_modes[] = { 31220997Skarels VV_STE|VV_LPB, /* digital loopback */ 31320997Skarels VV_STE, /* analog loopback */ 31420997Skarels VV_HEN /* network mode */ 31520997Skarels }; 31620997Skarels 31711209Ssam vvidentify(unit) 31813057Ssam int unit; 31911209Ssam { 32015764Sleres register struct vv_softc *vs; 32115764Sleres register struct uba_device *ui; 3227640Ssam register struct vvreg *addr; 32316581Skarels register struct mbuf *m; 32416581Skarels register struct vv_header *v; 32520997Skarels register int ubainfo; 32620997Skarels register int i, successes, failures, waitcount; 32720997Skarels u_short shost = -1; 3287640Ssam 32920997Skarels vs = &vv_softc[unit]; 33020997Skarels ui = vvinfo[unit]; 33120997Skarels addr = (struct vvreg *)ui->ui_addr; 33220997Skarels 3337640Ssam /* 3347024Ssam * Build a multicast message to identify our address 33520997Skarels * We need do this only once, since nobody else is about to use 33620997Skarels * the intermediate transmit buffer (ifu_w.ifrw_addr) that 33720997Skarels * if_ubainit() aquired for us. 3387024Ssam */ 33911209Ssam m = m_get(M_DONTWAIT, MT_HEADER); 34015794Sleres if (m == NULL) { 34115794Sleres printf("vv%d: can't initialize, m_get() failed\n", unit); 34213057Ssam return (0); 34315794Sleres } 34411192Ssam m->m_next = 0; 3457024Ssam m->m_off = MMINOFF; 3467024Ssam m->m_len = sizeof(struct vv_header); 3477024Ssam v = mtod(m, struct vv_header *); 34811192Ssam v->vh_dhost = VV_BROADCAST; /* multicast destination address */ 3497024Ssam v->vh_shost = 0; /* will be overwritten with ours */ 3507024Ssam v->vh_version = RING_VERSION; 35120997Skarels v->vh_type = RING_DIAGNOSTICS; 3527024Ssam v->vh_info = 0; 35320997Skarels /* map xmit message into uba, copying to intermediate buffer */ 35420997Skarels vs->vs_olen = if_wubaput(&vs->vs_ifuba, m); 35520997Skarels 3567024Ssam /* 35720997Skarels * For each of the modes (digital, analog, network), go through 35820997Skarels * a self-test that requires me to send VVIDENTSUCC good packets 35920997Skarels * in VVIDENTRETRY attempts. Use broadcast destination to find out 36020997Skarels * who I am, then use this as my address to check my address match 36120997Skarels * logic. Only data checked is the vh_type field. 3627024Ssam */ 3637640Ssam 36420997Skarels for (i = 0; i < 3; i++) { 36520997Skarels successes = 0; /* clear successes for this mode */ 36620997Skarels failures = 0; /* and clear failures, too */ 3677640Ssam 36820997Skarels /* take over device, and leave ring */ 36920997Skarels addr->vvicsr = VV_RST; 37020997Skarels addr->vvocsr = VV_RST; 37120997Skarels addr->vvicsr = vv_modes[i]; /* test mode */ 37220997Skarels 37320997Skarels /* 37420997Skarels * let the flag and token timers pop so that the init ring bit 37520997Skarels * will be allowed to work, by waiting about 1 second 37620997Skarels */ 37720997Skarels DELAY(1000000L); 37820997Skarels 37920997Skarels /* 38020997Skarels * retry loop 38120997Skarels */ 38220997Skarels while ((successes < VVIDENTSUCC) && (failures < VVIDENTRETRY)) 38320997Skarels { 38420997Skarels /* start a receive */ 38520997Skarels ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; 38620997Skarels addr->vvicsr = VV_RST | vv_modes[i]; /* abort last */ 38720997Skarels addr->vviba = (u_short) ubainfo; 38820997Skarels addr->vviea = (u_short) (ubainfo >> 16); 38920997Skarels addr->vviwc = -(VVBUFSIZE) >> 1; 39020997Skarels addr->vvicsr = vv_modes[i] | VV_DEN | VV_ENB; 39120997Skarels 39220997Skarels /* purge stale data from BDP */ 39320997Skarels if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 39420997Skarels UBAPURGE(vs->vs_ifuba.ifu_uba, 39520997Skarels vs->vs_ifuba.ifu_w.ifrw_bdp); 39620997Skarels 39720997Skarels /* do a transmit */ 39820997Skarels ubainfo = vs->vs_ifuba.ifu_w.ifrw_info; 39920997Skarels addr->vvocsr = VV_RST; /* abort last try */ 40020997Skarels addr->vvoba = (u_short) ubainfo; 40120997Skarels addr->vvoea = (u_short) (ubainfo >> 16); 40220997Skarels addr->vvowc = -((vs->vs_olen + 1) >> 1); 40320997Skarels addr->vvocsr = VV_CPB | VV_DEN | VV_INR | VV_ENB; 40420997Skarels 40520997Skarels /* poll receive side for completion */ 40620997Skarels DELAY(10000); /* give it a chance */ 40720997Skarels for (waitcount = 0; waitcount < 10; waitcount++) { 40820997Skarels if (addr->vvicsr & VV_RDY) 40920997Skarels goto gotit; 41020997Skarels DELAY(1000); 41120997Skarels } 41220997Skarels failures++; /* no luck */ 41311209Ssam continue; 41420997Skarels 41520997Skarels gotit: /* we got something--is it any good? */ 41620997Skarels if ((addr->vvicsr & (VVRERR|VV_LDE)) || 41721779Skarels (addr->vvocsr & (VVXERR|VV_RFS))) { 41820997Skarels failures++; 41920997Skarels continue; 42020997Skarels } 42120997Skarels 42220997Skarels /* Purge BDP before looking at received packet */ 42320997Skarels if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 42420997Skarels UBAPURGE(vs->vs_ifuba.ifu_uba, 42520997Skarels vs->vs_ifuba.ifu_r.ifrw_bdp); 42624793Skarels m = if_rubaget(&vs->vs_ifuba, sizeof(struct vv_header), 42724793Skarels 0, 0); 42820997Skarels if (m != NULL) 42920997Skarels m_freem(m); 43020997Skarels 43120997Skarels v = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr); 43220997Skarels 43320997Skarels /* check message type, catch our node address */ 43420997Skarels if ((v->vh_type & 0xff) == RING_DIAGNOSTICS) { 43520997Skarels if (shost == -1) { 43620997Skarels shost = v->vh_shost & 0xff; 43720997Skarels /* send to ourself now */ 43820997Skarels ((struct vv_header *) 43920997Skarels (vs->vs_ifuba.ifu_r.ifrw_addr)) 44020997Skarels ->vh_dhost = shost; 44120997Skarels } 44220997Skarels successes++; 44320997Skarels v->vh_type = 0; /* clear to check again */ 44420997Skarels } 44511192Ssam } 44620997Skarels 44720997Skarels if (failures >= VVIDENTRETRY) 44820997Skarels { 44920997Skarels printf("vv%d: failed self-test after %d tries \ 45020997Skarels in %s mode\n", 45120997Skarels unit, VVIDENTRETRY, i == 0 ? "digital loopback" : 45220997Skarels (i == 1 ? "analog loopback" : "network")); 45320997Skarels printf("vv%d: icsr = %b, ocsr = %b\n", 45420997Skarels unit, 0xffff & addr->vvicsr, VV_IBITS, 45520997Skarels 0xffff & addr->vvocsr, VV_OBITS); 45620997Skarels addr->vvicsr = VV_RST; /* kill the sick board */ 45720997Skarels addr->vvocsr = VV_RST; 45820997Skarels shost = -1; 45920997Skarels goto done; 46020997Skarels } 46111192Ssam } 46220997Skarels 46320997Skarels done: 46420997Skarels /* deallocate mbuf used for send packet (won't be one, anyways) */ 46515794Sleres if (vs->vs_ifuba.ifu_xtofree) { 4667024Ssam m_freem(vs->vs_ifuba.ifu_xtofree); 46715794Sleres vs->vs_ifuba.ifu_xtofree = 0; 46815794Sleres } 46920997Skarels 47020997Skarels return(shost); 4717024Ssam } 4727024Ssam 4737024Ssam /* 4747024Ssam * Start or restart output on interface. 47511192Ssam * If interface is active, this is a retransmit, so just 47611192Ssam * restuff registers and go. 4777024Ssam * If interface is not already active, get another datagram 4787024Ssam * to send off of the interface queue, and map it to the interface 4797024Ssam * before starting the output. 4807024Ssam */ 4817024Ssam vvstart(dev) 4827024Ssam dev_t dev; 4837024Ssam { 48416581Skarels register struct uba_device *ui; 48515764Sleres register struct vv_softc *vs; 4867024Ssam register struct vvreg *addr; 48716581Skarels register struct mbuf *m; 48816581Skarels register int unit, ubainfo, dest, s; 4897024Ssam 49016581Skarels unit = VVUNIT(dev); 49115764Sleres ui = vvinfo[unit]; 49215764Sleres vs = &vv_softc[unit]; 4937024Ssam if (vs->vs_oactive) 4947024Ssam goto restart; 4957024Ssam /* 4967024Ssam * Not already active: dequeue another request 4977024Ssam * and map it to the UNIBUS. If no more requests, 4987024Ssam * just return. 4997024Ssam */ 50015794Sleres s = splimp(); 5017024Ssam IF_DEQUEUE(&vs->vs_if.if_snd, m); 50215794Sleres splx(s); 50311209Ssam if (m == NULL) { 5047024Ssam vs->vs_oactive = 0; 5057024Ssam return; 5067024Ssam } 5077024Ssam dest = mtod(m, struct vv_header *)->vh_dhost; 5087024Ssam vs->vs_olen = if_wubaput(&vs->vs_ifuba, m); 5097024Ssam vs->vs_lastx = dest; 5107024Ssam restart: 5117024Ssam /* 5127024Ssam * Have request mapped to UNIBUS for transmission. 51315794Sleres * Purge any stale data from this BDP, and start the output. 51415794Sleres * 51515794Sleres * Make sure this packet will fit in the interface. 5167024Ssam */ 51720997Skarels if (vs->vs_olen > VVBUFSIZE) { 51820997Skarels printf("vv%d vs_olen: %d > VVBUFSIZE\n", unit, vs->vs_olen); 51911192Ssam panic("vvdriver vs_olen botch"); 52011192Ssam } 52120997Skarels 52220997Skarels vs->vs_if.if_timer = VVTIMEOUT; 52320997Skarels vs->vs_oactive = 1; 52420997Skarels 52520997Skarels /* ship it */ 5267024Ssam if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 5277024Ssam UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp); 5287024Ssam addr = (struct vvreg *)ui->ui_addr; 5297024Ssam ubainfo = vs->vs_ifuba.ifu_w.ifrw_info; 5307024Ssam addr->vvoba = (u_short) ubainfo; 5317024Ssam addr->vvoea = (u_short) (ubainfo >> 16); 5327024Ssam addr->vvowc = -((vs->vs_olen + 1) >> 1); 53320997Skarels addr->vvowc = -((vs->vs_olen + 1) >> 1); /* extra byte is garbage */ 53420997Skarels if (addr->vvocsr & VV_NOK) 53520997Skarels vs->vs_init++; /* count ring inits */ 5367024Ssam addr->vvocsr = VV_IEN | VV_CPB | VV_DEN | VV_INR | VV_ENB; 53715794Sleres vs->vs_if.if_timer = VVTIMEOUT; 5387024Ssam vs->vs_oactive = 1; 5397024Ssam } 5407024Ssam 5417024Ssam /* 54220997Skarels * proNET transmit interrupt 5437024Ssam * Start another output if more data to send. 5447024Ssam */ 5457024Ssam vvxint(unit) 5467024Ssam int unit; 5477024Ssam { 54815764Sleres register struct uba_device *ui; 54915764Sleres register struct vv_softc *vs; 5507024Ssam register struct vvreg *addr; 5517024Ssam register int oc; 5527024Ssam 55315764Sleres ui = vvinfo[unit]; 55415764Sleres vs = &vv_softc[unit]; 55515794Sleres vs->vs_if.if_timer = 0; 5567024Ssam addr = (struct vvreg *)ui->ui_addr; 5577024Ssam oc = 0xffff & (addr->vvocsr); 5587024Ssam if (vs->vs_oactive == 0) { 55920997Skarels vvprintf("vv%d: stray interrupt vvocsr = %b\n", unit, 56015764Sleres oc, VV_OBITS); 5617024Ssam return; 5627024Ssam } 56320997Skarels 56420997Skarels /* 56520997Skarels * we retransmit on soft error 56620997Skarels * TODO: sort retransmits to end of queue if possible! 56720997Skarels */ 56820997Skarels if (oc & (VV_OPT | VV_RFS)) { 56911192Ssam if (vs->vs_tries++ < VVRETRY) { 5707024Ssam if (oc & VV_OPT) 57120997Skarels vs->vs_otimeout++; 57220997Skarels if (oc & VV_RFS) { 57320997Skarels vs->vs_if.if_collisions++; 57420997Skarels vs->vs_refused++; 57520997Skarels } 57611192Ssam vvstart(unit); /* restart this message */ 5777024Ssam return; 5787024Ssam } 5797024Ssam } 5807024Ssam vs->vs_if.if_opackets++; 5817024Ssam vs->vs_oactive = 0; 5827024Ssam vs->vs_tries = 0; 58320997Skarels 5847024Ssam if (oc & VVXERR) { 5857024Ssam vs->vs_if.if_oerrors++; 58620997Skarels vvprintf("vv%d: error vvocsr = %b\n", unit, 0xffff & oc, 58715764Sleres VV_OBITS); 5887024Ssam } 5897024Ssam if (vs->vs_ifuba.ifu_xtofree) { 5907024Ssam m_freem(vs->vs_ifuba.ifu_xtofree); 5917024Ssam vs->vs_ifuba.ifu_xtofree = 0; 5927024Ssam } 5937024Ssam vvstart(unit); 5947024Ssam } 5957024Ssam 5967024Ssam /* 59715794Sleres * Transmit watchdog timer routine. 59815794Sleres * This routine gets called when we lose a transmit interrupt. 59915794Sleres * The best we can do is try to restart output. 60015794Sleres */ 60115794Sleres vvwatchdog(unit) 60215794Sleres int unit; 60315794Sleres { 60415794Sleres register struct vv_softc *vs; 60515794Sleres register int s; 60615794Sleres 60715794Sleres vs = &vv_softc[unit]; 60820997Skarels vvprintf("vv%d: lost a transmit interrupt.\n", unit); 60915794Sleres vs->vs_timeouts++; 61015794Sleres s = splimp(); 61115794Sleres vvstart(unit); 61215794Sleres splx(s); 61315794Sleres } 61415794Sleres 61515794Sleres /* 61620997Skarels * proNET interface receiver interrupt. 6177024Ssam * If input error just drop packet. 61815764Sleres * Otherwise purge input buffered data path and examine 6197024Ssam * packet to determine type. If can't determine length 62015764Sleres * from type, then have to drop packet. Otherwise decapsulate 6217024Ssam * packet based on type and pass to type specific higher-level 6227024Ssam * input routine. 6237024Ssam */ 6247024Ssam vvrint(unit) 6257024Ssam int unit; 6267024Ssam { 62715764Sleres register struct vv_softc *vs; 62816581Skarels register struct vvreg *addr; 6297024Ssam register struct vv_header *vv; 6307024Ssam register struct ifqueue *inq; 63116581Skarels register struct mbuf *m; 63215794Sleres int ubainfo, len, off, s; 6337640Ssam short resid; 6347024Ssam 63515764Sleres vs = &vv_softc[unit]; 63616581Skarels vs->vs_if.if_ipackets++; 63715764Sleres addr = (struct vvreg *)vvinfo[unit]->ui_addr; 63820997Skarels 6397024Ssam /* 6407024Ssam * Purge BDP; drop if input error indicated. 6417024Ssam */ 6427024Ssam if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 6437024Ssam UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_r.ifrw_bdp); 64420997Skarels 64520997Skarels /* 64620997Skarels * receive errors? 64720997Skarels */ 6487024Ssam if (addr->vvicsr & VVRERR) { 64920997Skarels vvprintf("vv%d: receive error, vvicsr = %b\n", unit, 65020997Skarels 0xffff&(addr->vvicsr), VV_IBITS); 65120997Skarels if (addr->vvicsr & VV_BDF) 65220997Skarels vs->vs_ibadf++; 6537640Ssam goto dropit; 6547024Ssam } 6557640Ssam 6567024Ssam /* 65720997Skarels * parity errors? 65820997Skarels */ 65920997Skarels if (addr->vvicsr & VV_LDE) { 66020997Skarels /* we don't have to clear it because the receive command */ 66120997Skarels /* writes 0 to parity bit */ 66220997Skarels vs->vs_parity++; 66320997Skarels #ifndef PRONET80 66420997Skarels /* 66520997Skarels * only on 10 megabit proNET is VV_LDE an end-to-end parity 66620997Skarels * bit. On 80 megabit, it returns to the intended use of 66720997Skarels * node-to-node parity. End-to-end parity errors on 80 megabit 66820997Skarels * give VV_BDF. 66920997Skarels */ 67020997Skarels goto dropit; 67120997Skarels #endif 67220997Skarels } 67320997Skarels 67420997Skarels /* 67520997Skarels * Get packet length from residual word count 6767640Ssam * 6777640Ssam * Compute header offset if trailer protocol 6787640Ssam * 6797640Ssam * Pull packet off interface. Off is nonzero if packet 6807640Ssam * has trailing header; if_rubaget will then force this header 6817640Ssam * information to be at the front. The vh_info field 6827640Ssam * carries the offset to the trailer data in trailer 6837640Ssam * format packets. 6847024Ssam */ 6857640Ssam vv = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr); 68611192Ssam vvtracehdr("vi", vv); 68720997Skarels resid = addr->vviwc & 01777; /* only low 10 bits valid */ 6887640Ssam if (resid) 68920997Skarels resid |= 0176000; /* high 6 bits are undefined */ 69020997Skarels len = ((VVBUFSIZE >> 1) + resid) << 1; 6917640Ssam len -= sizeof(struct vv_header); 69220997Skarels 69320997Skarels if ((addr->vvicsr & VV_DPR) || len > VVMRU || len <= 0) { 69420997Skarels vvprintf("vv%d: len too long or short, \ 69520997Skarels len = %d, vvicsr = %b\n", 69615794Sleres unit, len, 0xffff&(addr->vvicsr), VV_IBITS); 6977640Ssam goto dropit; 69815794Sleres } 69920997Skarels 70020997Skarels /* check the protocol header version */ 70120997Skarels if (vv->vh_version != RING_VERSION) { 70220997Skarels vvprintf("vv%d: bad protocol header version %d\n", 70320997Skarels unit, vv->vh_version & 0xff); 70420997Skarels goto dropit; 70520997Skarels } 70620997Skarels 7077640Ssam #define vvdataaddr(vv, off, type) ((type)(((caddr_t)((vv)+1)+(off)))) 70813057Ssam if (vv->vh_type >= RING_IPTrailer && 70913057Ssam vv->vh_type < RING_IPTrailer+RING_IPNTrailer) { 7107640Ssam off = (vv->vh_type - RING_IPTrailer) * 512; 71115794Sleres if (off > VVMTU) { 71220997Skarels vvprintf("vv%d: off > VVMTU, off = %d, vvicsr = %b\n", 71315794Sleres unit, off, 0xffff&(addr->vvicsr), VV_IBITS); 7147640Ssam goto dropit; 71515794Sleres } 7167640Ssam vv->vh_type = *vvdataaddr(vv, off, u_short *); 7177640Ssam resid = *(vvdataaddr(vv, off+2, u_short *)); 71815794Sleres if (off + resid > len) { 71920997Skarels vvprintf("vv%d: trailer packet too short\n", unit); 72020997Skarels vvprintf("vv%d: off = %d, resid = %d, vvicsr = %b\n", 72115794Sleres unit, off, resid, 72215794Sleres 0xffff&(addr->vvicsr), VV_IBITS); 7237640Ssam goto dropit; 72415794Sleres } 7257640Ssam len = off + resid; 72611209Ssam } else 7277640Ssam off = 0; 72820997Skarels 72915794Sleres if (len == 0) { 73020997Skarels vvprintf("vv%d: len is zero, vvicsr = %b\n", unit, 73115794Sleres 0xffff&(addr->vvicsr), VV_IBITS); 7327640Ssam goto dropit; 73315794Sleres } 73420997Skarels 73524793Skarels m = if_rubaget(&vs->vs_ifuba, len, off, &vs->vs_if); 73615794Sleres if (m == NULL) { 73720997Skarels vvprintf("vv%d: if_rubaget() failed, vvicsr = %b\n", unit, 73815794Sleres 0xffff&(addr->vvicsr), VV_IBITS); 7397640Ssam goto dropit; 74015794Sleres } 7417640Ssam if (off) { 74224793Skarels struct ifnet *ifp; 74324793Skarels 74424793Skarels ifp = *(mtod(m, struct ifnet **)); 74524793Skarels m->m_off += 2 * sizeof (u_short); 74624793Skarels m->m_len -= 2 * sizeof (u_short); 74724793Skarels *(mtod(m, struct ifnet **)) = ifp; 7487640Ssam } 74911192Ssam 75015794Sleres /* Keep track of source address of this packet */ 75115794Sleres vs->vs_lastr = vv->vh_shost; 75220997Skarels 75311192Ssam /* 75415764Sleres * Demultiplex on packet type 75511192Ssam */ 7567024Ssam switch (vv->vh_type) { 75711192Ssam 7587024Ssam #ifdef INET 7597024Ssam case RING_IP: 7607024Ssam schednetisr(NETISR_IP); 7617024Ssam inq = &ipintrq; 7627024Ssam break; 7637024Ssam #endif 7647024Ssam default: 76520997Skarels vvprintf("vv%d: unknown pkt type 0x%x\n", unit, vv->vh_type); 7667640Ssam m_freem(m); 7677024Ssam goto setup; 7687024Ssam } 76915794Sleres s = splimp(); 7707640Ssam if (IF_QFULL(inq)) { 7717640Ssam IF_DROP(inq); 7727640Ssam m_freem(m); 77311209Ssam } else 7747640Ssam IF_ENQUEUE(inq, m); 77515764Sleres 77615794Sleres splx(s); 7777024Ssam /* 77815764Sleres * Reset for the next packet. 7797024Ssam */ 78015764Sleres setup: 78115764Sleres ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; 78215764Sleres addr->vviba = (u_short) ubainfo; 78315764Sleres addr->vviea = (u_short) (ubainfo >> 16); 78420997Skarels addr->vviwc = -(VVBUFSIZE) >> 1; 78520997Skarels addr->vvicsr = VV_HEN | VV_IEN | VV_DEN | VV_ENB; 78615764Sleres return; 78711192Ssam 78811192Ssam /* 78911209Ssam * Drop packet on floor -- count them!! 79011192Ssam */ 7917640Ssam dropit: 7927640Ssam vs->vs_if.if_ierrors++; 7937640Ssam goto setup; 7947024Ssam } 7957024Ssam 7967024Ssam /* 79720997Skarels * proNET output routine. 7987024Ssam * Encapsulate a packet of type family for the local net. 7997024Ssam * Use trailer local net encapsulation if enough data in first 8007024Ssam * packet leaves a multiple of 512 bytes of data in remainder. 8017024Ssam */ 8027024Ssam vvoutput(ifp, m0, dst) 8037024Ssam struct ifnet *ifp; 8047024Ssam struct mbuf *m0; 8057024Ssam struct sockaddr *dst; 8067024Ssam { 80716581Skarels register struct mbuf *m; 8087024Ssam register struct vv_header *vv; 8097640Ssam register int off; 81016581Skarels register int unit; 81116581Skarels register struct vvreg *addr; 81216581Skarels register struct vv_softc *vs; 81316581Skarels register int s; 81416581Skarels int type, dest, error; 8157024Ssam 81616581Skarels m = m0; 81716581Skarels unit = ifp->if_unit; 81816581Skarels addr = (struct vvreg *)vvinfo[unit]->ui_addr; 81916581Skarels vs = &vv_softc[unit]; 82020997Skarels 82116581Skarels /* 82220997Skarels * Check to see if the input side has wedged due the UBA 82320997Skarels * vectoring through 0. 82416581Skarels * 82516581Skarels * We are lower than device ipl when we enter this routine, 82616581Skarels * so if the interface is ready with an input packet then 82716581Skarels * an input interrupt must have slipped through the cracks. 82816581Skarels * 82916581Skarels * Avoid the race with an input interrupt by watching to see 83016581Skarels * if any packets come in. 83116581Skarels */ 83216581Skarels s = vs->vs_if.if_ipackets; 83316581Skarels if (addr->vvicsr & VV_RDY && s == vs->vs_if.if_ipackets) { 83420997Skarels vvprintf("vv%d: lost a receive interrupt, icsr = %b\n", 83516581Skarels unit, 0xffff&(addr->vvicsr), VV_IBITS); 83616581Skarels s = splimp(); 83716581Skarels vvrint(unit); 83816581Skarels splx(s); 83916581Skarels } 84016581Skarels 8417024Ssam switch (dst->sa_family) { 84211192Ssam 8437024Ssam #ifdef INET 84415764Sleres case AF_INET: 84521779Skarels if (in_broadcast(((struct sockaddr_in *)dst)->sin_addr)) 84621779Skarels dest = VV_BROADCAST; 84721779Skarels else 84821779Skarels dest = in_lnaof(((struct sockaddr_in *)dst)->sin_addr); 84920997Skarels #ifdef LOOPBACK 85021779Skarels if (dest == vs->vs_host && (loif.if_flags & IFF_UP)) 85121779Skarels return (looutput(&loif, m0, dst)); 85220997Skarels #endif LOOPBACK 85321779Skarels if (dest >= 0x100) { 8547640Ssam error = EPERM; 8557640Ssam goto bad; 8567640Ssam } 8577640Ssam off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; 85820997Skarels /* 85920997Skarels * Trailerize, if the configuration allows it. 86020997Skarels * TODO: Need per host negotiation. 86120997Skarels */ 86213090Ssam if ((ifp->if_flags & IFF_NOTRAILERS) == 0) 86313090Ssam if (off > 0 && (off & 0x1ff) == 0 && 8647640Ssam m->m_off >= MMINOFF + 2 * sizeof (u_short)) { 8657640Ssam type = RING_IPTrailer + (off>>9); 8667640Ssam m->m_off -= 2 * sizeof (u_short); 8677640Ssam m->m_len += 2 * sizeof (u_short); 8687640Ssam *mtod(m, u_short *) = RING_IP; 8697640Ssam *(mtod(m, u_short *) + 1) = m->m_len; 8707640Ssam goto gottrailertype; 8717640Ssam } 8727024Ssam type = RING_IP; 8737024Ssam off = 0; 8747024Ssam goto gottype; 8757024Ssam #endif 8767024Ssam default: 87716581Skarels printf("vv%d: can't handle af%d\n", unit, dst->sa_family); 8787640Ssam error = EAFNOSUPPORT; 8797640Ssam goto bad; 8807024Ssam } 8817024Ssam 8827024Ssam gottrailertype: 8837024Ssam /* 8847024Ssam * Packet to be sent as trailer: move first packet 8857024Ssam * (control information) to end of chain. 8867024Ssam */ 8877024Ssam while (m->m_next) 8887024Ssam m = m->m_next; 8897024Ssam m->m_next = m0; 8907024Ssam m = m0->m_next; 8917024Ssam m0->m_next = 0; 8927024Ssam m0 = m; 8937024Ssam gottype: 8947024Ssam /* 8957024Ssam * Add local net header. If no space in first mbuf, 8967024Ssam * allocate another. 8977024Ssam */ 8987024Ssam if (m->m_off > MMAXOFF || 8997024Ssam MMINOFF + sizeof (struct vv_header) > m->m_off) { 90011209Ssam m = m_get(M_DONTWAIT, MT_HEADER); 90111209Ssam if (m == NULL) { 9027640Ssam error = ENOBUFS; 9037640Ssam goto bad; 9047024Ssam } 9057024Ssam m->m_next = m0; 9067024Ssam m->m_off = MMINOFF; 9077024Ssam m->m_len = sizeof (struct vv_header); 9087024Ssam } else { 9097024Ssam m->m_off -= sizeof (struct vv_header); 9107024Ssam m->m_len += sizeof (struct vv_header); 9117024Ssam } 9127024Ssam vv = mtod(m, struct vv_header *); 91321779Skarels vv->vh_shost = vs->vs_host; 91421779Skarels vv->vh_dhost = dest; 9157024Ssam vv->vh_version = RING_VERSION; 9167024Ssam vv->vh_type = type; 9177640Ssam vv->vh_info = off; 91811192Ssam vvtracehdr("vo", vv); 9197024Ssam 9207024Ssam /* 9217024Ssam * Queue message on interface, and start output if interface 9227024Ssam * not yet active. 9237024Ssam */ 9247024Ssam s = splimp(); 9257640Ssam if (IF_QFULL(&ifp->if_snd)) { 9267640Ssam IF_DROP(&ifp->if_snd); 9277640Ssam error = ENOBUFS; 9287640Ssam goto qfull; 9297640Ssam } 9307024Ssam IF_ENQUEUE(&ifp->if_snd, m); 93116581Skarels if (vs->vs_oactive == 0) 93216581Skarels vvstart(unit); 9337024Ssam splx(s); 9347640Ssam return (0); 9357640Ssam qfull: 9367640Ssam m0 = m; 9377640Ssam splx(s); 9387640Ssam bad: 9397640Ssam m_freem(m0); 9407640Ssam return(error); 9417024Ssam } 9427024Ssam 9437024Ssam /* 94413057Ssam * Process an ioctl request. 94513057Ssam */ 94613057Ssam vvioctl(ifp, cmd, data) 94713057Ssam register struct ifnet *ifp; 94813057Ssam int cmd; 94913057Ssam caddr_t data; 95013057Ssam { 95121779Skarels struct ifaddr *ifa = (struct ifaddr *) data; 95221779Skarels int s = splimp(), error = 0; 95313057Ssam 95413057Ssam switch (cmd) { 95513057Ssam 95613057Ssam case SIOCSIFADDR: 95724793Skarels ifp->if_flags |= IFF_UP; 95821779Skarels if ((ifp->if_flags & IFF_RUNNING) == 0) 95913057Ssam vvinit(ifp->if_unit); 96021779Skarels /* 96121779Skarels * Attempt to check agreement of protocol address 96221779Skarels * and board address. 96321779Skarels */ 96421779Skarels switch (ifa->ifa_addr.sa_family) { 96521779Skarels case AF_INET: 96621779Skarels if (in_lnaof(IA_SIN(ifa)->sin_addr) != 96721779Skarels vv_softc[ifp->if_unit].vs_host) 96825194Skarels error = EADDRNOTAVAIL; 96921779Skarels break; 97021779Skarels } 97113057Ssam break; 97213057Ssam 97313057Ssam default: 97413057Ssam error = EINVAL; 97513057Ssam } 97613057Ssam splx(s); 97721779Skarels return (error); 97813057Ssam } 97925190Skarels 98025190Skarels /* 98125190Skarels * vvprt_hdr(s, v) print the local net header in "v" 98225190Skarels * with title is "s" 98325190Skarels */ 98425190Skarels vvprt_hdr(s, v) 98525190Skarels char *s; 98625190Skarels register struct vv_header *v; 98725190Skarels { 98825190Skarels printf("%s: dsvti: 0x%x 0x%x 0x%x 0x%x 0x%x\n", 98925190Skarels s, 99025190Skarels 0xff & (int)(v->vh_dhost), 0xff & (int)(v->vh_shost), 99125190Skarels 0xff & (int)(v->vh_version), 0xff & (int)(v->vh_type), 99225190Skarels 0xffff & (int)(v->vh_info)); 99325190Skarels } 994*25275Sbloom #endif 995