/*- * 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. * * from: @(#)if_we.c 7.3 (Berkeley) 5/21/91 * $Id: if_we.c,v 1.11 1993/08/03 05:06:26 mycroft Exp $ */ #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 "select.h" #include "net/if.h" #include "net/if_types.h" #include "net/if_dl.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 "bpfilter.h" #if NBPFILTER > 0 #include "net/bpf.h" #include "net/bpfdesc.h" #endif #include "i386/isa/isa.h" #include "i386/isa/if_wereg.h" #include "i386/isa/isa_device.h" #include "i386/isa/icu.h" #if 0 #include "i386/include/pio.h" #endif static inline char *we_ring_copy(); /* * 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 */ #define ns_addrp 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 */ caddr_t we_bpf; /* Magic Cookie for BPF */ #ifdef WEDEBUG int fae, crc, mpa; #endif } 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; #ifdef WD8013 /* 20 Sep 92*/ union we_laar laar; laar.laar_byte = 0; #endif /* WD8013*/ wem.ms_byte = 0; /* 20 Sep 92*/ /* 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); #ifdef WD8013 /* 20 Sep 92*/ /* allow the NIC to access the shared RAM 16 bits at a time */ laar.addr_l19 = 1; laar.lan_16_en = 1; laar.mem_16_en = 1; outb(is->id_iobase+5, laar.laar_byte); /* Write a 0xc1 */ #endif /* WD8013*/ /* * 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) { /* 09 Sep 92*/ #ifdef WECOMPAT printf( "we: probe: checksum failed... installing anyway\n"); printf( "we: Danpex EW-2016 or other 8013 clone card?\n"); #else /* !WECOMPAT*/ return (0); #endif /* !WECOMPAT*/ } 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; struct ifaddr *ifa; struct sockaddr_dl *sdl; 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); /* Search down the ifa address list looking for the AF_LINK type entry */ ifa = ifp->if_addrlist; while ((ifa != 0) && (ifa->ifa_addr != 0) && (ifa->ifa_addr->sa_family != AF_LINK)) { ifa = ifa->ifa_next; } /* If we find an AF_LINK type entry, we well fill in the hardware addr */ if ((ifa != 0) && (ifa->ifa_addr != 0)) { /* Fill in the link level address for this interface */ sdl = (struct sockaddr_dl *)ifa->ifa_addr; sdl->sdl_type = IFT_ETHER; sdl->sdl_alen = ETHER_ADDR_LEN; sdl->sdl_slen = 0; bcopy(sc->we_addr, LLADDR(sdl), ETHER_ADDR_LEN); } #ifdef WEDEBUG sc->fae = sc->crc = sc->mpa = 0; #endif /* * Banner... */ printf("we%d: %s address %s\n", is->id_unit, (sc->we_type & WD_ETHERNET) ? "ethernet" : "starlan", ether_sprintf(sc->we_addr)); #if NBPFILTER > 0 bpfattach(&sc->we_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); #endif } /* * 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) { weintr(unit); /*log(LOG_WARNING,"we%d: soft reset\n", unit); westop(unit); weinit(unit);*/ } /* * 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(); 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); #ifdef WD8013 /* 20 Sep 92*/ /* enable 16 bit access if 8013 card */ outb(sc->we_io_nic_addr + WD_P0_DCR, WD_D_CONFIG16); #else /* !WD8013*/ outb(sc->we_io_nic_addr + WD_P0_DCR, WD_D_CONFIG); #endif /* !WD8013*/ 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 + 1); 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); #if NBPFILTER > 0 if (sc->we_if.if_flags & IFF_PROMISC) { outb(sc->we_io_nic_addr + WD_P0_RCR, WD_R_PRO | WD_R_SEP | WD_R_AR | WD_R_CONFIG); } else #endif 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); #if NBPFILTER > 0 if (sc->we_bpf) { u_short etype; int off, datasize, resid; struct ether_header *eh; struct trailer_header { u_short ether_type; u_short ether_residual; } trailer_header; char ether_packet[ETHERMTU+100]; char *ep; ep = ether_packet; /* * We handle trailers below: * Copy ether header first, then residual data, * then data. Put all this in a temporary buffer * 'ether_packet' and send off to bpf. Since the * system has generated this packet, we assume * that all of the offsets in the packet are * correct; if they're not, the system will almost * certainly crash in m_copydata. * We make no assumptions about how the data is * arranged in the mbuf chain (i.e. how much * data is in each mbuf, if mbuf clusters are * used, etc.), which is why we use m_copydata * to get the ether header rather than assume * that this is located in the first mbuf. */ /* copy ether header */ m_copydata(m, 0, sizeof(struct ether_header), ep); eh = (struct ether_header *) ep; ep += sizeof(struct ether_header); etype = ntohs(eh->ether_type); if (etype >= ETHERTYPE_TRAIL && etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { datasize = ((etype - ETHERTYPE_TRAIL) << 9); off = datasize + sizeof(struct ether_header); /* copy trailer_header into a data structure */ m_copydata(m, off, sizeof(struct trailer_header), &trailer_header.ether_type); /* copy residual data */ m_copydata(m, off+sizeof(struct trailer_header), resid = ntohs(trailer_header.ether_residual) - sizeof(struct trailer_header), ep); ep += resid; /* copy data */ m_copydata(m, sizeof(struct ether_header), datasize, ep); ep += datasize; /* restore original ether packet type */ eh->ether_type = trailer_header.ether_type; bpf_tap(sc->we_bpf, ether_packet, ep - ether_packet); } else bpf_mtap(sc->we_bpf, m); } #endif /* * 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); sc->we_if.if_timer = 3; (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; /* 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) { ++sc->we_if.if_ierrors; /* need to read these registers to clear status */ #ifndef WEDEBUG (void) inb(sc->we_io_nic_addr + 0xD); (void) inb(sc->we_io_nic_addr + 0xE); (void) inb(sc->we_io_nic_addr + 0xF); #else sc->fae += inb(sc->we_io_nic_addr + 0xD); sc->crc += inb(sc->we_io_nic_addr + 0xE); sc->mpa += inb(sc->we_io_nic_addr + 0xF); printf ("we%d: ierr %d fae %d crc %d mpa\n", unit, sc->fae, sc->crc, sc->mpa); #endif } /* 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); if (++bnry >= sc->we_vmem_size / WD_PAGE_SIZE) bnry = WD_TXBUF_SIZE; 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); 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) weread(sc, (caddr_t)(wer + 1), len); else printf("we%d: reject - bad length %d", unit, 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); 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); } } /* * 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_addrp); 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_addrp, sizeof(sc->ns_addrp)); } 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); #if NBPFILTER > 0 if (sc->we_if.if_flags & IFF_PROMISC) { outb(sc->we_io_nic_addr + WD_P0_RCR, WD_R_PRO | WD_R_SEP | WD_R_AR | WD_R_CONFIG); } else #endif outb(sc->we_io_nic_addr + WD_P0_RCR, WD_R_CONFIG); 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 ringoffset(sc, eh, off, type) \ ((type)( ((caddr_t)(eh)+(off) >= (sc)->we_vmem_end) ? \ (((caddr_t)(eh)+(off))) - (sc)->we_vmem_end \ + (sc)->we_vmem_ring: \ ((caddr_t)(eh)+(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; { caddr_t we_ring_copy(); struct ether_header *eh; struct mbuf *m, *head, *we_ring_to_mbuf(); int off, resid; u_short etype; struct trailer_header { u_short trail_type; u_short trail_residual; } trailer_header; ++sc->we_if.if_ipackets; /* Allocate a header mbuf */ MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == 0) goto bad; m->m_pkthdr.rcvif = &sc->we_if; m->m_pkthdr.len = len; m->m_len = 0; head = m; eh = (struct ether_header *)buf; #define EROUND ((sizeof(struct ether_header) + 3) & ~3) #define EOFF (EROUND - sizeof(struct ether_header)) /* * The following assumes there is room for * the ether header in the header mbuf */ head->m_data += EOFF; bcopy(buf, mtod(head, caddr_t), sizeof(struct ether_header)); buf += sizeof(struct ether_header); head->m_len += sizeof(struct ether_header); len -= sizeof(struct ether_header); etype = ntohs((u_short)eh->ether_type); /* * Deal with trailer protocol: * If trailer protocol, calculate the datasize as 'off', * which is also the offset to the trailer header. * Set resid to the amount of packet data following the * trailer header. * Finally, copy residual data into mbuf chain. */ if (etype >= ETHERTYPE_TRAIL && etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { off = (etype - ETHERTYPE_TRAIL) << 9; if ((off + sizeof(struct trailer_header)) > len) goto bad; /* insanity */ eh->ether_type = *ringoffset(sc, buf, off, u_short *); resid = ntohs(*ringoffset(sc, buf, off+2, u_short *)); if ((off + resid) > len) goto bad; /* insanity */ resid -= sizeof(struct trailer_header); if (resid < 0) goto bad; /* insanity */ m = we_ring_to_mbuf(sc, ringoffset(sc, buf, off+4, char *), head, resid); if (m == 0) goto bad; len = off; head->m_pkthdr.len -= 4; /* subtract trailer header */ } /* * Pull packet off interface. Or if this was a trailer packet, * the data portion is appended. */ m = we_ring_to_mbuf(sc, buf, m, len); if (m == 0) goto bad; #if NBPFILTER > 0 /* * Check if there's a bpf filter listening on this interface. * If so, hand off the raw packet to bpf. */ if (sc->we_bpf) { bpf_mtap(sc->we_bpf, head); } /* * Note that the interface cannot be in promiscuous mode if * there are no bpf listeners. And if we are in promiscuous * mode, we have to check if this packet is really ours. * * XXX This test does not support multicasts. */ if ((sc->we_if.if_flags & IFF_PROMISC) && bcmp(eh->ether_dhost, sc->we_addr, sizeof(eh->ether_dhost)) != 0 && bcmp(eh->ether_dhost, etherbroadcastaddr, sizeof(eh->ether_dhost)) != 0) { m_freem(head); return; } #endif /* * Fix up data start offset in mbuf to point past ether header */ m_adj(head, sizeof(struct ether_header)); /* * silly ether_input routine needs 'type' in host byte order */ eh->ether_type = ntohs(eh->ether_type); ether_input(&sc->we_if, eh, head); return; bad: if (head) m_freem(head); return; } /* * Supporting routines */ /* * Copy data from receive buffer to end of mbuf chain * allocate additional mbufs as needed. return pointer * to last mbuf in chain. * sc = we info * src = pointer in we ring buffer * dst = pointer to last mbuf in mbuf chain to copy to * amount = amount of data to copy */ struct mbuf * we_ring_to_mbuf(sc,src,dst,total_len) struct we_softc *sc; char *src; struct mbuf *dst; int total_len; { register struct mbuf *m = dst; while (total_len > 0) { register int amount = min(total_len, M_TRAILINGSPACE(m)); if (amount == 0) { /* no more data in this mbuf, alloc another */ /* * if there is enough data for an mbuf cluster, attempt * to allocate one of those, otherwise, a regular mbuf * will do. */ dst = m; MGET(m, M_DONTWAIT, MT_DATA); if (m == 0) return (0); if (total_len >= MINCLSIZE) MCLGET(m, M_DONTWAIT); m->m_len = 0; dst->m_next = m; amount = min(total_len, M_TRAILINGSPACE(m)); } src = we_ring_copy(sc, src, mtod(m, caddr_t) + m->m_len, amount); m->m_len += amount; total_len -= amount; } return (m); } static inline char * we_ring_copy(sc,src,dst,amount) struct we_softc *sc; char *src; char *dst; int amount; { int tmp_amount; /* does copy wrap to lower addr in ring buffer? */ if (src + amount > sc->we_vmem_end) { tmp_amount = sc->we_vmem_end - src; bcopy(src,dst,tmp_amount); /* copy amount up to end */ amount -= tmp_amount; src = sc->we_vmem_ring; dst += tmp_amount; } bcopy(src, dst, amount); return(src + amount); } #endif