974 lines
24 KiB
C
974 lines
24 KiB
C
/*-
|
|
* Copyright (c) 1990, 1991 William F. Jolitz.
|
|
* Copyright (c) 1990 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.
|
|
*
|
|
* from: @(#)if_ne.c 7.4 (Berkeley) 5/21/91
|
|
* $Id: if_ne.c,v 1.12 1993/10/01 23:12:04 mycroft Exp $
|
|
*/
|
|
|
|
/*
|
|
* NE2000/NE1000 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.
|
|
*
|
|
* Corrected for NE1000 by Andrew A. Chernov
|
|
*/
|
|
|
|
#include "ne.h"
|
|
#if NNE > 0
|
|
|
|
#include "param.h"
|
|
#include "systm.h"
|
|
#include "mbuf.h"
|
|
#include "buf.h"
|
|
#include "protosw.h"
|
|
#include "socket.h"
|
|
#include "ioctl.h"
|
|
#include "errno.h"
|
|
#include "syslog.h"
|
|
|
|
#include "net/if.h"
|
|
#include "net/netisr.h"
|
|
#include "net/route.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 "sys/select.h"
|
|
#include "net/bpf.h"
|
|
#include "net/bpfdesc.h"
|
|
#endif
|
|
|
|
#include "i386/isa/isa_device.h"
|
|
#include "i386/isa/if_nereg.h"
|
|
#include "i386/isa/icu.h"
|
|
|
|
int neprobe(), neattach(), neintr();
|
|
int nestart(),neinit(), ether_output(), neioctl();
|
|
|
|
struct isa_driver nedriver = {
|
|
neprobe, neattach, "ne",
|
|
};
|
|
|
|
struct mbuf *neget();
|
|
|
|
#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 {
|
|
struct arpcom ns_ac; /* Ethernet common part */
|
|
#define ns_if ns_ac.ac_if /* network-visible interface */
|
|
#define ns_addrp ns_ac.ac_enaddr /* hardware Ethernet address */
|
|
int ns_flags;
|
|
#define DSF_LOCK 1 /* block re-entering enstart */
|
|
int ns_oactive ;
|
|
int ns_mask ;
|
|
int ns_ba; /* byte addr in buffer ram of inc pkt */
|
|
int ns_cur; /* current page being filled */
|
|
struct prhdr ns_ph; /* hardware header of incoming packet*/
|
|
struct ether_header ns_eh; /* header of incoming packet */
|
|
u_char ns_pb[2048 /*ETHERMTU+sizeof(long)*/];
|
|
short ns_txstart; /* transmitter buffer start */
|
|
u_short ns_rxend; /* recevier buffer end */
|
|
short ns_port; /* i/o port base */
|
|
short ns_mode; /* word/byte mode */
|
|
caddr_t ns_bpf; /* BPF frob */
|
|
} ne_softc[NNE] ;
|
|
#define ENBUFSIZE (sizeof(struct ether_header) + ETHERMTU + 2 + ETHER_MIN_LEN)
|
|
|
|
#define PAT(n) (0xa55a + 37*(n))
|
|
|
|
u_char boarddata[16];
|
|
|
|
neprobe(dvp)
|
|
struct isa_device *dvp;
|
|
{
|
|
int val, i, s, sum, bytemode = 1, pat;
|
|
register struct ne_softc *ns = &ne_softc[dvp->id_unit];
|
|
register nec;
|
|
|
|
#ifdef lint
|
|
neintr(0);
|
|
#endif
|
|
|
|
nec = ns->ns_port = dvp->id_iobase;
|
|
s = splimp();
|
|
|
|
if (bytemode) {
|
|
/* Byte Transfers, Burst Mode Select, Fifo at 8 bytes */
|
|
ns->ns_mode = DSDC_BMS|DSDC_FT1;
|
|
ns->ns_txstart = TBUF8;
|
|
ns->ns_rxend = RBUFEND8;
|
|
} else {
|
|
word:
|
|
/* Word Transfers, Burst Mode Select, Fifo at 8 bytes */
|
|
ns->ns_mode = DSDC_WTS|DSDC_BMS|DSDC_FT1;
|
|
ns->ns_txstart = TBUF16;
|
|
ns->ns_rxend = RBUFEND16;
|
|
bytemode = 0;
|
|
}
|
|
|
|
/* Reset the bastard */
|
|
val = inb(nec + ne_reset);
|
|
DELAY(200);
|
|
outb(nec + ne_reset, val);
|
|
DELAY(200);
|
|
|
|
outb(nec + ds_cmd, DSCM_STOP|DSCM_NODMA);
|
|
|
|
i = 10000;
|
|
while ((inb(nec + ds0_isr) & DSIS_RESET) == 0 && i-- > 0);
|
|
if (i < 0) return (0);
|
|
|
|
outb(nec + ds0_isr, 0xff);
|
|
outb(nec + ds0_dcr, ns->ns_mode);
|
|
outb(nec + ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP);
|
|
DELAY(1000);
|
|
|
|
/* Check cmd reg and fail if not right */
|
|
if ((i = inb(nec + ds_cmd)) != (DSCM_NODMA|DSCM_PG0|DSCM_STOP))
|
|
return(0);
|
|
|
|
outb(nec + ds0_tcr, 0);
|
|
outb(nec + ds0_rcr, DSRC_MON);
|
|
outb(nec + ds0_pstart, (ns->ns_txstart+PKTSZ)/DS_PGSIZE);
|
|
outb(nec + ds0_pstop, ns->ns_rxend/DS_PGSIZE);
|
|
outb(nec + ds0_bnry, ns->ns_rxend/DS_PGSIZE);
|
|
outb(nec + ds0_imr, 0);
|
|
outb(nec + ds0_isr, 0);
|
|
outb(nec + ds_cmd, DSCM_NODMA|DSCM_PG1|DSCM_STOP);
|
|
outb(nec + ds1_curr, (ns->ns_txstart+PKTSZ)/DS_PGSIZE);
|
|
outb(nec + ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP);
|
|
|
|
|
|
#ifdef NEDEBUG
|
|
#define RCON 37
|
|
{ int i, rom;
|
|
|
|
rom=1;
|
|
printf("ne ram ");
|
|
|
|
for (i = 0; i < 0xfff0; i+=4) {
|
|
pat = PAT(i);
|
|
neput(ns, &pat,i,4);
|
|
nefetch(ns, &pat,i,4);
|
|
if (pat == PAT(i)) {
|
|
if (rom) {
|
|
rom=0;
|
|
printf(" %x", i);
|
|
}
|
|
} else {
|
|
if (!rom) {
|
|
rom=1;
|
|
printf("..%x ", i);
|
|
}
|
|
}
|
|
pat=0;
|
|
neput(ns, &pat,i,4);
|
|
}
|
|
printf("\n");
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* <groan> detect difference between units
|
|
* solely by where the RAM is decoded.
|
|
*/
|
|
pat = PAT(0);
|
|
neput(ns, &pat, ns->ns_txstart, 4);
|
|
nefetch(ns, &pat, ns->ns_txstart, 4);
|
|
if (pat != PAT(0)) {
|
|
if (bytemode)
|
|
goto word;
|
|
else return (0);
|
|
}
|
|
|
|
|
|
/* Extract board address */
|
|
nefetch (ns, (caddr_t)boarddata, 0, sizeof(boarddata));
|
|
|
|
for(i=0; i < 6; i++)
|
|
ns->ns_addrp[i] = boarddata[i*(2-bytemode)];
|
|
splx(s);
|
|
return (16);
|
|
}
|
|
|
|
/*
|
|
* Fetch from onboard ROM/RAM
|
|
*/
|
|
nefetch (ns, up, ad, len) struct ne_softc *ns; caddr_t up; {
|
|
u_char cmd;
|
|
register nec = ns->ns_port;
|
|
int counter = 100000;
|
|
|
|
cmd = inb (nec + ds_cmd);
|
|
outb (nec + ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START);
|
|
|
|
/* Setup remote dma */
|
|
outb (nec + ds0_isr, DSIS_RDC);
|
|
|
|
if ((ns->ns_mode & DSDC_WTS) && len&1)
|
|
len++; /* roundup to words */
|
|
|
|
outb (nec+ds0_rbcr0, len);
|
|
outb (nec+ds0_rbcr1, len>>8);
|
|
outb (nec+ds0_rsar0, ad);
|
|
outb (nec+ds0_rsar1, ad>>8);
|
|
|
|
/* Execute & extract from card */
|
|
outb (nec+ds_cmd, DSCM_RREAD|DSCM_PG0|DSCM_START);
|
|
|
|
if (ns->ns_mode & DSDC_WTS)
|
|
insw (nec+ne_data, up, len/2);
|
|
else
|
|
insb (nec+ne_data, up, len);
|
|
|
|
/* Wait till done, then shutdown feature */
|
|
while ((inb (nec+ds0_isr) & DSIS_RDC) == 0 && counter-- > 0)
|
|
;
|
|
outb (nec+ds0_isr, DSIS_RDC);
|
|
outb (nec+ds_cmd, cmd);
|
|
}
|
|
|
|
/*
|
|
* Put to onboard RAM
|
|
*/
|
|
neput (ns, up, ad, len) struct ne_softc *ns; caddr_t up; {
|
|
u_char cmd;
|
|
register nec = ns->ns_port;
|
|
int counter = 100000;
|
|
|
|
cmd = inb(nec+ds_cmd);
|
|
outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START);
|
|
|
|
/* Setup for remote dma */
|
|
outb (nec+ds0_isr, DSIS_RDC);
|
|
|
|
if ((ns->ns_mode & DSDC_WTS) && len&1)
|
|
len++; /* roundup to words */
|
|
|
|
outb (nec+ds0_rbcr0, len);
|
|
outb (nec+ds0_rbcr1, len>>8);
|
|
outb (nec+ds0_rsar0, ad);
|
|
outb (nec+ds0_rsar1, ad>>8);
|
|
|
|
/* Execute & stuff to card */
|
|
outb (nec+ds_cmd, DSCM_RWRITE|DSCM_PG0|DSCM_START);
|
|
if (ns->ns_mode & DSDC_WTS)
|
|
outsw (nec+ne_data, up, len/2);
|
|
else
|
|
outsb (nec+ne_data, up, len);
|
|
|
|
/* Wait till done, then shutdown feature */
|
|
while ((inb (nec+ds0_isr) & DSIS_RDC) == 0 && counter-- > 0)
|
|
;
|
|
outb (nec+ds0_isr, DSIS_RDC);
|
|
outb (nec+ds_cmd, cmd);
|
|
}
|
|
|
|
/*
|
|
* Reset of interface.
|
|
*/
|
|
nereset(unit, uban)
|
|
int unit, uban;
|
|
{
|
|
if (unit >= NNE)
|
|
return;
|
|
printf("ne%d: reset\n", unit);
|
|
ne_softc[unit].ns_flags &= ~DSF_LOCK;
|
|
neinit(unit);
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
neattach(dvp)
|
|
struct isa_device *dvp;
|
|
{
|
|
int unit = dvp->id_unit;
|
|
register struct ne_softc *ns = &ne_softc[unit];
|
|
register struct ifnet *ifp = &ns->ns_if;
|
|
|
|
ifp->if_unit = unit;
|
|
ifp->if_name = nedriver.name ;
|
|
ifp->if_mtu = ETHERMTU;
|
|
printf ("ne%d: ne%s ethernet address %s\n", unit,
|
|
(ns->ns_mode & DSDC_WTS) ? "2000" : "1000",
|
|
ether_sprintf(ns->ns_addrp));
|
|
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS;
|
|
ifp->if_init = neinit;
|
|
ifp->if_output = ether_output;
|
|
ifp->if_start = nestart;
|
|
ifp->if_ioctl = neioctl;
|
|
ifp->if_reset = nereset;
|
|
ifp->if_watchdog = 0;
|
|
if_attach(ifp);
|
|
|
|
#if NBPFILTER > 0
|
|
bpfattach(&ns->ns_bpf, ifp, DLT_EN10MB,
|
|
sizeof(struct ether_header));
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Initialization of interface; set up initialization block
|
|
* and transmit/receive descriptor rings.
|
|
*/
|
|
neinit(unit)
|
|
int unit;
|
|
{
|
|
register struct ne_softc *ns = &ne_softc[unit];
|
|
struct ifnet *ifp = &ns->ns_if;
|
|
int s;
|
|
int i; char *cp;
|
|
register nec = ns->ns_port;
|
|
|
|
if (ifp->if_addrlist == (struct ifaddr *)0) return;
|
|
if (ifp->if_flags & IFF_RUNNING) return;
|
|
|
|
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->ns_addrp[i]);
|
|
|
|
/* clr logical address hash filter for now */
|
|
for (i=0 ; i < 8 ; i++) outb(nec+ds1_mar0+i,0xff);
|
|
|
|
/* init 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);
|
|
|
|
/* Word Transfer select, Burst Mode Select, Fifo at 8 bytes */
|
|
outb(nec+ds0_dcr, ns->ns_mode);
|
|
|
|
outb(nec+ds0_tcr, 0);
|
|
outb (nec+ds0_rcr, DSRC_MON);
|
|
outb (nec+ds0_tpsr, 0);
|
|
outb(nec+ds0_pstart, (ns->ns_txstart+PKTSZ)/DS_PGSIZE);
|
|
outb(nec+ds0_pstop, ns->ns_rxend/DS_PGSIZE);
|
|
outb(nec+ds0_bnry, (ns->ns_txstart+PKTSZ)/DS_PGSIZE);
|
|
outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG1|DSCM_STOP);
|
|
outb(nec+ds1_curr, (ns->ns_txstart+PKTSZ)/DS_PGSIZE);
|
|
ns->ns_cur = (ns->ns_txstart+PKTSZ)/DS_PGSIZE;
|
|
outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START);
|
|
#if NBPFILTER > 0
|
|
if (ns->ns_if.if_flags & IFF_PROMISC)
|
|
outb (nec+ds0_rcr, DSRC_AB | DSRC_PRO | DSRC_AR | DSRC_SEP);
|
|
else
|
|
#endif
|
|
outb (nec+ds0_rcr, DSRC_AB);
|
|
outb(nec+ds0_dcr, ns->ns_mode);
|
|
outb (nec+ds0_imr, 0xff);
|
|
|
|
ns->ns_if.if_flags |= IFF_RUNNING;
|
|
ns->ns_flags &= ~DSF_LOCK;
|
|
ns->ns_oactive = 0; ns->ns_mask = ~0;
|
|
nestart(ifp);
|
|
splx(s);
|
|
}
|
|
|
|
/*
|
|
* Setup output on interface.
|
|
* Get another datagram to send off of the interface queue,
|
|
* and map it to the interface before starting the output.
|
|
* called only at splimp or interrupt level.
|
|
* XXX: Needs BPF hook.
|
|
*/
|
|
nestart(ifp)
|
|
struct ifnet *ifp;
|
|
{
|
|
register struct ne_softc *ns = &ne_softc[ifp->if_unit];
|
|
struct mbuf *m0, *m;
|
|
int len, i, total,t;
|
|
register nec = ns->ns_port;
|
|
u_char cmd;
|
|
u_short word;
|
|
int counter;
|
|
|
|
if (ns->ns_flags & DSF_LOCK)
|
|
return;
|
|
|
|
if ((ns->ns_if.if_flags & IFF_RUNNING) == 0)
|
|
return;
|
|
|
|
/*
|
|
* The DS8390 has only one transmit buffer, if it is busy we
|
|
* must wait until the transmit interrupt completes.
|
|
*/
|
|
outb(nec+ds_cmd,DSCM_NODMA|DSCM_START);
|
|
|
|
if (inb(nec+ds_cmd) & DSCM_TRANS)
|
|
return;
|
|
|
|
IF_DEQUEUE(&ns->ns_if.if_snd, m);
|
|
|
|
if (m == 0)
|
|
return;
|
|
|
|
ns->ns_flags |= DSF_LOCK; /* prevent entering nestart */
|
|
|
|
/*
|
|
* Copy the mbuf chain into the transmit buffer
|
|
*/
|
|
|
|
len = i = 0;
|
|
t = 0;
|
|
for (m0 = m; m != 0; m = m->m_next)
|
|
t += m->m_len;
|
|
|
|
/* next code derived from neput() */
|
|
|
|
if ((ns->ns_mode & DSDC_WTS) && t&1)
|
|
t++; /* roundup to words */
|
|
|
|
cmd = inb(nec+ds_cmd);
|
|
outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START);
|
|
|
|
/* Setup for remote dma */
|
|
outb (nec+ds0_isr, DSIS_RDC);
|
|
|
|
outb (nec+ds0_rbcr0, t);
|
|
outb (nec+ds0_rbcr1, t>>8);
|
|
outb (nec+ds0_rsar0, ns->ns_txstart);
|
|
outb (nec+ds0_rsar1, ns->ns_txstart>>8);
|
|
|
|
/* Execute & stuff to card */
|
|
outb (nec+ds_cmd, DSCM_RWRITE|DSCM_PG0|DSCM_START);
|
|
|
|
m = m0;
|
|
total = t;
|
|
if (ns->ns_mode & DSDC_WTS) { /* Word Mode */
|
|
while (m != 0) {
|
|
if (m->m_len > 1)
|
|
outsw(nec+ne_data, m->m_data, m->m_len / 2);
|
|
if (m->m_len & 1) {
|
|
word = (u_char) *(mtod(m, caddr_t) + m->m_len - 1);
|
|
if ((m = m->m_next) != 0) {
|
|
word |= *mtod(m, caddr_t) << 8;
|
|
m->m_len--;
|
|
m->m_data++;
|
|
}
|
|
outsw(nec+ne_data, (caddr_t)&word, 1);
|
|
} else
|
|
m = m->m_next;
|
|
}
|
|
}
|
|
else { /* Byte Mode */
|
|
while (m != 0) {
|
|
if (m->m_len > 0)
|
|
outsb(nec+ne_data, mtod(m, caddr_t), m->m_len);
|
|
m = m->m_next;
|
|
}
|
|
}
|
|
|
|
m_freem(m0);
|
|
|
|
counter = 100000;
|
|
/* Wait till done, then shutdown feature */
|
|
while ((inb (nec+ds0_isr) & DSIS_RDC) == 0 && counter-- > 0)
|
|
;
|
|
outb (nec+ds0_isr, DSIS_RDC);
|
|
outb (nec+ds_cmd, cmd);
|
|
|
|
/*
|
|
* Init transmit length registers, and set transmit start flag.
|
|
*/
|
|
|
|
len = total;
|
|
if (len < ETHER_MIN_LEN) len = ETHER_MIN_LEN;
|
|
outb(nec+ds0_tbcr0,len&0xff);
|
|
outb(nec+ds0_tbcr1,(len>>8)&0xff);
|
|
outb(nec+ds0_tpsr, ns->ns_txstart/DS_PGSIZE);
|
|
outb(nec+ds_cmd, DSCM_TRANS|DSCM_NODMA|DSCM_START);
|
|
}
|
|
|
|
/* buffer successor/predecessor in ring? */
|
|
#define succ(n) (((n)+1 >= ns->ns_rxend/DS_PGSIZE) ? (ns->ns_txstart+PKTSZ)/DS_PGSIZE : (n)+1)
|
|
#define pred(n) (((n)-1 < (ns->ns_txstart+PKTSZ)/DS_PGSIZE) ? ns->ns_rxend/DS_PGSIZE-1 : (n)-1)
|
|
|
|
/*
|
|
* Controller interrupt.
|
|
*/
|
|
neintr(unit)
|
|
{
|
|
register struct ne_softc *ns = &ne_softc[unit];
|
|
u_char cmd,isr;
|
|
register nec = ns->ns_port;
|
|
|
|
/* Save cmd, clear interrupt */
|
|
cmd = inb (nec+ds_cmd);
|
|
loop:
|
|
isr = inb (nec+ds0_isr);
|
|
outb(nec+ds_cmd,DSCM_NODMA|DSCM_START);
|
|
outb(nec+ds0_isr, isr);
|
|
|
|
/* Receiver error */
|
|
if (isr & DSIS_RXE) {
|
|
/* need to read these registers to clear status */
|
|
(void) inb(nec+ ds0_rsr);
|
|
(void) inb(nec+ 0xD);
|
|
(void) inb(nec + 0xE);
|
|
(void) inb(nec + 0xF);
|
|
ns->ns_if.if_ierrors++;
|
|
}
|
|
|
|
/* We received something; rummage thru tiny ring buffer */
|
|
if (isr & (DSIS_RX|DSIS_RXE|DSIS_ROVRN)) {
|
|
u_char pend,lastfree;
|
|
|
|
outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG1);
|
|
pend = inb(nec+ds1_curr);
|
|
outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG0);
|
|
lastfree = inb(nec+ds0_bnry);
|
|
|
|
/* Have we wrapped? */
|
|
if (lastfree >= ns->ns_rxend/DS_PGSIZE)
|
|
lastfree = (ns->ns_txstart+PKTSZ)/DS_PGSIZE;
|
|
if (pend < lastfree && ns->ns_cur < pend)
|
|
lastfree = ns->ns_cur;
|
|
else if (ns->ns_cur > lastfree)
|
|
lastfree = ns->ns_cur;
|
|
|
|
/* Something in the buffer? */
|
|
while (pend != lastfree) {
|
|
u_char nxt;
|
|
|
|
/* Extract header from microcephalic board */
|
|
nefetch(ns, &ns->ns_ph,lastfree*DS_PGSIZE,
|
|
sizeof(ns->ns_ph));
|
|
ns->ns_ba = lastfree*DS_PGSIZE+sizeof(ns->ns_ph);
|
|
|
|
/* Incipient paranoia */
|
|
if (ns->ns_ph.pr_status == DSRS_RPC ||
|
|
/* for dequna's */
|
|
ns->ns_ph.pr_status == 0x21)
|
|
nerecv (ns);
|
|
#ifdef NEDEBUG
|
|
else {
|
|
printf("cur %x pnd %x lfr %x ",
|
|
ns->ns_cur, pend, lastfree);
|
|
printf("nxt %x len %x ", ns->ns_ph.pr_nxtpg,
|
|
(ns->ns_ph.pr_sz1<<8)+ ns->ns_ph.pr_sz0);
|
|
printf("Bogus Sts %x\n", ns->ns_ph.pr_status);
|
|
}
|
|
#endif
|
|
|
|
nxt = ns->ns_ph.pr_nxtpg ;
|
|
|
|
/* Sanity check */
|
|
if ( nxt >= (ns->ns_txstart+PKTSZ)/DS_PGSIZE
|
|
&& nxt <= ns->ns_rxend/DS_PGSIZE && nxt <= pend)
|
|
ns->ns_cur = nxt;
|
|
else ns->ns_cur = nxt = pend;
|
|
|
|
/* Set the boundaries */
|
|
lastfree = nxt;
|
|
outb(nec+ds0_bnry, pred(nxt));
|
|
outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG1);
|
|
pend = inb(nec+ds1_curr);
|
|
outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG0);
|
|
}
|
|
outb(nec+ds_cmd, DSCM_START|DSCM_NODMA);
|
|
}
|
|
|
|
/* Transmit error */
|
|
if (isr & DSIS_TXE) {
|
|
ns->ns_flags &= ~DSF_LOCK;
|
|
/* Need to read these registers to clear status */
|
|
ns->ns_if.if_collisions += inb(nec+ds0_tbcr0);
|
|
ns->ns_if.if_oerrors++;
|
|
}
|
|
|
|
/* Packet Transmitted */
|
|
if (isr & DSIS_TX) {
|
|
ns->ns_flags &= ~DSF_LOCK;
|
|
++ns->ns_if.if_opackets;
|
|
ns->ns_if.if_collisions += inb(nec+ds0_tbcr0);
|
|
}
|
|
|
|
/* Receiver ovverun? */
|
|
if (isr & DSIS_ROVRN) {
|
|
log(LOG_ERR, "ne%d: ring buffer overflow; isr=%x\n",
|
|
ns-ne_softc, isr/*, DSIS_BITS*/);
|
|
outb(nec+ds0_rbcr0, 0);
|
|
outb(nec+ds0_rbcr1, 0);
|
|
outb(nec+ds0_tcr, DSTC_LB0);
|
|
outb(nec+ds0_rcr, DSRC_MON);
|
|
outb(nec+ds_cmd, DSCM_START|DSCM_NODMA);
|
|
outb(nec+ds0_rcr, DSRC_AB);
|
|
outb(nec+ds0_tcr, 0);
|
|
}
|
|
|
|
/* Any more to send? */
|
|
outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START);
|
|
nestart(&ns->ns_if);
|
|
outb (nec+ds_cmd, cmd);
|
|
outb (nec+ds0_imr, 0xff);
|
|
|
|
/* Still more to do? */
|
|
isr = inb (nec+ds0_isr);
|
|
if(isr) goto loop;
|
|
}
|
|
|
|
/*
|
|
* Ethernet interface receiver interface.
|
|
* If input error just drop packet.
|
|
* Otherwise examine packet to determine type. If can't determine length
|
|
* from type, then have to drop packet. Othewise decapsulate
|
|
* packet based on type and pass to type specific higher-level
|
|
* input routine.
|
|
*/
|
|
nerecv(ns)
|
|
register struct ne_softc *ns;
|
|
{
|
|
int len,i;
|
|
|
|
ns->ns_if.if_ipackets++;
|
|
len = ns->ns_ph.pr_sz0 + (ns->ns_ph.pr_sz1<<8);
|
|
if(len < ETHER_MIN_LEN || len > ETHER_MAX_LEN)
|
|
return;
|
|
|
|
/* this need not be so torturous - one/two bcopys at most into mbufs */
|
|
nefetch(ns, ns->ns_pb, ns->ns_ba, min(len,DS_PGSIZE-sizeof(ns->ns_ph)));
|
|
if (len > DS_PGSIZE-sizeof(ns->ns_ph)) {
|
|
int l = len - (DS_PGSIZE-sizeof(ns->ns_ph)), b ;
|
|
u_char *p = ns->ns_pb + (DS_PGSIZE-sizeof(ns->ns_ph));
|
|
|
|
if (++ns->ns_cur >= ns->ns_rxend/DS_PGSIZE)
|
|
ns->ns_cur = (ns->ns_txstart+PKTSZ)/DS_PGSIZE;
|
|
b = ns->ns_cur*DS_PGSIZE;
|
|
|
|
while (l >= DS_PGSIZE) {
|
|
nefetch(ns, p, b, DS_PGSIZE);
|
|
p += DS_PGSIZE; l -= DS_PGSIZE;
|
|
if (++ns->ns_cur >= ns->ns_rxend/DS_PGSIZE)
|
|
ns->ns_cur = (ns->ns_txstart+PKTSZ)/DS_PGSIZE;
|
|
b = ns->ns_cur*DS_PGSIZE;
|
|
}
|
|
if (l > 0)
|
|
nefetch(ns, p, b, l);
|
|
}
|
|
/* don't forget checksum! */
|
|
len -= sizeof(long) + sizeof(struct ether_header);
|
|
|
|
neread(ns,(caddr_t)(ns->ns_pb), len);
|
|
}
|
|
|
|
/*
|
|
* Pass a packet to the higher levels.
|
|
* We deal with the trailer protocol here.
|
|
*/
|
|
neread(ns, buf, len)
|
|
register struct ne_softc *ns;
|
|
char *buf;
|
|
int len;
|
|
{
|
|
register struct ether_header *eh;
|
|
struct mbuf *m;
|
|
int off, resid;
|
|
register struct ifqueue *inq;
|
|
u_short etype;
|
|
|
|
/*
|
|
* 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;
|
|
etype = ntohs((u_short)eh->ether_type);
|
|
#define nedataaddr(eh, off, type) ((type)(((caddr_t)((eh)+1)+(off))))
|
|
if (etype >= ETHERTYPE_TRAIL &&
|
|
etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
|
|
off = (etype - ETHERTYPE_TRAIL) * 512;
|
|
if (off >= ETHERMTU) return; /* sanity */
|
|
eh->ether_type = *nedataaddr(eh, off, u_short *);
|
|
resid = ntohs(*nedataaddr(eh, off+2, u_short *));
|
|
if (off + resid > len) return; /* sanity */
|
|
len = off + resid;
|
|
} else off = 0;
|
|
|
|
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 = neget(buf, len, off, &ns->ns_if);
|
|
if (m == 0) return;
|
|
|
|
#if NBPFILTER > 0
|
|
/*
|
|
* Check if there's a bpf filter listening on this interface.
|
|
* If so, hand off the raw packet to bpf.
|
|
*/
|
|
if (ns->ns_bpf) {
|
|
bpf_mtap(ns->ns_bpf, m);
|
|
}
|
|
|
|
/*
|
|
* 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 ((ns->ns_if.if_flags & IFF_PROMISC) &&
|
|
bcmp(eh->ether_dhost, ns->ns_addrp,
|
|
sizeof(eh->ether_dhost)) != 0 &&
|
|
bcmp(eh->ether_dhost, etherbroadcastaddr,
|
|
sizeof(eh->ether_dhost)) != 0) {
|
|
|
|
m_freem(m);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Drop packet header and save fixed up ether_type.
|
|
*/
|
|
#if NBPFILTER > 0
|
|
m_adj(m, sizeof(struct ether_header));
|
|
#endif
|
|
eh->ether_type = ntohs(eh->ether_type);
|
|
|
|
ether_input(&ns->ns_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 *
|
|
neget(buf, totlen, off, ifp)
|
|
caddr_t buf;
|
|
int totlen, off;
|
|
struct ifnet *ifp;
|
|
{
|
|
struct mbuf *top, *last, *m;
|
|
int len;
|
|
register caddr_t cp;
|
|
|
|
MGETHDR(m, M_DONTWAIT, MT_DATA);
|
|
if (m == 0)
|
|
return (0);
|
|
top = last = m;
|
|
|
|
#if NBPFILTER > 0
|
|
/*
|
|
* Copy the ethernet header first.
|
|
* XXX: This assumes that ether_header fits inside the mbuf.
|
|
*/
|
|
/*
|
|
* XXX: This line is to avoid `nfs_rcv odd length!' spewage.
|
|
*/
|
|
m->m_data += (MHLEN - sizeof(struct ether_header)) & 3;
|
|
bcopy(buf, mtod(m, caddr_t), sizeof(struct ether_header));
|
|
m->m_len = sizeof(struct ether_header);
|
|
m->m_pkthdr.len = totlen + sizeof(struct ether_header);
|
|
#else
|
|
m->m_len = 0;
|
|
m->m_pkthdr.len = totlen;
|
|
#endif
|
|
m->m_pkthdr.rcvif = ifp;
|
|
|
|
buf += sizeof(struct ether_header);
|
|
cp = buf;
|
|
|
|
if (off) {
|
|
cp += off + 2 * sizeof(u_short);
|
|
totlen -= off + 2 * sizeof(u_short);
|
|
}
|
|
|
|
copy:
|
|
while (totlen > 0) {
|
|
len = min(totlen, M_TRAILINGSPACE(m));
|
|
if (!len) {
|
|
MGET(m, M_DONTWAIT, MT_DATA);
|
|
if (m == 0) {
|
|
m_freem(top);
|
|
return (0);
|
|
}
|
|
if (totlen >= MINCLSIZE) {
|
|
MCLGET(m, M_DONTWAIT);
|
|
if (m == 0) {
|
|
m_freem(top);
|
|
return (0);
|
|
}
|
|
}
|
|
m->m_len = 0;
|
|
last->m_next = m;
|
|
last = m;
|
|
len = min(totlen, M_TRAILINGSPACE(m));
|
|
}
|
|
|
|
bcopy(cp, mtod(m, caddr_t) + m->m_len, len);
|
|
m->m_len += len;
|
|
totlen -= len;
|
|
cp += len;
|
|
}
|
|
|
|
if (off) {
|
|
totlen = off;
|
|
cp = buf;
|
|
off = 0;
|
|
goto copy;
|
|
}
|
|
|
|
return (top);
|
|
}
|
|
|
|
/*
|
|
* Process an ioctl request.
|
|
*/
|
|
neioctl(ifp, cmd, data)
|
|
register struct ifnet *ifp;
|
|
int cmd;
|
|
caddr_t data;
|
|
{
|
|
register struct ifaddr *ifa = (struct ifaddr *)data;
|
|
struct ne_softc *ns = &ne_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:
|
|
neinit(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 *)(ns->ns_addrp);
|
|
else {
|
|
/*
|
|
* The manual says we can't 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)ns->ns_addrp, sizeof(ns->ns_addrp));
|
|
}
|
|
neinit(ifp->if_unit); /* does ne_setaddr() */
|
|
break;
|
|
}
|
|
#endif
|
|
default:
|
|
neinit(ifp->if_unit);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case SIOCSIFFLAGS:
|
|
if ((ifp->if_flags & IFF_UP) == 0 &&
|
|
ifp->if_flags & IFF_RUNNING) {
|
|
ifp->if_flags &= ~IFF_RUNNING;
|
|
outb(ns->ns_port + ds_cmd, DSCM_STOP|DSCM_NODMA);
|
|
} else if (ifp->if_flags & IFF_UP &&
|
|
(ifp->if_flags & IFF_RUNNING) == 0) {
|
|
neinit(ifp->if_unit);
|
|
break;
|
|
}
|
|
#if NBPFILTER > 0
|
|
if (ns->ns_if.if_flags & IFF_PROMISC)
|
|
outb (ns->ns_port+ds0_rcr, DSRC_AB | DSRC_PRO |
|
|
DSRC_AR | DSRC_SEP);
|
|
else
|
|
#endif
|
|
outb (ns->ns_port+ds0_rcr, DSRC_AB);
|
|
break;
|
|
|
|
#ifdef notdef
|
|
case SIOCGHWADDR:
|
|
bcopy((caddr_t)ns->ns_addr, (caddr_t) &ifr->ifr_data,
|
|
sizeof(ns->ns_addr));
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
error = EINVAL;
|
|
}
|
|
splx(s);
|
|
return (error);
|
|
}
|
|
#endif
|