1*23557Ssklower /* 2*23557Ssklower * Copyright (c) 1982 Regents of the University of California. 3*23557Ssklower * All rights reserved. The Berkeley software License Agreement 4*23557Ssklower * specifies the terms and conditions for redistribution. 5*23557Ssklower * 6*23557Ssklower * @(#)if_il.c 6.8 (Berkeley) 06/19/85 7*23557Ssklower */ 86893Sfeldman 96893Sfeldman #include "il.h" 106893Sfeldman 116893Sfeldman /* 126893Sfeldman * Interlan Ethernet Communications Controller interface 136893Sfeldman */ 149797Ssam #include "../machine/pte.h" 159797Ssam 1617115Sbloom #include "param.h" 1717115Sbloom #include "systm.h" 1817115Sbloom #include "mbuf.h" 1917115Sbloom #include "buf.h" 2017115Sbloom #include "protosw.h" 2117115Sbloom #include "socket.h" 2217115Sbloom #include "vmmac.h" 2317115Sbloom #include "ioctl.h" 2417115Sbloom #include "errno.h" 258463Sroot 268463Sroot #include "../net/if.h" 278463Sroot #include "../net/netisr.h" 288463Sroot #include "../net/route.h" 29*23557Ssklower 30*23557Ssklower #ifdef INET 318419Swnj #include "../netinet/in.h" 328419Swnj #include "../netinet/in_systm.h" 3319865Skarels #include "../netinet/in_var.h" 348419Swnj #include "../netinet/ip.h" 358419Swnj #include "../netinet/ip_var.h" 3611575Ssam #include "../netinet/if_ether.h" 37*23557Ssklower #endif 38*23557Ssklower 3919949Sbloom #ifdef PUP 408419Swnj #include "../netpup/pup.h" 4119949Sbloom #endif 426893Sfeldman 43*23557Ssklower #ifdef NS 44*23557Ssklower #include "../netns/ns.h" 45*23557Ssklower #include "../netns/ns_if.h" 46*23557Ssklower #endif 47*23557Ssklower 488463Sroot #include "../vax/cpu.h" 498463Sroot #include "../vax/mtpr.h" 5017115Sbloom #include "if_il.h" 5117115Sbloom #include "if_ilreg.h" 5217115Sbloom #include "if_uba.h" 538463Sroot #include "../vaxuba/ubareg.h" 548463Sroot #include "../vaxuba/ubavar.h" 558463Sroot 566893Sfeldman int ilprobe(), ilattach(), ilrint(), ilcint(); 576893Sfeldman struct uba_device *ilinfo[NIL]; 586893Sfeldman u_short ilstd[] = { 0 }; 596893Sfeldman struct uba_driver ildriver = 606893Sfeldman { ilprobe, 0, ilattach, 0, ilstd, "il", ilinfo }; 616893Sfeldman #define ILUNIT(x) minor(x) 6213056Ssam int ilinit(),iloutput(),ilioctl(),ilreset(),ilwatch(); 636893Sfeldman 646893Sfeldman /* 656893Sfeldman * Ethernet software status per interface. 666893Sfeldman * 676893Sfeldman * Each interface is referenced by a network interface structure, 686893Sfeldman * is_if, which the routing code uses to locate the interface. 696893Sfeldman * This structure contains the output queue for the interface, its address, ... 706893Sfeldman * We also have, for each interface, a UBA interface structure, which 716893Sfeldman * contains information about the UNIBUS resources held by the interface: 726893Sfeldman * map registers, buffered data paths, etc. Information is cached in this 736893Sfeldman * structure for use by the if_uba.c routines in running the interface 746893Sfeldman * efficiently. 756893Sfeldman */ 766893Sfeldman struct il_softc { 7711575Ssam struct arpcom is_ac; /* Ethernet common part */ 7811575Ssam #define is_if is_ac.ac_if /* network-visible interface */ 7911575Ssam #define is_addr is_ac.ac_enaddr /* hardware Ethernet address */ 806893Sfeldman struct ifuba is_ifuba; /* UNIBUS resources */ 817261Ssam int is_flags; 827261Ssam #define ILF_OACTIVE 0x1 /* output is active */ 837261Ssam #define ILF_RCVPENDING 0x2 /* start rcv in ilcint */ 847261Ssam #define ILF_STATPENDING 0x4 /* stat cmd pending */ 857261Ssam short is_lastcmd; /* can't read csr, so must save it */ 867261Ssam short is_scaninterval; /* interval of stat collection */ 877261Ssam #define ILWATCHINTERVAL 60 /* once every 60 seconds */ 887261Ssam struct il_stats is_stats; /* holds on-board statistics */ 897261Ssam struct il_stats is_sum; /* summation over time */ 907261Ssam int is_ubaddr; /* mapping registers of is_stats */ 916893Sfeldman } il_softc[NIL]; 926893Sfeldman 936893Sfeldman ilprobe(reg) 946893Sfeldman caddr_t reg; 956893Sfeldman { 966893Sfeldman register int br, cvec; /* r11, r10 value-result */ 976893Sfeldman register struct ildevice *addr = (struct ildevice *)reg; 986893Sfeldman register i; 996893Sfeldman 1006893Sfeldman #ifdef lint 1016893Sfeldman br = 0; cvec = br; br = cvec; 1029179Ssam i = 0; ilrint(i); ilcint(i); ilwatch(i); 1036893Sfeldman #endif 1046893Sfeldman 1056893Sfeldman addr->il_csr = ILC_OFFLINE|IL_CIE; 1066893Sfeldman DELAY(100000); 1077261Ssam i = addr->il_csr; /* clear CDONE */ 1086893Sfeldman if (cvec > 0 && cvec != 0x200) 1096893Sfeldman cvec -= 4; 1106893Sfeldman return (1); 1116893Sfeldman } 1126893Sfeldman 1136893Sfeldman /* 1146893Sfeldman * Interface exists: make available by filling in network interface 1156893Sfeldman * record. System will initialize the interface when it is ready 1166893Sfeldman * to accept packets. A STATUS command is done to get the ethernet 1176893Sfeldman * address and other interesting data. 1186893Sfeldman */ 1196893Sfeldman ilattach(ui) 1206893Sfeldman struct uba_device *ui; 1216893Sfeldman { 1226893Sfeldman register struct il_softc *is = &il_softc[ui->ui_unit]; 1237220Ssam register struct ifnet *ifp = &is->is_if; 1246893Sfeldman register struct ildevice *addr = (struct ildevice *)ui->ui_addr; 1256893Sfeldman 1267220Ssam ifp->if_unit = ui->ui_unit; 1277220Ssam ifp->if_name = "il"; 1289746Ssam ifp->if_mtu = ETHERMTU; 12919865Skarels ifp->if_flags = IFF_BROADCAST; 1306893Sfeldman 1316893Sfeldman /* 1327261Ssam * Reset the board and map the statistics 1337261Ssam * buffer onto the Unibus. 1346893Sfeldman */ 1357261Ssam addr->il_csr = ILC_RESET; 1367261Ssam while ((addr->il_csr&IL_CDONE) == 0) 1377261Ssam ; 1387261Ssam if (addr->il_csr&IL_STATUS) 1397261Ssam printf("il%d: reset failed, csr=%b\n", ui->ui_unit, 1407261Ssam addr->il_csr, IL_BITS); 1416893Sfeldman 1429179Ssam is->is_ubaddr = uballoc(ui->ui_ubanum, (caddr_t)&is->is_stats, 14313056Ssam sizeof (struct il_stats), 0); 1447261Ssam addr->il_bar = is->is_ubaddr & 0xffff; 1457261Ssam addr->il_bcr = sizeof (struct il_stats); 1467261Ssam addr->il_csr = ((is->is_ubaddr >> 2) & IL_EUA)|ILC_STAT; 1477261Ssam while ((addr->il_csr&IL_CDONE) == 0) 1487261Ssam ; 1497261Ssam if (addr->il_csr&IL_STATUS) 1507261Ssam printf("il%d: status failed, csr=%b\n", ui->ui_unit, 1517261Ssam addr->il_csr, IL_BITS); 1527261Ssam ubarelse(ui->ui_ubanum, &is->is_ubaddr); 15311575Ssam #ifdef notdef 1546893Sfeldman printf("il%d: addr=%x:%x:%x:%x:%x:%x module=%s firmware=%s\n", 1556893Sfeldman ui->ui_unit, 1567261Ssam is->is_stats.ils_addr[0]&0xff, is->is_stats.ils_addr[1]&0xff, 1577261Ssam is->is_stats.ils_addr[2]&0xff, is->is_stats.ils_addr[3]&0xff, 1587261Ssam is->is_stats.ils_addr[4]&0xff, is->is_stats.ils_addr[5]&0xff, 1597261Ssam is->is_stats.ils_module, is->is_stats.ils_firmware); 16011575Ssam #endif 16119865Skarels bcopy((caddr_t)is->is_stats.ils_addr, (caddr_t)is->is_addr, 16219865Skarels sizeof (is->is_addr)); 1637220Ssam ifp->if_init = ilinit; 1647220Ssam ifp->if_output = iloutput; 16513056Ssam ifp->if_ioctl = ilioctl; 1668979Sroot ifp->if_reset = ilreset; 1676893Sfeldman is->is_ifuba.ifu_flags = UBA_CANTWAIT; 1687220Ssam #ifdef notdef 1697220Ssam is->is_ifuba.ifu_flags |= UBA_NEEDBDP; 1707220Ssam #endif 1717220Ssam if_attach(ifp); 1726893Sfeldman } 1736893Sfeldman 1746893Sfeldman /* 1756893Sfeldman * Reset of interface after UNIBUS reset. 1766893Sfeldman * If interface is on specified uba, reset its state. 1776893Sfeldman */ 1786893Sfeldman ilreset(unit, uban) 1796893Sfeldman int unit, uban; 1806893Sfeldman { 1816893Sfeldman register struct uba_device *ui; 1826893Sfeldman 1836893Sfeldman if (unit >= NIL || (ui = ilinfo[unit]) == 0 || ui->ui_alive == 0 || 1846893Sfeldman ui->ui_ubanum != uban) 1856893Sfeldman return; 1866893Sfeldman printf(" il%d", unit); 1876893Sfeldman ilinit(unit); 1886893Sfeldman } 1896893Sfeldman 1906893Sfeldman /* 1916893Sfeldman * Initialization of interface; clear recorded pending 1926893Sfeldman * operations, and reinitialize UNIBUS usage. 1936893Sfeldman */ 1946893Sfeldman ilinit(unit) 1956893Sfeldman int unit; 1966893Sfeldman { 1976893Sfeldman register struct il_softc *is = &il_softc[unit]; 1986893Sfeldman register struct uba_device *ui = ilinfo[unit]; 1996893Sfeldman register struct ildevice *addr; 20013056Ssam register struct ifnet *ifp = &is->is_if; 2017261Ssam int s; 2026893Sfeldman 20319865Skarels /* not yet, if address still unknown */ 20419865Skarels if (ifp->if_addrlist == (struct ifaddr *)0) 20511575Ssam return; 20611575Ssam 20713056Ssam if (ifp->if_flags & IFF_RUNNING) 20819865Skarels return; 2096893Sfeldman if (if_ubainit(&is->is_ifuba, ui->ui_ubanum, 2109746Ssam sizeof (struct il_rheader), (int)btoc(ETHERMTU)) == 0) { 2116893Sfeldman printf("il%d: can't initialize\n", unit); 2126893Sfeldman is->is_if.if_flags &= ~IFF_UP; 2136893Sfeldman return; 2146893Sfeldman } 2159179Ssam is->is_ubaddr = uballoc(ui->ui_ubanum, (caddr_t)&is->is_stats, 21613056Ssam sizeof (struct il_stats), 0); 21715071Skarels ifp->if_watchdog = ilwatch; 21815071Skarels is->is_scaninterval = ILWATCHINTERVAL; 21915071Skarels ifp->if_timer = is->is_scaninterval; 2206893Sfeldman addr = (struct ildevice *)ui->ui_addr; 2216893Sfeldman 2226893Sfeldman /* 2239179Ssam * Turn off source address insertion (it's faster this way), 22412488Ssam * and set board online. Former doesn't work if board is 22512488Ssam * already online (happens on ubareset), so we put it offline 22612488Ssam * first. 2279179Ssam */ 2289179Ssam s = splimp(); 22912488Ssam addr->il_csr = ILC_OFFLINE; 2309179Ssam while ((addr->il_csr & IL_CDONE) == 0) 2319179Ssam ; 23212488Ssam addr->il_csr = ILC_CISA; 2339179Ssam while ((addr->il_csr & IL_CDONE) == 0) 2349179Ssam ; 2359179Ssam /* 2366893Sfeldman * Set board online. 2376893Sfeldman * Hang receive buffer and start any pending 2386893Sfeldman * writes by faking a transmit complete. 2396893Sfeldman * Receive bcr is not a muliple of 4 so buffer 2406893Sfeldman * chaining can't happen. 2416893Sfeldman */ 2426893Sfeldman addr->il_csr = ILC_ONLINE; 2437220Ssam while ((addr->il_csr & IL_CDONE) == 0) 2447220Ssam ; 2456893Sfeldman addr->il_bar = is->is_ifuba.ifu_r.ifrw_info & 0xffff; 2469746Ssam addr->il_bcr = sizeof(struct il_rheader) + ETHERMTU + 6; 2477261Ssam addr->il_csr = 24813056Ssam ((is->is_ifuba.ifu_r.ifrw_info >> 2) & IL_EUA)|ILC_RCV|IL_RIE; 2497220Ssam while ((addr->il_csr & IL_CDONE) == 0) 2507220Ssam ; 2517261Ssam is->is_flags = ILF_OACTIVE; 25219865Skarels is->is_if.if_flags |= IFF_RUNNING; 2537261Ssam is->is_lastcmd = 0; 2546893Sfeldman ilcint(unit); 2556893Sfeldman splx(s); 2566893Sfeldman } 2576893Sfeldman 2586893Sfeldman /* 2596893Sfeldman * Start output on interface. 2606893Sfeldman * Get another datagram to send off of the interface queue, 2616893Sfeldman * and map it to the interface before starting the output. 2626893Sfeldman */ 2636893Sfeldman ilstart(dev) 2646893Sfeldman dev_t dev; 2656893Sfeldman { 2669179Ssam int unit = ILUNIT(dev), len; 2676893Sfeldman struct uba_device *ui = ilinfo[unit]; 2686893Sfeldman register struct il_softc *is = &il_softc[unit]; 2696893Sfeldman register struct ildevice *addr; 2706893Sfeldman struct mbuf *m; 2717220Ssam short csr; 2726893Sfeldman 2736893Sfeldman IF_DEQUEUE(&is->is_if.if_snd, m); 2747261Ssam addr = (struct ildevice *)ui->ui_addr; 2757261Ssam if (m == 0) { 2767261Ssam if ((is->is_flags & ILF_STATPENDING) == 0) 2777261Ssam return; 2789179Ssam addr->il_bar = is->is_ubaddr & 0xffff; 2797261Ssam addr->il_bcr = sizeof (struct il_stats); 2807261Ssam csr = ((is->is_ubaddr >> 2) & IL_EUA)|ILC_STAT|IL_RIE|IL_CIE; 2817261Ssam is->is_flags &= ~ILF_STATPENDING; 2827261Ssam goto startcmd; 2837261Ssam } 2846893Sfeldman len = if_wubaput(&is->is_ifuba, m); 2859179Ssam /* 2869179Ssam * Ensure minimum packet length. 2879179Ssam * This makes the safe assumtion that there are no virtual holes 2889179Ssam * after the data. 2899179Ssam * For security, it might be wise to zero out the added bytes, 2909179Ssam * but we're mainly interested in speed at the moment. 2919179Ssam */ 2929746Ssam if (len - sizeof(struct ether_header) < ETHERMIN) 2939746Ssam len = ETHERMIN + sizeof(struct ether_header); 2946893Sfeldman if (is->is_ifuba.ifu_flags & UBA_NEEDBDP) 2956893Sfeldman UBAPURGE(is->is_ifuba.ifu_uba, is->is_ifuba.ifu_w.ifrw_bdp); 2966893Sfeldman addr->il_bar = is->is_ifuba.ifu_w.ifrw_info & 0xffff; 2976893Sfeldman addr->il_bcr = len; 2987261Ssam csr = 2997261Ssam ((is->is_ifuba.ifu_w.ifrw_info >> 2) & IL_EUA)|ILC_XMIT|IL_CIE|IL_RIE; 3007261Ssam 3017261Ssam startcmd: 3027261Ssam is->is_lastcmd = csr & IL_CMD; 3037220Ssam addr->il_csr = csr; 3047261Ssam is->is_flags |= ILF_OACTIVE; 3056893Sfeldman } 3066893Sfeldman 3076893Sfeldman /* 3086893Sfeldman * Command done interrupt. 3096893Sfeldman */ 3106893Sfeldman ilcint(unit) 3116893Sfeldman int unit; 3126893Sfeldman { 3136893Sfeldman register struct il_softc *is = &il_softc[unit]; 3147220Ssam struct uba_device *ui = ilinfo[unit]; 3156893Sfeldman register struct ildevice *addr = (struct ildevice *)ui->ui_addr; 3167266Ssam short csr; 3176893Sfeldman 3187261Ssam if ((is->is_flags & ILF_OACTIVE) == 0) { 3197220Ssam printf("il%d: stray xmit interrupt, csr=%b\n", unit, 3207261Ssam addr->il_csr, IL_BITS); 3216893Sfeldman return; 3226893Sfeldman } 3237220Ssam 3247266Ssam csr = addr->il_csr; 3256893Sfeldman /* 3267261Ssam * Hang receive buffer if it couldn't 3277261Ssam * be done earlier (in ilrint). 3286893Sfeldman */ 3297261Ssam if (is->is_flags & ILF_RCVPENDING) { 3306893Sfeldman addr->il_bar = is->is_ifuba.ifu_r.ifrw_info & 0xffff; 3319746Ssam addr->il_bcr = sizeof(struct il_rheader) + ETHERMTU + 6; 3327261Ssam addr->il_csr = 3337261Ssam ((is->is_ifuba.ifu_r.ifrw_info >> 2) & IL_EUA)|ILC_RCV|IL_RIE; 3347220Ssam while ((addr->il_csr & IL_CDONE) == 0) 3357220Ssam ; 3367261Ssam is->is_flags &= ~ILF_RCVPENDING; 3376893Sfeldman } 3387261Ssam is->is_flags &= ~ILF_OACTIVE; 3397266Ssam csr &= IL_STATUS; 3407261Ssam switch (is->is_lastcmd) { 3417261Ssam 3427261Ssam case ILC_XMIT: 3437261Ssam is->is_if.if_opackets++; 3447266Ssam if (csr > ILERR_RETRIES) 3457261Ssam is->is_if.if_oerrors++; 3467261Ssam break; 3477261Ssam 3487261Ssam case ILC_STAT: 3497266Ssam if (csr == ILERR_SUCCESS) 3507261Ssam iltotal(is); 3517261Ssam break; 3527261Ssam } 3536893Sfeldman if (is->is_ifuba.ifu_xtofree) { 3546893Sfeldman m_freem(is->is_ifuba.ifu_xtofree); 3556893Sfeldman is->is_ifuba.ifu_xtofree = 0; 3566893Sfeldman } 3577261Ssam ilstart(unit); 3586893Sfeldman } 3596893Sfeldman 3606893Sfeldman /* 3616893Sfeldman * Ethernet interface receiver interrupt. 3626893Sfeldman * If input error just drop packet. 3636893Sfeldman * Otherwise purge input buffered data path and examine 3646893Sfeldman * packet to determine type. If can't determine length 3656893Sfeldman * from type, then have to drop packet. Othewise decapsulate 3666893Sfeldman * packet based on type and pass to type specific higher-level 3676893Sfeldman * input routine. 3686893Sfeldman */ 3696893Sfeldman ilrint(unit) 3706893Sfeldman int unit; 3716893Sfeldman { 3726893Sfeldman register struct il_softc *is = &il_softc[unit]; 3736893Sfeldman struct ildevice *addr = (struct ildevice *)ilinfo[unit]->ui_addr; 3746893Sfeldman register struct il_rheader *il; 3756893Sfeldman struct mbuf *m; 37615787Sleres int len, off, resid, s; 3776893Sfeldman register struct ifqueue *inq; 3786893Sfeldman 3796893Sfeldman is->is_if.if_ipackets++; 3806893Sfeldman if (is->is_ifuba.ifu_flags & UBA_NEEDBDP) 3816893Sfeldman UBAPURGE(is->is_ifuba.ifu_uba, is->is_ifuba.ifu_r.ifrw_bdp); 3826893Sfeldman il = (struct il_rheader *)(is->is_ifuba.ifu_r.ifrw_addr); 3836893Sfeldman len = il->ilr_length - sizeof(struct il_rheader); 3849746Ssam if ((il->ilr_status&(ILFSTAT_A|ILFSTAT_C)) || len < 46 || 3859746Ssam len > ETHERMTU) { 3866893Sfeldman is->is_if.if_ierrors++; 3876893Sfeldman #ifdef notdef 3886893Sfeldman if (is->is_if.if_ierrors % 100 == 0) 3896893Sfeldman printf("il%d: += 100 input errors\n", unit); 3906893Sfeldman #endif 3916893Sfeldman goto setup; 3926893Sfeldman } 3936893Sfeldman 3946893Sfeldman /* 39519865Skarels * Deal with trailer protocol: if type is trailer type 3966893Sfeldman * get true type from first 16-bit word past data. 3976893Sfeldman * Remember that type was trailer by setting off. 3986893Sfeldman */ 3999746Ssam il->ilr_type = ntohs((u_short)il->ilr_type); 4006893Sfeldman #define ildataaddr(il, off, type) ((type)(((caddr_t)((il)+1)+(off)))) 40119865Skarels if (il->ilr_type >= ETHERTYPE_TRAIL && 40219865Skarels il->ilr_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { 40319865Skarels off = (il->ilr_type - ETHERTYPE_TRAIL) * 512; 4049746Ssam if (off >= ETHERMTU) 4056893Sfeldman goto setup; /* sanity */ 4069746Ssam il->ilr_type = ntohs(*ildataaddr(il, off, u_short *)); 4079746Ssam resid = ntohs(*(ildataaddr(il, off+2, u_short *))); 4086893Sfeldman if (off + resid > len) 4096893Sfeldman goto setup; /* sanity */ 4106893Sfeldman len = off + resid; 4116893Sfeldman } else 4126893Sfeldman off = 0; 4136893Sfeldman if (len == 0) 4146893Sfeldman goto setup; 4156893Sfeldman 4166893Sfeldman /* 4176893Sfeldman * Pull packet off interface. Off is nonzero if packet 4186893Sfeldman * has trailing header; ilget will then force this header 4196893Sfeldman * information to be at the front, but we still have to drop 4206893Sfeldman * the type and length which are at the front of any trailer data. 4216893Sfeldman */ 4226893Sfeldman m = if_rubaget(&is->is_ifuba, len, off); 4236893Sfeldman if (m == 0) 4246893Sfeldman goto setup; 4256893Sfeldman if (off) { 4266893Sfeldman m->m_off += 2 * sizeof (u_short); 4276893Sfeldman m->m_len -= 2 * sizeof (u_short); 4286893Sfeldman } 4296893Sfeldman switch (il->ilr_type) { 4306893Sfeldman 4316893Sfeldman #ifdef INET 43219865Skarels case ETHERTYPE_IP: 4336893Sfeldman schednetisr(NETISR_IP); 4346893Sfeldman inq = &ipintrq; 4356893Sfeldman break; 43611575Ssam 43719865Skarels case ETHERTYPE_ARP: 43811575Ssam arpinput(&is->is_ac, m); 43913988Ssam goto setup; 4406893Sfeldman #endif 441*23557Ssklower #ifdef NS 442*23557Ssklower case ETHERTYPE_NS: 443*23557Ssklower schednetisr(NETISR_NS); 444*23557Ssklower inq = &nsintrq; 445*23557Ssklower break; 446*23557Ssklower 447*23557Ssklower #endif 4486893Sfeldman default: 4496893Sfeldman m_freem(m); 4506893Sfeldman goto setup; 4516893Sfeldman } 4526893Sfeldman 45315787Sleres s = splimp(); 4546893Sfeldman if (IF_QFULL(inq)) { 4556893Sfeldman IF_DROP(inq); 4566893Sfeldman m_freem(m); 45715787Sleres } else 45815787Sleres IF_ENQUEUE(inq, m); 45915787Sleres splx(s); 4606893Sfeldman 4616893Sfeldman setup: 4626893Sfeldman /* 4636893Sfeldman * Reset for next packet if possible. 4646893Sfeldman * If waiting for transmit command completion, set flag 4656893Sfeldman * and wait until command completes. 4666893Sfeldman */ 4677261Ssam if (is->is_flags & ILF_OACTIVE) { 4687261Ssam is->is_flags |= ILF_RCVPENDING; 4697220Ssam return; 4707220Ssam } 4717220Ssam addr->il_bar = is->is_ifuba.ifu_r.ifrw_info & 0xffff; 4729746Ssam addr->il_bcr = sizeof(struct il_rheader) + ETHERMTU + 6; 4737261Ssam addr->il_csr = 4747261Ssam ((is->is_ifuba.ifu_r.ifrw_info >> 2) & IL_EUA)|ILC_RCV|IL_RIE; 4757220Ssam while ((addr->il_csr & IL_CDONE) == 0) 4767220Ssam ; 4776893Sfeldman } 4786893Sfeldman 4796893Sfeldman /* 4806893Sfeldman * Ethernet output routine. 4816893Sfeldman * Encapsulate a packet of type family for the local net. 4826893Sfeldman * Use trailer local net encapsulation if enough data in first 4836893Sfeldman * packet leaves a multiple of 512 bytes of data in remainder. 4846893Sfeldman */ 4856893Sfeldman iloutput(ifp, m0, dst) 4866893Sfeldman struct ifnet *ifp; 4876893Sfeldman struct mbuf *m0; 4886893Sfeldman struct sockaddr *dst; 4896893Sfeldman { 49011575Ssam int type, s, error; 49119865Skarels u_char edst[6]; 49211575Ssam struct in_addr idst; 4936893Sfeldman register struct il_softc *is = &il_softc[ifp->if_unit]; 4946893Sfeldman register struct mbuf *m = m0; 4959746Ssam register struct ether_header *il; 4969179Ssam register int off; 4976893Sfeldman 4986893Sfeldman switch (dst->sa_family) { 4996893Sfeldman 5006893Sfeldman #ifdef INET 5016893Sfeldman case AF_INET: 50211575Ssam idst = ((struct sockaddr_in *)dst)->sin_addr; 50319865Skarels if (!arpresolve(&is->is_ac, m, &idst, edst)) 50411575Ssam return (0); /* if not yet resolved */ 5056893Sfeldman off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; 50613056Ssam /* need per host negotiation */ 50713056Ssam if ((ifp->if_flags & IFF_NOTRAILERS) == 0) 5086893Sfeldman if (off > 0 && (off & 0x1ff) == 0 && 5096893Sfeldman m->m_off >= MMINOFF + 2 * sizeof (u_short)) { 51019865Skarels type = ETHERTYPE_TRAIL + (off>>9); 5116893Sfeldman m->m_off -= 2 * sizeof (u_short); 5126893Sfeldman m->m_len += 2 * sizeof (u_short); 51319865Skarels *mtod(m, u_short *) = htons((u_short)ETHERTYPE_IP); 5149746Ssam *(mtod(m, u_short *) + 1) = htons((u_short)m->m_len); 5156893Sfeldman goto gottrailertype; 5166893Sfeldman } 51719865Skarels type = ETHERTYPE_IP; 5186893Sfeldman off = 0; 5196893Sfeldman goto gottype; 5206893Sfeldman #endif 521*23557Ssklower #ifdef NS 522*23557Ssklower case AF_NS: 523*23557Ssklower type = ETHERTYPE_NS; 524*23557Ssklower bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host), 525*23557Ssklower (caddr_t)edst, sizeof (edst)); 526*23557Ssklower off = 0; 527*23557Ssklower goto gottype; 528*23557Ssklower #endif 5296893Sfeldman 53011575Ssam case AF_UNSPEC: 53111575Ssam il = (struct ether_header *)dst->sa_data; 53219865Skarels bcopy((caddr_t)il->ether_dhost, (caddr_t)edst, sizeof (edst)); 53311575Ssam type = il->ether_type; 53411575Ssam goto gottype; 53511575Ssam 5366893Sfeldman default: 5376893Sfeldman printf("il%d: can't handle af%d\n", ifp->if_unit, 5386893Sfeldman dst->sa_family); 5396893Sfeldman error = EAFNOSUPPORT; 5406893Sfeldman goto bad; 5416893Sfeldman } 5426893Sfeldman 5436893Sfeldman gottrailertype: 5446893Sfeldman /* 5456893Sfeldman * Packet to be sent as trailer: move first packet 5466893Sfeldman * (control information) to end of chain. 5476893Sfeldman */ 5486893Sfeldman while (m->m_next) 5496893Sfeldman m = m->m_next; 5506893Sfeldman m->m_next = m0; 5516893Sfeldman m = m0->m_next; 5526893Sfeldman m0->m_next = 0; 5536893Sfeldman m0 = m; 5546893Sfeldman 5556893Sfeldman gottype: 5566893Sfeldman /* 5576893Sfeldman * Add local net header. If no space in first mbuf, 5586893Sfeldman * allocate another. 5596893Sfeldman */ 5606893Sfeldman if (m->m_off > MMAXOFF || 5619746Ssam MMINOFF + sizeof (struct ether_header) > m->m_off) { 5629650Ssam m = m_get(M_DONTWAIT, MT_HEADER); 5636893Sfeldman if (m == 0) { 5646893Sfeldman error = ENOBUFS; 5656893Sfeldman goto bad; 5666893Sfeldman } 5676893Sfeldman m->m_next = m0; 5686893Sfeldman m->m_off = MMINOFF; 5699746Ssam m->m_len = sizeof (struct ether_header); 5706893Sfeldman } else { 5719746Ssam m->m_off -= sizeof (struct ether_header); 5729746Ssam m->m_len += sizeof (struct ether_header); 5736893Sfeldman } 5749746Ssam il = mtod(m, struct ether_header *); 5759746Ssam il->ether_type = htons((u_short)type); 57619865Skarels bcopy((caddr_t)edst, (caddr_t)il->ether_dhost, sizeof (edst)); 57719865Skarels bcopy((caddr_t)is->is_addr, (caddr_t)il->ether_shost, 57819865Skarels sizeof(il->ether_shost)); 5796893Sfeldman 5806893Sfeldman /* 5816893Sfeldman * Queue message on interface, and start output if interface 5826893Sfeldman * not yet active. 5836893Sfeldman */ 5846893Sfeldman s = splimp(); 5856893Sfeldman if (IF_QFULL(&ifp->if_snd)) { 5866893Sfeldman IF_DROP(&ifp->if_snd); 5877220Ssam splx(s); 5887220Ssam m_freem(m); 5897220Ssam return (ENOBUFS); 5906893Sfeldman } 5916893Sfeldman IF_ENQUEUE(&ifp->if_snd, m); 5927261Ssam if ((is->is_flags & ILF_OACTIVE) == 0) 5936893Sfeldman ilstart(ifp->if_unit); 5946893Sfeldman splx(s); 5956893Sfeldman return (0); 5967220Ssam 5976893Sfeldman bad: 5986893Sfeldman m_freem(m0); 5997220Ssam return (error); 6006893Sfeldman } 6017261Ssam 6027261Ssam /* 6037261Ssam * Watchdog routine, request statistics from board. 6047261Ssam */ 6057261Ssam ilwatch(unit) 6067261Ssam int unit; 6077261Ssam { 6087261Ssam register struct il_softc *is = &il_softc[unit]; 6097261Ssam register struct ifnet *ifp = &is->is_if; 6107261Ssam int s; 6117261Ssam 6127261Ssam if (is->is_flags & ILF_STATPENDING) { 6137261Ssam ifp->if_timer = is->is_scaninterval; 6147261Ssam return; 6157261Ssam } 6167261Ssam s = splimp(); 6177261Ssam is->is_flags |= ILF_STATPENDING; 6187261Ssam if ((is->is_flags & ILF_OACTIVE) == 0) 6197261Ssam ilstart(ifp->if_unit); 6207261Ssam splx(s); 6217261Ssam ifp->if_timer = is->is_scaninterval; 6227261Ssam } 6237261Ssam 6247261Ssam /* 6257261Ssam * Total up the on-board statistics. 6267261Ssam */ 6277261Ssam iltotal(is) 6287261Ssam register struct il_softc *is; 6297261Ssam { 6307261Ssam register u_short *interval, *sum, *end; 6317261Ssam 6327261Ssam interval = &is->is_stats.ils_frames; 6337261Ssam sum = &is->is_sum.ils_frames; 6347261Ssam end = is->is_sum.ils_fill2; 6357261Ssam while (sum < end) 6367261Ssam *sum++ += *interval++; 6377261Ssam is->is_if.if_collisions = is->is_sum.ils_collis; 6387261Ssam } 63913056Ssam 64013056Ssam /* 64113056Ssam * Process an ioctl request. 64213056Ssam */ 64313056Ssam ilioctl(ifp, cmd, data) 64413056Ssam register struct ifnet *ifp; 64513056Ssam int cmd; 64613056Ssam caddr_t data; 64713056Ssam { 64819865Skarels register struct ifaddr *ifa = (struct ifaddr *)data; 64913056Ssam int s = splimp(), error = 0; 65013056Ssam 65113056Ssam switch (cmd) { 65213056Ssam 65313056Ssam case SIOCSIFADDR: 65419865Skarels ifp->if_flags |= IFF_UP; 65513056Ssam ilinit(ifp->if_unit); 65619865Skarels 65719865Skarels switch (ifa->ifa_addr.sa_family) { 658*23557Ssklower #ifdef INET 65919865Skarels case AF_INET: 66019865Skarels ((struct arpcom *)ifp)->ac_ipaddr = 66119865Skarels IA_SIN(ifa)->sin_addr; 66219865Skarels arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); 66319865Skarels break; 664*23557Ssklower #endif 665*23557Ssklower #ifdef NS 666*23557Ssklower case AF_NS: 667*23557Ssklower IA_SNS(ifa)->sns_addr.x_host = 668*23557Ssklower * (union ns_host *) 669*23557Ssklower (il_softc[ifp->if_unit].is_addr); 670*23557Ssklower break; 671*23557Ssklower #endif 67219865Skarels } 67313056Ssam break; 67413056Ssam 67513056Ssam default: 67613056Ssam error = EINVAL; 67713056Ssam } 67813056Ssam splx(s); 67913056Ssam return (error); 68013056Ssam } 681