123557Ssklower /* 223557Ssklower * Copyright (c) 1982 Regents of the University of California. 323557Ssklower * All rights reserved. The Berkeley software License Agreement 423557Ssklower * specifies the terms and conditions for redistribution. 523557Ssklower * 6*26167Skarels * @(#)if_il.c 6.14 (Berkeley) 02/13/86 723557Ssklower */ 86893Sfeldman 96893Sfeldman #include "il.h" 1025274Sbloom #if NIL > 0 116893Sfeldman 126893Sfeldman /* 136893Sfeldman * Interlan Ethernet Communications Controller interface 146893Sfeldman */ 159797Ssam #include "../machine/pte.h" 169797Ssam 1717115Sbloom #include "param.h" 1817115Sbloom #include "systm.h" 1917115Sbloom #include "mbuf.h" 2017115Sbloom #include "buf.h" 2117115Sbloom #include "protosw.h" 2217115Sbloom #include "socket.h" 2317115Sbloom #include "vmmac.h" 2417115Sbloom #include "ioctl.h" 2517115Sbloom #include "errno.h" 268463Sroot 278463Sroot #include "../net/if.h" 288463Sroot #include "../net/netisr.h" 298463Sroot #include "../net/route.h" 3023557Ssklower 3123557Ssklower #ifdef INET 328419Swnj #include "../netinet/in.h" 338419Swnj #include "../netinet/in_systm.h" 3419865Skarels #include "../netinet/in_var.h" 358419Swnj #include "../netinet/ip.h" 3611575Ssam #include "../netinet/if_ether.h" 3723557Ssklower #endif 3823557Ssklower 3923557Ssklower #ifdef NS 4023557Ssklower #include "../netns/ns.h" 4123557Ssklower #include "../netns/ns_if.h" 4223557Ssklower #endif 4323557Ssklower 448463Sroot #include "../vax/cpu.h" 458463Sroot #include "../vax/mtpr.h" 4617115Sbloom #include "if_il.h" 4717115Sbloom #include "if_ilreg.h" 4817115Sbloom #include "if_uba.h" 498463Sroot #include "../vaxuba/ubareg.h" 508463Sroot #include "../vaxuba/ubavar.h" 518463Sroot 526893Sfeldman int ilprobe(), ilattach(), ilrint(), ilcint(); 536893Sfeldman struct uba_device *ilinfo[NIL]; 546893Sfeldman u_short ilstd[] = { 0 }; 556893Sfeldman struct uba_driver ildriver = 566893Sfeldman { ilprobe, 0, ilattach, 0, ilstd, "il", ilinfo }; 576893Sfeldman #define ILUNIT(x) minor(x) 5813056Ssam int ilinit(),iloutput(),ilioctl(),ilreset(),ilwatch(); 59*26167Skarels int ildebug = 0; 606893Sfeldman 616893Sfeldman /* 626893Sfeldman * Ethernet software status per interface. 636893Sfeldman * 646893Sfeldman * Each interface is referenced by a network interface structure, 656893Sfeldman * is_if, which the routing code uses to locate the interface. 666893Sfeldman * This structure contains the output queue for the interface, its address, ... 676893Sfeldman * We also have, for each interface, a UBA interface structure, which 686893Sfeldman * contains information about the UNIBUS resources held by the interface: 696893Sfeldman * map registers, buffered data paths, etc. Information is cached in this 706893Sfeldman * structure for use by the if_uba.c routines in running the interface 716893Sfeldman * efficiently. 726893Sfeldman */ 736893Sfeldman struct il_softc { 7411575Ssam struct arpcom is_ac; /* Ethernet common part */ 7511575Ssam #define is_if is_ac.ac_if /* network-visible interface */ 7611575Ssam #define is_addr is_ac.ac_enaddr /* hardware Ethernet address */ 776893Sfeldman struct ifuba is_ifuba; /* UNIBUS resources */ 787261Ssam int is_flags; 797261Ssam #define ILF_OACTIVE 0x1 /* output is active */ 807261Ssam #define ILF_RCVPENDING 0x2 /* start rcv in ilcint */ 817261Ssam #define ILF_STATPENDING 0x4 /* stat cmd pending */ 8225444Skarels #define ILF_RUNNING 0x8 /* board is running */ 8325976Skarels #define ILF_SETADDR 0x10 /* physical address is changed */ 847261Ssam short is_lastcmd; /* can't read csr, so must save it */ 857261Ssam short is_scaninterval; /* interval of stat collection */ 867261Ssam #define ILWATCHINTERVAL 60 /* once every 60 seconds */ 877261Ssam struct il_stats is_stats; /* holds on-board statistics */ 887261Ssam struct il_stats is_sum; /* summation over time */ 897261Ssam int is_ubaddr; /* mapping registers of is_stats */ 906893Sfeldman } il_softc[NIL]; 916893Sfeldman 926893Sfeldman ilprobe(reg) 936893Sfeldman caddr_t reg; 946893Sfeldman { 956893Sfeldman register int br, cvec; /* r11, r10 value-result */ 966893Sfeldman register struct ildevice *addr = (struct ildevice *)reg; 976893Sfeldman register i; 986893Sfeldman 996893Sfeldman #ifdef lint 1006893Sfeldman br = 0; cvec = br; br = cvec; 1019179Ssam i = 0; ilrint(i); ilcint(i); ilwatch(i); 1026893Sfeldman #endif 1036893Sfeldman 1046893Sfeldman addr->il_csr = ILC_OFFLINE|IL_CIE; 1056893Sfeldman DELAY(100000); 1067261Ssam i = addr->il_csr; /* clear CDONE */ 1076893Sfeldman if (cvec > 0 && cvec != 0x200) 1086893Sfeldman cvec -= 4; 1096893Sfeldman return (1); 1106893Sfeldman } 1116893Sfeldman 1126893Sfeldman /* 1136893Sfeldman * Interface exists: make available by filling in network interface 1146893Sfeldman * record. System will initialize the interface when it is ready 1156893Sfeldman * to accept packets. A STATUS command is done to get the ethernet 1166893Sfeldman * address and other interesting data. 1176893Sfeldman */ 1186893Sfeldman ilattach(ui) 1196893Sfeldman struct uba_device *ui; 1206893Sfeldman { 1216893Sfeldman register struct il_softc *is = &il_softc[ui->ui_unit]; 1227220Ssam register struct ifnet *ifp = &is->is_if; 1236893Sfeldman register struct ildevice *addr = (struct ildevice *)ui->ui_addr; 1246893Sfeldman 1257220Ssam ifp->if_unit = ui->ui_unit; 1267220Ssam ifp->if_name = "il"; 1279746Ssam ifp->if_mtu = ETHERMTU; 12819865Skarels ifp->if_flags = IFF_BROADCAST; 1296893Sfeldman 1306893Sfeldman /* 1317261Ssam * Reset the board and map the statistics 1327261Ssam * buffer onto the Unibus. 1336893Sfeldman */ 1347261Ssam addr->il_csr = ILC_RESET; 13525976Skarels (void)ilwait(ui, "reset"); 1366893Sfeldman 1379179Ssam is->is_ubaddr = uballoc(ui->ui_ubanum, (caddr_t)&is->is_stats, 13813056Ssam sizeof (struct il_stats), 0); 1397261Ssam addr->il_bar = is->is_ubaddr & 0xffff; 1407261Ssam addr->il_bcr = sizeof (struct il_stats); 1417261Ssam addr->il_csr = ((is->is_ubaddr >> 2) & IL_EUA)|ILC_STAT; 14225976Skarels (void)ilwait(ui, "status"); 1437261Ssam ubarelse(ui->ui_ubanum, &is->is_ubaddr); 144*26167Skarels if (ildebug) 145*26167Skarels printf("il%d: module=%s firmware=%s\n", ui->ui_unit, 146*26167Skarels is->is_stats.ils_module, is->is_stats.ils_firmware); 14719865Skarels bcopy((caddr_t)is->is_stats.ils_addr, (caddr_t)is->is_addr, 14819865Skarels sizeof (is->is_addr)); 14925976Skarels printf("il%d: hardware address %s\n", ui->ui_unit, 15025976Skarels ether_sprintf(is->is_addr)); 1517220Ssam ifp->if_init = ilinit; 1527220Ssam ifp->if_output = iloutput; 15313056Ssam ifp->if_ioctl = ilioctl; 1548979Sroot ifp->if_reset = ilreset; 1556893Sfeldman is->is_ifuba.ifu_flags = UBA_CANTWAIT; 1567220Ssam if_attach(ifp); 1576893Sfeldman } 1586893Sfeldman 15925976Skarels ilwait(ui, op) 16025976Skarels struct uba_device *ui; 16125976Skarels char *op; 16225976Skarels { 16325976Skarels register struct ildevice *addr = (struct ildevice *)ui->ui_addr; 16425976Skarels 16525976Skarels while ((addr->il_csr&IL_CDONE) == 0) 16625976Skarels ; 16725976Skarels if (addr->il_csr&IL_STATUS) { 16825976Skarels printf("il%d: %s failed, csr=%b\n", ui->ui_unit, op, 16925976Skarels addr->il_csr, IL_BITS); 17025976Skarels return (-1); 17125976Skarels } 17225976Skarels return (0); 17325976Skarels } 17425976Skarels 1756893Sfeldman /* 1766893Sfeldman * Reset of interface after UNIBUS reset. 1776893Sfeldman * If interface is on specified uba, reset its state. 1786893Sfeldman */ 1796893Sfeldman ilreset(unit, uban) 1806893Sfeldman int unit, uban; 1816893Sfeldman { 1826893Sfeldman register struct uba_device *ui; 1836893Sfeldman 1846893Sfeldman if (unit >= NIL || (ui = ilinfo[unit]) == 0 || ui->ui_alive == 0 || 1856893Sfeldman ui->ui_ubanum != uban) 1866893Sfeldman return; 1876893Sfeldman printf(" il%d", unit); 18825444Skarels il_softc[unit].is_if.if_flags &= ~IFF_RUNNING; 18925444Skarels il_softc[unit].is_flags &= ~ILF_RUNNING; 1906893Sfeldman ilinit(unit); 1916893Sfeldman } 1926893Sfeldman 1936893Sfeldman /* 1946893Sfeldman * Initialization of interface; clear recorded pending 1956893Sfeldman * operations, and reinitialize UNIBUS usage. 1966893Sfeldman */ 1976893Sfeldman ilinit(unit) 1986893Sfeldman int unit; 1996893Sfeldman { 2006893Sfeldman register struct il_softc *is = &il_softc[unit]; 2016893Sfeldman register struct uba_device *ui = ilinfo[unit]; 2026893Sfeldman register struct ildevice *addr; 20313056Ssam register struct ifnet *ifp = &is->is_if; 2047261Ssam int s; 2056893Sfeldman 20619865Skarels /* not yet, if address still unknown */ 20719865Skarels if (ifp->if_addrlist == (struct ifaddr *)0) 20811575Ssam return; 20925444Skarels if (is->is_flags & ILF_RUNNING) 21025444Skarels return; 21111575Ssam 21225444Skarels if ((ifp->if_flags & IFF_RUNNING) == 0) { 21325444Skarels if (if_ubainit(&is->is_ifuba, ui->ui_ubanum, 21425444Skarels sizeof (struct il_rheader), (int)btoc(ETHERMTU)) == 0) { 21525444Skarels printf("il%d: can't initialize\n", unit); 21625444Skarels is->is_if.if_flags &= ~IFF_UP; 21725444Skarels return; 21825444Skarels } 21925444Skarels is->is_ubaddr = uballoc(ui->ui_ubanum, (caddr_t)&is->is_stats, 22025444Skarels sizeof (struct il_stats), 0); 2216893Sfeldman } 22215071Skarels ifp->if_watchdog = ilwatch; 22315071Skarels is->is_scaninterval = ILWATCHINTERVAL; 22415071Skarels ifp->if_timer = is->is_scaninterval; 2256893Sfeldman addr = (struct ildevice *)ui->ui_addr; 2266893Sfeldman 2276893Sfeldman /* 2289179Ssam * Turn off source address insertion (it's faster this way), 22912488Ssam * and set board online. Former doesn't work if board is 23012488Ssam * already online (happens on ubareset), so we put it offline 23112488Ssam * first. 2329179Ssam */ 2339179Ssam s = splimp(); 23425444Skarels addr->il_csr = ILC_RESET; 23525976Skarels if (ilwait(ui, "hardware diag")) { 23625444Skarels is->is_if.if_flags &= ~IFF_UP; 23725444Skarels splx(s); 23825444Skarels return; 23925444Skarels } 24012488Ssam addr->il_csr = ILC_CISA; 2419179Ssam while ((addr->il_csr & IL_CDONE) == 0) 2429179Ssam ; 2439179Ssam /* 24426145Ssklower * If we must reprogram this board's physical ethernet 24526145Ssklower * address (as for secondary XNS interfaces), we do so 24626145Ssklower * before putting it on line, and starting receive requests. 24726145Ssklower * If you try this on an older 1010 board, it will total 24826145Ssklower * wedge the board. 24926145Ssklower */ 25026145Ssklower if (is->is_flags & ILF_SETADDR) { 25126145Ssklower bcopy((caddr_t)is->is_addr, (caddr_t)&is->is_stats, 25226145Ssklower sizeof is->is_addr); 25326145Ssklower addr->il_bar = is->is_ubaddr & 0xffff; 25426145Ssklower addr->il_bcr = sizeof is->is_addr; 25526145Ssklower addr->il_csr = ((is->is_ubaddr >> 2) & IL_EUA)|ILC_LDPA; 25626145Ssklower if (ilwait(ui, "setaddr")) 25726145Ssklower return; 25826145Ssklower addr->il_bar = is->is_ubaddr & 0xffff; 25926145Ssklower addr->il_bcr = sizeof (struct il_stats); 26026145Ssklower addr->il_csr = ((is->is_ubaddr >> 2) & IL_EUA)|ILC_STAT; 26126145Ssklower if (ilwait(ui, "verifying setaddr")) 26226145Ssklower return; 26326145Ssklower if (bcmp((caddr_t)is->is_stats.ils_addr, (caddr_t)is->is_addr, 26426145Ssklower sizeof (is->is_addr)) != 0) { 26526145Ssklower printf("il%d: setaddr didn't work\n", ui->ui_unit); 26626145Ssklower return; 26726145Ssklower } 26826145Ssklower } 26926145Ssklower /* 2706893Sfeldman * Set board online. 2716893Sfeldman * Hang receive buffer and start any pending 2726893Sfeldman * writes by faking a transmit complete. 27325444Skarels * Receive bcr is not a multiple of 8 so buffer 2746893Sfeldman * chaining can't happen. 2756893Sfeldman */ 2766893Sfeldman addr->il_csr = ILC_ONLINE; 2777220Ssam while ((addr->il_csr & IL_CDONE) == 0) 2787220Ssam ; 2796893Sfeldman addr->il_bar = is->is_ifuba.ifu_r.ifrw_info & 0xffff; 2809746Ssam addr->il_bcr = sizeof(struct il_rheader) + ETHERMTU + 6; 2817261Ssam addr->il_csr = 28213056Ssam ((is->is_ifuba.ifu_r.ifrw_info >> 2) & IL_EUA)|ILC_RCV|IL_RIE; 2837220Ssam while ((addr->il_csr & IL_CDONE) == 0) 2847220Ssam ; 2857261Ssam is->is_flags = ILF_OACTIVE; 28619865Skarels is->is_if.if_flags |= IFF_RUNNING; 28725444Skarels is->is_flags |= ILF_RUNNING; 2887261Ssam is->is_lastcmd = 0; 2896893Sfeldman ilcint(unit); 2906893Sfeldman splx(s); 2916893Sfeldman } 2926893Sfeldman 2936893Sfeldman /* 2946893Sfeldman * Start output on interface. 2956893Sfeldman * Get another datagram to send off of the interface queue, 2966893Sfeldman * and map it to the interface before starting the output. 2976893Sfeldman */ 2986893Sfeldman ilstart(dev) 2996893Sfeldman dev_t dev; 3006893Sfeldman { 3019179Ssam int unit = ILUNIT(dev), len; 3026893Sfeldman struct uba_device *ui = ilinfo[unit]; 3036893Sfeldman register struct il_softc *is = &il_softc[unit]; 3046893Sfeldman register struct ildevice *addr; 3056893Sfeldman struct mbuf *m; 3067220Ssam short csr; 3076893Sfeldman 3086893Sfeldman IF_DEQUEUE(&is->is_if.if_snd, m); 3097261Ssam addr = (struct ildevice *)ui->ui_addr; 3107261Ssam if (m == 0) { 3117261Ssam if ((is->is_flags & ILF_STATPENDING) == 0) 3127261Ssam return; 3139179Ssam addr->il_bar = is->is_ubaddr & 0xffff; 3147261Ssam addr->il_bcr = sizeof (struct il_stats); 3157261Ssam csr = ((is->is_ubaddr >> 2) & IL_EUA)|ILC_STAT|IL_RIE|IL_CIE; 3167261Ssam is->is_flags &= ~ILF_STATPENDING; 3177261Ssam goto startcmd; 3187261Ssam } 3196893Sfeldman len = if_wubaput(&is->is_ifuba, m); 3209179Ssam /* 3219179Ssam * Ensure minimum packet length. 3229179Ssam * This makes the safe assumtion that there are no virtual holes 3239179Ssam * after the data. 3249179Ssam * For security, it might be wise to zero out the added bytes, 3259179Ssam * but we're mainly interested in speed at the moment. 3269179Ssam */ 3279746Ssam if (len - sizeof(struct ether_header) < ETHERMIN) 3289746Ssam len = ETHERMIN + sizeof(struct ether_header); 3296893Sfeldman if (is->is_ifuba.ifu_flags & UBA_NEEDBDP) 3306893Sfeldman UBAPURGE(is->is_ifuba.ifu_uba, is->is_ifuba.ifu_w.ifrw_bdp); 3316893Sfeldman addr->il_bar = is->is_ifuba.ifu_w.ifrw_info & 0xffff; 3326893Sfeldman addr->il_bcr = len; 3337261Ssam csr = 3347261Ssam ((is->is_ifuba.ifu_w.ifrw_info >> 2) & IL_EUA)|ILC_XMIT|IL_CIE|IL_RIE; 3357261Ssam 3367261Ssam startcmd: 3377261Ssam is->is_lastcmd = csr & IL_CMD; 3387220Ssam addr->il_csr = csr; 3397261Ssam is->is_flags |= ILF_OACTIVE; 3406893Sfeldman } 3416893Sfeldman 3426893Sfeldman /* 3436893Sfeldman * Command done interrupt. 3446893Sfeldman */ 3456893Sfeldman ilcint(unit) 3466893Sfeldman int unit; 3476893Sfeldman { 3486893Sfeldman register struct il_softc *is = &il_softc[unit]; 3497220Ssam struct uba_device *ui = ilinfo[unit]; 3506893Sfeldman register struct ildevice *addr = (struct ildevice *)ui->ui_addr; 3517266Ssam short csr; 3526893Sfeldman 3537261Ssam if ((is->is_flags & ILF_OACTIVE) == 0) { 3547220Ssam printf("il%d: stray xmit interrupt, csr=%b\n", unit, 3557261Ssam addr->il_csr, IL_BITS); 3566893Sfeldman return; 3576893Sfeldman } 3587220Ssam 3597266Ssam csr = addr->il_csr; 3606893Sfeldman /* 3617261Ssam * Hang receive buffer if it couldn't 3627261Ssam * be done earlier (in ilrint). 3636893Sfeldman */ 3647261Ssam if (is->is_flags & ILF_RCVPENDING) { 36525444Skarels int s; 36625444Skarels 3676893Sfeldman addr->il_bar = is->is_ifuba.ifu_r.ifrw_info & 0xffff; 3689746Ssam addr->il_bcr = sizeof(struct il_rheader) + ETHERMTU + 6; 3697261Ssam addr->il_csr = 3707261Ssam ((is->is_ifuba.ifu_r.ifrw_info >> 2) & IL_EUA)|ILC_RCV|IL_RIE; 37125444Skarels s = splhigh(); 3727220Ssam while ((addr->il_csr & IL_CDONE) == 0) 3737220Ssam ; 37425444Skarels splx(s); 3757261Ssam is->is_flags &= ~ILF_RCVPENDING; 3766893Sfeldman } 3777261Ssam is->is_flags &= ~ILF_OACTIVE; 3787266Ssam csr &= IL_STATUS; 3797261Ssam switch (is->is_lastcmd) { 3807261Ssam 3817261Ssam case ILC_XMIT: 3827261Ssam is->is_if.if_opackets++; 3837266Ssam if (csr > ILERR_RETRIES) 3847261Ssam is->is_if.if_oerrors++; 3857261Ssam break; 3867261Ssam 3877261Ssam case ILC_STAT: 3887266Ssam if (csr == ILERR_SUCCESS) 3897261Ssam iltotal(is); 3907261Ssam break; 3917261Ssam } 3926893Sfeldman if (is->is_ifuba.ifu_xtofree) { 3936893Sfeldman m_freem(is->is_ifuba.ifu_xtofree); 3946893Sfeldman is->is_ifuba.ifu_xtofree = 0; 3956893Sfeldman } 3967261Ssam ilstart(unit); 3976893Sfeldman } 3986893Sfeldman 3996893Sfeldman /* 4006893Sfeldman * Ethernet interface receiver interrupt. 4016893Sfeldman * If input error just drop packet. 4026893Sfeldman * Otherwise purge input buffered data path and examine 4036893Sfeldman * packet to determine type. If can't determine length 4046893Sfeldman * from type, then have to drop packet. Othewise decapsulate 4056893Sfeldman * packet based on type and pass to type specific higher-level 4066893Sfeldman * input routine. 4076893Sfeldman */ 4086893Sfeldman ilrint(unit) 4096893Sfeldman int unit; 4106893Sfeldman { 4116893Sfeldman register struct il_softc *is = &il_softc[unit]; 4126893Sfeldman struct ildevice *addr = (struct ildevice *)ilinfo[unit]->ui_addr; 4136893Sfeldman register struct il_rheader *il; 4146893Sfeldman struct mbuf *m; 41515787Sleres int len, off, resid, s; 4166893Sfeldman register struct ifqueue *inq; 4176893Sfeldman 4186893Sfeldman is->is_if.if_ipackets++; 4196893Sfeldman if (is->is_ifuba.ifu_flags & UBA_NEEDBDP) 4206893Sfeldman UBAPURGE(is->is_ifuba.ifu_uba, is->is_ifuba.ifu_r.ifrw_bdp); 4216893Sfeldman il = (struct il_rheader *)(is->is_ifuba.ifu_r.ifrw_addr); 4226893Sfeldman len = il->ilr_length - sizeof(struct il_rheader); 4239746Ssam if ((il->ilr_status&(ILFSTAT_A|ILFSTAT_C)) || len < 46 || 4249746Ssam len > ETHERMTU) { 4256893Sfeldman is->is_if.if_ierrors++; 4266893Sfeldman #ifdef notdef 4276893Sfeldman if (is->is_if.if_ierrors % 100 == 0) 4286893Sfeldman printf("il%d: += 100 input errors\n", unit); 4296893Sfeldman #endif 4306893Sfeldman goto setup; 4316893Sfeldman } 4326893Sfeldman 4336893Sfeldman /* 43419865Skarels * Deal with trailer protocol: if type is trailer type 4356893Sfeldman * get true type from first 16-bit word past data. 4366893Sfeldman * Remember that type was trailer by setting off. 4376893Sfeldman */ 4389746Ssam il->ilr_type = ntohs((u_short)il->ilr_type); 4396893Sfeldman #define ildataaddr(il, off, type) ((type)(((caddr_t)((il)+1)+(off)))) 44019865Skarels if (il->ilr_type >= ETHERTYPE_TRAIL && 44119865Skarels il->ilr_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { 44219865Skarels off = (il->ilr_type - ETHERTYPE_TRAIL) * 512; 4439746Ssam if (off >= ETHERMTU) 4446893Sfeldman goto setup; /* sanity */ 4459746Ssam il->ilr_type = ntohs(*ildataaddr(il, off, u_short *)); 4469746Ssam resid = ntohs(*(ildataaddr(il, off+2, u_short *))); 4476893Sfeldman if (off + resid > len) 4486893Sfeldman goto setup; /* sanity */ 4496893Sfeldman len = off + resid; 4506893Sfeldman } else 4516893Sfeldman off = 0; 4526893Sfeldman if (len == 0) 4536893Sfeldman goto setup; 4546893Sfeldman 4556893Sfeldman /* 4566893Sfeldman * Pull packet off interface. Off is nonzero if packet 4576893Sfeldman * has trailing header; ilget will then force this header 4586893Sfeldman * information to be at the front, but we still have to drop 4596893Sfeldman * the type and length which are at the front of any trailer data. 4606893Sfeldman */ 46124791Skarels m = if_rubaget(&is->is_ifuba, len, off, &is->is_if); 4626893Sfeldman if (m == 0) 4636893Sfeldman goto setup; 4646893Sfeldman if (off) { 46524791Skarels struct ifnet *ifp; 46624791Skarels 46724791Skarels ifp = *(mtod(m, struct ifnet **)); 4686893Sfeldman m->m_off += 2 * sizeof (u_short); 4696893Sfeldman m->m_len -= 2 * sizeof (u_short); 47024791Skarels *(mtod(m, struct ifnet **)) = ifp; 4716893Sfeldman } 4726893Sfeldman switch (il->ilr_type) { 4736893Sfeldman 4746893Sfeldman #ifdef INET 47519865Skarels case ETHERTYPE_IP: 4766893Sfeldman schednetisr(NETISR_IP); 4776893Sfeldman inq = &ipintrq; 4786893Sfeldman break; 47911575Ssam 48019865Skarels case ETHERTYPE_ARP: 48111575Ssam arpinput(&is->is_ac, m); 48213988Ssam goto setup; 4836893Sfeldman #endif 48423557Ssklower #ifdef NS 48523557Ssklower case ETHERTYPE_NS: 48623557Ssklower schednetisr(NETISR_NS); 48723557Ssklower inq = &nsintrq; 48823557Ssklower break; 48923557Ssklower 49023557Ssklower #endif 4916893Sfeldman default: 4926893Sfeldman m_freem(m); 4936893Sfeldman goto setup; 4946893Sfeldman } 4956893Sfeldman 49615787Sleres s = splimp(); 4976893Sfeldman if (IF_QFULL(inq)) { 4986893Sfeldman IF_DROP(inq); 4996893Sfeldman m_freem(m); 50015787Sleres } else 50115787Sleres IF_ENQUEUE(inq, m); 50215787Sleres splx(s); 5036893Sfeldman 5046893Sfeldman setup: 5056893Sfeldman /* 5066893Sfeldman * Reset for next packet if possible. 5076893Sfeldman * If waiting for transmit command completion, set flag 5086893Sfeldman * and wait until command completes. 5096893Sfeldman */ 5107261Ssam if (is->is_flags & ILF_OACTIVE) { 5117261Ssam is->is_flags |= ILF_RCVPENDING; 5127220Ssam return; 5137220Ssam } 5147220Ssam addr->il_bar = is->is_ifuba.ifu_r.ifrw_info & 0xffff; 5159746Ssam addr->il_bcr = sizeof(struct il_rheader) + ETHERMTU + 6; 5167261Ssam addr->il_csr = 5177261Ssam ((is->is_ifuba.ifu_r.ifrw_info >> 2) & IL_EUA)|ILC_RCV|IL_RIE; 51825444Skarels s = splhigh(); 5197220Ssam while ((addr->il_csr & IL_CDONE) == 0) 5207220Ssam ; 52125444Skarels splx(s); 5226893Sfeldman } 5236893Sfeldman 5246893Sfeldman /* 5256893Sfeldman * Ethernet output routine. 5266893Sfeldman * Encapsulate a packet of type family for the local net. 5276893Sfeldman * Use trailer local net encapsulation if enough data in first 5286893Sfeldman * packet leaves a multiple of 512 bytes of data in remainder. 5296893Sfeldman */ 5306893Sfeldman iloutput(ifp, m0, dst) 5316893Sfeldman struct ifnet *ifp; 5326893Sfeldman struct mbuf *m0; 5336893Sfeldman struct sockaddr *dst; 5346893Sfeldman { 53511575Ssam int type, s, error; 53619865Skarels u_char edst[6]; 53711575Ssam struct in_addr idst; 5386893Sfeldman register struct il_softc *is = &il_softc[ifp->if_unit]; 5396893Sfeldman register struct mbuf *m = m0; 5409746Ssam register struct ether_header *il; 5419179Ssam register int off; 54225976Skarels int usetrailers; 5436893Sfeldman 54425444Skarels if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { 54525444Skarels error = ENETDOWN; 54625444Skarels goto bad; 54725444Skarels } 5486893Sfeldman switch (dst->sa_family) { 5496893Sfeldman 5506893Sfeldman #ifdef INET 5516893Sfeldman case AF_INET: 55211575Ssam idst = ((struct sockaddr_in *)dst)->sin_addr; 55325976Skarels if (!arpresolve(&is->is_ac, m, &idst, edst, &usetrailers)) 55411575Ssam return (0); /* if not yet resolved */ 5556893Sfeldman off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; 55625976Skarels if (usetrailers && off > 0 && (off & 0x1ff) == 0 && 5576893Sfeldman m->m_off >= MMINOFF + 2 * sizeof (u_short)) { 55819865Skarels type = ETHERTYPE_TRAIL + (off>>9); 5596893Sfeldman m->m_off -= 2 * sizeof (u_short); 5606893Sfeldman m->m_len += 2 * sizeof (u_short); 56119865Skarels *mtod(m, u_short *) = htons((u_short)ETHERTYPE_IP); 5629746Ssam *(mtod(m, u_short *) + 1) = htons((u_short)m->m_len); 5636893Sfeldman goto gottrailertype; 5646893Sfeldman } 56519865Skarels type = ETHERTYPE_IP; 5666893Sfeldman off = 0; 5676893Sfeldman goto gottype; 5686893Sfeldman #endif 56923557Ssklower #ifdef NS 57023557Ssklower case AF_NS: 57123557Ssklower type = ETHERTYPE_NS; 57223557Ssklower bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host), 57323557Ssklower (caddr_t)edst, sizeof (edst)); 57423557Ssklower off = 0; 57523557Ssklower goto gottype; 57623557Ssklower #endif 5776893Sfeldman 57811575Ssam case AF_UNSPEC: 57911575Ssam il = (struct ether_header *)dst->sa_data; 58019865Skarels bcopy((caddr_t)il->ether_dhost, (caddr_t)edst, sizeof (edst)); 58111575Ssam type = il->ether_type; 58211575Ssam goto gottype; 58311575Ssam 5846893Sfeldman default: 5856893Sfeldman printf("il%d: can't handle af%d\n", ifp->if_unit, 5866893Sfeldman dst->sa_family); 5876893Sfeldman error = EAFNOSUPPORT; 5886893Sfeldman goto bad; 5896893Sfeldman } 5906893Sfeldman 5916893Sfeldman gottrailertype: 5926893Sfeldman /* 5936893Sfeldman * Packet to be sent as trailer: move first packet 5946893Sfeldman * (control information) to end of chain. 5956893Sfeldman */ 5966893Sfeldman while (m->m_next) 5976893Sfeldman m = m->m_next; 5986893Sfeldman m->m_next = m0; 5996893Sfeldman m = m0->m_next; 6006893Sfeldman m0->m_next = 0; 6016893Sfeldman m0 = m; 6026893Sfeldman 6036893Sfeldman gottype: 6046893Sfeldman /* 6056893Sfeldman * Add local net header. If no space in first mbuf, 6066893Sfeldman * allocate another. 6076893Sfeldman */ 6086893Sfeldman if (m->m_off > MMAXOFF || 6099746Ssam MMINOFF + sizeof (struct ether_header) > m->m_off) { 6109650Ssam m = m_get(M_DONTWAIT, MT_HEADER); 6116893Sfeldman if (m == 0) { 6126893Sfeldman error = ENOBUFS; 6136893Sfeldman goto bad; 6146893Sfeldman } 6156893Sfeldman m->m_next = m0; 6166893Sfeldman m->m_off = MMINOFF; 6179746Ssam m->m_len = sizeof (struct ether_header); 6186893Sfeldman } else { 6199746Ssam m->m_off -= sizeof (struct ether_header); 6209746Ssam m->m_len += sizeof (struct ether_header); 6216893Sfeldman } 6229746Ssam il = mtod(m, struct ether_header *); 6239746Ssam il->ether_type = htons((u_short)type); 62419865Skarels bcopy((caddr_t)edst, (caddr_t)il->ether_dhost, sizeof (edst)); 62519865Skarels bcopy((caddr_t)is->is_addr, (caddr_t)il->ether_shost, 62619865Skarels sizeof(il->ether_shost)); 6276893Sfeldman 6286893Sfeldman /* 6296893Sfeldman * Queue message on interface, and start output if interface 6306893Sfeldman * not yet active. 6316893Sfeldman */ 6326893Sfeldman s = splimp(); 6336893Sfeldman if (IF_QFULL(&ifp->if_snd)) { 6346893Sfeldman IF_DROP(&ifp->if_snd); 6357220Ssam splx(s); 6367220Ssam m_freem(m); 6377220Ssam return (ENOBUFS); 6386893Sfeldman } 6396893Sfeldman IF_ENQUEUE(&ifp->if_snd, m); 6407261Ssam if ((is->is_flags & ILF_OACTIVE) == 0) 6416893Sfeldman ilstart(ifp->if_unit); 6426893Sfeldman splx(s); 6436893Sfeldman return (0); 6447220Ssam 6456893Sfeldman bad: 6466893Sfeldman m_freem(m0); 6477220Ssam return (error); 6486893Sfeldman } 6497261Ssam 6507261Ssam /* 6517261Ssam * Watchdog routine, request statistics from board. 6527261Ssam */ 6537261Ssam ilwatch(unit) 6547261Ssam int unit; 6557261Ssam { 6567261Ssam register struct il_softc *is = &il_softc[unit]; 6577261Ssam register struct ifnet *ifp = &is->is_if; 6587261Ssam int s; 6597261Ssam 6607261Ssam if (is->is_flags & ILF_STATPENDING) { 6617261Ssam ifp->if_timer = is->is_scaninterval; 6627261Ssam return; 6637261Ssam } 6647261Ssam s = splimp(); 6657261Ssam is->is_flags |= ILF_STATPENDING; 6667261Ssam if ((is->is_flags & ILF_OACTIVE) == 0) 6677261Ssam ilstart(ifp->if_unit); 6687261Ssam splx(s); 6697261Ssam ifp->if_timer = is->is_scaninterval; 6707261Ssam } 6717261Ssam 6727261Ssam /* 6737261Ssam * Total up the on-board statistics. 6747261Ssam */ 6757261Ssam iltotal(is) 6767261Ssam register struct il_softc *is; 6777261Ssam { 6787261Ssam register u_short *interval, *sum, *end; 6797261Ssam 6807261Ssam interval = &is->is_stats.ils_frames; 6817261Ssam sum = &is->is_sum.ils_frames; 6827261Ssam end = is->is_sum.ils_fill2; 6837261Ssam while (sum < end) 6847261Ssam *sum++ += *interval++; 6857261Ssam is->is_if.if_collisions = is->is_sum.ils_collis; 6867261Ssam } 68713056Ssam 68813056Ssam /* 68913056Ssam * Process an ioctl request. 69013056Ssam */ 69113056Ssam ilioctl(ifp, cmd, data) 69213056Ssam register struct ifnet *ifp; 69313056Ssam int cmd; 69413056Ssam caddr_t data; 69513056Ssam { 69619865Skarels register struct ifaddr *ifa = (struct ifaddr *)data; 69725444Skarels register struct il_softc *is = &il_softc[ifp->if_unit]; 69813056Ssam int s = splimp(), error = 0; 69913056Ssam 70013056Ssam switch (cmd) { 70113056Ssam 70213056Ssam case SIOCSIFADDR: 70319865Skarels ifp->if_flags |= IFF_UP; 70413056Ssam ilinit(ifp->if_unit); 70519865Skarels 70619865Skarels switch (ifa->ifa_addr.sa_family) { 70723557Ssklower #ifdef INET 70819865Skarels case AF_INET: 70919865Skarels ((struct arpcom *)ifp)->ac_ipaddr = 71019865Skarels IA_SIN(ifa)->sin_addr; 71119865Skarels arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); 71219865Skarels break; 71323557Ssklower #endif 71423557Ssklower #ifdef NS 71523557Ssklower case AF_NS: 71625976Skarels { 71725976Skarels register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); 71825976Skarels 71925976Skarels if (ns_nullhost(*ina)) { 72025976Skarels ina->x_host = * (union ns_host *) 72125976Skarels (il_softc[ifp->if_unit].is_addr); 72225976Skarels } else { 72325976Skarels il_setaddr(ina->x_host.c_host, ifp->if_unit); 72426145Ssklower return (0); 72525976Skarels } 72623557Ssklower break; 72725976Skarels } 72823557Ssklower #endif 72919865Skarels } 73013056Ssam break; 73113056Ssam 73225444Skarels case SIOCSIFFLAGS: 73325444Skarels if ((ifp->if_flags & IFF_UP) == 0 && 73425444Skarels is->is_flags & ILF_RUNNING) { 73525444Skarels ((struct ildevice *) 73625444Skarels (ilinfo[ifp->if_unit]->ui_addr))->il_csr = ILC_RESET; 73725444Skarels is->is_flags &= ~ILF_RUNNING; 73825444Skarels } else if (ifp->if_flags & IFF_UP && 73925444Skarels (is->is_flags & ILF_RUNNING) == 0) 74025444Skarels ilinit(ifp->if_unit); 74125444Skarels break; 74225444Skarels 74313056Ssam default: 74413056Ssam error = EINVAL; 74513056Ssam } 74613056Ssam splx(s); 74713056Ssam return (error); 74813056Ssam } 74925976Skarels 75025976Skarels /* 75125976Skarels * set ethernet address for unit 75225976Skarels */ 75325976Skarels il_setaddr(physaddr, unit) 75425976Skarels u_char *physaddr; 75525976Skarels int unit; 75625976Skarels { 75725976Skarels register struct il_softc *is = &il_softc[unit]; 75825976Skarels 75925976Skarels if (! (is->is_flags & ILF_RUNNING)) 76025976Skarels return; 76125976Skarels 76226145Ssklower bcopy((caddr_t)physaddr, (caddr_t)is->is_addr, sizeof is->is_addr); 76326145Ssklower is->is_flags &= ~ILF_RUNNING; 76426145Ssklower is->is_flags |= ILF_SETADDR; 76526145Ssklower ilinit(unit); 76625976Skarels } 76725274Sbloom #endif 768