NetBSD/sys/arch/amiga/dev/if_es.c
is 07b064e02e New ARP system, supports IPv4 over any hardware link.
Some of the stuff (e.g., rarpd, bootpd, dhcpd etc., libsa) still will
only support Ethernet. Tcpdump itself should be ok, but libpcap needs
lot of work.

For the detailed change history, look at the commit log entries for
the is-newarp branch.
1997-03-15 18:09:08 +00:00

1092 lines
27 KiB
C

/* $NetBSD: if_es.c,v 1.17 1997/03/15 18:09:25 is Exp $ */
/*
* Copyright (c) 1995 Michael L. Hitch
* 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 Michael L. Hitch.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
*/
/*
* SMC 91C90 Single-Chip Ethernet Controller
*/
#include "bpfilter.h"
#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/syslog.h>
#include <sys/ioctl.h>
#include <sys/errno.h>
#include <sys/device.h>
#include <net/if.h>
#include <net/if_ether.h>
#ifdef INET
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
#include <netinet/if_inarp.h>
#endif
#ifdef NS
#include <netns/ns.h>
#include <netns/ns_if.h>
#endif
#include <machine/cpu.h>
#include <machine/mtpr.h>
#include <amiga/amiga/device.h>
#include <amiga/amiga/isr.h>
#include <amiga/dev/zbusvar.h>
#include <amiga/dev/if_esreg.h>
#define SWAP(x) (((x & 0xff) << 8) | ((x >> 8) & 0xff))
#define USEPKTBUF
/*
* Ethernet software status per interface.
*
* Each interface is referenced by a network interface structure,
* es_if, which the routing code uses to locate the interface.
* This structure contains the output queue for the interface, its address, ...
*/
struct es_softc {
struct device sc_dev;
struct isr sc_isr;
struct ethercom sc_ethercom; /* common Ethernet structures */
void *sc_base; /* base address of board */
short sc_iflags;
unsigned short sc_intctl;
#ifdef ESDEBUG
int sc_debug;
short sc_intbusy; /* counter for interrupt rentered */
short sc_smcbusy; /* counter for other rentry checks */
#endif
};
#if NBPFILTER > 0
#include <net/bpf.h>
#include <net/bpfdesc.h>
#endif
#ifdef ESDEBUG
/* console error messages */
int esdebug = 0;
int estxints = 0; /* IST_TX with TX enabled */
int estxint2 = 0; /* IST_TX active after IST_TX_EMPTY */
int estxint3 = 0; /* IST_TX interrupt processed */
int estxint4 = 0; /* ~TEMPTY counts */
int estxint5 = 0; /* IST_TX_EMPTY interrupts */
void es_dump_smcregs __P((char *, union smcregs *));
#endif
int esintr __P((void *));
void esstart __P((struct ifnet *));
void eswatchdog __P((struct ifnet *));
int esioctl __P((struct ifnet *, u_long, caddr_t));
void esrint __P((struct es_softc *));
void estint __P((struct es_softc *));
void esinit __P((struct es_softc *));
void esreset __P((struct es_softc *));
void esstop __P((struct es_softc *));
int esmatch __P((struct device *, struct cfdata *, void *));
void esattach __P((struct device *, struct device *, void *));
struct cfattach es_ca = {
sizeof(struct es_softc), esmatch, esattach
};
struct cfdriver es_cd = {
NULL, "es", DV_IFNET
};
int
esmatch(parent, cfp, aux)
struct device *parent;
struct cfdata *cfp;
void *aux;
{
struct zbus_args *zap = aux;
/* Ameristar A4066 ethernet card */
if (zap->manid == 1053 && zap->prodid == 10)
return(1);
return (0);
}
/*
* Interface exists: make available by filling in network interface
* record. System will initialize the interface when it is ready
* to accept packets.
*/
void
esattach(parent, self, aux)
struct device *parent, *self;
void *aux;
{
struct es_softc *sc = (void *)self;
struct zbus_args *zap = aux;
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
unsigned long ser;
u_int8_t myaddr[ETHER_ADDR_LEN];
sc->sc_base = zap->va;
/*
* Manufacturer decides the 3 first bytes, i.e. ethernet vendor ID.
* (Currently only Ameristar.)
*/
myaddr[0] = 0x00;
myaddr[1] = 0x00;
myaddr[2] = 0x9f;
/*
* Serial number for board contains last 3 bytes.
*/
ser = (unsigned long) zap->serno;
myaddr[3] = (ser >> 16) & 0xff;
myaddr[4] = (ser >> 8) & 0xff;
myaddr[5] = (ser ) & 0xff;
/* Initialize ifnet structure. */
bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ);
ifp->if_softc = sc;
ifp->if_output = ether_output;
ifp->if_ioctl = esioctl;
ifp->if_start = esstart;
ifp->if_watchdog = eswatchdog;
/* XXX IFF_MULTICAST */
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS;
/* Attach the interface. */
if_attach(ifp);
ether_ifattach(ifp, myaddr);
/* Print additional info when attached. */
printf(": address %s\n", ether_sprintf(myaddr));
#if NBPFILTER > 0
bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header));
#endif
sc->sc_isr.isr_intr = esintr;
sc->sc_isr.isr_arg = sc;
sc->sc_isr.isr_ipl = 2;
add_isr(&sc->sc_isr);
}
#ifdef ESDEBUG
void
es_dump_smcregs(where, smc)
char *where;
union smcregs *smc;
{
u_short cur_bank = smc->b0.bsr & BSR_MASK;
printf("SMC registers %p from %s bank %04x\n", smc, where,
smc->b0.bsr);
smc->b0.bsr = BSR_BANK0;
printf("TCR %04x EPHSR %04x RCR %04x ECR %04x MIR %04x MCR %04x\n",
SWAP(smc->b0.tcr), SWAP(smc->b0.ephsr), SWAP(smc->b0.rcr),
SWAP(smc->b0.ecr), SWAP(smc->b0.mir), SWAP(smc->b0.mcr));
smc->b1.bsr = BSR_BANK1;
printf("CR %04x BAR %04x IAR %04x %04x %04x GPR %04x CTR %04x\n",
SWAP(smc->b1.cr), SWAP(smc->b1.bar), smc->b1.iar[0], smc->b1.iar[1],
smc->b1.iar[2], smc->b1.gpr, SWAP(smc->b1.ctr));
smc->b2.bsr = BSR_BANK2;
printf("MMUCR %04x PNR %02x ARR %02x FIFO %04x PTR %04x",
SWAP(smc->b2.mmucr), smc->b2.pnr, smc->b2.arr, smc->b2.fifo,
SWAP(smc->b2.ptr));
printf(" DATA %04x %04x IST %02x MSK %02x\n", smc->b2.data,
smc->b2.datax, smc->b2.ist, smc->b2.msk);
smc->b3.bsr = BSR_BANK3;
printf("MT %04x %04x %04x %04x\n",
smc->b3.mt[0], smc->b3.mt[1], smc->b3.mt[2], smc->b3.mt[3]);
smc->b3.bsr = cur_bank;
}
#endif
void
esstop(sc)
struct es_softc* sc;
{
}
void
esinit(sc)
struct es_softc *sc;
{
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
union smcregs *smc = sc->sc_base;
int s;
s = splnet();
#ifdef ESDEBUG
if (ifp->if_flags & IFF_RUNNING)
es_dump_smcregs("esinit", smc);
#endif
smc->b0.bsr = BSR_BANK0; /* Select bank 0 */
smc->b0.rcr = RCR_EPH_RST;
smc->b0.rcr = 0;
smc->b3.bsr = BSR_BANK3; /* Select bank 3 */
smc->b3.mt[0] = 0; /* clear Multicast table */
smc->b3.mt[1] = 0;
smc->b3.mt[2] = 0;
smc->b3.mt[3] = 0;
/* XXX set Multicast table from Multicast list */
smc->b1.bsr = BSR_BANK1; /* Select bank 1 */
smc->b1.cr = CR_RAM32K | CR_NO_WAIT_ST | CR_SET_SQLCH;
smc->b1.ctr = CTR_AUTO_RLSE;
smc->b1.iar[0] = *((unsigned short *) &LLADDR(ifp->if_sadl)[0]);
smc->b1.iar[1] = *((unsigned short *) &LLADDR(ifp->if_sadl)[2]);
smc->b1.iar[2] = *((unsigned short *) &LLADDR(ifp->if_sadl)[4]);
smc->b2.bsr = BSR_BANK2; /* Select bank 2 */
smc->b2.mmucr = MMUCR_RESET;
smc->b0.bsr = BSR_BANK0; /* Select bank 0 */
smc->b0.mcr = SWAP(0x0020); /* reserve 8K for transmit buffers */
smc->b0.tcr = TCR_PAD_EN | (TCR_TXENA + TCR_MON_CSN);
smc->b0.rcr = RCR_FILT_CAR | RCR_STRIP_CRC | RCR_RXEN;
/* XXX add multicast/promiscuous flags */
smc->b2.bsr = BSR_BANK2; /* Select bank 2 */
smc->b2.msk = sc->sc_intctl = MSK_RX_OVRN | MSK_RX;
/* Interface is now 'running', with no output active. */
ifp->if_flags |= IFF_RUNNING;
ifp->if_flags &= ~IFF_OACTIVE;
/* Attempt to start output, if any. */
esstart(ifp);
splx(s);
}
int
esintr(arg)
void *arg;
{
struct es_softc *sc = arg;
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
u_short intsts, intact;
union smcregs *smc;
int s = splnet();
smc = sc->sc_base;
#ifdef ESDEBUG
while ((smc->b2.bsr & BSR_MASK) != BSR_BANK2 &&
ifp->if_flags & IFF_RUNNING) {
printf("%s: intr BSR not 2: %04x\n", sc->sc_dev.dv_xname,
smc->b2.bsr);
smc->b2.bsr = BSR_BANK2;
}
#endif
intsts = smc->b2.ist;
intact = smc->b2.msk & intsts;
if ((intact) == 0) {
splx(s);
return (0);
}
#ifdef ESDEBUG
if (esdebug)
printf ("%s: esintr ist %02x msk %02x",
sc->sc_dev.dv_xname, intsts, smc->b2.msk);
if (sc->sc_intbusy++) {
printf("%s: esintr re-entered\n", sc->sc_dev.dv_xname);
panic("esintr re-entered");
}
if (sc->sc_smcbusy)
printf("%s: esintr interrupted busy %d\n", sc->sc_dev.dv_xname,
sc->sc_smcbusy);
#endif
smc->b2.msk = 0;
#ifdef ESDEBUG
if (esdebug)
printf ("=>%02x%02x pnr %02x arr %02x fifo %04x\n",
smc->b2.ist, smc->b2.ist, smc->b2.pnr, smc->b2.arr,
smc->b2.fifo);
#endif
if (intact & IST_ALLOC) {
sc->sc_intctl &= ~MSK_ALLOC;
#ifdef ESDEBUG
if (esdebug || 1)
printf ("%s: ist %02x", sc->sc_dev.dv_xname,
intsts);
#endif
if ((smc->b2.arr & ARR_FAILED) == 0) {
u_char save_pnr;
#ifdef ESDEBUG
if (esdebug || 1)
printf (" arr %02x\n", smc->b2.arr);
#endif
save_pnr = smc->b2.pnr;
smc->b2.pnr = smc->b2.arr;
smc->b2.mmucr = MMUCR_RLSPKT;
while (smc->b2.mmucr & MMUCR_BUSY)
;
smc->b2.pnr = save_pnr;
ifp->if_flags &= ~IFF_OACTIVE;
}
#ifdef ESDEBUG
else if (esdebug || 1)
printf (" IST_ALLOC with ARR_FAILED?\n");
#endif
}
#ifdef ESDEBUG
while ((smc->b2.bsr & BSR_MASK) != BSR_BANK2) {
printf("%s: intr+ BSR not 2: %04x\n", sc->sc_dev.dv_xname,
smc->b2.bsr);
smc->b2.bsr = BSR_BANK2;
}
#endif
while ((smc->b2.fifo & FIFO_REMPTY) == 0) {
esrint(sc);
}
#ifdef ESDEBUG
while ((smc->b2.bsr & BSR_MASK) != BSR_BANK2) {
printf("%s: intr++ BSR not 2: %04x\n", sc->sc_dev.dv_xname,
smc->b2.bsr);
smc->b2.bsr = BSR_BANK2;
}
#endif
if (intact & IST_RX_OVRN) {
printf ("%s: Overrun ist %02x", sc->sc_dev.dv_xname,
intsts);
smc->b2.ist = ACK_RX_OVRN;
printf ("->%02x\n", smc->b2.ist);
ifp->if_ierrors++;
}
if (intact & IST_TX_EMPTY) {
u_short ecr;
#ifdef ESDEBUG
if (esdebug)
printf ("%s: TX EMPTY %02x",
sc->sc_dev.dv_xname, intsts);
++estxint5; /* count # IST_TX_EMPTY ints */
#endif
smc->b2.ist = ACK_TX_EMPTY;
sc->sc_intctl &= ~(MSK_TX_EMPTY | MSK_TX);
#ifdef ESDEBUG
if (esdebug)
printf ("->%02x intcl %x pnr %02x arr %02x\n",
smc->b2.ist, sc->sc_intctl, smc->b2.pnr,
smc->b2.arr);
#endif
if (smc->b2.ist & IST_TX) {
intact |= IST_TX;
#ifdef ESDEBUG
++estxint2; /* count # TX after TX_EMPTY */
#endif
} else {
smc->b0.bsr = BSR_BANK0;
ecr = smc->b0.ecr; /* Get error counters */
if (ecr & 0xff00)
ifp->if_collisions += ((ecr >> 8) & 15) +
((ecr >> 11) & 0x1e);
smc->b2.bsr = BSR_BANK2;
#if 0
smc->b2.mmucr = MMUCR_RESET_TX; /* XXX reset TX FIFO */
#endif
}
}
if (intact & IST_TX) {
u_char tx_pnr, save_pnr;
u_short save_ptr, ephsr, tcr;
int n = 0;
#ifdef ESDEBUG
if (esdebug) {
printf ("%s: TX INT ist %02x",
sc->sc_dev.dv_xname, intsts);
printf ("->%02x\n", smc->b2.ist);
}
++estxint3; /* count # IST_TX */
#endif
zzzz:
#ifdef ESDEBUG
++estxint4; /* count # ~TEMPTY */
#endif
smc->b0.bsr = BSR_BANK0;
ephsr = smc->b0.ephsr; /* get EPHSR */
tcr = smc->b0.tcr; /* and TCR */
smc->b2.bsr = BSR_BANK2;
save_ptr = smc->b2.ptr;
save_pnr = smc->b2.pnr;
tx_pnr = smc->b2.fifo >> 8; /* pktno from completion fifo */
smc->b2.pnr = tx_pnr; /* set TX packet number */
smc->b2.ptr = PTR_READ; /* point to status word */
#if 0 /* XXXX */
printf("%s: esintr TXINT IST %02x PNR %02x(%d)",
sc->sc_dev.dv_xname, smc->b2.ist,
tx_pnr, n);
printf(" Status %04x", smc->b2.data);
printf(" EPHSR %04x\n", ephsr);
#endif
if ((smc->b2.data & EPHSR_TX_SUC) == 0 && (tcr & TCR_TXENA) == 0) {
/*
* Transmitter was stopped for some error. Enqueue
* the packet again and restart the transmitter.
* May need some check to limit the number of retries.
*/
smc->b2.mmucr = MMUCR_ENQ_TX;
smc->b0.bsr = BSR_BANK0;
smc->b0.tcr |= TCR_TXENA;
smc->b2.bsr = BSR_BANK2;
ifp->if_oerrors++;
sc->sc_intctl |= MSK_TX_EMPTY | MSK_TX;
} else {
/*
* This shouldn't have happened: IST_TX indicates
* the TX completion FIFO is not empty, but the
* status for the packet on the completion FIFO
* shows that the transmit was sucessful. Since
* AutoRelease is being used, a sucessful transmit
* should not result in a packet on the completion
* FIFO. Also, that packet doesn't seem to want
* to be acknowledged. If this occurs, just reset
* the TX FIFOs.
*/
#if 1
if (smc->b2.ist & IST_TX_EMPTY) {
smc->b2.mmucr = MMUCR_RESET_TX;
sc->sc_intctl &= ~(MSK_TX_EMPTY | MSK_TX);
}
#endif
#ifdef ESDEBUG
++estxints; /* count IST_TX with TX enabled */
#endif
}
smc->b2.pnr = save_pnr;
smc->b2.ptr = save_ptr;
smc->b2.ist = ACK_TX;
if ((smc->b2.fifo & FIFO_TEMPTY) == 0 && n++ < 32) {
#if 0 /* XXXX */
printf("%s: multiple TX int(%2d) pnr %02x ist %02x fifo %04x",
sc->sc_dev.dv_xname, n, tx_pnr, smc->b2.ist, smc->b2.fifo);
smc->w2.istmsk = ACK_TX << 8;
printf(" %04x\n", smc->b2.fifo);
#endif
if (tx_pnr != (smc->b2.fifo >> 8))
goto zzzz;
}
}
/* output packets */
estint(sc);
#ifdef ESDEBUG
while ((smc->b2.bsr & BSR_MASK) != BSR_BANK2) {
printf("%s: intr+++ BSR not 2: %04x\n", sc->sc_dev.dv_xname,
smc->b2.bsr);
smc->b2.bsr = BSR_BANK2;
}
#endif
smc->b2.msk = sc->sc_intctl;
#ifdef ESDEBUG
if (--sc->sc_intbusy) {
printf("%s: esintr busy on exit\n", sc->sc_dev.dv_xname);
panic("esintr busy on exit");
}
#endif
splx(s);
return (1);
}
void
esrint(sc)
struct es_softc *sc;
{
union smcregs *smc = sc->sc_base;
u_short len;
short cnt;
u_short pktctlw, pktlen, *buf;
volatile u_short *data;
#if 0
u_long *lbuf;
volatile u_long *ldata;
#endif
struct ifnet *ifp;
struct mbuf *top, **mp, *m;
struct ether_header *eh;
#ifdef USEPKTBUF
u_char *b, pktbuf[1530];
#endif
#ifdef ESDEBUG
int i;
#endif
ifp = &sc->sc_ethercom.ec_if;
#ifdef ESDEBUG
if (esdebug)
printf ("%s: esrint fifo %04x", sc->sc_dev.dv_xname,
smc->b2.fifo);
if (sc->sc_smcbusy++) {
printf("%s: esrint re-entered\n", sc->sc_dev.dv_xname);
panic("esrint re-entered");
}
while ((smc->b2.bsr & BSR_MASK) != BSR_BANK2) {
printf("%s: rint BSR not 2: %04x\n", sc->sc_dev.dv_xname,
smc->b2.bsr);
smc->b2.bsr = BSR_BANK2;
}
#endif
data = (u_short *)&smc->b2.data;
smc->b2.ptr = PTR_RCV | PTR_AUTOINCR | PTR_READ | SWAP(0x0002);
(void) smc->b2.mmucr;
#ifdef ESDEBUG
if (esdebug)
printf ("->%04x", smc->b2.fifo);
#endif
len = *data;
len = SWAP(len); /* Careful of macro side-effects */
#ifdef ESDEBUG
if (esdebug)
printf (" length %d", len);
#endif
smc->b2.ptr = PTR_RCV | (PTR_AUTOINCR + PTR_READ) | SWAP(0x0000);
(void) smc->b2.mmucr;
pktctlw = *data;
pktlen = *data;
pktctlw = SWAP(pktctlw);
pktlen = SWAP(pktlen) - 6;
if (pktctlw & RFSW_ODDFRM)
pktlen++;
if (len > 1530) {
printf("%s: Corrupted packet length-sts %04x bytcnt %04x len %04x bank %04x\n",
sc->sc_dev.dv_xname, pktctlw, pktlen, len, smc->b2.bsr);
/* XXX ignore packet, or just truncate? */
#if defined(ESDEBUG) && defined(DDB)
if ((smc->b2.bsr & BSR_MASK) != BSR_BANK2)
Debugger();
#endif
smc->b2.bsr = BSR_BANK2;
smc->b2.mmucr = MMUCR_REMRLS_RX;
while (smc->b2.mmucr & MMUCR_BUSY)
;
++ifp->if_ierrors;
#ifdef ESDEBUG
if (--sc->sc_smcbusy) {
printf("%s: esrintr busy on bad packet exit\n",
sc->sc_dev.dv_xname);
panic("esrintr busy on exit");
}
#endif
return;
}
#ifdef USEPKTBUF
#if 0
lbuf = (u_long *) pktbuf;
ldata = (u_long *)data;
cnt = (len - 4) / 4;
while (cnt--)
*lbuf++ = *ldata;
if ((len - 4) & 2) {
buf = (u_short *) lbuf;
*buf = *data;
}
#else
buf = (u_short *)pktbuf;
cnt = (len - 4) / 2;
while (cnt--)
*buf++ = *data;
#endif
smc->b2.mmucr = MMUCR_REMRLS_RX;
while (smc->b2.mmucr & MMUCR_BUSY)
;
#ifdef ESDEBUG
if (pktctlw & (RFSW_ALGNERR | RFSW_BADCRC | RFSW_TOOLNG | RFSW_TOOSHORT)) {
printf ("%s: Packet error %04x\n", sc->sc_dev.dv_xname, pktctlw);
/* count input error? */
}
if (esdebug) {
printf (" pktctlw %04x pktlen %04x fifo %04x\n", pktctlw, pktlen,
smc->b2.fifo);
for (i = 0; i < pktlen; ++i)
printf ("%02x%s", pktbuf[i], ((i & 31) == 31) ? "\n" :
"");
if (i & 31)
printf ("\n");
}
#endif
#else /* USEPKTBUF */
/* XXX copy directly from controller to mbuf */
#ifdef ESDEBUG
if (pktctlw & (RFSW_ALGNERR | RFSW_BADCRC | RFSW_TOOLNG | RFSW_TOOSHORT)) {
printf ("%s: Packet error %04x\n", sc->sc_dev.dv_xname, pktctlw);
/* count input error? */
}
if (esdebug) {
printf (" pktctlw %04x pktlen %04x fifo %04x\n", pktctlw, pktlen,
smc->b2.fifo);
}
#endif
#endif /* USEPKTBUF */
ifp->if_ipackets++;
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m == NULL)
return;
m->m_pkthdr.rcvif = ifp;
m->m_pkthdr.len = pktlen;
len = MHLEN;
top = NULL;
mp = &top;
#ifdef USEPKTBUF
b = pktbuf;
#endif
while (pktlen > 0) {
if (top) {
MGET(m, M_DONTWAIT, MT_DATA);
if (m == 0) {
m_freem(top);
return;
}
len = MLEN;
}
if (pktlen >= MINCLSIZE) {
MCLGET(m, M_DONTWAIT);
if (m->m_flags & M_EXT)
len = MCLBYTES;
}
m->m_len = len = min(pktlen, len);
#ifdef USEPKTBUF
bcopy((caddr_t)b, mtod(m, caddr_t), len);
b += len;
#else /* USEPKTBUF */
buf = mtod(m, u_short *);
cnt = len / 2;
while (cnt--)
*buf++ = *data;
if (len & 1)
*buf = *data; /* XXX should be byte store */
#ifdef ESDEBUG
if (esdebug) {
buf = mtod(m, u_short *);
for (i = 0; i < len; ++i)
printf ("%02x%s", ((u_char *)buf)[i],
((i & 31) == 31) ? "\n" : "");
if (i & 31)
printf ("\n");
}
#endif
#endif /* USEPKTBUF */
pktlen -= len;
*mp = m;
mp = &m->m_next;
}
#ifndef USEPKTBUF
smc->b2.mmucr = MMUCR_REMRLS_RX;
while (smc->b2.mmucr & MMUCR_BUSY)
;
#endif
eh = mtod(top, struct ether_header *);
#if NBPFILTER > 0
/*
* Check if there's a BPF listener on this interface. If so, hand off
* the raw packet to bpf.
*/
if (ifp->if_bpf) {
bpf_mtap(ifp->if_bpf, top);
/*
* 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.
*/
if ((sc->sc_ethercom.ec_if.if_flags & IFF_PROMISC) &&
(eh->ether_dhost[0] & 1) == 0 && /* !mcast and !bcast */
bcmp(eh->ether_dhost, LLADDR(ifp->if_sadl),
sizeof(eh->ether_dhost)) != 0) {
m_freem(top);
return;
}
}
#endif
top->m_pkthdr.len -= sizeof (*eh);
top->m_len -= sizeof (*eh);
top->m_data += sizeof (*eh);
ether_input(ifp, eh, top);
#ifdef ESDEBUG
if (--sc->sc_smcbusy) {
printf("%s: esintr busy on exit\n", sc->sc_dev.dv_xname);
panic("esintr busy on exit");
}
#endif
}
void
estint(sc)
struct es_softc *sc;
{
esstart(&sc->sc_ethercom.ec_if);
}
void
esstart(ifp)
struct ifnet *ifp;
{
struct es_softc *sc = ifp->if_softc;
union smcregs *smc = sc->sc_base;
struct mbuf *m0, *m;
#ifdef USEPKTBUF
u_short pktbuf[ETHERMTU + 2];
#else
u_short oddbyte, needbyte;
#endif
u_short pktctlw, pktlen;
u_short *buf;
volatile u_short *data;
#if 0
u_long *lbuf;
volatile u_long *ldata;
#endif
short cnt;
int i;
u_char active_pnr;
if ((sc->sc_ethercom.ec_if.if_flags & (IFF_RUNNING | IFF_OACTIVE)) !=
IFF_RUNNING)
return;
#ifdef ESDEBUG
if (sc->sc_smcbusy++) {
printf("%s: esstart re-entered\n", sc->sc_dev.dv_xname);
panic("esstart re-entred");
}
while ((smc->b2.bsr & BSR_MASK) != BSR_BANK2) {
printf("%s: esstart BSR not 2: %04x\n", sc->sc_dev.dv_xname,
smc->b2.bsr);
smc->b2.bsr = BSR_BANK2;
}
#endif
for (;;) {
#ifdef ESDEBUG
u_short start_ptr, end_ptr;
#endif
/*
* Sneak a peek at the next packet to get the length
* and see if the SMC 91C90 can accept it.
*/
m = sc->sc_ethercom.ec_if.if_snd.ifq_head;
if (!m)
break;
#ifdef ESDEBUG
if (esdebug && (m->m_next || m->m_len & 1))
printf("%s: esstart m_next %p m_len %d\n",
sc->sc_dev.dv_xname, m->m_next, m->m_len);
#endif
for (m0 = m, pktlen = 0; m0; m0 = m0->m_next)
pktlen += m0->m_len;
pktctlw = 0;
pktlen += 4;
if (pktlen & 1)
++pktlen; /* control byte after last byte */
else
pktlen += 2; /* control byte after pad byte */
smc->b2.mmucr = MMUCR_ALLOC | (pktlen & 0x0700);
for (i = 0; i <= 5; ++i)
if ((smc->b2.arr & ARR_FAILED) == 0)
break;
if (smc->b2.arr & ARR_FAILED) {
sc->sc_ethercom.ec_if.if_flags |= IFF_OACTIVE;
sc->sc_intctl |= MSK_ALLOC;
break;
}
active_pnr = smc->b2.pnr = smc->b2.arr;
#ifdef ESDEBUG
while ((smc->b2.bsr & BSR_MASK) != BSR_BANK2) {
printf("%s: esstart+ BSR not 2: %04x\n", sc->sc_dev.dv_xname,
smc->b2.bsr);
smc->b2.bsr = BSR_BANK2;
}
#endif
IF_DEQUEUE(&sc->sc_ethercom.ec_if.if_snd, m);
smc->b2.ptr = PTR_AUTOINCR;
(void) smc->b2.mmucr;
data = (u_short *)&smc->b2.data;
*data = SWAP(pktctlw);
*data = SWAP(pktlen);
#ifdef ESDEBUG
while ((smc->b2.bsr & BSR_MASK) != BSR_BANK2) {
printf("%s: esstart++ BSR not 2: %04x\n", sc->sc_dev.dv_xname,
smc->b2.bsr);
smc->b2.bsr = BSR_BANK2;
}
#endif
#ifdef USEPKTBUF
i = 0;
for (m0 = m; m; m = m->m_next) {
bcopy(mtod(m, caddr_t), (char *)pktbuf + i, m->m_len);
i += m->m_len;
}
if (i & 1) /* Figure out where to put control byte */
pktbuf[i/2] = (pktbuf[i/2] & 0xff00) | CTLB_ODD;
else
pktbuf[i/2] = 0;
pktlen -= 4;
#ifdef ESDEBUG
if (pktlen > sizeof(pktbuf) && i > (sizeof(pktbuf) * 2))
printf("%s: esstart packet longer than pktbuf\n",
sc->sc_dev.dv_xname);
#endif
#if 0 /* doesn't quite work? */
lbuf = (u_long *)(pktbuf);
ldata = (u_long *)data;
cnt = pktlen / 4;
while(cnt--)
*ldata = *lbuf++;
if (pktlen & 2) {
buf = (u_short *)lbuf;
*data = *buf;
}
#else
#ifdef ESDEBUG
while ((smc->b2.bsr & BSR_MASK) != BSR_BANK2) {
printf("%s: esstart++2 BSR not 2: %04x\n", sc->sc_dev.dv_xname,
smc->b2.bsr);
smc->b2.bsr = BSR_BANK2;
}
start_ptr = SWAP(smc->b2.ptr); /* save PTR before copy */
#endif
buf = pktbuf;
cnt = pktlen / 2;
while (cnt--)
*data = *buf++;
#ifdef ESDEBUG
end_ptr = SWAP(smc->b2.ptr); /* save PTR after copy */
#endif
#endif
#else /* USEPKTBUF */
pktctlw = 0;
oddbyte = needbyte = 0;
for (m0 = m; m; m = m->m_next) {
buf = mtod(m, u_short *);
cnt = m->m_len / 2;
if (needbyte) {
oddbyte |= *buf >> 8;
*data = oddbyte;
}
while (cnt--)
*data = *buf++;
if (m->m_len & 1)
pktctlw = (*buf & 0xff00) | CTLB_ODD;
if (m->m_len & 1 && m->m_next)
printf("%s: esstart odd byte count in mbuf\n",
sc->sc_dev.dv_xname);
}
*data = pktctlw;
#endif /* USEPKTBUF */
while ((smc->b2.bsr & BSR_MASK) != BSR_BANK2) {
/*
* The bank select register has changed. This seems
* to happen with my A2000/Zeus once in a while. It
* appears that the Ethernet chip resets while
* copying the transmit buffer. Requeue the current
* transmit buffer and reinitialize the interface.
* The initialize routine will take care of
* retransmitting the buffer. mhitch
*/
#ifdef DIAGNOSTIC
printf("%s: esstart+++ BSR not 2: %04x\n",
sc->sc_dev.dv_xname, smc->b2.bsr);
#endif
smc->b2.bsr = BSR_BANK2;
#ifdef ESDEBUG
printf("start_ptr %04x end_ptr %04x cur ptr %04x\n",
start_ptr, end_ptr, SWAP(smc->b2.ptr));
--sc->sc_smcbusy;
#endif
IF_PREPEND(&sc->sc_ethercom.ec_if.if_snd, m0);
esinit(sc); /* It's really hosed - reset */
return;
}
smc->b2.mmucr = MMUCR_ENQ_TX;
if (smc->b2.pnr != active_pnr)
printf("%s: esstart - PNR changed %x->%x\n",
sc->sc_dev.dv_xname, active_pnr, smc->b2.pnr);
#if NBPFILTER > 0
if (sc->sc_ethercom.ec_if.if_bpf)
bpf_mtap(sc->sc_ethercom.ec_if.if_bpf, m0);
#endif
m_freem(m0);
sc->sc_ethercom.ec_if.if_opackets++; /* move to interrupt? */
sc->sc_intctl |= MSK_TX_EMPTY | MSK_TX;
}
smc->b2.msk = sc->sc_intctl;
#ifdef ESDEBUG
while ((smc->b2.bsr & BSR_MASK) != BSR_BANK2) {
printf("%s: esstart++++ BSR not 2: %04x\n", sc->sc_dev.dv_xname,
smc->b2.bsr);
smc->b2.bsr = BSR_BANK2;
}
if (--sc->sc_smcbusy) {
printf("%s: esstart busy on exit\n", sc->sc_dev.dv_xname);
panic("esstart busy on exit");
}
#endif
}
int
esioctl(ifp, command, data)
register struct ifnet *ifp;
u_long command;
caddr_t data;
{
struct es_softc *sc = ifp->if_softc;
register struct ifaddr *ifa = (struct ifaddr *)data;
struct ifreq *ifr = (struct ifreq *)data;
int s, error = 0;
s = splnet();
switch (command) {
case SIOCSIFADDR:
ifp->if_flags |= IFF_UP;
switch (ifa->ifa_addr->sa_family) {
#ifdef INET
case AF_INET:
esinit(sc);
arp_ifinit(ifp, ifa);
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 *)LLADDR(ifp->if_sadl);
else
bcopy(ina->x_host.c_host,
LLADDR(ifp->if_sadl), ETHER_ADDR_LEN);
/* Set new address. */
esinit(sc);
break;
}
#endif
default:
esinit(sc);
break;
}
break;
case SIOCSIFFLAGS:
/*
* If interface is marked down and it is running, then stop it
*/
if ((ifp->if_flags & IFF_UP) == 0 &&
(ifp->if_flags & IFF_RUNNING) != 0) {
/*
* If interface is marked down and it is running, then
* stop it.
*/
esstop(sc);
ifp->if_flags &= ~IFF_RUNNING;
} else if ((ifp->if_flags & IFF_UP) != 0 &&
(ifp->if_flags & IFF_RUNNING) == 0) {
/*
* If interface is marked up and it is stopped, then
* start it.
*/
esinit(sc);
} else {
/*
* Reset the interface to pick up changes in any other
* flags that affect hardware registers.
*/
esstop(sc);
esinit(sc);
}
#ifdef ESDEBUG
if (ifp->if_flags & IFF_DEBUG)
esdebug = sc->sc_debug = 1;
else
esdebug = sc->sc_debug = 0;
#endif
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
error = (command == SIOCADDMULTI) ?
ether_addmulti(ifr, &sc->sc_ethercom) :
ether_delmulti(ifr, &sc->sc_ethercom);
if (error == ENETRESET) {
/*
* Multicast list has changed; set the hardware filter
* accordingly.
*/
/* XXX */
error = 0;
}
break;
default:
error = EINVAL;
}
splx(s);
return (error);
}
void
esreset(sc)
struct es_softc *sc;
{
int s;
s = splnet();
esstop(sc);
esinit(sc);
splx(s);
}
void
eswatchdog(ifp)
struct ifnet *ifp;
{
struct es_softc *sc = ifp->if_softc;
log(LOG_ERR, "%s: device timeout\n", sc->sc_dev.dv_xname);
++ifp->if_oerrors;
esreset(sc);
}