123557Ssklower /* 229367Smckusick * Copyright (c) 1982, 1986 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*29730Ssklower * @(#)if_il.c 7.2 (Berkeley) 08/07/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" 26*29730Ssklower #include "syslog.h" 278463Sroot 288463Sroot #include "../net/if.h" 298463Sroot #include "../net/netisr.h" 308463Sroot #include "../net/route.h" 3123557Ssklower 3223557Ssklower #ifdef INET 338419Swnj #include "../netinet/in.h" 348419Swnj #include "../netinet/in_systm.h" 3519865Skarels #include "../netinet/in_var.h" 368419Swnj #include "../netinet/ip.h" 3711575Ssam #include "../netinet/if_ether.h" 3823557Ssklower #endif 3923557Ssklower 4023557Ssklower #ifdef NS 4123557Ssklower #include "../netns/ns.h" 4223557Ssklower #include "../netns/ns_if.h" 4323557Ssklower #endif 4423557Ssklower 458463Sroot #include "../vax/cpu.h" 468463Sroot #include "../vax/mtpr.h" 4717115Sbloom #include "if_il.h" 4817115Sbloom #include "if_ilreg.h" 4917115Sbloom #include "if_uba.h" 508463Sroot #include "../vaxuba/ubareg.h" 518463Sroot #include "../vaxuba/ubavar.h" 528463Sroot 536893Sfeldman int ilprobe(), ilattach(), ilrint(), ilcint(); 546893Sfeldman struct uba_device *ilinfo[NIL]; 556893Sfeldman u_short ilstd[] = { 0 }; 566893Sfeldman struct uba_driver ildriver = 576893Sfeldman { ilprobe, 0, ilattach, 0, ilstd, "il", ilinfo }; 586893Sfeldman #define ILUNIT(x) minor(x) 5913056Ssam int ilinit(),iloutput(),ilioctl(),ilreset(),ilwatch(); 6026167Skarels int ildebug = 0; 616893Sfeldman 626893Sfeldman /* 636893Sfeldman * Ethernet software status per interface. 646893Sfeldman * 656893Sfeldman * Each interface is referenced by a network interface structure, 666893Sfeldman * is_if, which the routing code uses to locate the interface. 676893Sfeldman * This structure contains the output queue for the interface, its address, ... 686893Sfeldman * We also have, for each interface, a UBA interface structure, which 696893Sfeldman * contains information about the UNIBUS resources held by the interface: 706893Sfeldman * map registers, buffered data paths, etc. Information is cached in this 716893Sfeldman * structure for use by the if_uba.c routines in running the interface 726893Sfeldman * efficiently. 736893Sfeldman */ 746893Sfeldman struct il_softc { 7511575Ssam struct arpcom is_ac; /* Ethernet common part */ 7611575Ssam #define is_if is_ac.ac_if /* network-visible interface */ 7711575Ssam #define is_addr is_ac.ac_enaddr /* hardware Ethernet address */ 786893Sfeldman struct ifuba is_ifuba; /* UNIBUS resources */ 797261Ssam int is_flags; 807261Ssam #define ILF_OACTIVE 0x1 /* output is active */ 817261Ssam #define ILF_RCVPENDING 0x2 /* start rcv in ilcint */ 827261Ssam #define ILF_STATPENDING 0x4 /* stat cmd pending */ 8325444Skarels #define ILF_RUNNING 0x8 /* board is running */ 8425976Skarels #define ILF_SETADDR 0x10 /* physical address is changed */ 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; 13625976Skarels (void)ilwait(ui, "reset"); 1376893Sfeldman 1389179Ssam is->is_ubaddr = uballoc(ui->ui_ubanum, (caddr_t)&is->is_stats, 13913056Ssam sizeof (struct il_stats), 0); 1407261Ssam addr->il_bar = is->is_ubaddr & 0xffff; 1417261Ssam addr->il_bcr = sizeof (struct il_stats); 1427261Ssam addr->il_csr = ((is->is_ubaddr >> 2) & IL_EUA)|ILC_STAT; 14325976Skarels (void)ilwait(ui, "status"); 1447261Ssam ubarelse(ui->ui_ubanum, &is->is_ubaddr); 14526167Skarels if (ildebug) 14626167Skarels printf("il%d: module=%s firmware=%s\n", ui->ui_unit, 14726167Skarels is->is_stats.ils_module, is->is_stats.ils_firmware); 14819865Skarels bcopy((caddr_t)is->is_stats.ils_addr, (caddr_t)is->is_addr, 14919865Skarels sizeof (is->is_addr)); 15025976Skarels printf("il%d: hardware address %s\n", ui->ui_unit, 15125976Skarels ether_sprintf(is->is_addr)); 1527220Ssam ifp->if_init = ilinit; 1537220Ssam ifp->if_output = iloutput; 15413056Ssam ifp->if_ioctl = ilioctl; 1558979Sroot ifp->if_reset = ilreset; 1566893Sfeldman is->is_ifuba.ifu_flags = UBA_CANTWAIT; 1577220Ssam if_attach(ifp); 1586893Sfeldman } 1596893Sfeldman 16025976Skarels ilwait(ui, op) 16125976Skarels struct uba_device *ui; 16225976Skarels char *op; 16325976Skarels { 16425976Skarels register struct ildevice *addr = (struct ildevice *)ui->ui_addr; 16525976Skarels 16625976Skarels while ((addr->il_csr&IL_CDONE) == 0) 16725976Skarels ; 16825976Skarels if (addr->il_csr&IL_STATUS) { 16925976Skarels printf("il%d: %s failed, csr=%b\n", ui->ui_unit, op, 17025976Skarels addr->il_csr, IL_BITS); 17125976Skarels return (-1); 17225976Skarels } 17325976Skarels return (0); 17425976Skarels } 17525976Skarels 1766893Sfeldman /* 1776893Sfeldman * Reset of interface after UNIBUS reset. 1786893Sfeldman * If interface is on specified uba, reset its state. 1796893Sfeldman */ 1806893Sfeldman ilreset(unit, uban) 1816893Sfeldman int unit, uban; 1826893Sfeldman { 1836893Sfeldman register struct uba_device *ui; 1846893Sfeldman 1856893Sfeldman if (unit >= NIL || (ui = ilinfo[unit]) == 0 || ui->ui_alive == 0 || 1866893Sfeldman ui->ui_ubanum != uban) 1876893Sfeldman return; 1886893Sfeldman printf(" il%d", unit); 18925444Skarels il_softc[unit].is_if.if_flags &= ~IFF_RUNNING; 19025444Skarels il_softc[unit].is_flags &= ~ILF_RUNNING; 1916893Sfeldman ilinit(unit); 1926893Sfeldman } 1936893Sfeldman 1946893Sfeldman /* 1956893Sfeldman * Initialization of interface; clear recorded pending 1966893Sfeldman * operations, and reinitialize UNIBUS usage. 1976893Sfeldman */ 1986893Sfeldman ilinit(unit) 1996893Sfeldman int unit; 2006893Sfeldman { 2016893Sfeldman register struct il_softc *is = &il_softc[unit]; 2026893Sfeldman register struct uba_device *ui = ilinfo[unit]; 2036893Sfeldman register struct ildevice *addr; 20413056Ssam register struct ifnet *ifp = &is->is_if; 2057261Ssam int s; 2066893Sfeldman 20719865Skarels /* not yet, if address still unknown */ 20819865Skarels if (ifp->if_addrlist == (struct ifaddr *)0) 20911575Ssam return; 21025444Skarels if (is->is_flags & ILF_RUNNING) 21125444Skarels return; 21211575Ssam 21325444Skarels if ((ifp->if_flags & IFF_RUNNING) == 0) { 21425444Skarels if (if_ubainit(&is->is_ifuba, ui->ui_ubanum, 21525444Skarels sizeof (struct il_rheader), (int)btoc(ETHERMTU)) == 0) { 21625444Skarels printf("il%d: can't initialize\n", unit); 21725444Skarels is->is_if.if_flags &= ~IFF_UP; 21825444Skarels return; 21925444Skarels } 22025444Skarels is->is_ubaddr = uballoc(ui->ui_ubanum, (caddr_t)&is->is_stats, 22125444Skarels sizeof (struct il_stats), 0); 2226893Sfeldman } 22315071Skarels ifp->if_watchdog = ilwatch; 22415071Skarels is->is_scaninterval = ILWATCHINTERVAL; 22515071Skarels ifp->if_timer = is->is_scaninterval; 2266893Sfeldman addr = (struct ildevice *)ui->ui_addr; 2276893Sfeldman 2286893Sfeldman /* 2299179Ssam * Turn off source address insertion (it's faster this way), 23012488Ssam * and set board online. Former doesn't work if board is 23112488Ssam * already online (happens on ubareset), so we put it offline 23212488Ssam * first. 2339179Ssam */ 2349179Ssam s = splimp(); 23525444Skarels addr->il_csr = ILC_RESET; 23625976Skarels if (ilwait(ui, "hardware diag")) { 23725444Skarels is->is_if.if_flags &= ~IFF_UP; 23825444Skarels splx(s); 23925444Skarels return; 24025444Skarels } 24112488Ssam addr->il_csr = ILC_CISA; 2429179Ssam while ((addr->il_csr & IL_CDONE) == 0) 2439179Ssam ; 2449179Ssam /* 24526145Ssklower * If we must reprogram this board's physical ethernet 24626145Ssklower * address (as for secondary XNS interfaces), we do so 24726145Ssklower * before putting it on line, and starting receive requests. 24826145Ssklower * If you try this on an older 1010 board, it will total 24926145Ssklower * wedge the board. 25026145Ssklower */ 25126145Ssklower if (is->is_flags & ILF_SETADDR) { 25226145Ssklower bcopy((caddr_t)is->is_addr, (caddr_t)&is->is_stats, 25326145Ssklower sizeof is->is_addr); 25426145Ssklower addr->il_bar = is->is_ubaddr & 0xffff; 25526145Ssklower addr->il_bcr = sizeof is->is_addr; 25626145Ssklower addr->il_csr = ((is->is_ubaddr >> 2) & IL_EUA)|ILC_LDPA; 25726145Ssklower if (ilwait(ui, "setaddr")) 25826145Ssklower return; 25926145Ssklower addr->il_bar = is->is_ubaddr & 0xffff; 26026145Ssklower addr->il_bcr = sizeof (struct il_stats); 26126145Ssklower addr->il_csr = ((is->is_ubaddr >> 2) & IL_EUA)|ILC_STAT; 26226145Ssklower if (ilwait(ui, "verifying setaddr")) 26326145Ssklower return; 26426145Ssklower if (bcmp((caddr_t)is->is_stats.ils_addr, (caddr_t)is->is_addr, 26526145Ssklower sizeof (is->is_addr)) != 0) { 26626145Ssklower printf("il%d: setaddr didn't work\n", ui->ui_unit); 26726145Ssklower return; 26826145Ssklower } 26926145Ssklower } 27026145Ssklower /* 2716893Sfeldman * Set board online. 2726893Sfeldman * Hang receive buffer and start any pending 2736893Sfeldman * writes by faking a transmit complete. 27425444Skarels * Receive bcr is not a multiple of 8 so buffer 2756893Sfeldman * chaining can't happen. 2766893Sfeldman */ 2776893Sfeldman addr->il_csr = ILC_ONLINE; 2787220Ssam while ((addr->il_csr & IL_CDONE) == 0) 2797220Ssam ; 2806893Sfeldman addr->il_bar = is->is_ifuba.ifu_r.ifrw_info & 0xffff; 2819746Ssam addr->il_bcr = sizeof(struct il_rheader) + ETHERMTU + 6; 2827261Ssam addr->il_csr = 28313056Ssam ((is->is_ifuba.ifu_r.ifrw_info >> 2) & IL_EUA)|ILC_RCV|IL_RIE; 2847220Ssam while ((addr->il_csr & IL_CDONE) == 0) 2857220Ssam ; 2867261Ssam is->is_flags = ILF_OACTIVE; 28719865Skarels is->is_if.if_flags |= IFF_RUNNING; 28825444Skarels is->is_flags |= ILF_RUNNING; 2897261Ssam is->is_lastcmd = 0; 2906893Sfeldman ilcint(unit); 2916893Sfeldman splx(s); 2926893Sfeldman } 2936893Sfeldman 2946893Sfeldman /* 2956893Sfeldman * Start output on interface. 2966893Sfeldman * Get another datagram to send off of the interface queue, 2976893Sfeldman * and map it to the interface before starting the output. 2986893Sfeldman */ 2996893Sfeldman ilstart(dev) 3006893Sfeldman dev_t dev; 3016893Sfeldman { 3029179Ssam int unit = ILUNIT(dev), len; 3036893Sfeldman struct uba_device *ui = ilinfo[unit]; 3046893Sfeldman register struct il_softc *is = &il_softc[unit]; 3056893Sfeldman register struct ildevice *addr; 3066893Sfeldman struct mbuf *m; 3077220Ssam short csr; 3086893Sfeldman 3096893Sfeldman IF_DEQUEUE(&is->is_if.if_snd, m); 3107261Ssam addr = (struct ildevice *)ui->ui_addr; 3117261Ssam if (m == 0) { 3127261Ssam if ((is->is_flags & ILF_STATPENDING) == 0) 3137261Ssam return; 3149179Ssam addr->il_bar = is->is_ubaddr & 0xffff; 3157261Ssam addr->il_bcr = sizeof (struct il_stats); 3167261Ssam csr = ((is->is_ubaddr >> 2) & IL_EUA)|ILC_STAT|IL_RIE|IL_CIE; 3177261Ssam is->is_flags &= ~ILF_STATPENDING; 3187261Ssam goto startcmd; 3197261Ssam } 3206893Sfeldman len = if_wubaput(&is->is_ifuba, m); 3219179Ssam /* 3229179Ssam * Ensure minimum packet length. 3239179Ssam * This makes the safe assumtion that there are no virtual holes 3249179Ssam * after the data. 3259179Ssam * For security, it might be wise to zero out the added bytes, 3269179Ssam * but we're mainly interested in speed at the moment. 3279179Ssam */ 3289746Ssam if (len - sizeof(struct ether_header) < ETHERMIN) 3299746Ssam len = ETHERMIN + sizeof(struct ether_header); 3306893Sfeldman if (is->is_ifuba.ifu_flags & UBA_NEEDBDP) 3316893Sfeldman UBAPURGE(is->is_ifuba.ifu_uba, is->is_ifuba.ifu_w.ifrw_bdp); 3326893Sfeldman addr->il_bar = is->is_ifuba.ifu_w.ifrw_info & 0xffff; 3336893Sfeldman addr->il_bcr = len; 3347261Ssam csr = 3357261Ssam ((is->is_ifuba.ifu_w.ifrw_info >> 2) & IL_EUA)|ILC_XMIT|IL_CIE|IL_RIE; 3367261Ssam 3377261Ssam startcmd: 3387261Ssam is->is_lastcmd = csr & IL_CMD; 3397220Ssam addr->il_csr = csr; 3407261Ssam is->is_flags |= ILF_OACTIVE; 3416893Sfeldman } 3426893Sfeldman 3436893Sfeldman /* 3446893Sfeldman * Command done interrupt. 3456893Sfeldman */ 3466893Sfeldman ilcint(unit) 3476893Sfeldman int unit; 3486893Sfeldman { 3496893Sfeldman register struct il_softc *is = &il_softc[unit]; 3507220Ssam struct uba_device *ui = ilinfo[unit]; 3516893Sfeldman register struct ildevice *addr = (struct ildevice *)ui->ui_addr; 3527266Ssam short csr; 3536893Sfeldman 3547261Ssam if ((is->is_flags & ILF_OACTIVE) == 0) { 3557220Ssam printf("il%d: stray xmit interrupt, csr=%b\n", unit, 3567261Ssam addr->il_csr, IL_BITS); 3576893Sfeldman return; 3586893Sfeldman } 3597220Ssam 3607266Ssam csr = addr->il_csr; 3616893Sfeldman /* 3627261Ssam * Hang receive buffer if it couldn't 3637261Ssam * be done earlier (in ilrint). 3646893Sfeldman */ 3657261Ssam if (is->is_flags & ILF_RCVPENDING) { 36625444Skarels int s; 36725444Skarels 3686893Sfeldman addr->il_bar = is->is_ifuba.ifu_r.ifrw_info & 0xffff; 3699746Ssam addr->il_bcr = sizeof(struct il_rheader) + ETHERMTU + 6; 3707261Ssam addr->il_csr = 3717261Ssam ((is->is_ifuba.ifu_r.ifrw_info >> 2) & IL_EUA)|ILC_RCV|IL_RIE; 37225444Skarels s = splhigh(); 3737220Ssam while ((addr->il_csr & IL_CDONE) == 0) 3747220Ssam ; 37525444Skarels splx(s); 3767261Ssam is->is_flags &= ~ILF_RCVPENDING; 3776893Sfeldman } 3787261Ssam is->is_flags &= ~ILF_OACTIVE; 3797266Ssam csr &= IL_STATUS; 3807261Ssam switch (is->is_lastcmd) { 3817261Ssam 3827261Ssam case ILC_XMIT: 3837261Ssam is->is_if.if_opackets++; 3847266Ssam if (csr > ILERR_RETRIES) 3857261Ssam is->is_if.if_oerrors++; 3867261Ssam break; 3877261Ssam 3887261Ssam case ILC_STAT: 3897266Ssam if (csr == ILERR_SUCCESS) 3907261Ssam iltotal(is); 3917261Ssam break; 3927261Ssam } 3936893Sfeldman if (is->is_ifuba.ifu_xtofree) { 3946893Sfeldman m_freem(is->is_ifuba.ifu_xtofree); 3956893Sfeldman is->is_ifuba.ifu_xtofree = 0; 3966893Sfeldman } 3977261Ssam ilstart(unit); 3986893Sfeldman } 3996893Sfeldman 4006893Sfeldman /* 4016893Sfeldman * Ethernet interface receiver interrupt. 4026893Sfeldman * If input error just drop packet. 4036893Sfeldman * Otherwise purge input buffered data path and examine 4046893Sfeldman * packet to determine type. If can't determine length 4056893Sfeldman * from type, then have to drop packet. Othewise decapsulate 4066893Sfeldman * packet based on type and pass to type specific higher-level 4076893Sfeldman * input routine. 4086893Sfeldman */ 4096893Sfeldman ilrint(unit) 4106893Sfeldman int unit; 4116893Sfeldman { 4126893Sfeldman register struct il_softc *is = &il_softc[unit]; 4136893Sfeldman struct ildevice *addr = (struct ildevice *)ilinfo[unit]->ui_addr; 4146893Sfeldman register struct il_rheader *il; 4156893Sfeldman struct mbuf *m; 41615787Sleres int len, off, resid, s; 4176893Sfeldman register struct ifqueue *inq; 4186893Sfeldman 4196893Sfeldman is->is_if.if_ipackets++; 4206893Sfeldman if (is->is_ifuba.ifu_flags & UBA_NEEDBDP) 4216893Sfeldman UBAPURGE(is->is_ifuba.ifu_uba, is->is_ifuba.ifu_r.ifrw_bdp); 4226893Sfeldman il = (struct il_rheader *)(is->is_ifuba.ifu_r.ifrw_addr); 4236893Sfeldman len = il->ilr_length - sizeof(struct il_rheader); 4249746Ssam if ((il->ilr_status&(ILFSTAT_A|ILFSTAT_C)) || len < 46 || 4259746Ssam len > ETHERMTU) { 4266893Sfeldman is->is_if.if_ierrors++; 4276893Sfeldman #ifdef notdef 4286893Sfeldman if (is->is_if.if_ierrors % 100 == 0) 4296893Sfeldman printf("il%d: += 100 input errors\n", unit); 4306893Sfeldman #endif 4316893Sfeldman goto setup; 4326893Sfeldman } 4336893Sfeldman 4346893Sfeldman /* 43519865Skarels * Deal with trailer protocol: if type is trailer type 4366893Sfeldman * get true type from first 16-bit word past data. 4376893Sfeldman * Remember that type was trailer by setting off. 4386893Sfeldman */ 4399746Ssam il->ilr_type = ntohs((u_short)il->ilr_type); 4406893Sfeldman #define ildataaddr(il, off, type) ((type)(((caddr_t)((il)+1)+(off)))) 44119865Skarels if (il->ilr_type >= ETHERTYPE_TRAIL && 44219865Skarels il->ilr_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { 44319865Skarels off = (il->ilr_type - ETHERTYPE_TRAIL) * 512; 4449746Ssam if (off >= ETHERMTU) 4456893Sfeldman goto setup; /* sanity */ 4469746Ssam il->ilr_type = ntohs(*ildataaddr(il, off, u_short *)); 4479746Ssam resid = ntohs(*(ildataaddr(il, off+2, u_short *))); 4486893Sfeldman if (off + resid > len) 4496893Sfeldman goto setup; /* sanity */ 4506893Sfeldman len = off + resid; 4516893Sfeldman } else 4526893Sfeldman off = 0; 4536893Sfeldman if (len == 0) 4546893Sfeldman goto setup; 4556893Sfeldman 4566893Sfeldman /* 4576893Sfeldman * Pull packet off interface. Off is nonzero if packet 4586893Sfeldman * has trailing header; ilget will then force this header 4596893Sfeldman * information to be at the front, but we still have to drop 4606893Sfeldman * the type and length which are at the front of any trailer data. 4616893Sfeldman */ 46224791Skarels m = if_rubaget(&is->is_ifuba, len, off, &is->is_if); 4636893Sfeldman if (m == 0) 4646893Sfeldman goto setup; 4656893Sfeldman if (off) { 46624791Skarels struct ifnet *ifp; 46724791Skarels 46824791Skarels ifp = *(mtod(m, struct ifnet **)); 4696893Sfeldman m->m_off += 2 * sizeof (u_short); 4706893Sfeldman m->m_len -= 2 * sizeof (u_short); 47124791Skarels *(mtod(m, struct ifnet **)) = ifp; 4726893Sfeldman } 4736893Sfeldman switch (il->ilr_type) { 4746893Sfeldman 4756893Sfeldman #ifdef INET 47619865Skarels case ETHERTYPE_IP: 4776893Sfeldman schednetisr(NETISR_IP); 4786893Sfeldman inq = &ipintrq; 4796893Sfeldman break; 48011575Ssam 48119865Skarels case ETHERTYPE_ARP: 48211575Ssam arpinput(&is->is_ac, m); 48313988Ssam goto setup; 4846893Sfeldman #endif 48523557Ssklower #ifdef NS 48623557Ssklower case ETHERTYPE_NS: 48723557Ssklower schednetisr(NETISR_NS); 48823557Ssklower inq = &nsintrq; 48923557Ssklower break; 49023557Ssklower 49123557Ssklower #endif 4926893Sfeldman default: 4936893Sfeldman m_freem(m); 4946893Sfeldman goto setup; 4956893Sfeldman } 4966893Sfeldman 49715787Sleres s = splimp(); 4986893Sfeldman if (IF_QFULL(inq)) { 4996893Sfeldman IF_DROP(inq); 5006893Sfeldman m_freem(m); 50115787Sleres } else 50215787Sleres IF_ENQUEUE(inq, m); 50315787Sleres splx(s); 5046893Sfeldman 5056893Sfeldman setup: 5066893Sfeldman /* 5076893Sfeldman * Reset for next packet if possible. 5086893Sfeldman * If waiting for transmit command completion, set flag 5096893Sfeldman * and wait until command completes. 5106893Sfeldman */ 5117261Ssam if (is->is_flags & ILF_OACTIVE) { 5127261Ssam is->is_flags |= ILF_RCVPENDING; 5137220Ssam return; 5147220Ssam } 5157220Ssam addr->il_bar = is->is_ifuba.ifu_r.ifrw_info & 0xffff; 5169746Ssam addr->il_bcr = sizeof(struct il_rheader) + ETHERMTU + 6; 5177261Ssam addr->il_csr = 5187261Ssam ((is->is_ifuba.ifu_r.ifrw_info >> 2) & IL_EUA)|ILC_RCV|IL_RIE; 51925444Skarels s = splhigh(); 5207220Ssam while ((addr->il_csr & IL_CDONE) == 0) 5217220Ssam ; 52225444Skarels splx(s); 5236893Sfeldman } 5246893Sfeldman 5256893Sfeldman /* 5266893Sfeldman * Ethernet output routine. 5276893Sfeldman * Encapsulate a packet of type family for the local net. 5286893Sfeldman * Use trailer local net encapsulation if enough data in first 5296893Sfeldman * packet leaves a multiple of 512 bytes of data in remainder. 5306893Sfeldman */ 5316893Sfeldman iloutput(ifp, m0, dst) 5326893Sfeldman struct ifnet *ifp; 5336893Sfeldman struct mbuf *m0; 5346893Sfeldman struct sockaddr *dst; 5356893Sfeldman { 53611575Ssam int type, s, error; 53719865Skarels u_char edst[6]; 53811575Ssam struct in_addr idst; 5396893Sfeldman register struct il_softc *is = &il_softc[ifp->if_unit]; 5406893Sfeldman register struct mbuf *m = m0; 5419746Ssam register struct ether_header *il; 5429179Ssam register int off; 54325976Skarels int usetrailers; 5446893Sfeldman 54525444Skarels if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { 54625444Skarels error = ENETDOWN; 54725444Skarels goto bad; 54825444Skarels } 5496893Sfeldman switch (dst->sa_family) { 5506893Sfeldman 5516893Sfeldman #ifdef INET 5526893Sfeldman case AF_INET: 55311575Ssam idst = ((struct sockaddr_in *)dst)->sin_addr; 55425976Skarels if (!arpresolve(&is->is_ac, m, &idst, edst, &usetrailers)) 55511575Ssam return (0); /* if not yet resolved */ 5566893Sfeldman off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; 55725976Skarels if (usetrailers && off > 0 && (off & 0x1ff) == 0 && 5586893Sfeldman m->m_off >= MMINOFF + 2 * sizeof (u_short)) { 55919865Skarels type = ETHERTYPE_TRAIL + (off>>9); 5606893Sfeldman m->m_off -= 2 * sizeof (u_short); 5616893Sfeldman m->m_len += 2 * sizeof (u_short); 56219865Skarels *mtod(m, u_short *) = htons((u_short)ETHERTYPE_IP); 5639746Ssam *(mtod(m, u_short *) + 1) = htons((u_short)m->m_len); 5646893Sfeldman goto gottrailertype; 5656893Sfeldman } 56619865Skarels type = ETHERTYPE_IP; 5676893Sfeldman off = 0; 5686893Sfeldman goto gottype; 5696893Sfeldman #endif 57023557Ssklower #ifdef NS 57123557Ssklower case AF_NS: 57223557Ssklower type = ETHERTYPE_NS; 57323557Ssklower bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host), 57423557Ssklower (caddr_t)edst, sizeof (edst)); 57523557Ssklower off = 0; 57623557Ssklower goto gottype; 57723557Ssklower #endif 5786893Sfeldman 57911575Ssam case AF_UNSPEC: 58011575Ssam il = (struct ether_header *)dst->sa_data; 58119865Skarels bcopy((caddr_t)il->ether_dhost, (caddr_t)edst, sizeof (edst)); 58211575Ssam type = il->ether_type; 58311575Ssam goto gottype; 58411575Ssam 5856893Sfeldman default: 5866893Sfeldman printf("il%d: can't handle af%d\n", ifp->if_unit, 5876893Sfeldman dst->sa_family); 5886893Sfeldman error = EAFNOSUPPORT; 5896893Sfeldman goto bad; 5906893Sfeldman } 5916893Sfeldman 5926893Sfeldman gottrailertype: 5936893Sfeldman /* 5946893Sfeldman * Packet to be sent as trailer: move first packet 5956893Sfeldman * (control information) to end of chain. 5966893Sfeldman */ 5976893Sfeldman while (m->m_next) 5986893Sfeldman m = m->m_next; 5996893Sfeldman m->m_next = m0; 6006893Sfeldman m = m0->m_next; 6016893Sfeldman m0->m_next = 0; 6026893Sfeldman m0 = m; 6036893Sfeldman 6046893Sfeldman gottype: 6056893Sfeldman /* 6066893Sfeldman * Add local net header. If no space in first mbuf, 6076893Sfeldman * allocate another. 6086893Sfeldman */ 6096893Sfeldman if (m->m_off > MMAXOFF || 6109746Ssam MMINOFF + sizeof (struct ether_header) > m->m_off) { 6119650Ssam m = m_get(M_DONTWAIT, MT_HEADER); 6126893Sfeldman if (m == 0) { 6136893Sfeldman error = ENOBUFS; 6146893Sfeldman goto bad; 6156893Sfeldman } 6166893Sfeldman m->m_next = m0; 6176893Sfeldman m->m_off = MMINOFF; 6189746Ssam m->m_len = sizeof (struct ether_header); 6196893Sfeldman } else { 6209746Ssam m->m_off -= sizeof (struct ether_header); 6219746Ssam m->m_len += sizeof (struct ether_header); 6226893Sfeldman } 6239746Ssam il = mtod(m, struct ether_header *); 6249746Ssam il->ether_type = htons((u_short)type); 62519865Skarels bcopy((caddr_t)edst, (caddr_t)il->ether_dhost, sizeof (edst)); 62619865Skarels bcopy((caddr_t)is->is_addr, (caddr_t)il->ether_shost, 62719865Skarels sizeof(il->ether_shost)); 6286893Sfeldman 6296893Sfeldman /* 6306893Sfeldman * Queue message on interface, and start output if interface 6316893Sfeldman * not yet active. 6326893Sfeldman */ 6336893Sfeldman s = splimp(); 6346893Sfeldman if (IF_QFULL(&ifp->if_snd)) { 6356893Sfeldman IF_DROP(&ifp->if_snd); 6367220Ssam splx(s); 6377220Ssam m_freem(m); 6387220Ssam return (ENOBUFS); 6396893Sfeldman } 6406893Sfeldman IF_ENQUEUE(&ifp->if_snd, m); 6417261Ssam if ((is->is_flags & ILF_OACTIVE) == 0) 6426893Sfeldman ilstart(ifp->if_unit); 6436893Sfeldman splx(s); 6446893Sfeldman return (0); 6457220Ssam 6466893Sfeldman bad: 6476893Sfeldman m_freem(m0); 6487220Ssam return (error); 6496893Sfeldman } 6507261Ssam 6517261Ssam /* 6527261Ssam * Watchdog routine, request statistics from board. 6537261Ssam */ 6547261Ssam ilwatch(unit) 6557261Ssam int unit; 6567261Ssam { 6577261Ssam register struct il_softc *is = &il_softc[unit]; 6587261Ssam register struct ifnet *ifp = &is->is_if; 6597261Ssam int s; 6607261Ssam 6617261Ssam if (is->is_flags & ILF_STATPENDING) { 6627261Ssam ifp->if_timer = is->is_scaninterval; 6637261Ssam return; 6647261Ssam } 6657261Ssam s = splimp(); 6667261Ssam is->is_flags |= ILF_STATPENDING; 6677261Ssam if ((is->is_flags & ILF_OACTIVE) == 0) 6687261Ssam ilstart(ifp->if_unit); 6697261Ssam splx(s); 6707261Ssam ifp->if_timer = is->is_scaninterval; 6717261Ssam } 6727261Ssam 6737261Ssam /* 6747261Ssam * Total up the on-board statistics. 6757261Ssam */ 6767261Ssam iltotal(is) 6777261Ssam register struct il_softc *is; 6787261Ssam { 6797261Ssam register u_short *interval, *sum, *end; 6807261Ssam 6817261Ssam interval = &is->is_stats.ils_frames; 6827261Ssam sum = &is->is_sum.ils_frames; 6837261Ssam end = is->is_sum.ils_fill2; 6847261Ssam while (sum < end) 6857261Ssam *sum++ += *interval++; 6867261Ssam is->is_if.if_collisions = is->is_sum.ils_collis; 687*29730Ssklower if ((is->is_flags & ILF_SETADDR) && 688*29730Ssklower (bcmp((caddr_t)is->is_stats.ils_addr, (caddr_t)is->is_addr, 689*29730Ssklower sizeof (is->is_addr)) != 0)) { 690*29730Ssklower log(LOG_ERR, "il%d: physaddr reverted\n", is->is_if.if_unit); 691*29730Ssklower is->is_flags &= ~ILF_RUNNING; 692*29730Ssklower ilinit(is->is_if.if_unit); 693*29730Ssklower } 6947261Ssam } 69513056Ssam 69613056Ssam /* 69713056Ssam * Process an ioctl request. 69813056Ssam */ 69913056Ssam ilioctl(ifp, cmd, data) 70013056Ssam register struct ifnet *ifp; 70113056Ssam int cmd; 70213056Ssam caddr_t data; 70313056Ssam { 70419865Skarels register struct ifaddr *ifa = (struct ifaddr *)data; 70525444Skarels register struct il_softc *is = &il_softc[ifp->if_unit]; 70613056Ssam int s = splimp(), error = 0; 70713056Ssam 70813056Ssam switch (cmd) { 70913056Ssam 71013056Ssam case SIOCSIFADDR: 71119865Skarels ifp->if_flags |= IFF_UP; 71213056Ssam ilinit(ifp->if_unit); 71319865Skarels 71419865Skarels switch (ifa->ifa_addr.sa_family) { 71523557Ssklower #ifdef INET 71619865Skarels case AF_INET: 71719865Skarels ((struct arpcom *)ifp)->ac_ipaddr = 71819865Skarels IA_SIN(ifa)->sin_addr; 71919865Skarels arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); 72019865Skarels break; 72123557Ssklower #endif 72223557Ssklower #ifdef NS 72323557Ssklower case AF_NS: 72425976Skarels { 72525976Skarels register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); 72625976Skarels 72725976Skarels if (ns_nullhost(*ina)) { 72825976Skarels ina->x_host = * (union ns_host *) 72925976Skarels (il_softc[ifp->if_unit].is_addr); 73025976Skarels } else { 73125976Skarels il_setaddr(ina->x_host.c_host, ifp->if_unit); 73226145Ssklower return (0); 73325976Skarels } 73423557Ssklower break; 73525976Skarels } 73623557Ssklower #endif 73719865Skarels } 73813056Ssam break; 73913056Ssam 74025444Skarels case SIOCSIFFLAGS: 74125444Skarels if ((ifp->if_flags & IFF_UP) == 0 && 74225444Skarels is->is_flags & ILF_RUNNING) { 74325444Skarels ((struct ildevice *) 74425444Skarels (ilinfo[ifp->if_unit]->ui_addr))->il_csr = ILC_RESET; 74525444Skarels is->is_flags &= ~ILF_RUNNING; 74625444Skarels } else if (ifp->if_flags & IFF_UP && 74725444Skarels (is->is_flags & ILF_RUNNING) == 0) 74825444Skarels ilinit(ifp->if_unit); 74925444Skarels break; 75025444Skarels 75113056Ssam default: 75213056Ssam error = EINVAL; 75313056Ssam } 75413056Ssam splx(s); 75513056Ssam return (error); 75613056Ssam } 75725976Skarels 75825976Skarels /* 75925976Skarels * set ethernet address for unit 76025976Skarels */ 76125976Skarels il_setaddr(physaddr, unit) 76225976Skarels u_char *physaddr; 76325976Skarels int unit; 76425976Skarels { 76525976Skarels register struct il_softc *is = &il_softc[unit]; 76625976Skarels 76725976Skarels if (! (is->is_flags & ILF_RUNNING)) 76825976Skarels return; 76925976Skarels 77026145Ssklower bcopy((caddr_t)physaddr, (caddr_t)is->is_addr, sizeof is->is_addr); 77126145Ssklower is->is_flags &= ~ILF_RUNNING; 77226145Ssklower is->is_flags |= ILF_SETADDR; 77326145Ssklower ilinit(unit); 77425976Skarels } 77525274Sbloom #endif 776