123557Ssklower /* 229367Smckusick * Copyright (c) 1982, 1986 Regents of the University of California. 3*35323Sbostic * All rights reserved. 423557Ssklower * 5*35323Sbostic * Redistribution and use in source and binary forms are permitted 6*35323Sbostic * provided that the above copyright notice and this paragraph are 7*35323Sbostic * duplicated in all such forms and that any documentation, 8*35323Sbostic * advertising materials, and other materials related to such 9*35323Sbostic * distribution and use acknowledge that the software was developed 10*35323Sbostic * by the University of California, Berkeley. The name of the 11*35323Sbostic * University may not be used to endorse or promote products derived 12*35323Sbostic * from this software without specific prior written permission. 13*35323Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14*35323Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15*35323Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 16*35323Sbostic * 17*35323Sbostic * @(#)if_il.c 7.3 (Berkeley) 08/04/88 1823557Ssklower */ 196893Sfeldman 206893Sfeldman #include "il.h" 2125274Sbloom #if NIL > 0 226893Sfeldman 236893Sfeldman /* 246893Sfeldman * Interlan Ethernet Communications Controller interface 256893Sfeldman */ 269797Ssam #include "../machine/pte.h" 279797Ssam 2817115Sbloom #include "param.h" 2917115Sbloom #include "systm.h" 3017115Sbloom #include "mbuf.h" 3117115Sbloom #include "buf.h" 3217115Sbloom #include "protosw.h" 3317115Sbloom #include "socket.h" 3417115Sbloom #include "vmmac.h" 3517115Sbloom #include "ioctl.h" 3617115Sbloom #include "errno.h" 3729730Ssklower #include "syslog.h" 388463Sroot 398463Sroot #include "../net/if.h" 408463Sroot #include "../net/netisr.h" 418463Sroot #include "../net/route.h" 4223557Ssklower 4323557Ssklower #ifdef INET 448419Swnj #include "../netinet/in.h" 458419Swnj #include "../netinet/in_systm.h" 4619865Skarels #include "../netinet/in_var.h" 478419Swnj #include "../netinet/ip.h" 4811575Ssam #include "../netinet/if_ether.h" 4923557Ssklower #endif 5023557Ssklower 5123557Ssklower #ifdef NS 5223557Ssklower #include "../netns/ns.h" 5323557Ssklower #include "../netns/ns_if.h" 5423557Ssklower #endif 5523557Ssklower 568463Sroot #include "../vax/cpu.h" 578463Sroot #include "../vax/mtpr.h" 5817115Sbloom #include "if_il.h" 5917115Sbloom #include "if_ilreg.h" 6017115Sbloom #include "if_uba.h" 618463Sroot #include "../vaxuba/ubareg.h" 628463Sroot #include "../vaxuba/ubavar.h" 638463Sroot 646893Sfeldman int ilprobe(), ilattach(), ilrint(), ilcint(); 656893Sfeldman struct uba_device *ilinfo[NIL]; 666893Sfeldman u_short ilstd[] = { 0 }; 676893Sfeldman struct uba_driver ildriver = 686893Sfeldman { ilprobe, 0, ilattach, 0, ilstd, "il", ilinfo }; 696893Sfeldman #define ILUNIT(x) minor(x) 7013056Ssam int ilinit(),iloutput(),ilioctl(),ilreset(),ilwatch(); 7126167Skarels int ildebug = 0; 726893Sfeldman 736893Sfeldman /* 746893Sfeldman * Ethernet software status per interface. 756893Sfeldman * 766893Sfeldman * Each interface is referenced by a network interface structure, 776893Sfeldman * is_if, which the routing code uses to locate the interface. 786893Sfeldman * This structure contains the output queue for the interface, its address, ... 796893Sfeldman * We also have, for each interface, a UBA interface structure, which 806893Sfeldman * contains information about the UNIBUS resources held by the interface: 816893Sfeldman * map registers, buffered data paths, etc. Information is cached in this 826893Sfeldman * structure for use by the if_uba.c routines in running the interface 836893Sfeldman * efficiently. 846893Sfeldman */ 856893Sfeldman struct il_softc { 8611575Ssam struct arpcom is_ac; /* Ethernet common part */ 8711575Ssam #define is_if is_ac.ac_if /* network-visible interface */ 8811575Ssam #define is_addr is_ac.ac_enaddr /* hardware Ethernet address */ 896893Sfeldman struct ifuba is_ifuba; /* UNIBUS resources */ 907261Ssam int is_flags; 917261Ssam #define ILF_OACTIVE 0x1 /* output is active */ 927261Ssam #define ILF_RCVPENDING 0x2 /* start rcv in ilcint */ 937261Ssam #define ILF_STATPENDING 0x4 /* stat cmd pending */ 9425444Skarels #define ILF_RUNNING 0x8 /* board is running */ 9525976Skarels #define ILF_SETADDR 0x10 /* physical address is changed */ 967261Ssam short is_lastcmd; /* can't read csr, so must save it */ 977261Ssam short is_scaninterval; /* interval of stat collection */ 987261Ssam #define ILWATCHINTERVAL 60 /* once every 60 seconds */ 997261Ssam struct il_stats is_stats; /* holds on-board statistics */ 1007261Ssam struct il_stats is_sum; /* summation over time */ 1017261Ssam int is_ubaddr; /* mapping registers of is_stats */ 1026893Sfeldman } il_softc[NIL]; 1036893Sfeldman 1046893Sfeldman ilprobe(reg) 1056893Sfeldman caddr_t reg; 1066893Sfeldman { 1076893Sfeldman register int br, cvec; /* r11, r10 value-result */ 1086893Sfeldman register struct ildevice *addr = (struct ildevice *)reg; 1096893Sfeldman register i; 1106893Sfeldman 1116893Sfeldman #ifdef lint 1126893Sfeldman br = 0; cvec = br; br = cvec; 1139179Ssam i = 0; ilrint(i); ilcint(i); ilwatch(i); 1146893Sfeldman #endif 1156893Sfeldman 1166893Sfeldman addr->il_csr = ILC_OFFLINE|IL_CIE; 1176893Sfeldman DELAY(100000); 1187261Ssam i = addr->il_csr; /* clear CDONE */ 1196893Sfeldman if (cvec > 0 && cvec != 0x200) 1206893Sfeldman cvec -= 4; 1216893Sfeldman return (1); 1226893Sfeldman } 1236893Sfeldman 1246893Sfeldman /* 1256893Sfeldman * Interface exists: make available by filling in network interface 1266893Sfeldman * record. System will initialize the interface when it is ready 1276893Sfeldman * to accept packets. A STATUS command is done to get the ethernet 1286893Sfeldman * address and other interesting data. 1296893Sfeldman */ 1306893Sfeldman ilattach(ui) 1316893Sfeldman struct uba_device *ui; 1326893Sfeldman { 1336893Sfeldman register struct il_softc *is = &il_softc[ui->ui_unit]; 1347220Ssam register struct ifnet *ifp = &is->is_if; 1356893Sfeldman register struct ildevice *addr = (struct ildevice *)ui->ui_addr; 1366893Sfeldman 1377220Ssam ifp->if_unit = ui->ui_unit; 1387220Ssam ifp->if_name = "il"; 1399746Ssam ifp->if_mtu = ETHERMTU; 14019865Skarels ifp->if_flags = IFF_BROADCAST; 1416893Sfeldman 1426893Sfeldman /* 1437261Ssam * Reset the board and map the statistics 1447261Ssam * buffer onto the Unibus. 1456893Sfeldman */ 1467261Ssam addr->il_csr = ILC_RESET; 14725976Skarels (void)ilwait(ui, "reset"); 1486893Sfeldman 1499179Ssam is->is_ubaddr = uballoc(ui->ui_ubanum, (caddr_t)&is->is_stats, 15013056Ssam sizeof (struct il_stats), 0); 1517261Ssam addr->il_bar = is->is_ubaddr & 0xffff; 1527261Ssam addr->il_bcr = sizeof (struct il_stats); 1537261Ssam addr->il_csr = ((is->is_ubaddr >> 2) & IL_EUA)|ILC_STAT; 15425976Skarels (void)ilwait(ui, "status"); 1557261Ssam ubarelse(ui->ui_ubanum, &is->is_ubaddr); 15626167Skarels if (ildebug) 15726167Skarels printf("il%d: module=%s firmware=%s\n", ui->ui_unit, 15826167Skarels is->is_stats.ils_module, is->is_stats.ils_firmware); 15919865Skarels bcopy((caddr_t)is->is_stats.ils_addr, (caddr_t)is->is_addr, 16019865Skarels sizeof (is->is_addr)); 16125976Skarels printf("il%d: hardware address %s\n", ui->ui_unit, 16225976Skarels ether_sprintf(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 if_attach(ifp); 1696893Sfeldman } 1706893Sfeldman 17125976Skarels ilwait(ui, op) 17225976Skarels struct uba_device *ui; 17325976Skarels char *op; 17425976Skarels { 17525976Skarels register struct ildevice *addr = (struct ildevice *)ui->ui_addr; 17625976Skarels 17725976Skarels while ((addr->il_csr&IL_CDONE) == 0) 17825976Skarels ; 17925976Skarels if (addr->il_csr&IL_STATUS) { 18025976Skarels printf("il%d: %s failed, csr=%b\n", ui->ui_unit, op, 18125976Skarels addr->il_csr, IL_BITS); 18225976Skarels return (-1); 18325976Skarels } 18425976Skarels return (0); 18525976Skarels } 18625976Skarels 1876893Sfeldman /* 1886893Sfeldman * Reset of interface after UNIBUS reset. 1896893Sfeldman * If interface is on specified uba, reset its state. 1906893Sfeldman */ 1916893Sfeldman ilreset(unit, uban) 1926893Sfeldman int unit, uban; 1936893Sfeldman { 1946893Sfeldman register struct uba_device *ui; 1956893Sfeldman 1966893Sfeldman if (unit >= NIL || (ui = ilinfo[unit]) == 0 || ui->ui_alive == 0 || 1976893Sfeldman ui->ui_ubanum != uban) 1986893Sfeldman return; 1996893Sfeldman printf(" il%d", unit); 20025444Skarels il_softc[unit].is_if.if_flags &= ~IFF_RUNNING; 20125444Skarels il_softc[unit].is_flags &= ~ILF_RUNNING; 2026893Sfeldman ilinit(unit); 2036893Sfeldman } 2046893Sfeldman 2056893Sfeldman /* 2066893Sfeldman * Initialization of interface; clear recorded pending 2076893Sfeldman * operations, and reinitialize UNIBUS usage. 2086893Sfeldman */ 2096893Sfeldman ilinit(unit) 2106893Sfeldman int unit; 2116893Sfeldman { 2126893Sfeldman register struct il_softc *is = &il_softc[unit]; 2136893Sfeldman register struct uba_device *ui = ilinfo[unit]; 2146893Sfeldman register struct ildevice *addr; 21513056Ssam register struct ifnet *ifp = &is->is_if; 2167261Ssam int s; 2176893Sfeldman 21819865Skarels /* not yet, if address still unknown */ 21919865Skarels if (ifp->if_addrlist == (struct ifaddr *)0) 22011575Ssam return; 22125444Skarels if (is->is_flags & ILF_RUNNING) 22225444Skarels return; 22311575Ssam 22425444Skarels if ((ifp->if_flags & IFF_RUNNING) == 0) { 22525444Skarels if (if_ubainit(&is->is_ifuba, ui->ui_ubanum, 22625444Skarels sizeof (struct il_rheader), (int)btoc(ETHERMTU)) == 0) { 22725444Skarels printf("il%d: can't initialize\n", unit); 22825444Skarels is->is_if.if_flags &= ~IFF_UP; 22925444Skarels return; 23025444Skarels } 23125444Skarels is->is_ubaddr = uballoc(ui->ui_ubanum, (caddr_t)&is->is_stats, 23225444Skarels sizeof (struct il_stats), 0); 2336893Sfeldman } 23415071Skarels ifp->if_watchdog = ilwatch; 23515071Skarels is->is_scaninterval = ILWATCHINTERVAL; 23615071Skarels ifp->if_timer = is->is_scaninterval; 2376893Sfeldman addr = (struct ildevice *)ui->ui_addr; 2386893Sfeldman 2396893Sfeldman /* 2409179Ssam * Turn off source address insertion (it's faster this way), 24112488Ssam * and set board online. Former doesn't work if board is 24212488Ssam * already online (happens on ubareset), so we put it offline 24312488Ssam * first. 2449179Ssam */ 2459179Ssam s = splimp(); 24625444Skarels addr->il_csr = ILC_RESET; 24725976Skarels if (ilwait(ui, "hardware diag")) { 24825444Skarels is->is_if.if_flags &= ~IFF_UP; 24925444Skarels splx(s); 25025444Skarels return; 25125444Skarels } 25212488Ssam addr->il_csr = ILC_CISA; 2539179Ssam while ((addr->il_csr & IL_CDONE) == 0) 2549179Ssam ; 2559179Ssam /* 25626145Ssklower * If we must reprogram this board's physical ethernet 25726145Ssklower * address (as for secondary XNS interfaces), we do so 25826145Ssklower * before putting it on line, and starting receive requests. 25926145Ssklower * If you try this on an older 1010 board, it will total 26026145Ssklower * wedge the board. 26126145Ssklower */ 26226145Ssklower if (is->is_flags & ILF_SETADDR) { 26326145Ssklower bcopy((caddr_t)is->is_addr, (caddr_t)&is->is_stats, 26426145Ssklower sizeof is->is_addr); 26526145Ssklower addr->il_bar = is->is_ubaddr & 0xffff; 26626145Ssklower addr->il_bcr = sizeof is->is_addr; 26726145Ssklower addr->il_csr = ((is->is_ubaddr >> 2) & IL_EUA)|ILC_LDPA; 26826145Ssklower if (ilwait(ui, "setaddr")) 26926145Ssklower return; 27026145Ssklower addr->il_bar = is->is_ubaddr & 0xffff; 27126145Ssklower addr->il_bcr = sizeof (struct il_stats); 27226145Ssklower addr->il_csr = ((is->is_ubaddr >> 2) & IL_EUA)|ILC_STAT; 27326145Ssklower if (ilwait(ui, "verifying setaddr")) 27426145Ssklower return; 27526145Ssklower if (bcmp((caddr_t)is->is_stats.ils_addr, (caddr_t)is->is_addr, 27626145Ssklower sizeof (is->is_addr)) != 0) { 27726145Ssklower printf("il%d: setaddr didn't work\n", ui->ui_unit); 27826145Ssklower return; 27926145Ssklower } 28026145Ssklower } 28126145Ssklower /* 2826893Sfeldman * Set board online. 2836893Sfeldman * Hang receive buffer and start any pending 2846893Sfeldman * writes by faking a transmit complete. 28525444Skarels * Receive bcr is not a multiple of 8 so buffer 2866893Sfeldman * chaining can't happen. 2876893Sfeldman */ 2886893Sfeldman addr->il_csr = ILC_ONLINE; 2897220Ssam while ((addr->il_csr & IL_CDONE) == 0) 2907220Ssam ; 2916893Sfeldman addr->il_bar = is->is_ifuba.ifu_r.ifrw_info & 0xffff; 2929746Ssam addr->il_bcr = sizeof(struct il_rheader) + ETHERMTU + 6; 2937261Ssam addr->il_csr = 29413056Ssam ((is->is_ifuba.ifu_r.ifrw_info >> 2) & IL_EUA)|ILC_RCV|IL_RIE; 2957220Ssam while ((addr->il_csr & IL_CDONE) == 0) 2967220Ssam ; 2977261Ssam is->is_flags = ILF_OACTIVE; 29819865Skarels is->is_if.if_flags |= IFF_RUNNING; 29925444Skarels is->is_flags |= ILF_RUNNING; 3007261Ssam is->is_lastcmd = 0; 3016893Sfeldman ilcint(unit); 3026893Sfeldman splx(s); 3036893Sfeldman } 3046893Sfeldman 3056893Sfeldman /* 3066893Sfeldman * Start output on interface. 3076893Sfeldman * Get another datagram to send off of the interface queue, 3086893Sfeldman * and map it to the interface before starting the output. 3096893Sfeldman */ 3106893Sfeldman ilstart(dev) 3116893Sfeldman dev_t dev; 3126893Sfeldman { 3139179Ssam int unit = ILUNIT(dev), len; 3146893Sfeldman struct uba_device *ui = ilinfo[unit]; 3156893Sfeldman register struct il_softc *is = &il_softc[unit]; 3166893Sfeldman register struct ildevice *addr; 3176893Sfeldman struct mbuf *m; 3187220Ssam short csr; 3196893Sfeldman 3206893Sfeldman IF_DEQUEUE(&is->is_if.if_snd, m); 3217261Ssam addr = (struct ildevice *)ui->ui_addr; 3227261Ssam if (m == 0) { 3237261Ssam if ((is->is_flags & ILF_STATPENDING) == 0) 3247261Ssam return; 3259179Ssam addr->il_bar = is->is_ubaddr & 0xffff; 3267261Ssam addr->il_bcr = sizeof (struct il_stats); 3277261Ssam csr = ((is->is_ubaddr >> 2) & IL_EUA)|ILC_STAT|IL_RIE|IL_CIE; 3287261Ssam is->is_flags &= ~ILF_STATPENDING; 3297261Ssam goto startcmd; 3307261Ssam } 3316893Sfeldman len = if_wubaput(&is->is_ifuba, m); 3329179Ssam /* 3339179Ssam * Ensure minimum packet length. 3349179Ssam * This makes the safe assumtion that there are no virtual holes 3359179Ssam * after the data. 3369179Ssam * For security, it might be wise to zero out the added bytes, 3379179Ssam * but we're mainly interested in speed at the moment. 3389179Ssam */ 3399746Ssam if (len - sizeof(struct ether_header) < ETHERMIN) 3409746Ssam len = ETHERMIN + sizeof(struct ether_header); 3416893Sfeldman if (is->is_ifuba.ifu_flags & UBA_NEEDBDP) 3426893Sfeldman UBAPURGE(is->is_ifuba.ifu_uba, is->is_ifuba.ifu_w.ifrw_bdp); 3436893Sfeldman addr->il_bar = is->is_ifuba.ifu_w.ifrw_info & 0xffff; 3446893Sfeldman addr->il_bcr = len; 3457261Ssam csr = 3467261Ssam ((is->is_ifuba.ifu_w.ifrw_info >> 2) & IL_EUA)|ILC_XMIT|IL_CIE|IL_RIE; 3477261Ssam 3487261Ssam startcmd: 3497261Ssam is->is_lastcmd = csr & IL_CMD; 3507220Ssam addr->il_csr = csr; 3517261Ssam is->is_flags |= ILF_OACTIVE; 3526893Sfeldman } 3536893Sfeldman 3546893Sfeldman /* 3556893Sfeldman * Command done interrupt. 3566893Sfeldman */ 3576893Sfeldman ilcint(unit) 3586893Sfeldman int unit; 3596893Sfeldman { 3606893Sfeldman register struct il_softc *is = &il_softc[unit]; 3617220Ssam struct uba_device *ui = ilinfo[unit]; 3626893Sfeldman register struct ildevice *addr = (struct ildevice *)ui->ui_addr; 3637266Ssam short csr; 3646893Sfeldman 3657261Ssam if ((is->is_flags & ILF_OACTIVE) == 0) { 3667220Ssam printf("il%d: stray xmit interrupt, csr=%b\n", unit, 3677261Ssam addr->il_csr, IL_BITS); 3686893Sfeldman return; 3696893Sfeldman } 3707220Ssam 3717266Ssam csr = addr->il_csr; 3726893Sfeldman /* 3737261Ssam * Hang receive buffer if it couldn't 3747261Ssam * be done earlier (in ilrint). 3756893Sfeldman */ 3767261Ssam if (is->is_flags & ILF_RCVPENDING) { 37725444Skarels int s; 37825444Skarels 3796893Sfeldman addr->il_bar = is->is_ifuba.ifu_r.ifrw_info & 0xffff; 3809746Ssam addr->il_bcr = sizeof(struct il_rheader) + ETHERMTU + 6; 3817261Ssam addr->il_csr = 3827261Ssam ((is->is_ifuba.ifu_r.ifrw_info >> 2) & IL_EUA)|ILC_RCV|IL_RIE; 38325444Skarels s = splhigh(); 3847220Ssam while ((addr->il_csr & IL_CDONE) == 0) 3857220Ssam ; 38625444Skarels splx(s); 3877261Ssam is->is_flags &= ~ILF_RCVPENDING; 3886893Sfeldman } 3897261Ssam is->is_flags &= ~ILF_OACTIVE; 3907266Ssam csr &= IL_STATUS; 3917261Ssam switch (is->is_lastcmd) { 3927261Ssam 3937261Ssam case ILC_XMIT: 3947261Ssam is->is_if.if_opackets++; 3957266Ssam if (csr > ILERR_RETRIES) 3967261Ssam is->is_if.if_oerrors++; 3977261Ssam break; 3987261Ssam 3997261Ssam case ILC_STAT: 4007266Ssam if (csr == ILERR_SUCCESS) 4017261Ssam iltotal(is); 4027261Ssam break; 4037261Ssam } 4046893Sfeldman if (is->is_ifuba.ifu_xtofree) { 4056893Sfeldman m_freem(is->is_ifuba.ifu_xtofree); 4066893Sfeldman is->is_ifuba.ifu_xtofree = 0; 4076893Sfeldman } 4087261Ssam ilstart(unit); 4096893Sfeldman } 4106893Sfeldman 4116893Sfeldman /* 4126893Sfeldman * Ethernet interface receiver interrupt. 4136893Sfeldman * If input error just drop packet. 4146893Sfeldman * Otherwise purge input buffered data path and examine 4156893Sfeldman * packet to determine type. If can't determine length 4166893Sfeldman * from type, then have to drop packet. Othewise decapsulate 4176893Sfeldman * packet based on type and pass to type specific higher-level 4186893Sfeldman * input routine. 4196893Sfeldman */ 4206893Sfeldman ilrint(unit) 4216893Sfeldman int unit; 4226893Sfeldman { 4236893Sfeldman register struct il_softc *is = &il_softc[unit]; 4246893Sfeldman struct ildevice *addr = (struct ildevice *)ilinfo[unit]->ui_addr; 4256893Sfeldman register struct il_rheader *il; 4266893Sfeldman struct mbuf *m; 42715787Sleres int len, off, resid, s; 4286893Sfeldman register struct ifqueue *inq; 4296893Sfeldman 4306893Sfeldman is->is_if.if_ipackets++; 4316893Sfeldman if (is->is_ifuba.ifu_flags & UBA_NEEDBDP) 4326893Sfeldman UBAPURGE(is->is_ifuba.ifu_uba, is->is_ifuba.ifu_r.ifrw_bdp); 4336893Sfeldman il = (struct il_rheader *)(is->is_ifuba.ifu_r.ifrw_addr); 4346893Sfeldman len = il->ilr_length - sizeof(struct il_rheader); 4359746Ssam if ((il->ilr_status&(ILFSTAT_A|ILFSTAT_C)) || len < 46 || 4369746Ssam len > ETHERMTU) { 4376893Sfeldman is->is_if.if_ierrors++; 4386893Sfeldman #ifdef notdef 4396893Sfeldman if (is->is_if.if_ierrors % 100 == 0) 4406893Sfeldman printf("il%d: += 100 input errors\n", unit); 4416893Sfeldman #endif 4426893Sfeldman goto setup; 4436893Sfeldman } 4446893Sfeldman 4456893Sfeldman /* 44619865Skarels * Deal with trailer protocol: if type is trailer type 4476893Sfeldman * get true type from first 16-bit word past data. 4486893Sfeldman * Remember that type was trailer by setting off. 4496893Sfeldman */ 4509746Ssam il->ilr_type = ntohs((u_short)il->ilr_type); 4516893Sfeldman #define ildataaddr(il, off, type) ((type)(((caddr_t)((il)+1)+(off)))) 45219865Skarels if (il->ilr_type >= ETHERTYPE_TRAIL && 45319865Skarels il->ilr_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { 45419865Skarels off = (il->ilr_type - ETHERTYPE_TRAIL) * 512; 4559746Ssam if (off >= ETHERMTU) 4566893Sfeldman goto setup; /* sanity */ 4579746Ssam il->ilr_type = ntohs(*ildataaddr(il, off, u_short *)); 4589746Ssam resid = ntohs(*(ildataaddr(il, off+2, u_short *))); 4596893Sfeldman if (off + resid > len) 4606893Sfeldman goto setup; /* sanity */ 4616893Sfeldman len = off + resid; 4626893Sfeldman } else 4636893Sfeldman off = 0; 4646893Sfeldman if (len == 0) 4656893Sfeldman goto setup; 4666893Sfeldman 4676893Sfeldman /* 4686893Sfeldman * Pull packet off interface. Off is nonzero if packet 4696893Sfeldman * has trailing header; ilget will then force this header 4706893Sfeldman * information to be at the front, but we still have to drop 4716893Sfeldman * the type and length which are at the front of any trailer data. 4726893Sfeldman */ 47324791Skarels m = if_rubaget(&is->is_ifuba, len, off, &is->is_if); 4746893Sfeldman if (m == 0) 4756893Sfeldman goto setup; 4766893Sfeldman if (off) { 47724791Skarels struct ifnet *ifp; 47824791Skarels 47924791Skarels ifp = *(mtod(m, struct ifnet **)); 4806893Sfeldman m->m_off += 2 * sizeof (u_short); 4816893Sfeldman m->m_len -= 2 * sizeof (u_short); 48224791Skarels *(mtod(m, struct ifnet **)) = ifp; 4836893Sfeldman } 4846893Sfeldman switch (il->ilr_type) { 4856893Sfeldman 4866893Sfeldman #ifdef INET 48719865Skarels case ETHERTYPE_IP: 4886893Sfeldman schednetisr(NETISR_IP); 4896893Sfeldman inq = &ipintrq; 4906893Sfeldman break; 49111575Ssam 49219865Skarels case ETHERTYPE_ARP: 49311575Ssam arpinput(&is->is_ac, m); 49413988Ssam goto setup; 4956893Sfeldman #endif 49623557Ssklower #ifdef NS 49723557Ssklower case ETHERTYPE_NS: 49823557Ssklower schednetisr(NETISR_NS); 49923557Ssklower inq = &nsintrq; 50023557Ssklower break; 50123557Ssklower 50223557Ssklower #endif 5036893Sfeldman default: 5046893Sfeldman m_freem(m); 5056893Sfeldman goto setup; 5066893Sfeldman } 5076893Sfeldman 50815787Sleres s = splimp(); 5096893Sfeldman if (IF_QFULL(inq)) { 5106893Sfeldman IF_DROP(inq); 5116893Sfeldman m_freem(m); 51215787Sleres } else 51315787Sleres IF_ENQUEUE(inq, m); 51415787Sleres splx(s); 5156893Sfeldman 5166893Sfeldman setup: 5176893Sfeldman /* 5186893Sfeldman * Reset for next packet if possible. 5196893Sfeldman * If waiting for transmit command completion, set flag 5206893Sfeldman * and wait until command completes. 5216893Sfeldman */ 5227261Ssam if (is->is_flags & ILF_OACTIVE) { 5237261Ssam is->is_flags |= ILF_RCVPENDING; 5247220Ssam return; 5257220Ssam } 5267220Ssam addr->il_bar = is->is_ifuba.ifu_r.ifrw_info & 0xffff; 5279746Ssam addr->il_bcr = sizeof(struct il_rheader) + ETHERMTU + 6; 5287261Ssam addr->il_csr = 5297261Ssam ((is->is_ifuba.ifu_r.ifrw_info >> 2) & IL_EUA)|ILC_RCV|IL_RIE; 53025444Skarels s = splhigh(); 5317220Ssam while ((addr->il_csr & IL_CDONE) == 0) 5327220Ssam ; 53325444Skarels splx(s); 5346893Sfeldman } 5356893Sfeldman 5366893Sfeldman /* 5376893Sfeldman * Ethernet output routine. 5386893Sfeldman * Encapsulate a packet of type family for the local net. 5396893Sfeldman * Use trailer local net encapsulation if enough data in first 5406893Sfeldman * packet leaves a multiple of 512 bytes of data in remainder. 5416893Sfeldman */ 5426893Sfeldman iloutput(ifp, m0, dst) 5436893Sfeldman struct ifnet *ifp; 5446893Sfeldman struct mbuf *m0; 5456893Sfeldman struct sockaddr *dst; 5466893Sfeldman { 54711575Ssam int type, s, error; 54819865Skarels u_char edst[6]; 54911575Ssam struct in_addr idst; 5506893Sfeldman register struct il_softc *is = &il_softc[ifp->if_unit]; 5516893Sfeldman register struct mbuf *m = m0; 5529746Ssam register struct ether_header *il; 5539179Ssam register int off; 55425976Skarels int usetrailers; 5556893Sfeldman 55625444Skarels if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { 55725444Skarels error = ENETDOWN; 55825444Skarels goto bad; 55925444Skarels } 5606893Sfeldman switch (dst->sa_family) { 5616893Sfeldman 5626893Sfeldman #ifdef INET 5636893Sfeldman case AF_INET: 56411575Ssam idst = ((struct sockaddr_in *)dst)->sin_addr; 56525976Skarels if (!arpresolve(&is->is_ac, m, &idst, edst, &usetrailers)) 56611575Ssam return (0); /* if not yet resolved */ 5676893Sfeldman off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; 56825976Skarels if (usetrailers && off > 0 && (off & 0x1ff) == 0 && 5696893Sfeldman m->m_off >= MMINOFF + 2 * sizeof (u_short)) { 57019865Skarels type = ETHERTYPE_TRAIL + (off>>9); 5716893Sfeldman m->m_off -= 2 * sizeof (u_short); 5726893Sfeldman m->m_len += 2 * sizeof (u_short); 57319865Skarels *mtod(m, u_short *) = htons((u_short)ETHERTYPE_IP); 5749746Ssam *(mtod(m, u_short *) + 1) = htons((u_short)m->m_len); 5756893Sfeldman goto gottrailertype; 5766893Sfeldman } 57719865Skarels type = ETHERTYPE_IP; 5786893Sfeldman off = 0; 5796893Sfeldman goto gottype; 5806893Sfeldman #endif 58123557Ssklower #ifdef NS 58223557Ssklower case AF_NS: 58323557Ssklower type = ETHERTYPE_NS; 58423557Ssklower bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host), 58523557Ssklower (caddr_t)edst, sizeof (edst)); 58623557Ssklower off = 0; 58723557Ssklower goto gottype; 58823557Ssklower #endif 5896893Sfeldman 59011575Ssam case AF_UNSPEC: 59111575Ssam il = (struct ether_header *)dst->sa_data; 59219865Skarels bcopy((caddr_t)il->ether_dhost, (caddr_t)edst, sizeof (edst)); 59311575Ssam type = il->ether_type; 59411575Ssam goto gottype; 59511575Ssam 5966893Sfeldman default: 5976893Sfeldman printf("il%d: can't handle af%d\n", ifp->if_unit, 5986893Sfeldman dst->sa_family); 5996893Sfeldman error = EAFNOSUPPORT; 6006893Sfeldman goto bad; 6016893Sfeldman } 6026893Sfeldman 6036893Sfeldman gottrailertype: 6046893Sfeldman /* 6056893Sfeldman * Packet to be sent as trailer: move first packet 6066893Sfeldman * (control information) to end of chain. 6076893Sfeldman */ 6086893Sfeldman while (m->m_next) 6096893Sfeldman m = m->m_next; 6106893Sfeldman m->m_next = m0; 6116893Sfeldman m = m0->m_next; 6126893Sfeldman m0->m_next = 0; 6136893Sfeldman m0 = m; 6146893Sfeldman 6156893Sfeldman gottype: 6166893Sfeldman /* 6176893Sfeldman * Add local net header. If no space in first mbuf, 6186893Sfeldman * allocate another. 6196893Sfeldman */ 6206893Sfeldman if (m->m_off > MMAXOFF || 6219746Ssam MMINOFF + sizeof (struct ether_header) > m->m_off) { 6229650Ssam m = m_get(M_DONTWAIT, MT_HEADER); 6236893Sfeldman if (m == 0) { 6246893Sfeldman error = ENOBUFS; 6256893Sfeldman goto bad; 6266893Sfeldman } 6276893Sfeldman m->m_next = m0; 6286893Sfeldman m->m_off = MMINOFF; 6299746Ssam m->m_len = sizeof (struct ether_header); 6306893Sfeldman } else { 6319746Ssam m->m_off -= sizeof (struct ether_header); 6329746Ssam m->m_len += sizeof (struct ether_header); 6336893Sfeldman } 6349746Ssam il = mtod(m, struct ether_header *); 6359746Ssam il->ether_type = htons((u_short)type); 63619865Skarels bcopy((caddr_t)edst, (caddr_t)il->ether_dhost, sizeof (edst)); 63719865Skarels bcopy((caddr_t)is->is_addr, (caddr_t)il->ether_shost, 63819865Skarels sizeof(il->ether_shost)); 6396893Sfeldman 6406893Sfeldman /* 6416893Sfeldman * Queue message on interface, and start output if interface 6426893Sfeldman * not yet active. 6436893Sfeldman */ 6446893Sfeldman s = splimp(); 6456893Sfeldman if (IF_QFULL(&ifp->if_snd)) { 6466893Sfeldman IF_DROP(&ifp->if_snd); 6477220Ssam splx(s); 6487220Ssam m_freem(m); 6497220Ssam return (ENOBUFS); 6506893Sfeldman } 6516893Sfeldman IF_ENQUEUE(&ifp->if_snd, m); 6527261Ssam if ((is->is_flags & ILF_OACTIVE) == 0) 6536893Sfeldman ilstart(ifp->if_unit); 6546893Sfeldman splx(s); 6556893Sfeldman return (0); 6567220Ssam 6576893Sfeldman bad: 6586893Sfeldman m_freem(m0); 6597220Ssam return (error); 6606893Sfeldman } 6617261Ssam 6627261Ssam /* 6637261Ssam * Watchdog routine, request statistics from board. 6647261Ssam */ 6657261Ssam ilwatch(unit) 6667261Ssam int unit; 6677261Ssam { 6687261Ssam register struct il_softc *is = &il_softc[unit]; 6697261Ssam register struct ifnet *ifp = &is->is_if; 6707261Ssam int s; 6717261Ssam 6727261Ssam if (is->is_flags & ILF_STATPENDING) { 6737261Ssam ifp->if_timer = is->is_scaninterval; 6747261Ssam return; 6757261Ssam } 6767261Ssam s = splimp(); 6777261Ssam is->is_flags |= ILF_STATPENDING; 6787261Ssam if ((is->is_flags & ILF_OACTIVE) == 0) 6797261Ssam ilstart(ifp->if_unit); 6807261Ssam splx(s); 6817261Ssam ifp->if_timer = is->is_scaninterval; 6827261Ssam } 6837261Ssam 6847261Ssam /* 6857261Ssam * Total up the on-board statistics. 6867261Ssam */ 6877261Ssam iltotal(is) 6887261Ssam register struct il_softc *is; 6897261Ssam { 6907261Ssam register u_short *interval, *sum, *end; 6917261Ssam 6927261Ssam interval = &is->is_stats.ils_frames; 6937261Ssam sum = &is->is_sum.ils_frames; 6947261Ssam end = is->is_sum.ils_fill2; 6957261Ssam while (sum < end) 6967261Ssam *sum++ += *interval++; 6977261Ssam is->is_if.if_collisions = is->is_sum.ils_collis; 69829730Ssklower if ((is->is_flags & ILF_SETADDR) && 69929730Ssklower (bcmp((caddr_t)is->is_stats.ils_addr, (caddr_t)is->is_addr, 70029730Ssklower sizeof (is->is_addr)) != 0)) { 70129730Ssklower log(LOG_ERR, "il%d: physaddr reverted\n", is->is_if.if_unit); 70229730Ssklower is->is_flags &= ~ILF_RUNNING; 70329730Ssklower ilinit(is->is_if.if_unit); 70429730Ssklower } 7057261Ssam } 70613056Ssam 70713056Ssam /* 70813056Ssam * Process an ioctl request. 70913056Ssam */ 71013056Ssam ilioctl(ifp, cmd, data) 71113056Ssam register struct ifnet *ifp; 71213056Ssam int cmd; 71313056Ssam caddr_t data; 71413056Ssam { 71519865Skarels register struct ifaddr *ifa = (struct ifaddr *)data; 71625444Skarels register struct il_softc *is = &il_softc[ifp->if_unit]; 71713056Ssam int s = splimp(), error = 0; 71813056Ssam 71913056Ssam switch (cmd) { 72013056Ssam 72113056Ssam case SIOCSIFADDR: 72219865Skarels ifp->if_flags |= IFF_UP; 72313056Ssam ilinit(ifp->if_unit); 72419865Skarels 72519865Skarels switch (ifa->ifa_addr.sa_family) { 72623557Ssklower #ifdef INET 72719865Skarels case AF_INET: 72819865Skarels ((struct arpcom *)ifp)->ac_ipaddr = 72919865Skarels IA_SIN(ifa)->sin_addr; 73019865Skarels arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); 73119865Skarels break; 73223557Ssklower #endif 73323557Ssklower #ifdef NS 73423557Ssklower case AF_NS: 73525976Skarels { 73625976Skarels register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); 73725976Skarels 73825976Skarels if (ns_nullhost(*ina)) { 73925976Skarels ina->x_host = * (union ns_host *) 74025976Skarels (il_softc[ifp->if_unit].is_addr); 74125976Skarels } else { 74225976Skarels il_setaddr(ina->x_host.c_host, ifp->if_unit); 74326145Ssklower return (0); 74425976Skarels } 74523557Ssklower break; 74625976Skarels } 74723557Ssklower #endif 74819865Skarels } 74913056Ssam break; 75013056Ssam 75125444Skarels case SIOCSIFFLAGS: 75225444Skarels if ((ifp->if_flags & IFF_UP) == 0 && 75325444Skarels is->is_flags & ILF_RUNNING) { 75425444Skarels ((struct ildevice *) 75525444Skarels (ilinfo[ifp->if_unit]->ui_addr))->il_csr = ILC_RESET; 75625444Skarels is->is_flags &= ~ILF_RUNNING; 75725444Skarels } else if (ifp->if_flags & IFF_UP && 75825444Skarels (is->is_flags & ILF_RUNNING) == 0) 75925444Skarels ilinit(ifp->if_unit); 76025444Skarels break; 76125444Skarels 76213056Ssam default: 76313056Ssam error = EINVAL; 76413056Ssam } 76513056Ssam splx(s); 76613056Ssam return (error); 76713056Ssam } 76825976Skarels 76925976Skarels /* 77025976Skarels * set ethernet address for unit 77125976Skarels */ 77225976Skarels il_setaddr(physaddr, unit) 77325976Skarels u_char *physaddr; 77425976Skarels int unit; 77525976Skarels { 77625976Skarels register struct il_softc *is = &il_softc[unit]; 77725976Skarels 77825976Skarels if (! (is->is_flags & ILF_RUNNING)) 77925976Skarels return; 78025976Skarels 78126145Ssklower bcopy((caddr_t)physaddr, (caddr_t)is->is_addr, sizeof is->is_addr); 78226145Ssklower is->is_flags &= ~ILF_RUNNING; 78326145Ssklower is->is_flags |= ILF_SETADDR; 78426145Ssklower ilinit(unit); 78525976Skarels } 78625274Sbloom #endif 787