1995-04-20 02:16:30 +04:00
|
|
|
/* $NetBSD: if_le.c,v 1.21 1995/04/19 22:16:30 mycroft Exp $ */
|
1994-10-26 10:22:45 +03:00
|
|
|
|
1993-05-13 17:56:20 +04:00
|
|
|
/*
|
|
|
|
* Copyright (c) 1982, 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.
|
|
|
|
*
|
1994-10-26 10:22:45 +03:00
|
|
|
* @(#)if_le.c 7.6 (Berkeley) 5/8/91
|
1993-05-13 17:56:20 +04:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "le.h"
|
|
|
|
#if NLE > 0
|
|
|
|
|
|
|
|
#include "bpfilter.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
* AMD 7990 LANCE
|
|
|
|
*/
|
1994-02-05 08:06:54 +03:00
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/mbuf.h>
|
|
|
|
#include <sys/buf.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/syslog.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/malloc.h>
|
|
|
|
#include <sys/errno.h>
|
|
|
|
|
|
|
|
#include <net/if.h>
|
|
|
|
#include <net/netisr.h>
|
|
|
|
#include <net/route.h>
|
|
|
|
#if NBPFILTER > 0
|
|
|
|
#include <net/bpf.h>
|
|
|
|
#include <net/bpfdesc.h>
|
|
|
|
#endif
|
1993-05-13 17:56:20 +04:00
|
|
|
|
|
|
|
#ifdef INET
|
1994-02-05 08:06:54 +03:00
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <netinet/in_systm.h>
|
|
|
|
#include <netinet/in_var.h>
|
|
|
|
#include <netinet/ip.h>
|
|
|
|
#include <netinet/if_ether.h>
|
1993-05-13 17:56:20 +04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef NS
|
1994-02-05 08:06:54 +03:00
|
|
|
#include <netns/ns.h>
|
|
|
|
#include <netns/ns_if.h>
|
1993-05-13 17:56:20 +04:00
|
|
|
#endif
|
|
|
|
|
1994-02-05 08:06:54 +03:00
|
|
|
#include <machine/cpu.h>
|
|
|
|
#include <machine/mtpr.h>
|
1994-09-18 04:43:19 +04:00
|
|
|
#include <hp300/hp300/isr.h>
|
|
|
|
#ifdef USELEDS
|
|
|
|
#include <hp300/hp300/led.h>
|
|
|
|
#endif
|
1993-05-13 17:56:20 +04:00
|
|
|
|
1994-02-05 08:06:54 +03:00
|
|
|
#include <hp300/dev/device.h>
|
|
|
|
#include <hp300/dev/if_lereg.h>
|
1993-05-13 17:56:20 +04:00
|
|
|
|
1994-07-06 05:36:23 +04:00
|
|
|
|
|
|
|
#define ETHER_MIN_LEN 64
|
|
|
|
#define ETHER_MAX_LEN 1518
|
|
|
|
#define ETHER_ADDR_LEN 6
|
|
|
|
|
|
|
|
|
1993-05-13 17:56:20 +04:00
|
|
|
/* offsets for: ID, REGS, MEM, NVRAM */
|
|
|
|
int lestd[] = { 0, 0x4000, 0x8000, 0xC008 };
|
|
|
|
|
|
|
|
struct isr le_isr[NLE];
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ethernet software status per interface.
|
|
|
|
*
|
|
|
|
* Each interface is referenced by a network interface structure,
|
1994-07-06 05:36:23 +04:00
|
|
|
* arpcom.ac_if, which the routing code uses to locate the interface.
|
1993-05-13 17:56:20 +04:00
|
|
|
* This structure contains the output queue for the interface, its address, ...
|
|
|
|
*/
|
|
|
|
struct le_softc {
|
1994-07-06 05:36:23 +04:00
|
|
|
struct arpcom sc_arpcom; /* common Ethernet structures */
|
|
|
|
struct lereg0 *sc_r0; /* DIO registers */
|
|
|
|
struct lereg1 *sc_r1; /* LANCE registers */
|
|
|
|
void *sc_mem;
|
|
|
|
struct init_block *sc_init;
|
|
|
|
struct mds *sc_rd, *sc_td;
|
|
|
|
u_char *sc_rbuf, *sc_tbuf;
|
|
|
|
int sc_last_rd, sc_last_td;
|
|
|
|
int sc_no_td;
|
|
|
|
#ifdef LEDEBUG
|
|
|
|
int sc_debug;
|
|
|
|
#endif
|
1993-05-13 17:56:20 +04:00
|
|
|
} le_softc[NLE];
|
|
|
|
|
1994-02-06 03:46:02 +03:00
|
|
|
int leintr __P((int));
|
1995-04-20 02:16:30 +04:00
|
|
|
int leioctl __P((struct ifnet *, u_long, caddr_t));
|
|
|
|
void lestart __P((struct ifnet *));
|
|
|
|
void lewatchdog __P((int));
|
1994-07-06 05:36:23 +04:00
|
|
|
static inline void lewrcsr __P((/* struct le_softc *, u_short, u_short */));
|
|
|
|
static inline u_short lerdcsr __P((/* struct le_softc *, u_short */));
|
|
|
|
void leinit __P((struct le_softc *));
|
|
|
|
void lememinit __P((struct le_softc *));
|
|
|
|
void lereset __P((struct le_softc *));
|
|
|
|
void lestop __P((struct le_softc *));
|
|
|
|
void letint __P((int));
|
|
|
|
void lerint __P((int));
|
|
|
|
void leread __P((struct le_softc *, u_char *, int));
|
|
|
|
struct mbuf *leget __P((u_char *, int, struct ifnet *));
|
|
|
|
#ifdef LEDEBUG
|
|
|
|
void recv_print __P((struct le_softc *, int));
|
|
|
|
void xmit_print __P((struct le_softc *, int));
|
|
|
|
#endif
|
|
|
|
void lesetladrf __P((struct arpcom *, u_long *));
|
|
|
|
|
|
|
|
int leattach __P((struct hp_device *));
|
1994-02-06 03:46:02 +03:00
|
|
|
|
|
|
|
struct driver ledriver = {
|
|
|
|
leattach, "le",
|
|
|
|
};
|
|
|
|
|
1994-07-06 05:36:23 +04:00
|
|
|
static inline void
|
|
|
|
lewrcsr(sc, port, val)
|
|
|
|
struct le_softc *sc;
|
|
|
|
register u_short port;
|
|
|
|
register u_short val;
|
|
|
|
{
|
|
|
|
register struct lereg0 *ler0 = sc->sc_r0;
|
|
|
|
register struct lereg1 *ler1 = sc->sc_r1;
|
|
|
|
|
|
|
|
do {
|
|
|
|
ler1->ler1_rap = port;
|
|
|
|
} while ((ler0->ler0_status & LE_ACK) == 0);
|
|
|
|
do {
|
|
|
|
ler1->ler1_rdp = val;
|
|
|
|
} while ((ler0->ler0_status & LE_ACK) == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline u_short
|
|
|
|
lerdcsr(sc, port)
|
|
|
|
struct le_softc *sc;
|
|
|
|
register u_short port;
|
|
|
|
{
|
|
|
|
register struct lereg0 *ler0 = sc->sc_r0;
|
|
|
|
register struct lereg1 *ler1 = sc->sc_r1;
|
|
|
|
register u_short val;
|
|
|
|
|
|
|
|
do {
|
|
|
|
ler1->ler1_rap = port;
|
|
|
|
} while ((ler0->ler0_status & LE_ACK) == 0);
|
|
|
|
do {
|
|
|
|
val = ler1->ler1_rdp;
|
|
|
|
} while ((ler0->ler0_status & LE_ACK) == 0);
|
|
|
|
return (val);
|
|
|
|
}
|
|
|
|
|
1993-05-13 17:56:20 +04:00
|
|
|
/*
|
|
|
|
* Interface exists: make available by filling in network interface
|
|
|
|
* record. System will initialize the interface when it is ready
|
|
|
|
* to accept packets.
|
|
|
|
*/
|
1994-02-06 03:46:02 +03:00
|
|
|
int
|
1993-05-13 17:56:20 +04:00
|
|
|
leattach(hd)
|
|
|
|
struct hp_device *hd;
|
|
|
|
{
|
|
|
|
register struct lereg0 *ler0;
|
1994-02-05 08:06:54 +03:00
|
|
|
struct le_softc *sc = &le_softc[hd->hp_unit];
|
1994-07-06 05:36:23 +04:00
|
|
|
struct ifnet *ifp = &sc->sc_arpcom.ac_if;
|
1993-05-13 17:56:20 +04:00
|
|
|
char *cp;
|
|
|
|
int i;
|
|
|
|
|
1994-02-05 08:06:54 +03:00
|
|
|
ler0 = sc->sc_r0 = (struct lereg0 *)(lestd[0] + (int)hd->hp_addr);
|
1993-05-13 17:56:20 +04:00
|
|
|
if (ler0->ler0_id != LEID)
|
|
|
|
return(0);
|
1994-07-06 05:36:23 +04:00
|
|
|
sc->sc_r1 = (struct lereg1 *)(lestd[1] + (int)hd->hp_addr);
|
|
|
|
sc->sc_mem = (void *)(lestd[2] + (int)hd->hp_addr);
|
1993-05-13 17:56:20 +04:00
|
|
|
le_isr[hd->hp_unit].isr_intr = leintr;
|
|
|
|
hd->hp_ipl = le_isr[hd->hp_unit].isr_ipl = LE_IPL(ler0->ler0_status);
|
|
|
|
le_isr[hd->hp_unit].isr_arg = hd->hp_unit;
|
|
|
|
ler0->ler0_id = 0xFF;
|
|
|
|
DELAY(100);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read the ethernet address off the board, one nibble at a time.
|
|
|
|
*/
|
|
|
|
cp = (char *)(lestd[3] + (int)hd->hp_addr);
|
1994-07-06 05:36:23 +04:00
|
|
|
for (i = 0; i < sizeof(sc->sc_arpcom.ac_enaddr); i++) {
|
|
|
|
sc->sc_arpcom.ac_enaddr[i] = (*++cp & 0xF) << 4;
|
1993-05-13 17:56:20 +04:00
|
|
|
cp++;
|
1994-07-06 05:36:23 +04:00
|
|
|
sc->sc_arpcom.ac_enaddr[i] |= *++cp & 0xF;
|
1993-05-13 17:56:20 +04:00
|
|
|
cp++;
|
|
|
|
}
|
|
|
|
printf("le%d: hardware address %s\n", hd->hp_unit,
|
1994-07-06 05:36:23 +04:00
|
|
|
ether_sprintf(sc->sc_arpcom.ac_enaddr));
|
1993-05-13 17:56:20 +04:00
|
|
|
|
|
|
|
isrlink(&le_isr[hd->hp_unit]);
|
|
|
|
ler0->ler0_status = LE_IE;
|
|
|
|
|
|
|
|
ifp->if_unit = hd->hp_unit;
|
|
|
|
ifp->if_name = "le";
|
|
|
|
ifp->if_output = ether_output;
|
|
|
|
ifp->if_start = lestart;
|
1994-07-06 05:36:23 +04:00
|
|
|
ifp->if_ioctl = leioctl;
|
|
|
|
ifp->if_watchdog = lewatchdog;
|
1994-02-16 23:15:18 +03:00
|
|
|
ifp->if_flags =
|
|
|
|
IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS | IFF_MULTICAST;
|
1994-07-06 05:36:23 +04:00
|
|
|
|
1994-05-13 12:36:17 +04:00
|
|
|
if_attach(ifp);
|
|
|
|
ether_ifattach(ifp);
|
1994-07-06 05:36:23 +04:00
|
|
|
|
1993-05-13 17:56:20 +04:00
|
|
|
#if NBPFILTER > 0
|
1994-05-13 12:36:17 +04:00
|
|
|
bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header));
|
1993-05-13 17:56:20 +04:00
|
|
|
#endif
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
1994-02-05 08:06:54 +03:00
|
|
|
void
|
1994-07-06 05:36:23 +04:00
|
|
|
lereset(sc)
|
|
|
|
struct le_softc *sc;
|
1993-05-13 17:56:20 +04:00
|
|
|
{
|
1994-02-15 02:03:54 +03:00
|
|
|
|
1994-07-06 05:36:23 +04:00
|
|
|
leinit(sc);
|
|
|
|
}
|
1994-02-05 08:06:54 +03:00
|
|
|
|
1995-04-20 02:16:30 +04:00
|
|
|
void
|
1994-07-06 05:36:23 +04:00
|
|
|
lewatchdog(unit)
|
1995-04-20 02:16:30 +04:00
|
|
|
int unit;
|
1994-07-06 05:36:23 +04:00
|
|
|
{
|
|
|
|
struct le_softc *sc = &le_softc[unit];
|
1994-02-05 08:06:54 +03:00
|
|
|
|
1994-07-06 05:36:23 +04:00
|
|
|
log(LOG_ERR, "le%d: device timeout\n", unit);
|
|
|
|
++sc->sc_arpcom.ac_if.if_oerrors;
|
1995-04-20 02:16:30 +04:00
|
|
|
|
1994-07-06 05:36:23 +04:00
|
|
|
lereset(sc);
|
1994-02-05 08:06:54 +03:00
|
|
|
}
|
|
|
|
|
1994-07-06 05:36:23 +04:00
|
|
|
#define LANCE_ADDR(sc, a) \
|
|
|
|
((u_long)(a) - (u_long)sc->sc_mem)
|
|
|
|
|
|
|
|
/* LANCE initialization block set up. */
|
1994-02-06 03:46:02 +03:00
|
|
|
void
|
1994-07-06 05:36:23 +04:00
|
|
|
lememinit(sc)
|
|
|
|
register struct le_softc *sc;
|
1994-02-05 09:58:08 +03:00
|
|
|
{
|
1994-07-06 05:36:23 +04:00
|
|
|
struct ifnet *ifp = &sc->sc_arpcom.ac_if;
|
|
|
|
int i;
|
|
|
|
void *mem;
|
|
|
|
u_long a;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* At this point we assume that the memory allocated to the Lance is
|
|
|
|
* quadword aligned. If it isn't then the initialisation is going
|
|
|
|
* fail later on.
|
|
|
|
*/
|
|
|
|
mem = sc->sc_mem;
|
|
|
|
|
|
|
|
sc->sc_init = mem;
|
|
|
|
#if NBPFILTER > 0
|
|
|
|
if (ifp->if_flags & IFF_PROMISC)
|
|
|
|
sc->sc_init->mode = LE_NORMAL | LE_PROM;
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
sc->sc_init->mode = LE_NORMAL;
|
|
|
|
for (i = 0; i < ETHER_ADDR_LEN; i++)
|
|
|
|
sc->sc_init->padr[i] = sc->sc_arpcom.ac_enaddr[i^1];
|
|
|
|
lesetladrf(&sc->sc_arpcom, sc->sc_init->ladrf);
|
|
|
|
mem += sizeof(struct init_block);
|
|
|
|
|
|
|
|
sc->sc_rd = mem;
|
|
|
|
a = LANCE_ADDR(sc, mem);
|
|
|
|
sc->sc_init->rdra = a;
|
|
|
|
sc->sc_init->rlen = ((a >> 16) & 0xff) | (RLEN << 13);
|
|
|
|
mem += NRBUF * sizeof(struct mds);
|
|
|
|
|
|
|
|
sc->sc_td = mem;
|
|
|
|
a = LANCE_ADDR(sc, mem);
|
|
|
|
sc->sc_init->tdra = a;
|
|
|
|
sc->sc_init->tlen = ((a >> 16) & 0xff) | (TLEN << 13);
|
|
|
|
mem += NTBUF * sizeof(struct mds);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set up receive ring descriptors.
|
|
|
|
*/
|
|
|
|
sc->sc_rbuf = mem;
|
|
|
|
for (i = 0; i < NRBUF; i++) {
|
|
|
|
a = LANCE_ADDR(sc, mem);
|
|
|
|
sc->sc_rd[i].addr = a;
|
|
|
|
sc->sc_rd[i].flags = ((a >> 16) & 0xff) | LE_OWN;
|
|
|
|
sc->sc_rd[i].bcnt = -BUFSIZE;
|
|
|
|
sc->sc_rd[i].mcnt = 0;
|
|
|
|
mem += BUFSIZE;
|
1994-02-05 09:58:08 +03:00
|
|
|
}
|
1994-07-06 05:36:23 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Set up transmit ring descriptors.
|
|
|
|
*/
|
|
|
|
sc->sc_tbuf = mem;
|
|
|
|
for (i = 0; i < NTBUF; i++) {
|
|
|
|
a = LANCE_ADDR(sc, mem);
|
|
|
|
sc->sc_td[i].addr = a;
|
|
|
|
sc->sc_td[i].flags= ((a >> 16) & 0xff);
|
|
|
|
sc->sc_td[i].bcnt = 0xf000;
|
|
|
|
sc->sc_td[i].mcnt = 0;
|
|
|
|
mem += BUFSIZE;
|
1994-02-05 09:58:08 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1994-02-06 03:46:02 +03:00
|
|
|
void
|
1994-07-06 05:36:23 +04:00
|
|
|
lestop(sc)
|
|
|
|
struct le_softc *sc;
|
1994-02-05 08:06:54 +03:00
|
|
|
{
|
1993-05-13 17:56:20 +04:00
|
|
|
|
1994-07-06 05:36:23 +04:00
|
|
|
lewrcsr(sc, 0, LE_STOP);
|
1993-05-13 17:56:20 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1994-07-06 05:36:23 +04:00
|
|
|
* Initialization of interface; set up initialization block
|
|
|
|
* and transmit/receive descriptor rings.
|
1993-05-13 17:56:20 +04:00
|
|
|
*/
|
1994-02-06 03:46:02 +03:00
|
|
|
void
|
1994-07-06 05:36:23 +04:00
|
|
|
leinit(sc)
|
|
|
|
register struct le_softc *sc;
|
1993-05-13 17:56:20 +04:00
|
|
|
{
|
1994-07-06 05:36:23 +04:00
|
|
|
struct ifnet *ifp = &sc->sc_arpcom.ac_if;
|
1993-05-13 17:56:20 +04:00
|
|
|
int s;
|
1994-07-06 05:36:23 +04:00
|
|
|
register int timo;
|
|
|
|
u_long a;
|
1993-05-13 17:56:20 +04:00
|
|
|
|
1994-07-06 05:36:23 +04:00
|
|
|
/* Address not known. */
|
|
|
|
if (!ifp->if_addrlist)
|
1993-05-13 17:56:20 +04:00
|
|
|
return;
|
1994-07-06 05:36:23 +04:00
|
|
|
|
|
|
|
s = splimp();
|
|
|
|
|
|
|
|
/* Don't want to get in a weird state. */
|
|
|
|
lewrcsr(sc, 0, LE_STOP);
|
|
|
|
DELAY(100);
|
|
|
|
|
|
|
|
sc->sc_last_rd = sc->sc_last_td = sc->sc_no_td = 0;
|
|
|
|
|
|
|
|
/* Set up LANCE init block. */
|
|
|
|
lememinit(sc);
|
|
|
|
|
|
|
|
/* Turn on byte swapping. */
|
|
|
|
lewrcsr(sc, 3, LE_BSWP);
|
|
|
|
|
|
|
|
/* Give LANCE the physical address of its init block. */
|
|
|
|
a = LANCE_ADDR(sc, sc->sc_init);
|
|
|
|
lewrcsr(sc, 1, a);
|
|
|
|
lewrcsr(sc, 2, (a >> 16) & 0xff);
|
|
|
|
|
|
|
|
/* Try to initialize the LANCE. */
|
|
|
|
DELAY(100);
|
|
|
|
lewrcsr(sc, 0, LE_INIT);
|
|
|
|
|
|
|
|
/* Wait for initialization to finish. */
|
|
|
|
for (timo = 100000; timo; timo--)
|
|
|
|
if (lerdcsr(sc, 0) & LE_IDON)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (lerdcsr(sc, 0) & LE_IDON) {
|
|
|
|
/* Start the LANCE. */
|
|
|
|
lewrcsr(sc, 0, LE_INEA | LE_STRT | LE_IDON);
|
1993-05-13 17:56:20 +04:00
|
|
|
ifp->if_flags |= IFF_RUNNING;
|
1994-07-06 05:36:23 +04:00
|
|
|
ifp->if_flags &= ~IFF_OACTIVE;
|
|
|
|
lestart(ifp);
|
|
|
|
} else
|
|
|
|
printf("le%d: card failed to initialize\n", ifp->if_unit);
|
1993-05-13 17:56:20 +04:00
|
|
|
|
1994-07-06 05:36:23 +04:00
|
|
|
(void) splx(s);
|
|
|
|
}
|
1994-02-05 09:58:08 +03:00
|
|
|
|
1994-07-06 05:36:23 +04:00
|
|
|
/*
|
|
|
|
* Controller interrupt.
|
|
|
|
*/
|
1994-02-06 03:46:02 +03:00
|
|
|
int
|
1993-05-13 17:56:20 +04:00
|
|
|
leintr(unit)
|
1994-07-06 05:36:23 +04:00
|
|
|
int unit;
|
1993-05-13 17:56:20 +04:00
|
|
|
{
|
1994-02-05 08:06:54 +03:00
|
|
|
register struct le_softc *sc = &le_softc[unit];
|
1994-07-06 05:36:23 +04:00
|
|
|
register u_short isr;
|
1993-05-13 17:56:20 +04:00
|
|
|
|
1994-07-06 05:36:23 +04:00
|
|
|
isr = lerdcsr(sc, 0);
|
|
|
|
#ifdef LEDEBUG
|
|
|
|
if (sc->sc_debug)
|
|
|
|
printf("le%d: leintr entering with isr=%04x\n",
|
|
|
|
unit, isr);
|
|
|
|
#endif
|
|
|
|
if ((isr & LE_INTR) == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
do {
|
|
|
|
lewrcsr(sc, 0,
|
|
|
|
isr & (LE_INEA | LE_BABL | LE_MISS | LE_MERR |
|
|
|
|
LE_RINT | LE_TINT | LE_IDON));
|
|
|
|
if (isr & (LE_BABL | LE_CERR | LE_MISS | LE_MERR)) {
|
|
|
|
if (isr & LE_BABL) {
|
|
|
|
printf("le%d: BABL\n", unit);
|
|
|
|
sc->sc_arpcom.ac_if.if_oerrors++;
|
|
|
|
}
|
|
|
|
#if 0
|
|
|
|
if (isr & LE_CERR) {
|
|
|
|
printf("le%d: CERR\n", unit);
|
|
|
|
sc->sc_arpcom.ac_if.if_collisions++;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (isr & LE_MISS) {
|
1994-07-16 01:20:48 +04:00
|
|
|
#if 0
|
1994-07-06 05:36:23 +04:00
|
|
|
printf("le%d: MISS\n", unit);
|
1994-07-16 01:20:48 +04:00
|
|
|
#endif
|
1994-07-06 05:36:23 +04:00
|
|
|
sc->sc_arpcom.ac_if.if_ierrors++;
|
|
|
|
}
|
|
|
|
if (isr & LE_MERR) {
|
|
|
|
printf("le%d: MERR\n", unit);
|
|
|
|
lereset(sc);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((isr & LE_RXON) == 0) {
|
|
|
|
printf("le%d: receiver disabled\n", unit);
|
|
|
|
sc->sc_arpcom.ac_if.if_ierrors++;
|
1994-02-05 08:06:54 +03:00
|
|
|
lereset(sc);
|
1994-07-06 05:36:23 +04:00
|
|
|
goto out;
|
1993-05-13 17:56:20 +04:00
|
|
|
}
|
1994-07-06 05:36:23 +04:00
|
|
|
if ((isr & LE_TXON) == 0) {
|
|
|
|
printf("le%d: transmitter disabled\n", unit);
|
|
|
|
sc->sc_arpcom.ac_if.if_oerrors++;
|
|
|
|
lereset(sc);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isr & LE_RINT) {
|
|
|
|
/* Reset watchdog timer. */
|
|
|
|
sc->sc_arpcom.ac_if.if_timer = 0;
|
|
|
|
lerint(unit);
|
|
|
|
}
|
|
|
|
if (isr & LE_TINT) {
|
|
|
|
/* Reset watchdog timer. */
|
|
|
|
sc->sc_arpcom.ac_if.if_timer = 0;
|
|
|
|
letint(unit);
|
|
|
|
}
|
|
|
|
|
|
|
|
isr = lerdcsr(sc, 0);
|
|
|
|
} while ((isr & LE_INTR) != 0);
|
|
|
|
|
|
|
|
#ifdef LEDEBUG
|
|
|
|
if (sc->sc_debug)
|
|
|
|
printf("le%d: leintr returning with isr=%04x\n",
|
|
|
|
unit, isr);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
out:
|
|
|
|
return 1;
|
1993-05-13 17:56:20 +04:00
|
|
|
}
|
|
|
|
|
1994-07-06 05:36:23 +04:00
|
|
|
#define NEXTTDS \
|
|
|
|
if (++tmd == NTBUF) tmd=0, cdm=sc->sc_td; else ++cdm
|
|
|
|
|
1994-07-10 21:53:12 +04:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
1995-04-20 02:16:30 +04:00
|
|
|
void
|
1994-07-10 21:53:12 +04:00
|
|
|
lestart(ifp)
|
|
|
|
struct ifnet *ifp;
|
|
|
|
{
|
|
|
|
register struct le_softc *sc = &le_softc[ifp->if_unit];
|
|
|
|
register int tmd;
|
|
|
|
struct mds *cdm;
|
|
|
|
struct mbuf *m0, *m;
|
|
|
|
u_char *buffer;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
if ((sc->sc_arpcom.ac_if.if_flags & (IFF_RUNNING | IFF_OACTIVE)) !=
|
|
|
|
IFF_RUNNING)
|
|
|
|
return;
|
|
|
|
|
|
|
|
tmd = sc->sc_last_td;
|
|
|
|
cdm = &sc->sc_td[tmd];
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
if (sc->sc_no_td >= NTBUF) {
|
|
|
|
sc->sc_arpcom.ac_if.if_flags |= IFF_OACTIVE;
|
|
|
|
#ifdef LEDEBUG
|
|
|
|
if (sc->sc_debug)
|
|
|
|
printf("no_td = %d, last_td = %d\n", sc->sc_no_td,
|
|
|
|
sc->sc_last_td);
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef LEDEBUG
|
|
|
|
if (cdm->flags & LE_OWN) {
|
|
|
|
sc->sc_arpcom.ac_if.if_flags |= IFF_OACTIVE;
|
|
|
|
printf("missing buffer, no_td = %d, last_td = %d\n",
|
|
|
|
sc->sc_no_td, sc->sc_last_td);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
IF_DEQUEUE(&sc->sc_arpcom.ac_if.if_snd, m);
|
|
|
|
if (!m)
|
|
|
|
break;
|
|
|
|
|
|
|
|
++sc->sc_no_td;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copy the mbuf chain into the transmit buffer.
|
|
|
|
*/
|
|
|
|
buffer = sc->sc_tbuf + (BUFSIZE * sc->sc_last_td);
|
|
|
|
len = 0;
|
|
|
|
for (m0 = m; m; m = m->m_next) {
|
|
|
|
bcopy(mtod(m, caddr_t), buffer, m->m_len);
|
|
|
|
buffer += m->m_len;
|
|
|
|
len += m->m_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef LEDEBUG
|
|
|
|
if (len > ETHER_MAX_LEN)
|
|
|
|
printf("packet length %d\n", len);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if NBPFILTER > 0
|
|
|
|
if (sc->sc_arpcom.ac_if.if_bpf)
|
|
|
|
bpf_mtap(sc->sc_arpcom.ac_if.if_bpf, m0);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
m_freem(m0);
|
|
|
|
len = max(len, ETHER_MIN_LEN);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Init transmit registers, and set transmit start flag.
|
|
|
|
*/
|
|
|
|
cdm->bcnt = -len;
|
|
|
|
cdm->mcnt = 0;
|
1994-07-16 01:20:48 +04:00
|
|
|
cdm->flags |= LE_OWN | LE_STP | LE_ENP;
|
1994-07-10 21:53:12 +04:00
|
|
|
|
|
|
|
#ifdef LEDEBUG
|
|
|
|
if (sc->sc_debug)
|
|
|
|
xmit_print(sc, sc->sc_last_td);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
lewrcsr(sc, 0, LE_INEA | LE_TDMD);
|
|
|
|
|
|
|
|
NEXTTDS;
|
|
|
|
}
|
|
|
|
|
|
|
|
sc->sc_last_td = tmd;
|
|
|
|
}
|
|
|
|
|
1994-02-06 03:46:02 +03:00
|
|
|
void
|
1994-07-06 05:36:23 +04:00
|
|
|
letint(unit)
|
|
|
|
int unit;
|
1993-05-13 17:56:20 +04:00
|
|
|
{
|
1994-07-06 05:36:23 +04:00
|
|
|
register struct le_softc *sc = &le_softc[unit];
|
|
|
|
register int tmd = (sc->sc_last_td - sc->sc_no_td + NTBUF) % NTBUF;
|
|
|
|
struct mds *cdm = &sc->sc_td[tmd];
|
|
|
|
|
1994-09-18 04:43:19 +04:00
|
|
|
#ifdef USELEDS
|
|
|
|
if (inledcontrol == 0)
|
|
|
|
ledcontrol(0, 0, LED_LANXMT);
|
|
|
|
#endif
|
|
|
|
|
1994-07-06 05:36:23 +04:00
|
|
|
if (cdm->flags & LE_OWN) {
|
|
|
|
/* Race condition with loop below. */
|
|
|
|
#ifdef LEDEBUG
|
|
|
|
if (sc->sc_debug)
|
|
|
|
printf("le%d: extra tint\n", unit);
|
|
|
|
#endif
|
1993-05-13 17:56:20 +04:00
|
|
|
return;
|
|
|
|
}
|
1994-02-05 09:58:08 +03:00
|
|
|
|
1994-07-06 05:36:23 +04:00
|
|
|
sc->sc_arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
|
1994-02-05 09:58:08 +03:00
|
|
|
|
1994-07-06 05:36:23 +04:00
|
|
|
do {
|
|
|
|
if (sc->sc_no_td <= 0)
|
|
|
|
break;
|
|
|
|
#ifdef LEDEBUG
|
|
|
|
if (sc->sc_debug)
|
|
|
|
printf("trans cdm = %x\n", cdm);
|
|
|
|
#endif
|
|
|
|
sc->sc_arpcom.ac_if.if_opackets++;
|
|
|
|
--sc->sc_no_td;
|
1994-07-26 02:54:58 +04:00
|
|
|
if (cdm->mcnt & (LE_TBUFF | LE_UFLO | LE_LCOL | LE_LCAR | LE_RTRY)) {
|
|
|
|
if (cdm->mcnt & LE_TBUFF)
|
1994-07-06 05:36:23 +04:00
|
|
|
printf("le%d: TBUFF\n", unit);
|
1994-07-26 02:54:58 +04:00
|
|
|
if ((cdm->mcnt & (LE_TBUFF | LE_UFLO)) == LE_UFLO)
|
1994-07-06 05:36:23 +04:00
|
|
|
printf("le%d: UFLO\n", unit);
|
1994-07-26 02:54:58 +04:00
|
|
|
if (cdm->mcnt & LE_UFLO) {
|
1994-02-05 09:58:08 +03:00
|
|
|
lereset(sc);
|
1994-07-06 05:36:23 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
#if 0
|
1994-07-26 02:54:58 +04:00
|
|
|
if (cdm->mcnt & LE_LCOL) {
|
1994-07-06 05:36:23 +04:00
|
|
|
printf("le%d: late collision\n", unit);
|
|
|
|
sc->sc_arpcom.ac_if.if_collisions++;
|
|
|
|
}
|
1994-07-26 02:54:58 +04:00
|
|
|
if (cdm->mcnt & LE_LCAR)
|
1994-07-06 05:36:23 +04:00
|
|
|
printf("le%d: lost carrier\n", unit);
|
1994-07-26 02:54:58 +04:00
|
|
|
if (cdm->mcnt & LE_RTRY) {
|
1994-07-06 05:36:23 +04:00
|
|
|
printf("le%d: excessive collisions, tdr %d\n",
|
1994-07-26 02:54:58 +04:00
|
|
|
unit, cdm->mcnt & 0x1ff);
|
|
|
|
sc->sc_arpcom.ac_if.if_collisions += 16;
|
1994-07-06 05:36:23 +04:00
|
|
|
}
|
|
|
|
#endif
|
1994-07-26 02:54:58 +04:00
|
|
|
} else if (cdm->flags & LE_ONE)
|
|
|
|
sc->sc_arpcom.ac_if.if_collisions++;
|
|
|
|
else if (cdm->flags & LE_MORE)
|
|
|
|
/* Real number is unknown. */
|
|
|
|
sc->sc_arpcom.ac_if.if_collisions += 2;
|
1994-07-06 05:36:23 +04:00
|
|
|
NEXTTDS;
|
|
|
|
} while ((cdm->flags & LE_OWN) == 0);
|
1993-05-13 17:56:20 +04:00
|
|
|
|
1994-07-06 05:36:23 +04:00
|
|
|
lestart(&sc->sc_arpcom.ac_if);
|
|
|
|
}
|
1993-05-13 17:56:20 +04:00
|
|
|
|
1994-07-06 05:36:23 +04:00
|
|
|
#define NEXTRDS \
|
|
|
|
if (++rmd == NRBUF) rmd=0, cdm=sc->sc_rd; else ++cdm
|
|
|
|
|
|
|
|
/* only called from one place, so may as well integrate */
|
1994-02-06 03:46:02 +03:00
|
|
|
void
|
1994-07-06 05:36:23 +04:00
|
|
|
lerint(unit)
|
|
|
|
int unit;
|
1993-05-13 17:56:20 +04:00
|
|
|
{
|
1994-07-06 05:36:23 +04:00
|
|
|
register struct le_softc *sc = &le_softc[unit];
|
|
|
|
register int rmd = sc->sc_last_rd;
|
|
|
|
struct mds *cdm = &sc->sc_rd[rmd];
|
|
|
|
|
1994-09-18 04:43:19 +04:00
|
|
|
#ifdef USELEDS
|
|
|
|
if (inledcontrol == 0)
|
|
|
|
ledcontrol(0, 0, LED_LANRCV);
|
|
|
|
#endif
|
|
|
|
|
1994-07-06 05:36:23 +04:00
|
|
|
if (cdm->flags & LE_OWN) {
|
|
|
|
/* Race condition with loop below. */
|
|
|
|
#ifdef LEDEBUG
|
|
|
|
if (sc->sc_debug)
|
|
|
|
printf("le%d: extra rint\n", unit);
|
|
|
|
#endif
|
|
|
|
return;
|
1993-05-13 17:56:20 +04:00
|
|
|
}
|
|
|
|
|
1994-07-06 05:36:23 +04:00
|
|
|
/* Process all buffers with valid data. */
|
|
|
|
do {
|
|
|
|
if (cdm->flags & (LE_FRAM | LE_OFLO | LE_CRC | LE_RBUFF)) {
|
|
|
|
if ((cdm->flags & (LE_FRAM | LE_OFLO | LE_ENP)) == (LE_FRAM | LE_ENP))
|
|
|
|
printf("le%d: FRAM\n", unit);
|
|
|
|
if ((cdm->flags & (LE_OFLO | LE_ENP)) == LE_OFLO)
|
|
|
|
printf("le%d: OFLO\n", unit);
|
|
|
|
if ((cdm->flags & (LE_CRC | LE_OFLO | LE_ENP)) == (LE_CRC | LE_ENP))
|
|
|
|
printf("le%d: CRC\n", unit);
|
|
|
|
if (cdm->flags & LE_RBUFF)
|
|
|
|
printf("le%d: RBUFF\n", unit);
|
|
|
|
} else if (cdm->flags & (LE_STP | LE_ENP) != (LE_STP | LE_ENP)) {
|
1993-05-13 17:56:20 +04:00
|
|
|
do {
|
1994-07-06 05:36:23 +04:00
|
|
|
cdm->mcnt = 0;
|
1994-07-16 01:20:48 +04:00
|
|
|
cdm->flags |= LE_OWN;
|
1994-07-06 05:36:23 +04:00
|
|
|
NEXTRDS;
|
|
|
|
} while ((cdm->flags & (LE_OWN | LE_ERR | LE_STP | LE_ENP)) == 0);
|
|
|
|
sc->sc_last_rd = rmd;
|
|
|
|
printf("le%d: chained buffer\n", unit);
|
|
|
|
if ((cdm->flags & (LE_OWN | LE_ERR | LE_STP | LE_ENP)) != LE_ENP) {
|
1994-02-05 08:06:54 +03:00
|
|
|
lereset(sc);
|
1993-05-13 17:56:20 +04:00
|
|
|
return;
|
|
|
|
}
|
1994-07-06 05:36:23 +04:00
|
|
|
} else {
|
|
|
|
#ifdef LEDEBUG
|
|
|
|
if (sc->sc_debug)
|
|
|
|
recv_print(sc, sc->sc_last_rd);
|
|
|
|
#endif
|
|
|
|
leread(sc, sc->sc_rbuf + (BUFSIZE * rmd),
|
|
|
|
(int)cdm->mcnt);
|
|
|
|
sc->sc_arpcom.ac_if.if_ipackets++;
|
|
|
|
}
|
|
|
|
|
|
|
|
cdm->mcnt = 0;
|
1994-07-16 01:20:48 +04:00
|
|
|
cdm->flags |= LE_OWN;
|
1994-07-06 05:36:23 +04:00
|
|
|
NEXTRDS;
|
|
|
|
#ifdef LEDEBUG
|
|
|
|
if (sc->sc_debug)
|
|
|
|
printf("sc->sc_last_rd = %x, cdm = %x\n",
|
|
|
|
sc->sc_last_rd, cdm);
|
|
|
|
#endif
|
|
|
|
} while ((cdm->flags & LE_OWN) == 0);
|
|
|
|
|
|
|
|
sc->sc_last_rd = rmd;
|
1993-05-13 17:56:20 +04:00
|
|
|
}
|
|
|
|
|
1994-07-06 05:36:23 +04:00
|
|
|
/*
|
|
|
|
* Pass a packet to the higher levels.
|
|
|
|
*/
|
1994-02-06 03:46:02 +03:00
|
|
|
void
|
1994-02-05 08:06:54 +03:00
|
|
|
leread(sc, buf, len)
|
|
|
|
register struct le_softc *sc;
|
1994-07-06 05:36:23 +04:00
|
|
|
u_char *buf;
|
1993-05-13 17:56:20 +04:00
|
|
|
int len;
|
|
|
|
{
|
1994-07-16 01:20:48 +04:00
|
|
|
struct ifnet *ifp;
|
1994-07-06 05:36:23 +04:00
|
|
|
struct mbuf *m;
|
1994-07-16 01:20:48 +04:00
|
|
|
struct ether_header *eh;
|
1993-05-13 17:56:20 +04:00
|
|
|
|
1994-07-16 01:20:48 +04:00
|
|
|
len -= 4;
|
1994-07-06 05:36:23 +04:00
|
|
|
if (len <= 0)
|
|
|
|
return;
|
1993-05-13 17:56:20 +04:00
|
|
|
|
1994-07-06 05:36:23 +04:00
|
|
|
/* Pull packet off interface. */
|
1994-07-16 01:20:48 +04:00
|
|
|
ifp = &sc->sc_arpcom.ac_if;
|
|
|
|
m = leget(buf, len, ifp);
|
1994-07-06 05:36:23 +04:00
|
|
|
if (m == 0)
|
1993-05-13 17:56:20 +04:00
|
|
|
return;
|
1994-02-05 08:06:54 +03:00
|
|
|
|
1994-07-16 01:20:48 +04:00
|
|
|
/* We assume that the header fit entirely in one mbuf. */
|
|
|
|
eh = mtod(m, struct ether_header *);
|
|
|
|
|
1993-05-13 17:56:20 +04:00
|
|
|
#if NBPFILTER > 0
|
|
|
|
/*
|
1994-07-06 05:36:23 +04:00
|
|
|
* Check if there's a BPF listener on this interface.
|
|
|
|
* If so, hand off the raw packet to BPF.
|
1993-05-13 17:56:20 +04:00
|
|
|
*/
|
1994-07-16 01:20:48 +04:00
|
|
|
if (ifp->if_bpf) {
|
|
|
|
bpf_mtap(ifp->if_bpf, m);
|
1994-07-06 05:36:23 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
1994-07-16 01:20:48 +04:00
|
|
|
if ((ifp->if_flags & IFF_PROMISC) &&
|
1994-07-06 05:36:23 +04:00
|
|
|
(eh->ether_dhost[0] & 1) == 0 && /* !mcast and !bcast */
|
|
|
|
bcmp(eh->ether_dhost, sc->sc_arpcom.ac_enaddr,
|
|
|
|
sizeof(eh->ether_dhost)) != 0) {
|
|
|
|
m_freem(m);
|
1993-05-13 17:56:20 +04:00
|
|
|
return;
|
1994-07-06 05:36:23 +04:00
|
|
|
}
|
1993-05-13 17:56:20 +04:00
|
|
|
}
|
|
|
|
#endif
|
1994-02-05 08:06:54 +03:00
|
|
|
|
1994-07-16 01:20:48 +04:00
|
|
|
/* We assume that the header fit entirely in one mbuf. */
|
|
|
|
m->m_pkthdr.len -= sizeof(*eh);
|
|
|
|
m->m_len -= sizeof(*eh);
|
|
|
|
m->m_data += sizeof(*eh);
|
|
|
|
|
|
|
|
ether_input(ifp, eh, m);
|
1993-05-13 17:56:20 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1994-07-06 05:36:23 +04:00
|
|
|
* Supporting routines
|
1993-05-13 17:56:20 +04:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
1994-07-06 05:36:23 +04:00
|
|
|
* Pull data off an interface.
|
|
|
|
* Len is length of data, with local net header stripped.
|
|
|
|
* We copy the data into mbufs. When full cluster sized units are present
|
|
|
|
* we copy into clusters.
|
1993-05-13 17:56:20 +04:00
|
|
|
*/
|
|
|
|
struct mbuf *
|
1994-07-06 05:36:23 +04:00
|
|
|
leget(buf, totlen, ifp)
|
|
|
|
u_char *buf;
|
|
|
|
int totlen;
|
1993-05-13 17:56:20 +04:00
|
|
|
struct ifnet *ifp;
|
|
|
|
{
|
1994-07-16 01:20:48 +04:00
|
|
|
struct mbuf *top, **mp, *m;
|
1994-07-06 05:36:23 +04:00
|
|
|
int len;
|
1993-05-13 17:56:20 +04:00
|
|
|
|
|
|
|
MGETHDR(m, M_DONTWAIT, MT_DATA);
|
|
|
|
if (m == 0)
|
1994-07-06 05:36:23 +04:00
|
|
|
return 0;
|
1993-05-13 17:56:20 +04:00
|
|
|
m->m_pkthdr.rcvif = ifp;
|
|
|
|
m->m_pkthdr.len = totlen;
|
1994-07-16 01:20:48 +04:00
|
|
|
len = MHLEN;
|
1994-07-06 05:36:23 +04:00
|
|
|
top = 0;
|
|
|
|
mp = ⊤
|
1993-05-13 17:56:20 +04:00
|
|
|
|
|
|
|
while (totlen > 0) {
|
|
|
|
if (top) {
|
|
|
|
MGET(m, M_DONTWAIT, MT_DATA);
|
|
|
|
if (m == 0) {
|
|
|
|
m_freem(top);
|
1994-07-06 05:36:23 +04:00
|
|
|
return 0;
|
1993-05-13 17:56:20 +04:00
|
|
|
}
|
1994-07-16 01:20:48 +04:00
|
|
|
len = MLEN;
|
1993-05-13 17:56:20 +04:00
|
|
|
}
|
1994-07-16 01:20:48 +04:00
|
|
|
if (totlen >= MINCLSIZE) {
|
1993-05-13 17:56:20 +04:00
|
|
|
MCLGET(m, M_DONTWAIT);
|
|
|
|
if (m->m_flags & M_EXT)
|
1994-07-16 01:20:48 +04:00
|
|
|
len = MCLBYTES;
|
1993-05-13 17:56:20 +04:00
|
|
|
}
|
1994-07-16 01:20:48 +04:00
|
|
|
m->m_len = len = min(totlen, len);
|
|
|
|
bcopy((caddr_t)buf, mtod(m, caddr_t), len);
|
|
|
|
buf += len;
|
|
|
|
totlen -= len;
|
1993-05-13 17:56:20 +04:00
|
|
|
*mp = m;
|
|
|
|
mp = &m->m_next;
|
|
|
|
}
|
1994-07-06 05:36:23 +04:00
|
|
|
|
|
|
|
return top;
|
1993-05-13 17:56:20 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Process an ioctl request.
|
|
|
|
*/
|
1994-02-06 03:46:02 +03:00
|
|
|
int
|
1993-05-13 17:56:20 +04:00
|
|
|
leioctl(ifp, cmd, data)
|
|
|
|
register struct ifnet *ifp;
|
1995-04-20 02:16:30 +04:00
|
|
|
u_long cmd;
|
1993-05-13 17:56:20 +04:00
|
|
|
caddr_t data;
|
|
|
|
{
|
1994-02-05 08:06:54 +03:00
|
|
|
struct le_softc *sc = &le_softc[ifp->if_unit];
|
1994-07-06 05:36:23 +04:00
|
|
|
struct ifaddr *ifa = (struct ifaddr *)data;
|
|
|
|
struct ifreq *ifr = (struct ifreq *)data;
|
|
|
|
int s, error = 0;
|
|
|
|
|
|
|
|
s = splimp();
|
1993-05-13 17:56:20 +04:00
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
|
|
|
|
case SIOCSIFADDR:
|
|
|
|
ifp->if_flags |= IFF_UP;
|
1994-07-06 05:36:23 +04:00
|
|
|
|
1993-05-13 17:56:20 +04:00
|
|
|
switch (ifa->ifa_addr->sa_family) {
|
|
|
|
#ifdef INET
|
|
|
|
case AF_INET:
|
1995-04-20 02:16:30 +04:00
|
|
|
leinit(sc);
|
|
|
|
arp_ifinit(&sc->sc_arpcom, ifa);
|
1993-05-13 17:56:20 +04:00
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
#ifdef NS
|
1994-07-06 05:36:23 +04:00
|
|
|
/* XXX - This code is probably wrong. */
|
1993-05-13 17:56:20 +04:00
|
|
|
case AF_NS:
|
|
|
|
{
|
1994-07-06 05:36:23 +04:00
|
|
|
register struct ns_addr *ina = &IA_SNS(ifa)->sns_addr;
|
1993-05-13 17:56:20 +04:00
|
|
|
|
|
|
|
if (ns_nullhost(*ina))
|
1994-07-06 05:36:23 +04:00
|
|
|
ina->x_host =
|
|
|
|
*(union ns_host *)(sc->sc_arpcom.ac_enaddr);
|
|
|
|
else
|
|
|
|
bcopy(ina->x_host.c_host,
|
|
|
|
sc->sc_arpcom.ac_enaddr,
|
|
|
|
sizeof(sc->sc_arpcom.ac_enaddr));
|
|
|
|
/* Set new address. */
|
|
|
|
leinit(sc);
|
1993-05-13 17:56:20 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
default:
|
1994-07-06 05:36:23 +04:00
|
|
|
leinit(sc);
|
1993-05-13 17:56:20 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SIOCSIFFLAGS:
|
|
|
|
/*
|
1994-07-06 05:36:23 +04:00
|
|
|
* If interface is marked down and it is running, then stop it
|
1993-05-13 17:56:20 +04:00
|
|
|
*/
|
1994-07-06 05:36:23 +04:00
|
|
|
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.
|
|
|
|
*/
|
|
|
|
lestop(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.
|
|
|
|
*/
|
|
|
|
leinit(sc);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Reset the interface to pick up changes in any other
|
|
|
|
* flags that affect hardware registers.
|
|
|
|
*/
|
|
|
|
/*lestop(sc);*/
|
|
|
|
leinit(sc);
|
1993-05-13 17:56:20 +04:00
|
|
|
}
|
1994-07-06 05:36:23 +04:00
|
|
|
#ifdef LEDEBUG
|
|
|
|
if (ifp->if_flags & IFF_DEBUG)
|
|
|
|
sc->sc_debug = 1;
|
|
|
|
else
|
|
|
|
sc->sc_debug = 0;
|
|
|
|
#endif
|
1993-05-13 17:56:20 +04:00
|
|
|
break;
|
|
|
|
|
1994-02-05 08:06:54 +03:00
|
|
|
case SIOCADDMULTI:
|
|
|
|
case SIOCDELMULTI:
|
1994-07-06 05:36:23 +04:00
|
|
|
error = (cmd == SIOCADDMULTI) ?
|
|
|
|
ether_addmulti(ifr, &sc->sc_arpcom):
|
|
|
|
ether_delmulti(ifr, &sc->sc_arpcom);
|
|
|
|
|
1994-02-05 08:06:54 +03:00
|
|
|
if (error == ENETRESET) {
|
|
|
|
/*
|
1994-07-06 05:36:23 +04:00
|
|
|
* Multicast list has changed; set the hardware filter
|
|
|
|
* accordingly.
|
1994-02-05 08:06:54 +03:00
|
|
|
*/
|
1994-07-06 05:36:23 +04:00
|
|
|
leinit(sc);
|
1994-02-05 08:06:54 +03:00
|
|
|
error = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
1993-05-13 17:56:20 +04:00
|
|
|
default:
|
|
|
|
error = EINVAL;
|
|
|
|
}
|
1994-07-06 05:36:23 +04:00
|
|
|
(void) splx(s);
|
|
|
|
return error;
|
1993-05-13 17:56:20 +04:00
|
|
|
}
|
|
|
|
|
1994-07-06 05:36:23 +04:00
|
|
|
#ifdef LEDEBUG
|
1994-02-06 03:46:02 +03:00
|
|
|
void
|
1994-07-06 05:36:23 +04:00
|
|
|
recv_print(sc, no)
|
|
|
|
struct le_softc *sc;
|
|
|
|
int no;
|
1993-05-13 17:56:20 +04:00
|
|
|
{
|
1994-07-06 05:36:23 +04:00
|
|
|
struct mds *rmd;
|
|
|
|
int i, printed = 0;
|
|
|
|
u_short len;
|
|
|
|
|
|
|
|
rmd = &sc->sc_rd[no];
|
|
|
|
len = rmd->mcnt;
|
|
|
|
printf("%s: receive buffer %d, len = %d\n", sc->sc_dev.dv_xname, no,
|
|
|
|
len);
|
|
|
|
printf("%s: status %x\n", sc->sc_dev.dv_xname, lerdcsr(sc, 0));
|
|
|
|
for (i = 0; i < len; i++) {
|
|
|
|
if (!printed) {
|
|
|
|
printed = 1;
|
|
|
|
printf("%s: data: ", sc->sc_dev.dv_xname);
|
|
|
|
}
|
|
|
|
printf("%x ", *(sc->sc_rbuf + (BUFSIZE*no) + i));
|
|
|
|
}
|
|
|
|
if (printed)
|
|
|
|
printf("\n");
|
1993-05-13 17:56:20 +04:00
|
|
|
}
|
1994-07-06 05:36:23 +04:00
|
|
|
|
1994-02-06 03:46:02 +03:00
|
|
|
void
|
1994-07-06 05:36:23 +04:00
|
|
|
xmit_print(sc, no)
|
|
|
|
struct le_softc *sc;
|
|
|
|
int no;
|
1993-05-13 17:56:20 +04:00
|
|
|
{
|
1994-07-06 05:36:23 +04:00
|
|
|
struct mds *rmd;
|
|
|
|
int i, printed=0;
|
|
|
|
u_short len;
|
|
|
|
|
|
|
|
rmd = &sc->sc_td[no];
|
|
|
|
len = -rmd->bcnt;
|
|
|
|
printf("%s: transmit buffer %d, len = %d\n", sc->sc_dev.dv_xname, no,
|
|
|
|
len);
|
|
|
|
printf("%s: status %x\n", sc->sc_dev.dv_xname, lerdcsr(sc, 0));
|
|
|
|
printf("%s: addr %x, flags %x, bcnt %x, mcnt %x\n",
|
|
|
|
sc->sc_dev.dv_xname, rmd->addr, rmd->flags, rmd->bcnt, rmd->mcnt);
|
|
|
|
for (i = 0; i < len; i++) {
|
|
|
|
if (!printed) {
|
|
|
|
printed = 1;
|
|
|
|
printf("%s: data: ", sc->sc_dev.dv_xname);
|
|
|
|
}
|
|
|
|
printf("%x ", *(sc->sc_tbuf + (BUFSIZE*no) + i));
|
|
|
|
}
|
|
|
|
if (printed)
|
|
|
|
printf("\n");
|
1993-05-13 17:56:20 +04:00
|
|
|
}
|
1994-07-06 05:36:23 +04:00
|
|
|
#endif /* LEDEBUG */
|
1993-05-13 17:56:20 +04:00
|
|
|
|
1994-07-06 05:36:23 +04:00
|
|
|
/*
|
|
|
|
* Set up the logical address filter.
|
|
|
|
*/
|
1994-02-06 03:46:02 +03:00
|
|
|
void
|
1994-07-06 05:36:23 +04:00
|
|
|
lesetladrf(ac, af)
|
|
|
|
struct arpcom *ac;
|
|
|
|
u_long *af;
|
1993-05-13 17:56:20 +04:00
|
|
|
{
|
1994-07-06 05:36:23 +04:00
|
|
|
struct ifnet *ifp = &ac->ac_if;
|
|
|
|
struct ether_multi *enm;
|
|
|
|
register u_char *cp, c;
|
|
|
|
register u_long crc;
|
|
|
|
register int i, len;
|
|
|
|
struct ether_multistep step;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set up multicast address filter by passing all multicast addresses
|
|
|
|
* through a crc generator, and then using the high order 6 bits as an
|
|
|
|
* index into the 64 bit logical address filter. The high order bit
|
|
|
|
* selects the word, while the rest of the bits select the bit within
|
|
|
|
* the word.
|
|
|
|
*/
|
1993-05-13 17:56:20 +04:00
|
|
|
|
1994-07-06 05:36:23 +04:00
|
|
|
if (ifp->if_flags & IFF_PROMISC) {
|
|
|
|
ifp->if_flags |= IFF_ALLMULTI;
|
|
|
|
af[0] = af[1] = 0xffffffff;
|
1993-05-13 17:56:20 +04:00
|
|
|
return;
|
1994-07-06 05:36:23 +04:00
|
|
|
}
|
1993-05-13 17:56:20 +04:00
|
|
|
|
1994-07-06 05:36:23 +04:00
|
|
|
af[0] = af[1] = 0;
|
|
|
|
ETHER_FIRST_MULTI(step, ac, enm);
|
|
|
|
while (enm != NULL) {
|
|
|
|
if (bcmp(enm->enm_addrlo, enm->enm_addrhi,
|
|
|
|
sizeof(enm->enm_addrlo)) != 0) {
|
|
|
|
/*
|
|
|
|
* We must listen to a range of multicast addresses.
|
|
|
|
* For now, just accept all multicasts, rather than
|
|
|
|
* trying to set only those filter bits needed to match
|
|
|
|
* the range. (At this time, the only use of address
|
|
|
|
* ranges is for IP multicast routing, for which the
|
|
|
|
* range is big enough to require all bits set.)
|
|
|
|
*/
|
|
|
|
ifp->if_flags |= IFF_ALLMULTI;
|
|
|
|
af[0] = af[1] = 0xffffffff;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
cp = enm->enm_addrlo;
|
|
|
|
crc = 0xffffffff;
|
|
|
|
for (len = sizeof(enm->enm_addrlo); --len >= 0;) {
|
|
|
|
c = *cp++;
|
|
|
|
for (i = 8; --i >= 0;) {
|
|
|
|
if ((crc & 0x01) ^ (c & 0x01)) {
|
|
|
|
crc >>= 1;
|
|
|
|
crc ^= 0x6db88320 | 0x80000000;
|
|
|
|
} else
|
|
|
|
crc >>= 1;
|
|
|
|
c >>= 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Just want the 6 most significant bits. */
|
|
|
|
crc >>= 26;
|
|
|
|
|
|
|
|
/* Turn on the corresponding bit in the filter. */
|
1994-09-30 00:24:12 +03:00
|
|
|
af[crc >> 5] |= 1 << ((crc & 0x1f) ^ 16);
|
1994-07-06 05:36:23 +04:00
|
|
|
|
|
|
|
ETHER_NEXT_MULTI(step, enm);
|
|
|
|
}
|
|
|
|
ifp->if_flags &= ~IFF_ALLMULTI;
|
1993-05-13 17:56:20 +04:00
|
|
|
}
|
1994-07-07 05:31:00 +04:00
|
|
|
|
|
|
|
#endif /* NLE > 0 */
|