📄 if_ne.c
字号:
/*- * Copyright (c) 1990, 1991 William F. Jolitz. * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * 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_ne.c 8.1 (Berkeley) 6/11/93 *//* * NE-1000/NE-2000 Ethernet driver * * Parts inspired from Tim Tucker's if_wd driver for the wd8003, * insight on the ne2000 gained from Robert Clements PC/FTP driver. *//* #define NEDEBUG */#include <sys/param.h>#include <sys/systm.h>#include <sys/mbuf.h>#include <sys/buf.h>#include <sys/protosw.h>#include <sys/socket.h>#include <sys/ioctl.h>#include <sys/errno.h>#include <sys/syslog.h>#ifdef PROM#include "pci/device.h"#else#include <sys/device.h>#endif#include <net/if.h>#include <net/netisr.h>#include <net/route.h>#if NBPFILTER > 0#include <net/bpf.h>#include <net/bpfdesc.h>#endif#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#ifdef PMON#include <machine/endian.h>#include "mips.h"#include "sbd.h"#else#include <i386/isa/isavar.h>#include <i386/isa/icu.h>#include <machine/cpu.h>#endif#include "if_nereg.h"#ifdef NEDEBUG#define dprintf(x) printf x#else#define dprintf(x)#endif#define ETHER_MIN_LEN 64#define ETHER_MAX_LEN 1536/* * Ethernet software status per interface. * * Each interface is referenced by a network interface structure, * ns_if, which the routing code uses to locate the interface. * This structure contains the output queue for the interface, its address, ... */struct ne_softc {#ifndef PROM struct device ne_dev; /* base device (must be first) */ struct isadev ne_id; /* ISA device */ struct intrhand ne_ih; /* interrupt vectoring */ #endif struct arpcom ns_ac; /* Ethernet common part */#define ns_if ns_ac.ac_if /* network-visible interface */#define sc_addr ns_ac.ac_enaddr /* hardware Ethernet address */ int ns_base; /* the board base address */ int ns_ba; /* byte addr in buffer ram of inc pkt */ int ns_cur; /* current page being filled */ int ns_ifoldflags; /* previous if_flags */ u_char ns_ne1000; /* true if the board is NE1000 */ u_char ns_tbuf; /* start of TX buffer (pages) */ u_char ns_rbuf; /* begin of RX ring (pages) */ u_char ns_rbufend; /* end of RX ring (pages) */ struct prhdr ns_ph; /* hardware header of incoming packet*/ struct ether_header ns_eh; /* header of incoming packet */ char ns_pb[2048 /*ETHERMTU+sizeof(long)*/];};/* * Prototypes */#ifdef PROMint neprobe __P((struct device *, void *, void *));#elseint neprobe __P((struct device *, struct cfdata *, void *));#endifvoid neattach __P((struct device *, struct device *, void *));int neintr __P((struct ne_softc *));int neinit __P((int));int nestart __P((struct ifnet *));int neioctl __P((struct ifnet *, int, caddr_t));static nememtest __P((int, int, int));void nefetch __P((int, int, caddr_t, int, int));void neput __P((int, int, caddr_t, int, int));void nerecv __P((struct ne_softc *));void neread __P((struct ne_softc *, char *, int));struct mbuf *neget __P((caddr_t, int, int, struct ifnet *));struct cfdriver necd =#ifdef PROM { NULL, "en", neprobe, neattach, DV_IFNET, sizeof(struct ne_softc) };#else { NULL, "ne", neprobe, neattach, DV_NET, sizeof(struct ne_softc) };#endif#ifdef PROMstruct isa_attach_args { unsigned int ia_iobase; unsigned int ia_iosize; unsigned int ia_unit; void *ia_aux;};#define outb(base, v) isa_outb(base, v)#define outsb(base, addr, len) isa_outsb(base, addr, len)#define inb(base) isa_inb(base)#define insb(base, addr, len) isa_insb(base, addr, len)#define inw(base) isa_inw(base)#define insw(base, addr, len) isa_insw(base, addr, len)#define outw(base, v) isa_outw(base, v)#define outsw(base, addr, len) isa_outsw(base, addr, len)#endif/* * Probe routine *//* ARGSUSED */neprobe(parent, data, aux) struct device *parent;#ifdef PROM void *data;#else struct cfdata *data;#endif void *aux;{ void neforceintr(); struct cfdata *cf = data; register struct isa_attach_args *ia = (struct isa_attach_args *) aux; register nec = ia->ia_iobase; int val, i;#ifdef lint neintr(0);#endif#ifndef PROM if (isa_portcheck(nec, NE_NPORT) == 0) { aprint_debug("port range overlaps existing, "); return (0); }#endif /* * Check configuration parameters */ if (!NE_IOBASEVALID(nec)) { printf("ne%d: invalid i/o base address %x\n", cf->cf_unit, nec); return (0); } /* * Reset the board and check its existence */ dprintf(("ne%d: port %x RESET\n", nec, cf->cf_unit)); val = inb(nec+ne_reset); DELAY(10000); outb(nec+ne_reset, val); outb(nec+ds_cmd, DSCM_STOP|DSCM_NODMA); dprintf(("ne%d: WAIT FOR RES", cf->cf_unit)); i = 100000; while ((inb(nec+ds0_isr)&DSIS_RESET) == 0 && i-- > 0) ; dprintf(("-- res=%d\n", i)); if (i < 0) return (0); /* Reset interrupts */ outb(nec+ds0_isr, 0xff); /* * Select NE1000 params -- byte transfers, burst mode, FIFO at 8 bytes. */ outb(nec+ds0_dcr, DSDC_BMS|DSDC_FT1); outb(nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP); DELAY(100); if (inb(nec+ds_cmd) != (DSCM_NODMA|DSCM_PG0|DSCM_STOP)) return (0); /* * Try to touch NE-2000 memory. Use NE-1000 (byte-mode) transfer, * as word mode wedges an NE-1000. */ ia->ia_aux = (void *) 0; /* NE-2000 by default */ dprintf(("TRY NE2000")); outb(nec+ds0_dcr, DSDC_BMS|DSDC_FT1); if (nememtest(nec, 1, NE2000_TBUF)) { dprintf((" TRY NE1000")); if (nememtest(nec, 1, NE1000_TBUF)) { /* * Last chance: try NE-2000 with word transfers. * Some "compatibles" don't do byte transfers. */ dprintf(("TRY NE2000 word-mode")); outb(nec+ds0_dcr, DSDC_WTS|DSDC_BMS|DSDC_FT1); if (nememtest(nec, 0, NE2000_TBUF)) {#ifdef PROM printf("mem test failed, ");#else aprint_debug("mem test failed, ");#endif dprintf(("OOPS\n")); return (0); } } else /* It's NE-1000 */ ia->ia_aux = (void *)1; } dprintf(("OK\n"));#ifndef PROM /* * Find out IRQ if unknown */ if (ia->ia_irq == IRQUNK) { ia->ia_irq = isa_discoverintr(neforceintr, aux); outb(nec+ds0_imr, 0); if (ffs(ia->ia_irq) - 1 == 0) return (0); }#endif ia->ia_iosize = NE_NPORT; return (1);}/* * Test on-board memory at the specified address, returning 0 if good. */staticnememtest(nec, ne1000, addr) register int nec; int ne1000; int addr;{ static char test_pattern[32] = "*** This is a test pattern ***"; char testbuf[32]; dprintf((" -- PUT --")); neput(nec, ne1000, test_pattern, addr, sizeof(test_pattern)); dprintf((" FETCH -- ")); nefetch(nec, ne1000, testbuf, addr, sizeof(test_pattern)); return (bcmp(testbuf, test_pattern, sizeof(test_pattern)));}#ifndef PROM/* * force the card to interrupt (tell us where it is) for autoconfiguration */ voidneforceintr(arg) void *arg;{ int s; register nec = ((struct isa_attach_args *) arg)->ia_iobase; s = splhigh(); /* init chip regs */ outb(nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP); outb(nec+ds0_rbcr0, 0); outb(nec+ds0_rbcr1, 0); outb(nec+ds0_imr, 0); outb(nec+ds0_isr, 0xff); outb(nec+ds0_dcr, DSDC_BMS|DSDC_FT1); outb(nec+ds0_tcr, 0); outb(nec+ds0_rcr, DSRC_MON); outb(nec+ds0_tpsr, 0); outb(nec+ds0_pstart, NE1000_RBUF / DS_PGSIZE); outb(nec+ds0_pstop, NE1000_RBUFEND / DS_PGSIZE); outb(nec+ds0_bnry, NE1000_RBUF / DS_PGSIZE); outb(nec+ds_cmd, DSCM_NODMA|DSCM_PG1|DSCM_STOP); outb(nec+ds1_curr, NE1000_RBUF / DS_PGSIZE); outb(nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START); outb(nec+ds0_rcr, DSRC_AB); outb(nec+ds0_imr, 0xff); outb(nec+ds0_tbcr0, ETHER_MIN_LEN); outb(nec+ds0_tbcr1, 0); outb(nec+ds0_tpsr, NE1000_TBUF / DS_PGSIZE); outb(nec+ds_cmd, DSCM_TRANS|DSCM_NODMA|DSCM_START); splx(s);}#endif/* * Interface exists: make available by filling in network interface * record. System will initialize the interface when it is ready * to accept packets. We get the ethernet address here. *//* ARGSUSED */voidneattach(parent, self, aux) struct device *parent, *self; void *aux;{ register struct ne_softc *ns = (struct ne_softc *) self; register struct isa_attach_args *ia = (struct isa_attach_args *) aux; register nec = ia->ia_iobase;#ifdef PROM int unit = ia->ia_unit;#else int unit = ns->ne_dev.dv_unit;#endif register struct ifnet *ifp = &ns->ns_if; int i; /* * Set up NE1000/NE2000 stuff */ ns->ns_base = nec; outb(nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP); if ((int)(ia->ia_aux)) { ns->ns_ne1000 = 1; ns->ns_tbuf = NE1000_TBUF / DS_PGSIZE; ns->ns_rbuf = NE1000_RBUF / DS_PGSIZE; ns->ns_rbufend = NE1000_RBUFEND / DS_PGSIZE; outb(nec+ds0_dcr, DSDC_BMS|DSDC_FT1); } else { /* Setup NE2000 params */ ns->ns_ne1000 = 0; ns->ns_tbuf = NE2000_TBUF / DS_PGSIZE; ns->ns_rbuf = NE2000_RBUF / DS_PGSIZE; ns->ns_rbufend = NE2000_RBUFEND / DS_PGSIZE; outb(nec+ds0_dcr, DSDC_WTS|DSDC_BMS|DSDC_FT1); } /* * Make sure the board is silent and * prepare for extracting the PROM address */ outb(nec+ds0_rcr, DSRC_MON); outb(nec+ds0_imr, 0); outb(nec+ds0_isr, 0); outb(nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP); /* * Extract board address */ dprintf(("ne%d: EXTRACT ADDR\n", unit)); nefetch(nec, ns->ns_ne1000, ns->ns_pb, 0, 2 * ETHER_ADDR_LEN); if (ns->ns_ne1000) for (i = 0; i < ETHER_ADDR_LEN; i++) ns->sc_addr[i] = ns->ns_pb[i]; else for (i = 0; i < ETHER_ADDR_LEN; i++) ns->sc_addr[i] = ((u_short *)(ns->ns_pb))[i]; /* * Initialize interface structure */ ifp->if_unit = unit; ifp->if_name = necd.cd_name; ifp->if_mtu = ETHERMTU;#ifndef IFF_MULTICAST#define IFF_MULTICAST 0#endif ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX | IFF_NOTRAILERS; ifp->if_init = neinit; ifp->if_start = nestart; ifp->if_ioctl = neioctl; ifp->if_watchdog = 0; ether_attach(ifp);#ifdef PROM printf(": NE-%c000 Ethernet, address %s\n", ns->ns_ne1000 ? '1' : '2', ether_sprintf(ns->sc_addr));#else printf(": NE-%c000", ns->ns_ne1000 ? '1' : '2'); aprint_naive(" Ethernet"); aprint_normal(", address %s", ether_sprintf(ns->sc_addr)); printf("\n");#endif#if NBPFILTER > 0 bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header));#endif#ifndef PROM isa_establish(&ns->ne_id, &ns->ne_dev); ns->ne_ih.ih_fun = neintr; ns->ne_ih.ih_arg = (void *)ns; intr_establish(ia->ia_irq, &ns->ne_ih, DV_NET);#endif}/* * Initialization of interface; set up initialization block * and transmit/receive descriptor rings. */neinit(unit) int unit;{ register struct ne_softc *ns = necd.cd_devs[unit]; struct ifnet *ifp = &ns->ns_if; int s; register nec = ns->ns_base; register i; if (ifp->if_addrlist == (struct ifaddr *) 0) return (0); if ((ifp->if_flags & IFF_RUNNING) && ifp->if_flags == ns->ns_ifoldflags) return (0); s = splimp(); /* set physical address on ethernet */ outb(nec+ds_cmd, DSCM_NODMA|DSCM_PG1|DSCM_STOP); for (i = 0; i < 6; i++) outb(nec+ds1_par0+i, ns->sc_addr[i]); /* clr logical address hash filter for now */ for (i = 0; i < 8; i++) outb(nec+ds1_mar0+i, 0xff); /* * Init DS8390 */ outb(nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP); outb(nec+ds0_rbcr0, 0); outb(nec+ds0_rbcr1, 0); outb(nec+ds0_imr, 0); outb(nec+ds0_isr, 0xff); outb(nec+ds0_dcr, ns->ns_ne1000 ? DSDC_BMS|DSDC_FT1 : DSDC_WTS|DSDC_BMS|DSDC_FT1); outb(nec+ds0_tcr, 0); outb(nec+ds0_rcr, DSRC_MON); outb(nec+ds0_tpsr, 0); outb(nec+ds0_pstart, ns->ns_rbuf); outb(nec+ds0_pstop, ns->ns_rbufend); outb(nec+ds0_bnry, ns->ns_rbuf); outb(nec+ds_cmd, DSCM_NODMA|DSCM_PG1|DSCM_STOP); outb(nec+ds1_curr, ns->ns_rbuf); outb(nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START); if (ifp->if_flags & IFF_PROMISC) outb(nec+ds0_rcr, DSRC_AB|DSRC_AM|DSRC_PRO);#if IFF_MULTICAST > 0 else if (ns->ns_ac.ac_multiaddrs != 0 || ifp->if_flags & IFF_ALLMULTI) outb(nec+ds0_rcr, DSRC_AB|DSRC_AM);#endif else outb(nec+ds0_rcr, DSRC_AB); outb(nec+ds0_imr, 0xff); ns->ns_cur = ns->ns_rbuf; ns->ns_if.if_flags &= ~IFF_OACTIVE; ns->ns_if.if_flags |= IFF_RUNNING; nestart(ifp); splx(s);}/* * Setup output on interface. * Get another datagram to send off of the interface queue, * and copy it to the interface before starting the output. * Called only at splimp or interrupt level. */nestart(ifp) struct ifnet *ifp;{ register struct ne_softc *ns = necd.cd_devs[ifp->if_unit]; register nec = ns->ns_base; struct mbuf *m0, *m; u_char cmd, oddword[2]; int len; IF_DEQUEUE(&ns->ns_if.if_snd, m0); if (m0 == 0) return (0); ns->ns_if.if_flags |= IFF_OACTIVE; /* prevent entering nestart */ #if NBPFILTER > 0 /* * Feed outgoing packet to bpf */ if (ns->ns_if.if_bpf) bpf_mtap(ns->ns_if.if_bpf, m0);#endif /* * Calculate the length of a packet. * XXX should use m->m_hdr.len. */ for (len = 0, m = m0; m != 0; m = m->m_next) len += m->m_len; if ((len & 01) && !ns->ns_ne1000) len++; /* Setup for remote dma */ cmd = inb(nec+ds_cmd); outb(nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START); outb(nec+ds0_isr, DSIS_RDC); outb(nec+ds0_rbcr0, len); outb(nec+ds0_rbcr1, len >> 8); outb(nec+ds0_rsar0, 0); outb(nec+ds0_rsar1, ns->ns_tbuf); outb(nec+ds_cmd, DSCM_RWRITE|DSCM_PG0|DSCM_START); /* * Push out data from each mbuf, watching out * for 0 length mbufs and odd-length mbufs (on ne2000). */ if (ns->ns_ne1000) { for (m = m0; m != 0; m = m->m_next) { if (m->m_len == 0) continue; outsb(nec+ne_data, m->m_data, m->m_len); } } else { for (m = m0; m != 0; ) { if (m->m_len >= 2) outsw(nec+ne_data, m->m_data, m->m_len / 2); if (m->m_len & 1) { oddword[0] = *(mtod(m, caddr_t) + m->m_len - 1); if (m = m->m_next) { oddword[1] = *(mtod(m, caddr_t)); m->m_data++; m->m_len--; } else oddword[1] = 0; outsw(nec+ne_data, oddword, 1); /* "outw" */ } else m = m->m_next; } } /* Wait till done, then shutdown feature */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -