1*7640Ssam /* if_vv.c 4.6 82/08/01 */ 27024Ssam 37024Ssam /* 47024Ssam * Proteon 10 Meg Ring Driver. 57024Ssam * This device is called "vv" because its "real name", 67024Ssam * V2LNI won't work if shortened to the obvious "v2". 77024Ssam * Hence the subterfuge. 87024Ssam */ 97024Ssam #include "../h/param.h" 107024Ssam #include "../h/systm.h" 117024Ssam #include "../h/mbuf.h" 127024Ssam #include "../h/pte.h" 137024Ssam #include "../h/buf.h" 147024Ssam #include "../h/protosw.h" 157024Ssam #include "../h/socket.h" 167024Ssam #include "../h/ubareg.h" 177024Ssam #include "../h/ubavar.h" 187024Ssam #include "../h/cpu.h" 197024Ssam #include "../h/mtpr.h" 207024Ssam #include "../h/vmmac.h" 217024Ssam #include "../net/in.h" 227024Ssam #include "../net/in_systm.h" 237024Ssam #include "../net/if.h" 247024Ssam #include "../net/if_vv.h" 257024Ssam #include "../net/if_uba.h" 267024Ssam #include "../net/ip.h" 277024Ssam #include "../net/ip_var.h" 287024Ssam #include "../net/route.h" 29*7640Ssam #include <errno.h> 307024Ssam 317024Ssam #include "vv.h" 327024Ssam 337024Ssam /* 347024Ssam * N.B. - if WIRECENTER is defined wrong, it can well break 357024Ssam * the hardware!! 367024Ssam */ 37*7640Ssam 387024Ssam #define WIRECENTER 397024Ssam 407024Ssam #ifdef WIRECENTER 417024Ssam #define VV_CONF VV_HEN /* drive wire center relay */ 427024Ssam #else 437024Ssam #define VV_CONF VV_STE /* allow operation without wire center */ 447024Ssam #endif 457024Ssam 467024Ssam #define VVMTU (1024+512) 47*7640Ssam #define VVMRU (1024+512+16) /* space for trailer */ 487024Ssam 49*7640Ssam int vv_dotrailer = 1, /* so can do trailers selectively */ 50*7640Ssam vv_trace = 0; 51*7640Ssam 527024Ssam int vvprobe(), vvattach(), vvrint(), vvxint(); 537024Ssam struct uba_device *vvinfo[NVV]; 547024Ssam u_short vvstd[] = { 0 }; 557024Ssam struct uba_driver vvdriver = 567024Ssam { vvprobe, 0, vvattach, 0, vvstd, "vv", vvinfo }; 577024Ssam #define VVUNIT(x) minor(x) 587024Ssam int vvinit(),vvoutput(),vvreset(); 597024Ssam 607024Ssam /* 617024Ssam * Software status of each interface. 627024Ssam * 637024Ssam * Each interface is referenced by a network interface structure, 647024Ssam * vs_if, which the routing code uses to locate the interface. 657024Ssam * This structure contains the output queue for the interface, its address, ... 667024Ssam * We also have, for each interface, a UBA interface structure, which 677024Ssam * contains information about the UNIBUS resources held by the interface: 687024Ssam * map registers, buffered data paths, etc. Information is cached in this 697024Ssam * structure for use by the if_uba.c routines in running the interface 707024Ssam * efficiently. 717024Ssam */ 727024Ssam struct vv_softc { 737024Ssam struct ifnet vs_if; /* network-visible interface */ 747024Ssam struct ifuba vs_ifuba; /* UNIBUS resources */ 757024Ssam short vs_oactive; /* is output active? */ 767024Ssam short vs_olen; /* length of last output */ 777024Ssam u_short vs_lastx; /* last destination address */ 787024Ssam short vs_tries; /* current retry count */ 797024Ssam short vs_init; /* number of ring inits */ 807024Ssam short vs_flush; /* number of flushed packets */ 817024Ssam short vs_nottaken; /* number of packets refused */ 827024Ssam } vv_softc[NVV]; 837024Ssam 847024Ssam vvprobe(reg) 857024Ssam caddr_t reg; 867024Ssam { 877024Ssam register int br, cvec; 887024Ssam register struct vvreg *addr = (struct vvreg *)reg; 897024Ssam 907024Ssam #ifdef lint 917024Ssam br = 0; cvec = br; br = cvec; 927024Ssam #endif 937024Ssam /* reset interface, enable, and wait till dust settles */ 947024Ssam addr->vvicsr = VV_RST; 957024Ssam addr->vvocsr = VV_RST; 967024Ssam DELAY(100000); 977024Ssam /* generate interrupt by doing 1 word DMA from 0 in uba space!! */ 987024Ssam addr->vvocsr = VV_IEN; /* enable interrupt */ 997024Ssam addr->vvoba = 0; /* low 16 bits */ 1007024Ssam addr->vvoea = 0; /* extended bits */ 1017024Ssam addr->vvowc = -1; /* for 1 word */ 1027024Ssam addr->vvocsr |= VV_DEN; /* start the DMA */ 1037024Ssam DELAY(100000); 1047024Ssam addr->vvocsr = 0; 1057024Ssam if (cvec && cvec != 0x200) 1067024Ssam cvec -= 4; /* backup so vector => recieve */ 1077024Ssam return(1); 1087024Ssam } 1097024Ssam 1107024Ssam /* 1117024Ssam * Interface exists: make available by filling in network interface 1127024Ssam * record. System will initialize the interface when it is ready 1137024Ssam * to accept packets. 1147024Ssam */ 1157024Ssam vvattach(ui) 1167024Ssam struct uba_device *ui; 1177024Ssam { 1187024Ssam register struct vv_softc *vs = &vv_softc[ui->ui_unit]; 1197024Ssam register struct sockaddr_in *sin; 1207024Ssam 1217024Ssam vs->vs_if.if_unit = ui->ui_unit; 1227024Ssam vs->vs_if.if_name = "vv"; 1237024Ssam vs->vs_if.if_mtu = VVMTU; 1247024Ssam vs->vs_if.if_net = ui->ui_flags; 1257024Ssam vs->vs_if.if_host[0] = 0; /* this will be reset in vvinit() */ 1267024Ssam 1277024Ssam sin = (struct sockaddr_in *)&vs->vs_if.if_addr; 1287024Ssam sin->sin_family = AF_INET; 1297024Ssam sin->sin_addr = if_makeaddr(vs->vs_if.if_net, vs->vs_if.if_host[0]); 1307024Ssam 1317024Ssam sin = (struct sockaddr_in *)&vs->vs_if.if_broadaddr; 1327024Ssam sin->sin_family = AF_INET; 1337024Ssam sin->sin_addr = if_makeaddr(vs->vs_if.if_net, VV_BROADCAST); 1347024Ssam vs->vs_if.if_flags = IFF_BROADCAST; 1357024Ssam 1367024Ssam vs->vs_if.if_init = vvinit; 1377024Ssam vs->vs_if.if_output = vvoutput; 1387024Ssam vs->vs_if.if_ubareset = vvreset; 139*7640Ssam vs->vs_ifuba.ifu_flags = UBA_CANTWAIT | UBA_NEEDBDP | UBA_NEED16; 1407024Ssam if_attach(&vs->vs_if); 1417024Ssam } 1427024Ssam 1437024Ssam /* 1447024Ssam * Reset of interface after UNIBUS reset. 1457024Ssam * If interface is on specified uba, reset its state. 1467024Ssam */ 1477024Ssam vvreset(unit, uban) 1487024Ssam int unit, uban; 1497024Ssam { 1507024Ssam register struct uba_device *ui; 1517024Ssam 1527024Ssam if (unit >= NVV || (ui = vvinfo[unit]) == 0 || ui->ui_alive == 0 || 1537024Ssam ui->ui_ubanum != uban) 1547024Ssam return; 1557024Ssam printf(" vv%d", unit); 1567024Ssam vvinit(unit); 1577024Ssam } 1587024Ssam 1597024Ssam /* 1607024Ssam * Initialization of interface; clear recorded pending 1617024Ssam * operations, and reinitialize UNIBUS usage. 1627024Ssam */ 1637024Ssam vvinit(unit) 1647024Ssam int unit; 1657024Ssam { 1667024Ssam register struct vv_softc *vs = &vv_softc[unit]; 1677024Ssam register struct uba_device *ui = vvinfo[unit]; 1687024Ssam register struct vvreg *addr; 1697024Ssam struct sockaddr_in *sin; 170*7640Ssam int ubainfo, s; 1717024Ssam 172*7640Ssam addr = (struct vvreg *)ui->ui_addr; 1737024Ssam if (if_ubainit(&vs->vs_ifuba, ui->ui_ubanum, 1747024Ssam sizeof (struct vv_header), (int)btoc(VVMTU)) == 0) { 175*7640Ssam nogo: 1767024Ssam printf("vv%d: can't initialize\n", unit); 177*7640Ssam vs->vs_if.if_flags &= ~IFF_UP; 1787024Ssam return; 1797024Ssam } 1807024Ssam 1817024Ssam /* 182*7640Ssam * discover our host address and post it 183*7640Ssam */ 184*7640Ssam 185*7640Ssam vs->vs_if.if_host[0] = vvidentify(unit); 186*7640Ssam if (vs->vs_if.if_host[0] == 0) 187*7640Ssam goto nogo; 188*7640Ssam printf("vv%d: host %d\n", unit, vs->vs_if.if_host[0]); 189*7640Ssam sin = (struct sockaddr_in *)&vs->vs_if.if_addr; 190*7640Ssam sin->sin_family = AF_INET; 191*7640Ssam sin->sin_addr = 192*7640Ssam if_makeaddr(vs->vs_if.if_net, vs->vs_if.if_host[0]); 193*7640Ssam 194*7640Ssam /* 195*7640Ssam * Reset the interface, and join the ring 196*7640Ssam */ 197*7640Ssam addr->vvocsr = VV_RST | VV_CPB; /* clear packet buffer */ 198*7640Ssam addr->vvicsr = VV_RST | VV_CONF; /* close logical relay */ 199*7640Ssam sleep((caddr_t)&lbolt, PZERO); /* let contacts settle */ 200*7640Ssam vs->vs_init = 0; 201*7640Ssam vs->vs_flush = 0; 202*7640Ssam vs->vs_nottaken = 0; 203*7640Ssam 204*7640Ssam /* 205*7640Ssam * Hang a receive and start any 206*7640Ssam * pending writes by faking a transmit complete. 207*7640Ssam */ 208*7640Ssam s = splimp(); 209*7640Ssam ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; 210*7640Ssam addr->vviba = (u_short) ubainfo; 211*7640Ssam addr->vviea = (u_short) (ubainfo >> 16); 212*7640Ssam addr->vviwc = -(sizeof (struct vv_header) + VVMTU) >> 1; 213*7640Ssam addr->vvicsr = VV_IEN | VV_CONF | VV_DEN | VV_ENB; 214*7640Ssam vs->vs_oactive = 1; 215*7640Ssam vs->vs_if.if_flags |= IFF_UP; 216*7640Ssam vvxint(unit); 217*7640Ssam splx(s); 218*7640Ssam if_rtinit(&vs->vs_if, RTF_UP); 219*7640Ssam } 220*7640Ssam 221*7640Ssam /* 222*7640Ssam * vvidentify() - return our host address 223*7640Ssam */ 224*7640Ssam vvidentify(unit) 225*7640Ssam { 226*7640Ssam register struct vv_softc *vs = &vv_softc[unit]; 227*7640Ssam register struct uba_device *ui = vvinfo[unit]; 228*7640Ssam register struct vvreg *addr; 229*7640Ssam struct mbuf *m; 230*7640Ssam struct vv_header *v; 231*7640Ssam int ubainfo, retrying, attempts, waitcount, s; 232*7640Ssam 233*7640Ssam /* 2347024Ssam * Build a multicast message to identify our address 2357024Ssam */ 236*7640Ssam addr = (struct vvreg *)ui->ui_addr; 2377024Ssam attempts = 0; /* total attempts, including bad msg type */ 2387024Ssam retrying = 0; /* first time through */ 2397024Ssam m = m_get(M_DONTWAIT); 240*7640Ssam if (m == 0) { 241*7640Ssam printf("vvinit: can't get mbuf"); 242*7640Ssam return (0); 243*7640Ssam } 2447024Ssam m->m_off = MMINOFF; 2457024Ssam m->m_len = sizeof(struct vv_header); 2467024Ssam 2477024Ssam v = mtod(m, struct vv_header *); 2487024Ssam v->vh_dhost = 0; /* multicast destination address */ 2497024Ssam v->vh_shost = 0; /* will be overwritten with ours */ 2507024Ssam v->vh_version = RING_VERSION; 2517024Ssam v->vh_type = RING_WHOAMI; 2527024Ssam v->vh_info = 0; 253*7640Ssam vs->vs_olen = if_wubaput(&vs->vs_ifuba, m); 254*7640Ssam if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 255*7640Ssam UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp); 2567024Ssam 2577024Ssam /* 2587024Ssam * Reset interface, establish Digital Loopback Mode, and 2597024Ssam * send the multicast (to myself) with Input Copy enabled. 2607024Ssam */ 2617024Ssam retry: 2627024Ssam ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; 2637024Ssam addr->vvicsr = VV_RST; 2647024Ssam addr->vviba = (u_short) ubainfo; 2657024Ssam addr->vviea = (u_short) (ubainfo >> 16); 2667024Ssam addr->vviwc = -(sizeof (struct vv_header) + VVMTU) >> 1; 2677024Ssam addr->vvicsr = VV_STE | VV_DEN | VV_ENB | VV_LPB; 268*7640Ssam 269*7640Ssam /* let flag timers fire so ring will initialize */ 270*7640Ssam sleep((caddr_t) &lbolt, PZERO); 271*7640Ssam sleep((caddr_t) &lbolt, PZERO); 272*7640Ssam 2737024Ssam addr->vvocsr = VV_RST | VV_CPB; /* clear packet buffer */ 2747024Ssam ubainfo = vs->vs_ifuba.ifu_w.ifrw_info; 2757024Ssam addr->vvoba = (u_short) ubainfo; 2767024Ssam addr->vvoea = (u_short) (ubainfo >> 16); 2777024Ssam addr->vvowc = -((vs->vs_olen + 1) >> 1); 2787024Ssam addr->vvocsr = VV_CPB | VV_DEN | VV_INR | VV_ENB; 2797024Ssam 2807024Ssam /* 2817024Ssam * Wait for receive side to finish. 282*7640Ssam * Extract source address (which will be our own), 2837024Ssam * and post to interface structure. 2847024Ssam */ 2857024Ssam DELAY(1000); 286*7640Ssam for (waitcount = 0; (addr->vvicsr & VV_RDY) == 0; waitcount++) 2877024Ssam if (waitcount < 10) 2887024Ssam DELAY(1000); 2897024Ssam else { 290*7640Ssam if (attempts++ < 10) 2917024Ssam goto retry; 2927024Ssam else { 2937024Ssam printf("vv%d: can't initialize\n", unit); 2947024Ssam printf("vvinit loopwait: icsr = %b\n", 2957024Ssam 0xffff&(addr->vvicsr),VV_IBITS); 296*7640Ssam vs->vs_if.if_flags &= ~IFF_UP; 297*7640Ssam return (0); 2987024Ssam } 2997024Ssam } 3007024Ssam if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 3017024Ssam UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp); 3027024Ssam if (vs->vs_ifuba.ifu_xtofree) 3037024Ssam m_freem(vs->vs_ifuba.ifu_xtofree); 3047024Ssam if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 3057024Ssam UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_r.ifrw_bdp); 3067024Ssam m = if_rubaget(&vs->vs_ifuba, sizeof(struct vv_header), 0); 3077024Ssam if (m) 3087024Ssam m_freem(m); 3097024Ssam /* 3107024Ssam * check message type before we believe the source host address 3117024Ssam */ 3127024Ssam v = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr); 313*7640Ssam if (v->vh_type != RING_WHOAMI) 314*7640Ssam goto retry; 315*7640Ssam return (v->vh_shost); 3167024Ssam } 3177024Ssam 3187024Ssam /* 3197024Ssam * Start or restart output on interface. 3207024Ssam * If interface is not already active, get another datagram 3217024Ssam * to send off of the interface queue, and map it to the interface 3227024Ssam * before starting the output. 3237024Ssam */ 3247024Ssam vvstart(dev) 3257024Ssam dev_t dev; 3267024Ssam { 3277024Ssam int unit = VVUNIT(dev); 3287024Ssam struct uba_device *ui = vvinfo[unit]; 3297024Ssam register struct vv_softc *vs = &vv_softc[unit]; 3307024Ssam register struct vvreg *addr; 3317024Ssam struct mbuf *m; 332*7640Ssam int ubainfo, dest; 3337024Ssam 3347024Ssam if (vs->vs_oactive) 3357024Ssam goto restart; 3367024Ssam /* 3377024Ssam * Not already active: dequeue another request 3387024Ssam * and map it to the UNIBUS. If no more requests, 3397024Ssam * just return. 3407024Ssam */ 3417024Ssam IF_DEQUEUE(&vs->vs_if.if_snd, m); 3427024Ssam if (m == 0) { 3437024Ssam vs->vs_oactive = 0; 3447024Ssam return; 3457024Ssam } 3467024Ssam dest = mtod(m, struct vv_header *)->vh_dhost; 3477024Ssam vs->vs_olen = if_wubaput(&vs->vs_ifuba, m); 3487024Ssam vs->vs_lastx = dest; 3497024Ssam 3507024Ssam restart: 3517024Ssam /* 3527024Ssam * Have request mapped to UNIBUS for transmission. 3537024Ssam * Purge any stale data from this BDP, and start the otput. 3547024Ssam */ 3557024Ssam if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 3567024Ssam UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp); 3577024Ssam addr = (struct vvreg *)ui->ui_addr; 3587024Ssam ubainfo = vs->vs_ifuba.ifu_w.ifrw_info; 3597024Ssam addr->vvoba = (u_short) ubainfo; 3607024Ssam addr->vvoea = (u_short) (ubainfo >> 16); 3617024Ssam addr->vvowc = -((vs->vs_olen + 1) >> 1); 3627024Ssam addr->vvocsr = VV_IEN | VV_CPB | VV_DEN | VV_INR | VV_ENB; 3637024Ssam vs->vs_oactive = 1; 3647024Ssam } 3657024Ssam 3667024Ssam /* 3677024Ssam * VVLNI transmit interrupt 3687024Ssam * Start another output if more data to send. 3697024Ssam */ 3707024Ssam vvxint(unit) 3717024Ssam int unit; 3727024Ssam { 3737024Ssam register struct uba_device *ui = vvinfo[unit]; 3747024Ssam register struct vv_softc *vs = &vv_softc[unit]; 3757024Ssam register struct vvreg *addr; 3767024Ssam register int oc; 3777024Ssam 3787024Ssam addr = (struct vvreg *)ui->ui_addr; 3797024Ssam oc = 0xffff & (addr->vvocsr); 3807024Ssam if (vs->vs_oactive == 0) { 381*7640Ssam printf("vv%d: stray interrupt, vvocsr=%b\n", unit, 3827024Ssam oc, VV_OBITS); 3837024Ssam return; 3847024Ssam } 3857024Ssam if (oc & (VV_OPT | VV_RFS)) { 386*7640Ssam vs->vs_if.if_collisions++; 3877024Ssam if (++(vs->vs_tries) < VVRETRY) { 3887024Ssam if (oc & VV_OPT) 3897024Ssam vs->vs_init++; 3907024Ssam if (oc & VV_RFS) 3917024Ssam vs->vs_nottaken++; 3927024Ssam addr->vvocsr = VV_IEN | VV_ENB | VV_INR; 3937024Ssam return; 3947024Ssam } 3957024Ssam if (oc & VV_OPT) 3967024Ssam printf("vv%d: output timeout\n"); 3977024Ssam } 3987024Ssam vs->vs_if.if_opackets++; 3997024Ssam vs->vs_oactive = 0; 4007024Ssam vs->vs_tries = 0; 4017024Ssam if (oc & VVXERR) { 4027024Ssam vs->vs_if.if_oerrors++; 403*7640Ssam printf("vv%d: error, vvocsr=%b\n", unit, 0xffff & oc, 4047024Ssam VV_OBITS); 4057024Ssam } 4067024Ssam if (vs->vs_ifuba.ifu_xtofree) { 4077024Ssam m_freem(vs->vs_ifuba.ifu_xtofree); 4087024Ssam vs->vs_ifuba.ifu_xtofree = 0; 4097024Ssam } 4107024Ssam if (vs->vs_if.if_snd.ifq_head == 0) { 411*7640Ssam vs->vs_lastx = 256; 4127024Ssam return; 4137024Ssam } 4147024Ssam vvstart(unit); 4157024Ssam } 4167024Ssam 4177024Ssam /* 4187024Ssam * V2lni interface receiver interrupt. 4197024Ssam * If input error just drop packet. 4207024Ssam * Otherwise purge input buffered data path and examine 4217024Ssam * packet to determine type. If can't determine length 4227024Ssam * from type, then have to drop packet. Othewise decapsulate 4237024Ssam * packet based on type and pass to type specific higher-level 4247024Ssam * input routine. 4257024Ssam */ 4267024Ssam vvrint(unit) 4277024Ssam int unit; 4287024Ssam { 4297024Ssam register struct vv_softc *vs = &vv_softc[unit]; 4307024Ssam struct vvreg *addr = (struct vvreg *)vvinfo[unit]->ui_addr; 4317024Ssam register struct vv_header *vv; 4327024Ssam register struct ifqueue *inq; 4337024Ssam struct mbuf *m; 4347024Ssam int ubainfo, len, off; 435*7640Ssam short resid; 4367024Ssam 4377024Ssam vs->vs_if.if_ipackets++; 4387024Ssam /* 4397024Ssam * Purge BDP; drop if input error indicated. 4407024Ssam */ 4417024Ssam if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 4427024Ssam UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_r.ifrw_bdp); 4437024Ssam if (addr->vvicsr & VVRERR) { 444*7640Ssam /* 4457024Ssam printf("vv%d: error vvicsr = %b\n", unit, 4467024Ssam 0xffff&(addr->vvicsr), VV_IBITS); 447*7640Ssam */ 448*7640Ssam goto dropit; 4497024Ssam } 450*7640Ssam 4517024Ssam /* 452*7640Ssam * Get packet length from word count residue 453*7640Ssam * 454*7640Ssam * Compute header offset if trailer protocol 455*7640Ssam * 456*7640Ssam * Pull packet off interface. Off is nonzero if packet 457*7640Ssam * has trailing header; if_rubaget will then force this header 458*7640Ssam * information to be at the front. The vh_info field 459*7640Ssam * carries the offset to the trailer data in trailer 460*7640Ssam * format packets. 4617024Ssam */ 462*7640Ssam vv = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr); 463*7640Ssam if (vv_trace) 464*7640Ssam vvprt_hdr("vi", vv); 465*7640Ssam resid = addr->vviwc; 466*7640Ssam if (resid) 467*7640Ssam resid |= 0176000; /* ugly!!!! */ 468*7640Ssam len = (((sizeof (struct vv_header) + VVMRU) >> 1) + resid) << 1; 469*7640Ssam len -= sizeof(struct vv_header); 470*7640Ssam if (len > VVMRU) 471*7640Ssam goto dropit; 472*7640Ssam #define vvdataaddr(vv, off, type) ((type)(((caddr_t)((vv)+1)+(off)))) 473*7640Ssam if (vv_dotrailer && vv->vh_type >= RING_IPTrailer && 474*7640Ssam vv->vh_type < RING_IPTrailer+RING_IPNTrailer){ 475*7640Ssam off = (vv->vh_type - RING_IPTrailer) * 512; 476*7640Ssam if (off > VVMTU) 477*7640Ssam goto dropit; 478*7640Ssam vv->vh_type = *vvdataaddr(vv, off, u_short *); 479*7640Ssam resid = *(vvdataaddr(vv, off+2, u_short *)); 480*7640Ssam if (off + resid > len) 481*7640Ssam goto dropit; 482*7640Ssam len = off + resid; 483*7640Ssam } else 484*7640Ssam off = 0; 485*7640Ssam if (len == 0) 486*7640Ssam goto dropit; 487*7640Ssam m = if_rubaget(&vs->vs_ifuba, len, off); 488*7640Ssam if (m == 0) 489*7640Ssam goto dropit; 490*7640Ssam if (off) { 491*7640Ssam m->m_off += 2 * sizeof(u_short); 492*7640Ssam m->m_len -= 2 * sizeof(u_short); 493*7640Ssam } 4947024Ssam switch (vv->vh_type) { 4957024Ssam #ifdef INET 4967024Ssam case RING_IP: 4977024Ssam schednetisr(NETISR_IP); 4987024Ssam inq = &ipintrq; 4997024Ssam break; 5007024Ssam #endif 5017024Ssam default: 5027024Ssam printf("vv%d: unknown pkt type 0x%x\n", unit, vv->vh_type); 503*7640Ssam m_freem(m); 5047024Ssam goto setup; 5057024Ssam } 506*7640Ssam if (IF_QFULL(inq)) { 507*7640Ssam IF_DROP(inq); 508*7640Ssam m_freem(m); 509*7640Ssam } else 510*7640Ssam IF_ENQUEUE(inq, m); 5117024Ssam 5127024Ssam setup: 5137024Ssam /* 514*7640Ssam * Restart the read for next packet. 5157024Ssam */ 5167024Ssam ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; 5177024Ssam addr->vviba = (u_short) ubainfo; 5187024Ssam addr->vviea = (u_short) (ubainfo >> 16); 5197024Ssam addr->vviwc = -(sizeof (struct vv_header) + VVMTU) >> 1; 5207024Ssam addr->vvicsr = VV_RST | VV_CONF; 5217024Ssam addr->vvicsr |= VV_IEN | VV_DEN | VV_ENB; 522*7640Ssam return; 5237024Ssam 524*7640Ssam dropit: 525*7640Ssam vs->vs_if.if_ierrors++; 526*7640Ssam /* 527*7640Ssam printf("vv%d: error vvicsr = %b\n", unit, 528*7640Ssam 0xffff&(addr->vvicsr), VV_IBITS); 529*7640Ssam */ 530*7640Ssam goto setup; 5317024Ssam } 5327024Ssam 5337024Ssam /* 5347024Ssam * V2lni output routine. 5357024Ssam * Encapsulate a packet of type family for the local net. 5367024Ssam * Use trailer local net encapsulation if enough data in first 5377024Ssam * packet leaves a multiple of 512 bytes of data in remainder. 5387024Ssam */ 5397024Ssam vvoutput(ifp, m0, dst) 5407024Ssam struct ifnet *ifp; 5417024Ssam struct mbuf *m0; 5427024Ssam struct sockaddr *dst; 5437024Ssam { 5447024Ssam register struct mbuf *m = m0; 5457024Ssam register struct vv_header *vv; 546*7640Ssam register int off; 547*7640Ssam int type, dest, s, error; 5487024Ssam 5497024Ssam switch (dst->sa_family) { 5507024Ssam #ifdef INET 5517024Ssam case AF_INET: { 552*7640Ssam dest = ((struct sockaddr_in *)dst)->sin_addr.s_addr; 553*7640Ssam if (dest & 0x00ffff00) { 554*7640Ssam error = EPERM; 555*7640Ssam goto bad; 556*7640Ssam } 557*7640Ssam dest = (dest >> 24) & 0xff; 558*7640Ssam off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; 559*7640Ssam if (vv_dotrailer && off > 0 && (off & 0x1ff) == 0 && 560*7640Ssam m->m_off >= MMINOFF + 2 * sizeof (u_short)) { 561*7640Ssam type = RING_IPTrailer + (off>>9); 562*7640Ssam m->m_off -= 2 * sizeof (u_short); 563*7640Ssam m->m_len += 2 * sizeof (u_short); 564*7640Ssam *mtod(m, u_short *) = RING_IP; 565*7640Ssam *(mtod(m, u_short *) + 1) = m->m_len; 566*7640Ssam goto gottrailertype; 567*7640Ssam } 5687024Ssam type = RING_IP; 5697024Ssam off = 0; 5707024Ssam goto gottype; 5717024Ssam } 5727024Ssam #endif 5737024Ssam default: 5747024Ssam printf("vv%d: can't handle af%d\n", ifp->if_unit, 5757024Ssam dst->sa_family); 576*7640Ssam error = EAFNOSUPPORT; 577*7640Ssam goto bad; 5787024Ssam } 5797024Ssam 5807024Ssam gottrailertype: 5817024Ssam /* 5827024Ssam * Packet to be sent as trailer: move first packet 5837024Ssam * (control information) to end of chain. 5847024Ssam */ 5857024Ssam while (m->m_next) 5867024Ssam m = m->m_next; 5877024Ssam m->m_next = m0; 5887024Ssam m = m0->m_next; 5897024Ssam m0->m_next = 0; 5907024Ssam m0 = m; 5917024Ssam 5927024Ssam gottype: 5937024Ssam /* 5947024Ssam * Add local net header. If no space in first mbuf, 5957024Ssam * allocate another. 5967024Ssam */ 5977024Ssam if (m->m_off > MMAXOFF || 5987024Ssam MMINOFF + sizeof (struct vv_header) > m->m_off) { 5997024Ssam m = m_get(M_DONTWAIT); 6007024Ssam if (m == 0) { 601*7640Ssam error = ENOBUFS; 602*7640Ssam goto bad; 6037024Ssam } 6047024Ssam m->m_next = m0; 6057024Ssam m->m_off = MMINOFF; 6067024Ssam m->m_len = sizeof (struct vv_header); 6077024Ssam } else { 6087024Ssam m->m_off -= sizeof (struct vv_header); 6097024Ssam m->m_len += sizeof (struct vv_header); 6107024Ssam } 6117024Ssam vv = mtod(m, struct vv_header *); 6127024Ssam vv->vh_shost = ifp->if_host[0]; 6137024Ssam vv->vh_dhost = dest; 6147024Ssam vv->vh_version = RING_VERSION; 6157024Ssam vv->vh_type = type; 616*7640Ssam vv->vh_info = off; 617*7640Ssam if (vv_trace) 618*7640Ssam vvprt_hdr("vo", vv); 6197024Ssam 6207024Ssam /* 6217024Ssam * Queue message on interface, and start output if interface 6227024Ssam * not yet active. 6237024Ssam */ 6247024Ssam s = splimp(); 625*7640Ssam if (IF_QFULL(&ifp->if_snd)) { 626*7640Ssam IF_DROP(&ifp->if_snd); 627*7640Ssam error = ENOBUFS; 628*7640Ssam goto qfull; 629*7640Ssam } 6307024Ssam IF_ENQUEUE(&ifp->if_snd, m); 6317024Ssam if (vv_softc[ifp->if_unit].vs_oactive == 0) 6327024Ssam vvstart(ifp->if_unit); 6337024Ssam splx(s); 634*7640Ssam return (0); 635*7640Ssam 636*7640Ssam qfull: 637*7640Ssam m0 = m; 638*7640Ssam splx(s); 639*7640Ssam bad: 640*7640Ssam m_freem(m0); 641*7640Ssam return(error); 6427024Ssam } 6437024Ssam 6447024Ssam /* 6457024Ssam * vvprt_hdr(s, v) print the local net header in "v" 6467024Ssam * with title is "s" 6477024Ssam */ 6487024Ssam vvprt_hdr(s, v) 6497024Ssam char *s; 6507024Ssam register struct vv_header *v; 6517024Ssam { 6527024Ssam printf("%s: dsvti: 0x%x 0x%x 0x%x 0x%x 0x%x\n", 6537024Ssam s, 6547024Ssam 0xff & (int)(v->vh_dhost), 0xff & (int)(v->vh_shost), 6557024Ssam 0xff & (int)(v->vh_version), 0xff & (int)(v->vh_type), 6567024Ssam 0xffff & (int)(v->vh_info)); 6577024Ssam } 6587024Ssam 6597024Ssam /* 6607024Ssam * print "l" hex bytes starting at "s" 6617024Ssam */ 6627024Ssam vvprt_hex(s, l) 6637024Ssam char *s; 6647024Ssam int l; 6657024Ssam { 6667024Ssam register int i; 6677024Ssam register int z; 6687024Ssam 6697024Ssam for (i=0 ; i < l; i++) { 6707024Ssam z = 0xff & (int)(*(s + i)); 6717024Ssam printf("%c%c ", 6727024Ssam "0123456789abcdef"[(z >> 4) & 0x0f], 6737024Ssam "0123456789abcdef"[z & 0x0f] 6747024Ssam ); 6757024Ssam } 6767024Ssam } 677