From edce34dd94be18cde40dda5d95fdad2dc5ad3cb2 Mon Sep 17 00:00:00 2001 From: "William F. Jolitz" Date: Mon, 13 Jul 1992 02:33:03 -0800 Subject: [PATCH] 386BSD 0.1 development Work on file usr/src/sys.386bsd/i386/isa/if_we.c Co-Authored-By: Lynne Greer Jolitz Synthesized-from: 386BSD-0.1 --- usr/src/sys.386bsd/i386/isa/if_we.c | 808 ++++++++++++++++++++++++++++ 1 file changed, 808 insertions(+) create mode 100644 usr/src/sys.386bsd/i386/isa/if_we.c diff --git a/usr/src/sys.386bsd/i386/isa/if_we.c b/usr/src/sys.386bsd/i386/isa/if_we.c new file mode 100644 index 0000000000..9f47ae0552 --- /dev/null +++ b/usr/src/sys.386bsd/i386/isa/if_we.c @@ -0,0 +1,808 @@ +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Tim L. Tucker. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)if_we.c 7.3 (Berkeley) 5/21/91 + */ + +/* + * Modification history + * + * 8/28/89 - Initial version(if_wd.c), Tim L Tucker + */ + +#include "we.h" +#if NWE > 0 +/* + * Western Digital 8003 ethernet/starlan adapter + * + * Supports the following interface cards: + * WD8003E, WD8003EBT, WD8003S, WD8003SBT, WD8013EBT + * + * The Western Digital card is one of many AT/MCA ethernet interfaces + * based on the National DS8390 Network Interface chip set. + */ +#include "param.h" +#include "mbuf.h" +#include "socket.h" +#include "ioctl.h" +#include "errno.h" +#include "syslog.h" + +#include "net/if.h" +#include "net/netisr.h" + +#ifdef INET +#include "netinet/in.h" +#include "netinet/in_systm.h" +#include "netinet/in_var.h" +#include "netinet/ip.h" +#include "netinet/if_ether.h" +#endif + +#ifdef NS +#include "netns/ns.h" +#include "netns/ns_if.h" +#endif + +#include "i386/isa/isa.h" +#include "i386/isa/if_wereg.h" +#include "i386/isa/isa_device.h" +#include "i386/isa/icu.h" + +/* + * This constant should really be 60 because the we adds 4 bytes of crc. + * However when set to 60 our packets are ignored by deunas , 3coms are + * okay ?????????????????????????????????????????? + */ +#define ETHER_MIN_LEN 64 +#define ETHER_ADDR_LEN 6 +#define ETHER_HDR_SIZE 14 + +/* + * Ethernet software status per interface. + * + * Each interface is referenced by a network interface structure, + * qe_if, which the routing code uses to locate the interface. + * This structure contains the output queue for the interface, its address, ... + */ +struct we_softc { + struct arpcom we_ac; /* Ethernet common part */ +#define we_if we_ac.ac_if /* network-visible interface */ +#define we_addr we_ac.ac_enaddr /* hardware Ethernet address */ + + u_char we_flags; /* software state */ +#define WDF_RUNNING 0x01 +#define WDF_TXBUSY 0x02 + + u_char we_type; /* interface type code */ + u_short we_vector; /* interrupt vector */ + short we_io_ctl_addr; /* i/o bus address, control */ + short we_io_nic_addr; /* i/o bus address, DS8390 */ + + caddr_t we_vmem_addr; /* card RAM virtual memory base */ + u_long we_vmem_size; /* card RAM bytes */ + caddr_t we_vmem_ring; /* receive ring RAM vaddress */ + caddr_t we_vmem_end; /* receive ring RAM end */ +} we_softc[NWE]; + +int weprobe(), weattach(), weintr(), westart(); +int weinit(), ether_output(), weioctl(), wereset(), wewatchdog(); + +struct isa_driver wedriver = { + weprobe, weattach, "we", +}; + +static unsigned short wemask[] = + { IRQ9, IRQ3, IRQ5, IRQ7, IRQ10, IRQ11, IRQ15, IRQ4 }; + +/* + * Probe the WD8003 to see if its there + */ +weprobe(is) + struct isa_device *is; +{ + register int i; + register struct we_softc *sc = &we_softc[is->id_unit]; + union we_mem_sel wem; + u_char sum; + + /* reset card to force it into a known state. */ + outb(is->id_iobase, 0x80); + DELAY(100); + outb(is->id_iobase, 0x00); + /* wait in the case this card is reading it's EEROM */ + DELAY(5000); + + /* + * Here we check the card ROM, if the checksum passes, and the + * type code and ethernet address check out, then we know we have + * a wd8003 card. + * + * Autoconfiguration: No warning message is printed on error. + */ + for (sum = 0, i = 0; i < 8; ++i) + sum += inb(is->id_iobase + WD_ROM_OFFSET + i); + if (sum != WD_CHECKSUM) + return (0); + sc->we_type = inb(is->id_iobase + WD_ROM_OFFSET + 6); +#ifdef nope + if ((sc->we_type & WD_REVMASK) != 2 /* WD8003E or WD8003S */ + && (sc->we_type & WD_REVMASK) != 4 /* WD8003EBT */ + && (sc->we_type & WD_REVMASK) != 6) /* WD8003ELB? */ + return (0); +#endif +/*printf("type %x ", sc->we_type);*/ + if (sc->we_type & WD_SOFTCONFIG) { + int iv = inb(is->id_iobase + 1) & 4 | + ((inb(is->id_iobase+4) & 0x60) >> 5); +/*printf("iv %d ", iv);*/ + if (wemask[iv] != is->id_irq) + return(0); + outb(is->id_iobase+4, inb(is->id_iobase+4) | 0x80); + } + + /* + * Setup card RAM area and i/o addresses + * Kernel Virtual to segment C0000-DFFFF????? + */ + sc->we_io_ctl_addr = is->id_iobase; + sc->we_io_nic_addr = sc->we_io_ctl_addr + WD_NIC_OFFSET; + sc->we_vector = is->id_irq; + sc->we_vmem_addr = (caddr_t)is->id_maddr; + sc->we_vmem_size = is->id_msize; + sc->we_vmem_ring = sc->we_vmem_addr + (WD_PAGE_SIZE * WD_TXBUF_SIZE); + sc->we_vmem_end = sc->we_vmem_addr + is->id_msize; + + /* + * Save board ROM station address + */ + for (i = 0; i < ETHER_ADDR_LEN; ++i) + sc->we_addr[i] = inb(sc->we_io_ctl_addr + WD_ROM_OFFSET + i); + + /* + * Mapin interface memory, setup memory select register + */ + wem.ms_addr = kvtop(sc->we_vmem_addr) >> 13; + wem.ms_enable = 1; + wem.ms_reset = 0; + outb(sc->we_io_ctl_addr, wem.ms_byte); + + /* + * clear interface memory, then sum to make sure its valid + */ + for (i = 0; i < sc->we_vmem_size; ++i) + sc->we_vmem_addr[i] = 0x0; + for (sum = 0, i = 0; i < sc->we_vmem_size; ++i) + sum += sc->we_vmem_addr[i]; + if (sum != 0x0) { + printf("we%d: wd8003 dual port RAM address error\n", is->id_unit); + return (0); + } + + return (WD_IO_PORTS); +} + +/* + * Interface exists: make available by filling in network interface + * record. System will initialize the interface when it is ready + * to accept packets. + */ +weattach(is) + struct isa_device *is; +{ + register struct we_softc *sc = &we_softc[is->id_unit]; + register struct ifnet *ifp = &sc->we_if; + union we_command wecmd; + + wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND); + wecmd.cs_stp = 1; + wecmd.cs_sta = 0; + wecmd.cs_ps = 0; + outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte); + /* + * Initialize ifnet structure + */ + ifp->if_unit = is->id_unit; + ifp->if_name = "we" ; + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS ; + ifp->if_init = weinit; + ifp->if_output = ether_output; + ifp->if_start = westart; + ifp->if_ioctl = weioctl; + ifp->if_reset = wereset; + ifp->if_watchdog = wewatchdog; + if_attach(ifp); + + /* + * Banner... + */ + printf(" %s address %s", + (sc->we_type & WD_ETHERNET) ? "ethernet" : "starlan", + ether_sprintf(sc->we_addr)); +} + +/* + * Reset of interface. + */ +wereset(unit, uban) + int unit, uban; +{ + if (unit >= NWE) + return; + printf("we%d: reset\n", unit); +/* we_softc[unit].we_flags &= ~WDF_RUNNING; */ + weinit(unit); +} + +/* + * Take interface offline. + */ +westop(unit) + int unit; +{ + register struct we_softc *sc = &we_softc[unit]; + union we_command wecmd; + int s; + + /* + * Shutdown DS8390 + */ + s = splimp(); + wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND); + wecmd.cs_stp = 1; + wecmd.cs_sta = 0; + wecmd.cs_ps = 0; + outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte); + (void) splx(s); +} + +wewatchdog(unit) { + + log(LOG_WARNING,"we%d: soft reset\n", unit); + westop(unit); + weinit(unit); +} + +static Bdry; +/* + * Initialization of interface (really just DS8390). + */ +weinit(unit) + int unit; +{ + register struct we_softc *sc = &we_softc[unit]; + register struct ifnet *ifp = &sc->we_if; + union we_command wecmd; + int i, s; + + /* address not known */ + if (ifp->if_addrlist == (struct ifaddr *)0) + return; + + /* already running */ + /*if (ifp->if_flags & IFF_RUNNING) return; */ + + /* + * Initialize DS8390 in order given in NSC NIC manual. + * this is stock code...please see the National manual for details. + */ + s = splhigh(); +Bdry = 0; + wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND); + wecmd.cs_stp = 1; + wecmd.cs_sta = 0; + wecmd.cs_ps = 0; + outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte); + outb(sc->we_io_nic_addr + WD_P0_DCR, WD_D_CONFIG); + outb(sc->we_io_nic_addr + WD_P0_RBCR0, 0); + outb(sc->we_io_nic_addr + WD_P0_RBCR1, 0); + outb(sc->we_io_nic_addr + WD_P0_RCR, WD_R_MON); + outb(sc->we_io_nic_addr + WD_P0_TCR, WD_T_CONFIG); + outb(sc->we_io_nic_addr + WD_P0_TPSR, 0); + outb(sc->we_io_nic_addr + WD_P0_PSTART, WD_TXBUF_SIZE); + outb(sc->we_io_nic_addr + WD_P0_PSTOP, + sc->we_vmem_size / WD_PAGE_SIZE); + outb(sc->we_io_nic_addr + WD_P0_BNRY, WD_TXBUF_SIZE); + outb(sc->we_io_nic_addr + WD_P0_ISR, 0xff); + outb(sc->we_io_nic_addr + WD_P0_IMR, WD_I_CONFIG); + wecmd.cs_ps = 1; + outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte); + for (i = 0; i < ETHER_ADDR_LEN; ++i) + outb(sc->we_io_nic_addr + WD_P1_PAR0 + i, sc->we_addr[i]); + for (i = 0; i < ETHER_ADDR_LEN; ++i) /* == broadcast addr */ + outb(sc->we_io_nic_addr + WD_P1_MAR0 + i, 0xff); + outb(sc->we_io_nic_addr + WD_P1_CURR, WD_TXBUF_SIZE); + wecmd.cs_ps = 0; + wecmd.cs_stp = 0; + wecmd.cs_sta = 1; + wecmd.cs_rd = 0x4; + outb(sc->we_io_nic_addr + WD_P1_COMMAND, wecmd.cs_byte); + outb(sc->we_io_nic_addr + WD_P0_RCR, WD_R_CONFIG); + + /* + * Take the interface out of reset, program the vector, + * enable interrupts, and tell the world we are up. + */ + ifp->if_flags |= IFF_RUNNING; + sc->we_flags &= ~WDF_TXBUSY; + (void) splx(s); + westart(ifp); +} + +/* + * Start output on interface. + */ +westart(ifp) + struct ifnet *ifp; +{ + register struct we_softc *sc = &we_softc[ifp->if_unit]; + struct mbuf *m0, *m; + register caddr_t buffer; + int len, s; + union we_command wecmd; + + /* + * The DS8390 has only one transmit buffer, if it is busy we + * must wait until the transmit interrupt completes. + */ + s = splhigh(); + if (sc->we_flags & WDF_TXBUSY) { + (void) splx(s); + return; + } + IF_DEQUEUE(&sc->we_if.if_snd, m); + if (m == 0) { + (void) splx(s); + return; + } + sc->we_flags |= WDF_TXBUSY; + (void) splx(s); + + /* + * Copy the mbuf chain into the transmit buffer + */ + buffer = sc->we_vmem_addr; + len = 0; + for (m0 = m; m != 0; m = m->m_next) { + bcopy(mtod(m, caddr_t), buffer, m->m_len); + buffer += m->m_len; + len += m->m_len; + } + + m_freem(m0); + + /* + * Init transmit length registers, and set transmit start flag. + */ + s = splhigh(); + len = MAX(len, ETHER_MIN_LEN); + wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND); + wecmd.cs_ps = 0; + outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte); + outb(sc->we_io_nic_addr + WD_P0_TBCR0, len & 0xff); + outb(sc->we_io_nic_addr + WD_P0_TBCR1, len >> 8); + wecmd.cs_txp = 1; + outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte); + (void) splx(s); +} + +/* + * Ethernet interface interrupt processor + */ +weintr(unit) + int unit; +{ + register struct we_softc *sc = &we_softc[unit]; + union we_command wecmd; + union we_interrupt weisr; + + unit =0; + + /* disable onboard interrupts, then get interrupt status */ + wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND); + wecmd.cs_ps = 0; + outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte); + weisr.is_byte = inb(sc->we_io_nic_addr + WD_P0_ISR); +loop: + outb(sc->we_io_nic_addr + WD_P0_ISR, weisr.is_byte); + + /* transmit error */ + if (weisr.is_txe) { + /* need to read these registers to clear status */ + sc->we_if.if_collisions += + inb(sc->we_io_nic_addr + WD_P0_TBCR0); + ++sc->we_if.if_oerrors; + } + + /* receiver error */ + if (weisr.is_rxe) { + /* need to read these registers to clear status */ + (void) inb(sc->we_io_nic_addr + 0xD); + (void) inb(sc->we_io_nic_addr + 0xE); + (void) inb(sc->we_io_nic_addr + 0xF); + ++sc->we_if.if_ierrors; + } + + /* normal transmit complete */ + if (weisr.is_ptx || weisr.is_txe) + wetint (unit); + + /* normal receive notification */ + if (weisr.is_prx || weisr.is_rxe) + werint (unit); + + /* try to start transmit */ + westart(&sc->we_if); + + /* re-enable onboard interrupts */ + wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND); + wecmd.cs_ps = 0; + outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte); + outb(sc->we_io_nic_addr + WD_P0_IMR, 0xff/*WD_I_CONFIG*/); + weisr.is_byte = inb(sc->we_io_nic_addr + WD_P0_ISR); + if (weisr.is_byte) goto loop; +} + +/* + * Ethernet interface transmit interrupt. + */ +wetint(unit) + int unit; +{ + register struct we_softc *sc = &we_softc[unit]; + + /* + * Do some statistics (assume page zero of NIC mapped in) + */ + sc->we_flags &= ~WDF_TXBUSY; + sc->we_if.if_timer = 0; + ++sc->we_if.if_opackets; + sc->we_if.if_collisions += inb(sc->we_io_nic_addr + WD_P0_TBCR0); +} + +/* + * Ethernet interface receiver interrupt. + */ +werint(unit) + int unit; +{ + register struct we_softc *sc = &we_softc[unit]; + u_char bnry, curr; + long len; + union we_command wecmd; + struct we_ring *wer; + + /* + * Traverse the receive ring looking for packets to pass back. + * The search is complete when we find a descriptor not in use. + */ + wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND); + wecmd.cs_ps = 0; + outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte); + bnry = inb(sc->we_io_nic_addr + WD_P0_BNRY); + wecmd.cs_ps = 1; + outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte); + curr = inb(sc->we_io_nic_addr + WD_P1_CURR); +if(Bdry) + bnry =Bdry; + + while (bnry != curr) + { + /* get pointer to this buffer header structure */ + wer = (struct we_ring *)(sc->we_vmem_addr + (bnry << 8)); + + /* count includes CRC */ + len = wer->we_count - 4; + if (len > 30 && len <= ETHERMTU+100 + /*&& (*(char *)wer == 1 || *(char *) wer == 0x21)*/) + weread(sc, (caddr_t)(wer + 1), len); + else printf("reject %d", len); + +outofbufs: + wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND); + wecmd.cs_ps = 0; + outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte); + + /* advance on chip Boundry register */ + if((caddr_t) wer + WD_PAGE_SIZE - 1 > sc->we_vmem_end) { + bnry = WD_TXBUF_SIZE; + outb(sc->we_io_nic_addr + WD_P0_BNRY, + sc->we_vmem_size / WD_PAGE_SIZE-1); + + } else { + if (len > 30 && len <= ETHERMTU+100) + bnry = wer->we_next_packet; + else bnry = curr; + + /* watch out for NIC overflow, reset Boundry if invalid */ + if ((bnry - 1) < WD_TXBUF_SIZE) { + outb(sc->we_io_nic_addr + WD_P0_BNRY, + (sc->we_vmem_size / WD_PAGE_SIZE) - 1); + bnry = WD_TXBUF_SIZE; + } else + outb(sc->we_io_nic_addr + WD_P0_BNRY, bnry-1); + } + + /* refresh our copy of CURR */ + wecmd.cs_ps = 1; + outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte); + curr = inb(sc->we_io_nic_addr + WD_P1_CURR); + } +Bdry = bnry; +} + +/* + * Process an ioctl request. + */ +weioctl(ifp, cmd, data) + register struct ifnet *ifp; + int cmd; + caddr_t data; +{ + register struct ifaddr *ifa = (struct ifaddr *)data; + struct we_softc *sc = &we_softc[ifp->if_unit]; + struct ifreq *ifr = (struct ifreq *)data; + int s = splimp(), error = 0; + + + switch (cmd) { + + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + weinit(ifp->if_unit); /* before arpwhohas */ + ((struct arpcom *)ifp)->ac_ipaddr = + IA_SIN(ifa)->sin_addr; + arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); + break; +#endif +#ifdef NS + case AF_NS: + { + register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); + + if (ns_nullhost(*ina)) + ina->x_host = *(union ns_host *)(sc->ns_addr); + else { + /* + * The manual says we cant change the address + * while the receiver is armed, + * so reset everything + */ + ifp->if_flags &= ~IFF_RUNNING; + bcopy((caddr_t)ina->x_host.c_host, + (caddr_t)sc->ns_addr, sizeof(sc->ns_addr)); + } + weinit(ifp->if_unit); /* does ne_setaddr() */ + break; + } +#endif + default: + weinit(ifp->if_unit); + break; + } + break; + + case SIOCSIFFLAGS: + if ((ifp->if_flags & IFF_UP) == 0 && + ifp->if_flags & IFF_RUNNING) { + ifp->if_flags &= ~IFF_RUNNING; + westop(ifp->if_unit); + } else if (ifp->if_flags & IFF_UP && + (ifp->if_flags & IFF_RUNNING) == 0) + weinit(ifp->if_unit); + break; + +#ifdef notdef + case SIOCGHWADDR: + bcopy((caddr_t)sc->sc_addr, (caddr_t) &ifr->ifr_data, + sizeof(sc->sc_addr)); + break; +#endif + + default: + error = EINVAL; + } + splx(s); + return (error); +} +/* + * set ethernet address for unit + */ +wesetaddr(physaddr, unit) + u_char *physaddr; + int unit; +{ + register struct we_softc *sc = &we_softc[unit]; + register int i; + + /* + * Rewrite ethernet address, and then force restart of NIC + */ + for (i = 0; i < ETHER_ADDR_LEN; i++) + sc->we_addr[i] = physaddr[i]; + sc->we_flags &= ~WDF_RUNNING; + weinit(unit); +} + +#define wedataaddr(sc, eh, off, type) \ + ((type) ((caddr_t)((eh)+1)+(off) >= (sc)->we_vmem_end) ? \ + (((caddr_t)((eh)+1)+(off))) - (sc)->we_vmem_end \ + + (sc)->we_vmem_ring: \ + ((caddr_t)((eh)+1)+(off))) +/* + * Pass a packet to the higher levels. + * We deal with the trailer protocol here. + */ +weread(sc, buf, len) + register struct we_softc *sc; + char *buf; + int len; +{ + register struct ether_header *eh; + struct mbuf *m, *weget(); + int off, resid; + + /* + * Deal with trailer protocol: if type is trailer type + * get true type from first 16-bit word past data. + * Remember that type was trailer by setting off. + */ + eh = (struct ether_header *)buf; + eh->ether_type = ntohs((u_short)eh->ether_type); + if (eh->ether_type >= ETHERTYPE_TRAIL && + eh->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { + off = (eh->ether_type - ETHERTYPE_TRAIL) * 512; + if (off >= ETHERMTU) return; /* sanity */ + eh->ether_type = ntohs(*wedataaddr(sc, eh, off, u_short *)); + resid = ntohs(*(wedataaddr(sc, eh, off+2, u_short *))); + if (off + resid > len) return; /* sanity */ + len = off + resid; + } else off = 0; + + len -= sizeof(struct ether_header); + if (len <= 0) return; + + /* + * Pull packet off interface. Off is nonzero if packet + * has trailing header; neget will then force this header + * information to be at the front, but we still have to drop + * the type and length which are at the front of any trailer data. + */ + m = weget(buf, len, off, &sc->we_if, sc); + if (m == 0) return; + ether_input(&sc->we_if, eh, m); +} + +/* + * Supporting routines + */ + +/* + * Pull read data off a interface. + * Len is length of data, with local net header stripped. + * Off is non-zero if a trailer protocol was used, and + * gives the offset of the trailer information. + * We copy the trailer information and then all the normal + * data into mbufs. When full cluster sized units are present + * we copy into clusters. + */ +struct mbuf * +weget(buf, totlen, off0, ifp, sc) + caddr_t buf; + int totlen, off0; + struct ifnet *ifp; + struct we_softc *sc; +{ + struct mbuf *top, **mp, *m, *p; + int off = off0, len; + register caddr_t cp = buf; + char *epkt; + int tc =totlen; + + buf += sizeof(struct ether_header); + cp = buf; + epkt = cp + totlen; + + if (off) { + cp += off + 2 * sizeof(u_short); + totlen -= 2 * sizeof(u_short); + } + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == 0) + return (0); + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = totlen; + m->m_len = MHLEN; + + top = 0; + mp = ⊤ + while (totlen > 0) { + if (top) { + MGET(m, M_DONTWAIT, MT_DATA); + if (m == 0) { + m_freem(top); + return (0); + } + m->m_len = MLEN; + } + len = min(totlen, epkt - cp); + if (len >= MINCLSIZE) { + MCLGET(m, M_DONTWAIT); + if (m->m_flags & M_EXT) + m->m_len = len = min(len, MCLBYTES); + else + len = m->m_len; + } else { + /* + * Place initial small packet/header at end of mbuf. + */ + if (len < m->m_len) { + if (top == 0 && len + max_linkhdr <= m->m_len) + m->m_data += max_linkhdr; + m->m_len = len; + } else + len = m->m_len; + } + + totlen -= len; + /* only do up to end of buffer */ + if (cp+len > sc->we_vmem_end) { + unsigned toend = sc->we_vmem_end - cp; + + bcopy(cp, mtod(m, caddr_t), toend); + cp = sc->we_vmem_ring; + bcopy(cp, mtod(m, caddr_t)+toend, len - toend); + cp += len - toend; + epkt = cp + totlen; + } else { + bcopy(cp, mtod(m, caddr_t), (unsigned)len); + cp += len; + } + *mp = m; + mp = &m->m_next; + if (cp == epkt) { + cp = buf; + epkt = cp + tc; + } + } + return (top); +} +#endif -- 2.20.1