summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordg <dg@FreeBSD.org>1994-08-12 06:51:12 +0000
committerdg <dg@FreeBSD.org>1994-08-12 06:51:12 +0000
commitcc490038a0f6ece1aa559fbd7f0f2ff39eecbf5a (patch)
treec656aff1523b520b6a5e7f3897ad11ef631a40bc
parent0bf5c587d2b0703e87888f837c671426497a4ca1 (diff)
downloadFreeBSD-src-cc490038a0f6ece1aa559fbd7f0f2ff39eecbf5a.zip
FreeBSD-src-cc490038a0f6ece1aa559fbd7f0f2ff39eecbf5a.tar.gz
New ethernet device driver from Matt Thomas:
This driver supports all the DEC EtherWORKS III NICs (DE203, DE204, and DE205) and the later DEC EtherWORKS II NICs (DE200, DE201, DE202, DE422). DEPCA-style boards prior to the DE200 have not been tested and may not work. Submitted by: Matt Thomas (thomas@lkg.dec.com)
-rw-r--r--sys/conf/files.i3863
-rw-r--r--sys/i386/conf/files.i3863
-rw-r--r--sys/i386/isa/ic/am7990.h109
-rw-r--r--sys/i386/isa/ic/lemac.h177
-rw-r--r--sys/i386/isa/if_le.c2144
5 files changed, 2434 insertions, 2 deletions
diff --git a/sys/conf/files.i386 b/sys/conf/files.i386
index f70e799..b7a8604 100644
--- a/sys/conf/files.i386
+++ b/sys/conf/files.i386
@@ -1,7 +1,7 @@
# This file tells config what files go into building a kernel,
# files marked standard are always included.
#
-# $Id: files.i386,v 1.32 1994/04/29 21:49:02 gclarkii Exp $
+# $Id: files.i386,v 1.34 1994/05/25 08:52:02 rgrimes Exp $
#
i386/i386/autoconf.c standard device-driver
i386/i386/cons.c standard
@@ -31,6 +31,7 @@ i386/isa/if_ep.c optional ep device-driver
i386/isa/if_ie.c optional ie device-driver
i386/isa/if_is.c optional is device-driver
i386/isa/if_ix.c optional ix device-driver
+i386/isa/if_le.c optional le device-driver
i386/isa/isa.c optional isa device-driver
i386/isa/lpa.c optional lpa device-driver
i386/isa/lpt.c optional lpt device-driver
diff --git a/sys/i386/conf/files.i386 b/sys/i386/conf/files.i386
index f70e799..b7a8604 100644
--- a/sys/i386/conf/files.i386
+++ b/sys/i386/conf/files.i386
@@ -1,7 +1,7 @@
# This file tells config what files go into building a kernel,
# files marked standard are always included.
#
-# $Id: files.i386,v 1.32 1994/04/29 21:49:02 gclarkii Exp $
+# $Id: files.i386,v 1.34 1994/05/25 08:52:02 rgrimes Exp $
#
i386/i386/autoconf.c standard device-driver
i386/i386/cons.c standard
@@ -31,6 +31,7 @@ i386/isa/if_ep.c optional ep device-driver
i386/isa/if_ie.c optional ie device-driver
i386/isa/if_is.c optional is device-driver
i386/isa/if_ix.c optional ix device-driver
+i386/isa/if_le.c optional le device-driver
i386/isa/isa.c optional isa device-driver
i386/isa/lpa.c optional lpa device-driver
i386/isa/lpt.c optional lpt device-driver
diff --git a/sys/i386/isa/ic/am7990.h b/sys/i386/isa/ic/am7990.h
new file mode 100644
index 0000000..ea8a0e6
--- /dev/null
+++ b/sys/i386/isa/ic/am7990.h
@@ -0,0 +1,109 @@
+/*
+ * AMD 7990 (LANCE) definitions
+ *
+ *
+ */
+
+#if defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN
+#define LN_BITFIELD2(a, b) b, a
+#define LN_BITFIELD3(a, b, c) c, b, a
+#define LN_BITFIELD4(a, b, c, d) d, c, b, a
+#else
+#define LN_BITFIELD2(a, b) a, b
+#define LN_BITFIELD3(a, b, c) a, b, c
+#define LN_BITFIELD4(a, b, c, d) a, b, c, d
+#endif
+
+#define LN_ADDR_LO(addr) ((addr) & 0xFFFF)
+#define LN_ADDR_HI(addr) (((addr) >> 16) & 0xFF)
+
+typedef struct {
+ unsigned short r_addr_lo;
+ unsigned short LN_BITFIELD3(r_addr_hi : 8,
+ : 5,
+ r_log2_size : 3);
+} ln_ring_t;
+
+#define LN_MC_MASK 0x3F /* Only 6 bits of the CRC */
+
+typedef struct {
+ unsigned short ln_mode;
+#define LN_MODE_RXD 0x0001 /* ( W) Receiver Disabled */
+#define LN_MODE_TXD 0x0002 /* ( W) Transmitter Disabled */
+#define LN_MODE_LOOP 0x0004 /* ( W) Enable Loopback */
+#define LN_MODE_NOTXCRC 0x0008 /* ( W) Don't Calculate TX CRCs */
+#define LN_MODE_FRCCOLL 0x0010 /* ( W) Force Collision */
+#define LN_MODE_NORETRY 0x0020 /* ( W) No Transmit Retries */
+#define LN_MODE_INTLOOP 0x0040 /* ( W) Internal Loopback */
+#define LN_MODE_PROMISC 0x8000 /* ( W) Promiscious Mode */
+ unsigned short ln_physaddr[3];
+ unsigned short ln_multi_mask[4];
+ ln_ring_t ln_rxring;
+ ln_ring_t ln_txring;
+} ln_initb_t;
+
+typedef struct {
+ unsigned short d_addr_lo;
+ unsigned char d_addr_hi;
+ unsigned char d_flag;
+#define LN_DFLAG_EOP 0x0001 /* (RW) End Of Packet */
+#define LN_DFLAG_SOP 0x0002 /* (RW) Start Of Packet */
+#define LN_DFLAG_RxBUFERROR 0x0004 /* (R ) Receive - Buffer Error */
+#define LN_DFLAG_TxDEFERRED 0x0004 /* (R ) Transmit - Initially Deferred */
+#define LN_DFLAG_RxBADCRC 0x0008 /* (R ) Receive - Bad Checksum */
+#define LN_DFLAG_TxONECOLL 0x0008 /* (R ) Transmit - Single Collision */
+#define LN_DFLAG_RxOVERFLOW 0x0010 /* (R ) Receive - Overflow Error */
+#define LN_DFLAG_TxMULTCOLL 0x0010 /* (R ) Transmit - Multiple Collisions */
+#define LN_DFLAG_RxFRAMING 0x0020 /* (R ) Receive - Framing Error */
+#define LN_DFLAG_RxERRSUM 0x0040 /* (R ) Receive - Error Summary */
+#define LN_DFLAG_TxERRSUM 0x0040 /* (R ) Transmit - Error Summary */
+#define LN_DFLAG_OWNER 0x0080 /* (RW) Owner (1=Am7990, 0=host) */
+ signed short d_buflen; /* ( W) Two's complement */
+ unsigned short d_status;
+#define LN_DSTS_RxLENMASK 0x0FFF /* (R ) Recieve Length */
+#define LN_DSTS_TxTDRMASK 0x03FF /* (R ) Transmit - Time Domain Reflectometer */
+#define LN_DSTS_TxEXCCOLL 0x0400 /* (R ) Transmit - Excessive Collisions */
+#define LN_DSTS_TxCARRLOSS 0x0800 /* (R ) Transmit - Carrier Loss */
+#define LN_DSTS_TxLATECOLL 0x1000 /* (R ) Transmit - Late Collision */
+#define LN_DSTS_TxUNDERFLOW 0x4000 /* (R ) Transmit - Underflow */
+#define LN_DSTS_TxBUFERROR 0x8000 /* (R ) Transmit - Buffer Error */
+} ln_desc_t;
+
+
+
+
+#define LN_CSR0 0x0000
+
+#define LN_CSR0_INIT 0x0001 /* (RS) Initialize Am 7990 */
+#define LN_CSR0_START 0x0002 /* (RS) Start Am7990 */
+#define LN_CSR0_STOP 0x0004 /* (RS) Reset Am7990 */
+#define LN_CSR0_TXDEMAND 0x0008 /* (RS) Transmit On Demand */
+#define LN_CSR0_TXON 0x0010 /* (R ) Transmitter Enabled */
+#define LN_CSR0_RXON 0x0020 /* (R ) Receiver Enabled */
+#define LN_CSR0_ENABINTR 0x0040 /* (RW) Interrupt Enabled */
+#define LN_CSR0_PENDINTR 0x0080 /* (R ) Interrupt Pending */
+#define LN_CSR0_INITDONE 0x0100 /* (RC) Initialization Done */
+#define LN_CSR0_TXINT 0x0200 /* (RC) Transmit Interrupt */
+#define LN_CSR0_RXINT 0x0400 /* (RC) Receive Interrupt */
+#define LN_CSR0_MEMERROR 0x0800 /* (RC) Memory Error */
+#define LN_CSR0_MISS 0x1000 /* (RC) No Available Receive Buffers */
+#define LN_CSR0_CERR 0x2000 /* (RC) SQE failed */
+#define LN_CSR0_BABL 0x4000 /* (RC) Transmit Babble */
+#define LN_CSR0_ERRSUM 0x8000 /* (R ) Error Summary (last 4) */
+#define LN_CSR0_CLEAR 0x7F00 /* Clear Status Bit */
+
+/*
+ * CSR1 -- Init Block Address (Low 16 Bits -- Must be Word Aligned)
+ * CSR2 -- Init Block Address (High 8 Bits)
+ */
+#define LN_CSR1 0x0001
+#define LN_CSR2 0x0002
+
+/*
+ * CSR3 -- Hardware Control
+ */
+
+#define LN_CSR3 0x0003
+#define LN_CSR3_BCON 0x0001 /* (RW) BM/HOLD Control */
+#define LN_CSR3_ALE 0x0002 /* (RW) ALE Control */
+#define LN_CSR3_BSWP 0x0004 /* (RW) Byte Swap */
diff --git a/sys/i386/isa/ic/lemac.h b/sys/i386/isa/ic/lemac.h
new file mode 100644
index 0000000..2c919d8
--- /dev/null
+++ b/sys/i386/isa/ic/lemac.h
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 1994 Matt Thomas (thomas@lkg.dec.com)
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software withough 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.
+ *
+ * $Id: lemac.h,v 1.1 1994/08/01 16:03:42 thomas Exp $
+ */
+#ifndef _LEMAC_H_
+#define _LEMAC_H_
+
+/*
+ * This is list of registers used on a DEC EtherWORKS III card.
+ * Each board occupies a 32 byte register space. This can be
+ * in either EISA or ISA space. Currently we only support ISA
+ * space.
+ */
+
+#define LEMAC_REG_CS 0x00 /* Control and Status */
+#define LEMAC_REG_CTL 0x01 /* Control */
+#define LEMAC_REG_IC 0x02 /* Interrupt Control */
+#define LEMAC_REG_TS 0x03 /* Transmit Status */
+#define LEMAC_REG_RSVD1 0x04 /* Reserved (not used) */
+#define LEMAC_REG_RSVD2 0x05 /* Reserved (not used) */
+#define LEMAC_REG_FMQ 0x06 /* Free Memory Queue */
+#define LEMAC_REG_FMC 0x07 /* Free Memory Queue Count */
+#define LEMAC_REG_RQ 0x08 /* Receive Queue */
+#define LEMAC_REG_RQC 0x09 /* Receive Queue Count */
+#define LEMAC_REG_TQ 0x0A /* Transmit Queue */
+#define LEMAC_REG_TQC 0x0B /* Transmit Queue Count */
+#define LEMAC_REG_TDQ 0x0C /* Transmit Done Queue */
+#define LEMAC_REG_TDC 0x0D /* Transmit Done Queue Count */
+#define LEMAC_REG_PI1 0x0E /* Page Index #1 */
+#define LEMAC_REG_PI2 0x0F /* Page Index #2 */
+#define LEMAC_REG_DAT 0x10 /* Data */
+#define LEMAC_REG_IOP 0x11 /* I/O Page */
+#define LEMAC_REG_IOB 0x12 /* I/O Base */
+#define LEMAC_REG_MPN 0x13 /* Memory Page */
+#define LEMAC_REG_MBR 0x14 /* Memory Base */
+#define LEMAC_REG_APD 0x15 /* Address PROM */
+#define LEMAC_REG_EE1 0x16 /* EEPROM Data #1 */
+#define LEMAC_REG_EE2 0x17 /* EEPROM Data #2 */
+#define LEMAC_REG_PA0 0x18 /* Physical Address (Byte 0) */
+#define LEMAC_REG_PA1 0x19 /* Physical Address (Byte 1) */
+#define LEMAC_REG_PA2 0x1A /* Physical Address (Byte 2) */
+#define LEMAC_REG_PA3 0x1B /* Physical Address (Byte 3) */
+#define LEMAC_REG_PA4 0x1C /* Physical Address (Byte 4) */
+#define LEMAC_REG_PA5 0x1D /* Physical Address (Byte 5) */
+#define LEMAC_REG_CNF 0x1E /* Configuration Management */
+#define LEMAC_IOSPACE 0x20 /* LEMAC uses 32 bytes of IOSPACE */
+
+
+#define LEMAC_REG_EID0 0x80 /* EISA Identification 0 */
+#define LEMAC_REG_EID1 0x81 /* EISA Identification 1 */
+#define LEMAC_REG_EID2 0x82 /* EISA Identification 2 */
+#define LEMAC_REG_EID3 0x83 /* EISA Identification 3 */
+#define LEMAC_REG_EIC 0x84 /* EISA Control */
+
+/* Control Page (Page 0) Definitions */
+
+#define LEMAC_MCTBL_BITS 9
+#define LEMAC_MCTBL_OFF 512
+#define LEMAC_MCTBL_SIZE (1 << (LEMAC_MCTBL_BITS - 3))
+#define LEMAC_CRC32_POLY 0xEDB88320UL /* CRC-32 Poly -- Little Endian) */
+
+/* EEPROM Definitions */
+
+#define LEMAC_EEP_CKSUM 0 /* The valid checksum is 0 */
+#define LEMAC_EEP_SIZE 32 /* EEPROM is 32 bytes */
+#define LEMAC_EEP_DELAY 2000 /* 2ms = 2000us */
+#define LEMAC_EEP_PRDNM 8 /* Product Name Offset */
+#define LEMAC_EEP_PRDNMSZ 8 /* Product Name Size */
+#define LEMAC_EEP_SWFLAGS 16 /* Software Options Offset */
+#define LEMAC_EEP_SETUP 23 /* Setup Options Offset */
+
+#define LEMAC_EEP_SW_SQE 0x10 /* Enable TX_SQE on Transmits */
+#define LEMAC_EEP_SW_LAB 0x08 /* Enable TX_LAB on Transmits */
+#define LEMAC_EEP_ST_DRAM 0x02 /* Enable extra DRAM */
+
+#define LEMAC_ADP_ROMSZ 32 /* Size of Address PROM */
+
+/* Receive Status Definitions */
+
+#define LEMAC_RX_PLL 0x01 /* Phase Lock Lost */
+#define LEMAC_RX_CRC 0x02 /* CRC Error */
+#define LEMAC_RX_DBE 0x04 /* Dribble Bit Error */
+#define LEMAC_RX_MCM 0x08 /* Multicast Match */
+#define LEMAC_RX_IAM 0x10 /* Individual Address Match */
+#define LEMAC_RX_OK 0x80 /* No Errors */
+
+/* Transmit Status Definitions (not valid if TXD == 0) */
+
+#define LEMAC_TS_RTRYMSK 0x0F /* Retries of last TX PDU */
+#define LEMAC_TS_ECL 0x10 /* Excessive collision of ... */
+#define LEMAC_TS_LCL 0x20 /* Late collision of ... */
+#define LEMAC_TS_ID 0x40 /* Initially Deferred ... */
+
+/* Transmit Control Definitions */
+
+#define LEMAC_TX_ISA 0x01 /* Insert Source Address (no) */
+#define LEMAC_TX_IFC 0x02 /* Insert Frame Check (yes) */
+#define LEMAC_TX_PAD 0x04 /* Zero PAD to mininum length (yes) */
+#define LEMAC_TX_LAB 0x08 /* Less Agressive Backoff (no) */
+#define LEMAC_TX_QMD 0x10 /* Q-Mode (yes) */
+#define LEMAC_TX_STP 0x20 /* Stop on Error (yes) */
+#define LEMAC_TX_SQE 0x40 /* SQE Enable (yes) */
+
+#define LEMAC_TX_FLAGS (LEMAC_TX_IFC|LEMAC_TX_PAD|LEMAC_TX_QMD|\
+ LEMAC_TX_STP|LEMAC_TX_SQE)
+#define LEMAC_TX_HDRSZ 4 /* Size of TX header */
+
+/* Transmit Done Queue Status Definitions */
+
+#define LEMAC_TDQ_COL 0x03 /* Collision Mask */
+#define LEMAC_TDQ_NOCOL 0x00 /* No Collisions */
+#define LEMAC_TDQ_ONECOL 0x01 /* One Collision */
+#define LEMAC_TDQ_MULCOL 0x02 /* Multiple Collisions */
+#define LEMAC_TDQ_EXCCOL 0x03 /* Excesive Collisions */
+#define LEMAC_TDQ_ID 0x04 /* Initially Deferred */
+#define LEMAC_TDQ_LCL 0x08 /* Late Collision (will TX_STP) */
+
+/* Control / Status Definitions */
+
+#define LEMAC_CS_RXD 0x01 /* Receiver Disabled */
+#define LEMAC_CS_TXD 0x02 /* Transmitter Disabled */
+#define LEMAC_CS_RNE 0x04 /* Receive Queue Not Empty */
+#define LEMAC_CS_TNE 0x08 /* Transmit Done Queue Not Empty */
+#define LEMAC_CS_MBZ4 0x10 /* MBZ */
+#define LEMAC_CS_MCE 0x20 /* Multicast Enable */
+#define LEMAC_CS_PME 0x40 /* Promiscuous Mode Enable */
+#define LEMAC_CS_RA 0x80 /* Runt Accept */
+
+/* Control Definitions */
+
+#define LEMAC_CTL_LED 0x02 /* LED state (inverted) */
+
+/* Interrupt Control Definitions */
+
+#define LEMAC_IC_RXD 0x01 /* Enable RXD Interrupt */
+#define LEMAC_IC_TXD 0x02 /* Enable TXD Interrupt */
+#define LEMAC_IC_RNE 0x04 /* Enable RNE Interrupt */
+#define LEMAC_IC_TNE 0x08 /* Enable TNE Interrupt */
+#define LEMAC_IC_ALL 0x0F /* Enable RXD,TXD,RNE,TNE */
+#define LEMAC_IC_IRQMSK 0x60 /* Interrupt Select */
+#define LEMAC_IC_IRQ5 0x00 /* Select IRQ 5 */
+#define LEMAC_IC_IRQ10 0x20 /* Select IRQ 10 */
+#define LEMAC_IC_IRQ11 0x40 /* Select IRQ 11 */
+#define LEMAC_IC_IRQ15 0x60 /* Select IRQ 15 */
+#define LEMAC_IC_IE 0x80 /* Interrupt Enable */
+
+/* I/O Page Definitions */
+
+#define LEMAC_IOP_EEINIT 0xC0 /* Perform a board init/reset */
+#define LEMAC_IOP_EEREAD 0xE0 /* Start a read from EEPROM */
+
+/* Configuration / Management Definitions */
+
+#define LEMAC_CNF_DRAM 0x02 /* Extra on-board DRAM is available */
+
+#endif /* _LEMAC_H_ */
diff --git a/sys/i386/isa/if_le.c b/sys/i386/isa/if_le.c
new file mode 100644
index 0000000..e604e05
--- /dev/null
+++ b/sys/i386/isa/if_le.c
@@ -0,0 +1,2144 @@
+/*-
+ * Copyright (c) 1994 Matt Thomas (thomas@lkg.dec.com)
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software withough 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.
+ *
+ * $Id: if_le.c,v 1.8 1994/08/05 20:20:54 thomas Exp $
+ *
+ * $Log: if_le.c,v $
+ * Revision 1.8 1994/08/05 20:20:54 thomas
+ * Enable change log
+ *
+ * Revision 1.7 1994/08/05 20:20:14 thomas
+ * *** empty log message ***
+ *
+ */
+
+/*
+ * DEC EtherWORKS 2 Ethernet Controllers
+ * DEC EtherWORKS 3 Ethernet Controllers
+ *
+ * Written by Matt Thomas
+ * BPF support code stolen directly from if_ec.c
+ *
+ * This driver supports the DEPCA, DE100, DE101, DE200, DE201,
+ * DE2002, DE203, DE204, DE205, and DE422 cards.
+ */
+
+#include "le.h"
+#if NLE > 0
+
+#include "param.h"
+#include "systm.h"
+#include "mbuf.h"
+#include "protosw.h"
+#include "socket.h"
+#include "ioctl.h"
+#include "errno.h"
+#include "malloc.h"
+#include "syslog.h"
+
+#include "net/if.h"
+#include "net/if_types.h"
+#include "net/if_dl.h"
+#include "net/route.h"
+
+#include "bpfilter.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 "i386/isa/isa.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/icu.h"
+
+#include "vm/vm.h"
+
+#if NBPFILTER > 0
+#include "net/bpf.h"
+#include "net/bpfdesc.h"
+#endif
+
+/* Forward declarations */
+typedef struct le_softc le_softc_t;
+typedef struct le_board le_board_t;
+
+typedef u_short le_mcbits_t;
+#define LE_MC_NBPW_LOG2 4
+#define LE_MC_NBPW (1 << LE_MC_NBPW_LOG2)
+
+#if !defined(LE_NOLEMAC)
+/*
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ *
+ * Start of DEC EtherWORKS III (LEMAC) dependent structures
+ *
+ */
+#include "i386/isa/ic/lemac.h" /* Include LEMAC definitions */
+
+static int lemac_probe(le_softc_t *sc, const le_board_t *bd, int *msize);
+
+struct le_lemac_info {
+ u_int lemac__lastpage; /* last 2K page */
+ u_int lemac__memmode; /* Are we in 2K, 32K, or 64K mode */
+ u_int lemac__membase; /* Physical address of start of RAM */
+ u_int lemac__txctl; /* Transmit Control Byte */
+ u_int lemac__txmax; /* Maximum # of outstanding transmits */
+ le_mcbits_t lemac__mctbl[LEMAC_MCTBL_SIZE/sizeof(le_mcbits_t)];
+ /* local copy of multicast table */
+ u_char lemac__eeprom[LEMAC_EEP_SIZE]; /* local copy eeprom */
+ char lemac__prodname[LEMAC_EEP_PRDNMSZ+1]; /* prodname name */
+#define lemac_lastpage le_un.un_lemac.lemac__lastpage
+#define lemac_memmode le_un.un_lemac.lemac__memmode
+#define lemac_membase le_un.un_lemac.lemac__membase
+#define lemac_txctl le_un.un_lemac.lemac__txctl
+#define lemac_txmax le_un.un_lemac.lemac__txmax
+#define lemac_mctbl le_un.un_lemac.lemac__mctbl
+#define lemac_eeprom le_un.un_lemac.lemac__eeprom
+#define lemac_prodname le_un.un_lemac.lemac__prodname
+};
+#endif /* !defined(LE_NOLEMAC) */
+
+#if !defined(LE_NOLANCE)
+/*
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ *
+ * Start of DEC EtherWORKS II (LANCE) dependent structures
+ *
+ */
+
+#include "i386/isa/ic/am7990.h"
+
+#ifndef LN_DOSTATS
+#define LN_DOSTATS 1
+#endif
+
+static int depca_probe(le_softc_t *sc, const le_board_t *bd, int *msize);
+
+typedef struct lance_descinfo lance_descinfo_t;
+typedef struct lance_ring lance_ring_t;
+
+typedef unsigned lance_addr_t;
+
+struct lance_descinfo {
+ caddr_t di_addr; /* address of descriptor */
+ lance_addr_t di_bufaddr; /* LANCE address of buffer owned by descriptor */
+ unsigned di_buflen; /* size of buffer owned by descriptor */
+ struct mbuf *di_mbuf; /* mbuf being transmitted/received */
+};
+
+struct lance_ring {
+ lance_descinfo_t *ri_first; /* Pointer to first descriptor in ring */
+ lance_descinfo_t *ri_last; /* Pointer to last + 1 descriptor in ring */
+ lance_descinfo_t *ri_nextin; /* Pointer to next one to be given to HOST */
+ lance_descinfo_t *ri_nextout; /* Pointer to next one to be given to LANCE */
+ unsigned ri_max; /* Size of Ring - 1 */
+ unsigned ri_free; /* Number of free rings entires (owned by HOST) */
+ lance_addr_t ri_heap; /* Start of RAM for this ring */
+ lance_addr_t ri_heapend; /* End + 1 of RAM for this ring */
+ lance_addr_t ri_outptr; /* Pointer to first output byte */
+ unsigned ri_outsize; /* Space remaining for output */
+};
+
+struct le_lance_info {
+ unsigned lance__csr1; /* LANCE Address of init block (low 16) */
+ unsigned lance__csr2; /* LANCE Address of init block (high 8) */
+ unsigned lance__csr3; /* Copy of CSR3 */
+ unsigned lance__rap; /* IO Port Offset of RAP */
+ unsigned lance__rdp; /* IO Port Offset of RDP */
+ unsigned lance__ramoffset; /* Offset to valid LANCE RAM */
+ unsigned lance__ramsize; /* Amount of RAM shared by LANCE */
+ unsigned lance__rxbufsize; /* Size of a receive buffer */
+ ln_initb_t lance__initb; /* local copy of LANCE initblock */
+ ln_initb_t *lance__raminitb; /* copy to board's LANCE initblock (debugging) */
+ ln_desc_t *lance__ramdesc; /* copy to board's LANCE descriptors (debugging) */
+ lance_ring_t lance__rxinfo; /* Receive ring information */
+ lance_ring_t lance__txinfo; /* Transmit ring information */
+#define lance_csr1 le_un.un_lance.lance__csr1
+#define lance_csr2 le_un.un_lance.lance__csr2
+#define lance_csr3 le_un.un_lance.lance__csr3
+#define lance_rap le_un.un_lance.lance__rap
+#define lance_rdp le_un.un_lance.lance__rdp
+#define lance_ramoffset le_un.un_lance.lance__ramoffset
+#define lance_ramsize le_un.un_lance.lance__ramsize
+#define lance_rxbufsize le_un.un_lance.lance__rxbufsize
+#define lance_initb le_un.un_lance.lance__initb
+#define lance_raminitb le_un.un_lance.lance__raminitb
+#define lance_ramdesc le_un.un_lance.lance__ramdesc
+#define lance_rxinfo le_un.un_lance.lance__rxinfo
+#define lance_txinfo le_un.un_lance.lance__txinfo
+};
+#endif /* !defined(LE_NOLANCE) */
+
+/*
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ *
+ * Start of Common Code
+ *
+ */
+
+static void (*le_intrvec[NLE])(le_softc_t *sc);
+
+/*
+ * Ethernet status, per interface.
+ */
+struct le_softc {
+ struct arpcom le_ac; /* Common Ethernet/ARP Structure */
+ caddr_t le_membase; /* Starting memory address (virtual) */
+ unsigned le_iobase; /* Starting I/O base address */
+ unsigned le_irq; /* Interrupt Request Value */
+ unsigned le_flags; /* local copy of if_flags */
+#define LE_BRDCSTONLY 0x01000000 /* If only broadcast is enabled */
+ u_int le_mcmask; /* bit mask for CRC-32 for multicast hash */
+ le_mcbits_t *le_mctbl; /* pointer to multicast table */
+ const char *le_prodname; /* product name DE20x-xx */
+#if NBPFILTER > 0
+ caddr_t le_bpf; /* BPF context */
+#endif
+ u_char le_hwaddr[6]; /* local copy of hwaddr */
+ unsigned le_scast_drops; /* singlecast drops */
+ unsigned le_mcast_drops; /* multicast drops */
+ unsigned le_bcast_drops; /* broadcast drops */
+ union {
+#if !defined(LE_NOLEMAC)
+ struct le_lemac_info un_lemac; /* LEMAC specific information */
+#endif
+#if !defined(LE_NOLANCE)
+ struct le_lance_info un_lance; /* Am7990 specific information */
+#endif
+ } le_un;
+};
+
+static int le_probe(struct isa_device *dvp);
+static int le_attach(struct isa_device *dvp);
+static int le_ioctl(struct ifnet *ifp, int command, caddr_t data);
+extern int le_intr(int unit);
+static void le_input(le_softc_t *sc, caddr_t seg1, size_t total_len,
+ size_t len2, caddr_t seg2);
+static void le_multi_filter(le_softc_t *sc);
+static void le_multi_op(le_softc_t *sc, const u_char *mca, int oper_flg);
+static int le_read_macaddr(le_softc_t *sc, int ioreg, int skippat);
+
+#define LE_CRC32_POLY 0xEDB88320UL /* CRC-32 Poly -- Little Endian */
+
+struct le_board {
+ int (*bd_probe)(le_softc_t *sc, const le_board_t *bd, int *msize);
+};
+
+
+le_softc_t le_softc[NLE];
+
+const le_board_t le_boards[] = {
+#if !defined(LE_NOLEMAC)
+ { lemac_probe }, /* DE20[345] */
+#endif
+#if !defined(LE_NOLANCE)
+ { depca_probe }, /* DE{20[012],422} */
+#endif
+ { NULL } /* Must Be Last! */
+};
+
+/*
+ * This tells the autoconf code how to set us up.
+ */
+struct isa_driver ledriver = {
+ le_probe, le_attach, "le",
+};
+
+unsigned le_intrs[NLE];
+
+#define LE_ADDREQUAL(a1, a2) \
+ (((u_short *)a1)[0] == ((u_short *)a2)[0] \
+ || ((u_short *)a1)[1] == ((u_short *)a2)[1] \
+ || ((u_short *)a1)[2] == ((u_short *)a2)[2])
+#define LE_ADDRBRDCST(a1) \
+ (((u_short *)a1)[0] == 0xFFFFU \
+ || ((u_short *)a1)[1] == 0xFFFFU \
+ || ((u_short *)a1)[2] == 0xFFFFU)
+
+#define LE_INL(sc, reg) \
+({ u_long data; \
+ __asm __volatile("inl %1, %0": "=a" (data): "d" ((u_short)((sc)->le_iobase + (reg)))); \
+ data; })
+
+
+#define LE_OUTL(sc, reg, data) \
+ ({__asm __volatile("outl %0, %1"::"a" ((u_long)(data)), "d" ((u_short)((sc)->le_iobase + (reg))));})
+
+#define LE_INW(sc, reg) \
+({ u_short data; \
+ __asm __volatile("inw %1, %0": "=a" (data): "d" ((u_short)((sc)->le_iobase + (reg)))); \
+ data; })
+
+
+#define LE_OUTW(sc, reg, data) \
+ ({__asm __volatile("outw %0, %1"::"a" ((u_short)(data)), "d" ((u_short)((sc)->le_iobase + (reg))));})
+
+#define LE_INB(sc, reg) \
+({ u_char data; \
+ __asm __volatile("inb %1, %0": "=a" (data): "d" ((u_short)((sc)->le_iobase + (reg)))); \
+ data; })
+
+
+#define LE_OUTB(sc, reg, data) \
+ ({__asm __volatile("outb %0, %1"::"a" ((u_char)(data)), "d" ((u_short)((sc)->le_iobase + (reg))));})
+
+#define LE_IFP(sc) (&(sc)->le_ac.ac_if)
+
+#define MEMCPY(to, from, len) bcopy(from, to, len)
+#define MEMSET(where, what, howmuch) bzero(where, howmuch)
+#define MEMCMP(l, r, len) bcmp(l, r, len)
+
+static int
+le_probe(
+ struct isa_device *dvp)
+{
+ le_softc_t *sc = &le_softc[dvp->id_unit];
+ const le_board_t *bd;
+ int iospace;
+
+ if (dvp->id_unit >= NLE) {
+ printf("%s%d not configured -- too many devices\n",
+ ledriver.name, dvp->id_unit);
+ return 0;
+ }
+
+ sc->le_iobase = dvp->id_iobase;
+ sc->le_membase = (u_char *) dvp->id_maddr;
+ sc->le_irq = dvp->id_irq;
+ LE_IFP(sc)->if_name = ledriver.name;
+ LE_IFP(sc)->if_unit = dvp->id_unit;
+
+ /*
+ * Find and Initialize board..
+ */
+
+ sc->le_flags &= ~(IFF_UP|IFF_ALLMULTI);
+
+ for (bd = le_boards; bd->bd_probe != NULL; bd++) {
+ if ((iospace = (*bd->bd_probe)(sc, bd, &dvp->id_msize)) != 0) {
+ return iospace;
+ }
+ }
+ printf("%s%d: no board found at 0x%x\n",
+ LE_IFP(sc)->if_name, LE_IFP(sc)->if_unit, dvp->id_iobase);
+ return 0;
+}
+
+static int
+le_attach(
+ struct isa_device *dvp)
+{
+ le_softc_t *sc = &le_softc[dvp->id_unit];
+ struct ifnet *ifp = LE_IFP(sc);
+ struct ifaddr *ifa = ifp->if_addrlist;
+
+ ifp->if_mtu = ETHERMTU;
+ printf("%s%d: %s ethernet address %s\n",
+ ifp->if_name, ifp->if_unit,
+ sc->le_prodname,
+ ether_sprintf(sc->le_ac.ac_enaddr));
+
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS;
+#ifdef MULTICAST
+ ifp->if_flags |= IFF_MULTICAST;
+#endif /* MULTICAST */
+
+ ifp->if_output = ether_output;
+ ifp->if_ioctl = le_ioctl;
+ ifp->if_type = IFT_ETHER;
+ ifp->if_addrlen = 6;
+ ifp->if_hdrlen = 14;
+
+#if NBPFILTER > 0
+ bpfattach(&sc->le_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header));
+#endif
+
+ if_attach(ifp);
+
+ while (ifa && ifa->ifa_addr && ifa->ifa_addr->sa_family != AF_LINK)
+ ifa = ifa->ifa_next;
+
+ if (ifa != NULL && ifa->ifa_addr != NULL) {
+ struct sockaddr_dl *sdl;
+ /*
+ * Provide our ether address to the higher layers
+ */
+ sdl = (struct sockaddr_dl *) ifa->ifa_addr;
+ sdl->sdl_type = IFT_ETHER;
+ sdl->sdl_alen = 6;
+ sdl->sdl_slen = 0;
+ MEMCPY(LLADDR(sdl), sc->le_ac.ac_enaddr, 6);
+ }
+ return 1;
+}
+
+int
+le_intr(
+ int unit)
+{
+ int s = splimp();
+
+ le_intrs[unit]++;
+ (*le_intrvec[unit])(&le_softc[unit]);
+
+ splx(s);
+ return unit;
+}
+
+#define LE_XTRA 0
+
+static void
+le_input(
+ le_softc_t *sc,
+ caddr_t seg1,
+ size_t total_len,
+ size_t len1,
+ caddr_t seg2)
+{
+ struct ether_header eh;
+ struct mbuf *m;
+
+ if (total_len - sizeof(eh) > ETHERMTU
+ || total_len - sizeof(eh) < ETHERMIN) {
+ LE_IFP(sc)->if_ierrors++;
+ return;
+ }
+ MEMCPY(&eh, seg1, sizeof(eh));
+ eh.ether_type = ntohs(eh.ether_type);
+
+#if NBPFILTER > 0
+ if (sc->le_bpf != NULL && seg2 == NULL) {
+ bpf_tap(sc->le_bpf, seg1, total_len);
+ /*
+ * If this is single cast but not to us
+ * drop it!
+ */
+ if ((eh.ether_dhost[0] & 1) == 0) {
+ if (!LE_ADDREQUAL(eh.ether_dhost, sc->le_ac.ac_enaddr)) {
+ sc->le_scast_drops++;
+ return;
+ }
+ } else if ((sc->le_flags & IFF_MULTICAST) == 0) {
+ sc->le_mcast_drops++;
+ return;
+ } else if (sc->le_flags & LE_BRDCSTONLY) {
+ if (!LE_ADDRBRDCST(eh.ether_dhost)) {
+ sc->le_bcast_drops++;
+ return;
+ }
+ }
+ }
+#endif
+ seg1 += sizeof(eh); total_len -= sizeof(eh); len1 -= sizeof(eh);
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ LE_IFP(sc)->if_ierrors++;
+ return;
+ }
+ m->m_pkthdr.len = total_len;
+ m->m_pkthdr.rcvif = LE_IFP(sc);
+ if (total_len + LE_XTRA > MHLEN /* >= MINCLSIZE */) {
+ MCLGET(m, M_DONTWAIT);
+ if ((m->m_flags & M_EXT) == 0) {
+ m_free(m);
+ LE_IFP(sc)->if_ierrors++;
+ return;
+ }
+ } else if (total_len + LE_XTRA > MHLEN && MINCLSIZE == (MHLEN+MLEN)) {
+ MGET(m->m_next, M_DONTWAIT, MT_DATA);
+ if (m->m_next == NULL) {
+ m_free(m);
+ LE_IFP(sc)->if_ierrors++;
+ return;
+ }
+ m->m_next->m_len = total_len - MHLEN - LE_XTRA;
+ len1 = total_len = MHLEN - LE_XTRA;
+ MEMCPY(mtod(m->m_next, caddr_t), &seg1[MHLEN-LE_XTRA], m->m_next->m_len);
+ } else if (total_len + LE_XTRA > MHLEN) {
+ panic("le_input: pkt of unknown length");
+ }
+ m->m_data += LE_XTRA;
+ m->m_len = total_len;
+ MEMCPY(mtod(m, caddr_t), seg1, len1);
+ if (seg2 != NULL)
+ MEMCPY(mtod(m, caddr_t) + len1, seg2, total_len - len1);
+#if NBPFILTER > 0
+ if (sc->le_bpf != NULL && seg2 != NULL) {
+ bpf_mtap(sc->le_bpf, m);
+ /*
+ * If this is single cast but not to us
+ * drop it!
+ */
+ if ((eh.ether_dhost[0] & 1) == 0) {
+ if (!LE_ADDREQUAL(eh.ether_dhost, sc->le_ac.ac_enaddr)) {
+ sc->le_scast_drops++;
+ m_freem(m);
+ return;
+ }
+ } else if ((sc->le_flags & IFF_MULTICAST) == 0) {
+ sc->le_mcast_drops++;
+ m_freem(m);
+ return;
+ } else if (sc->le_flags & LE_BRDCSTONLY) {
+ if (!LE_ADDRBRDCST(eh.ether_dhost)) {
+ sc->le_bcast_drops++;
+ m_freem(m);
+ return;
+ }
+ }
+ }
+#endif
+ ether_input(LE_IFP(sc), &eh, m);
+}
+
+static int
+le_ioctl(
+ struct ifnet *ifp,
+ int cmd,
+ caddr_t data)
+{
+ le_softc_t *sc = &le_softc[ifp->if_unit];
+ int s, error = 0;
+
+ if ((sc->le_flags & IFF_UP) == 0)
+ return EIO;
+
+ s = splimp();
+
+ switch (cmd) {
+ case SIOCSIFADDR: {
+ struct ifaddr *ifa = (struct ifaddr *)data;
+
+ ifp->if_flags |= IFF_UP;
+ switch(ifa->ifa_addr->sa_family) {
+#ifdef INET
+ case AF_INET: {
+ (*ifp->if_init)(ifp->if_unit);
+ ((struct arpcom *)ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr;
+ arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
+ break;
+ }
+#endif /* INET */
+
+#ifdef NS
+ /* This magic copied from if_is.c; I don't use XNS,
+ * so I have no way of telling if this actually
+ * works or not.
+ */
+ case AF_NS: {
+ struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
+ if (ns_nullhost(*ina)) {
+ ina->x_host = *(union ns_host *)(sc->le_ac.ac_enaddr);
+ } else {
+ ifp->if_flags &= ~IFF_RUNNING;
+ bcopy((caddr_t)ina->x_host.c_host,
+ (caddr_t)sc->le_ac.ac_enaddr,
+ sizeof sc->le_ac.ac_enaddr);
+ }
+
+ (*ifp->if_init)(ifp->if_unit);
+ break;
+ }
+#endif /* NS */
+
+ default: {
+ (*ifp->if_init)(ifp->if_unit);
+ break;
+ }
+ }
+ break;
+ }
+
+ case SIOCSIFFLAGS: {
+ (*ifp->if_init)(ifp->if_unit);
+ break;
+ }
+
+#ifdef MULTICAST
+ case SIOCADDMULTI:
+ case SIOCDELMULTI: {
+ /*
+ * Update multicast listeners
+ */
+ if (cmd == SIOCADDMULTI)
+ error = ether_addmulti((struct ifreq *)data, &sc->le_ac);
+ else
+ error = ether_delmulti((struct ifreq *)data, &sc->le_ac);
+
+ if (error == ENETRESET) {
+ /* reset multicast filtering */
+ (*ifp->if_init)(ifp->if_unit);
+ error = 0;
+ }
+ break;
+ }
+
+#endif /* MULTICAST */
+
+ default: {
+ error = EINVAL;
+ }
+ }
+
+ splx(s);
+ return error;
+}
+
+/*
+ * This is the standard method of reading the DEC Address ROMS.
+ * I don't understand it but it does work.
+ */
+static int
+le_read_macaddr(
+ le_softc_t *sc,
+ int ioreg,
+ int skippat)
+{
+ int cksum, rom_cksum;
+
+ if (!skippat) {
+ int idx, idx2, found, octet;
+ static u_char testpat[] = { 0xFF, 0, 0x55, 0xAA, 0xFF, 0, 0x55, 0xAA };
+ idx2 = found = 0;
+
+ for (idx = 0; idx < 32; idx++) {
+ octet = LE_INB(sc, ioreg);
+
+ if (octet == testpat[idx2]) {
+ if (++idx2 == sizeof testpat) {
+ ++found;
+ break;
+ }
+ } else {
+ idx2 = 0;
+ }
+ }
+
+ if (!found)
+ return -1;
+ }
+
+ cksum = 0;
+ sc->le_hwaddr[0] = LE_INB(sc, ioreg);
+ sc->le_hwaddr[1] = LE_INB(sc, ioreg);
+
+ cksum = *(u_short *) &sc->le_hwaddr[0];
+
+ sc->le_hwaddr[2] = LE_INB(sc, ioreg);
+ sc->le_hwaddr[3] = LE_INB(sc, ioreg);
+ cksum *= 2;
+ if (cksum > 65535) cksum -= 65535;
+ cksum += *(u_short *) &sc->le_hwaddr[2];
+ if (cksum > 65535) cksum -= 65535;
+
+ sc->le_hwaddr[4] = LE_INB(sc, ioreg);
+ sc->le_hwaddr[5] = LE_INB(sc, ioreg);
+ cksum *= 2;
+ if (cksum > 65535) cksum -= 65535;
+ cksum += *(u_short *) &sc->le_hwaddr[4];
+ if (cksum >= 65535) cksum -= 65535;
+
+ rom_cksum = LE_INB(sc, ioreg);
+ rom_cksum |= LE_INB(sc, ioreg) << 8;
+
+ if (cksum != rom_cksum)
+ return -1;
+ return 0;
+}
+
+static void
+le_multi_filter(
+ le_softc_t *sc)
+{
+#ifdef MULTICAST
+ struct ether_multistep step;
+ struct ether_multi *enm;
+#endif
+#ifdef ISO
+ extern char all_es_snpa[];
+#endif
+
+ MEMSET(sc->le_mctbl, 0, (sc->le_mcmask + 1) / 8);
+
+ if (LE_IFP(sc)->if_flags & IFF_ALLMULTI) {
+ sc->le_flags |= IFF_MULTICAST|IFF_ALLMULTI;
+ return;
+ }
+ sc->le_flags &= ~IFF_MULTICAST;
+ if (sc->le_ac.ac_ipaddr.s_addr != 0) {
+ le_multi_op(sc, etherbroadcastaddr, TRUE);
+ sc->le_flags |= LE_BRDCSTONLY|IFF_MULTICAST;
+ }
+#ifdef ISO
+ le_multi_op(sc, all_es_snpa, TRUE);
+#endif
+
+#ifdef MULTICAST
+ ETHER_FIRST_MULTI(step, &sc->le_ac, enm);
+ if (enm != NULL)
+ sc->le_flags |= IFF_MULTICAST;
+ while (enm != NULL) {
+ if (MEMCMP(enm->enm_addrlo, enm->enm_addrhi, 6) != 0) {
+ sc->le_flags |= IFF_ALLMULTI;
+ return;
+ }
+ le_multi_op(sc, enm->enm_addrlo, TRUE);
+ ETHER_NEXT_MULTI(step, enm);
+ sc->le_flags &= ~LE_BRDCSTONLY;
+ }
+ sc->le_flags &= ~IFF_ALLMULTI;
+#endif
+}
+
+static void
+le_multi_op(
+ le_softc_t *sc,
+ const u_char *mca,
+ int enable)
+{
+ u_int idx, bit, data, crc = 0xFFFFFFFFUL;
+
+#ifdef __alpha
+ for (data = *(__unaligned u_long *) mca, bit = 0; bit < 48; bit++, data >>=
+1)
+ crc = (crc >> 1) ^ (((crc ^ data) & 1) ? LE_CRC32_POLY : 0);
+#else
+ for (idx = 0; idx < 6; idx++)
+ for (data = *mca++, bit = 0; bit < 8; bit++, data >>= 1)
+ crc = (crc >> 1) ^ (((crc ^ data) & 1) ? LE_CRC32_POLY : 0);
+#endif
+ /*
+ * The following two line convert the N bit index into a longword index
+ * and a longword mask.
+ */
+ crc &= sc->le_mcmask;
+ bit = 1 << (crc & (LE_MC_NBPW -1));
+ idx = crc >> (LE_MC_NBPW_LOG2);
+
+ /*
+ * Set or clear hash filter bit in our table.
+ */
+ if (enable) {
+ sc->le_mctbl[idx] |= bit; /* Set Bit */
+ } else {
+ sc->le_mctbl[idx] &= ~bit; /* Clear Bit */
+ }
+}
+
+#if !defined(LE_NOLEMAC)
+/*
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ *
+ * Start of DEC EtherWORKS III (LEMAC) dependent code
+ *
+ */
+
+#define LEMAC_INTR_ENABLE(sc) \
+ LE_OUTB(sc, LEMAC_REG_IC, LE_INB(sc, LEMAC_REG_IC) | LEMAC_IC_ALL)
+
+#define LEMAC_INTR_DISABLE(sc) \
+ LE_OUTB(sc, LEMAC_REG_IC, LE_INB(sc, LEMAC_REG_IC) & ~LEMAC_IC_ALL)
+
+#define LEMAC_64K_MODE(mbase) (((mbase) >= 0x0A) && ((mbase) <= 0x0F))
+#define LEMAC_32K_MODE(mbase) (((mbase) >= 0x14) && ((mbase) <= 0x1F))
+#define LEMAC_2K_MODE(mbase) ( (mbase) >= 0x40)
+
+static int lemac_probe(le_softc_t *sc, const le_board_t *bd, int *msize);
+static void lemac_init(int unit);
+static void lemac_start(struct ifnet *ifp);
+static void lemac_reset(int unit, int dummy);
+static void lemac_intr(le_softc_t *sc);
+static void lemac_rne_intr(le_softc_t *sc);
+static void lemac_tne_intr(le_softc_t *sc);
+static void lemac_txd_intr(le_softc_t *sc, unsigned cs_value);
+static void lemac_rxd_intr(le_softc_t *sc, unsigned cs_value);
+static int lemac_read_eeprom(le_softc_t *sc);
+static void lemac_init_adapmem(le_softc_t *sc);
+
+static const le_mcbits_t lemac_allmulti_mctbl[16] = {
+ 0xFFFFFFFFU, 0xFFFFFFFFU, 0xFFFFFFFFU, 0xFFFFFFFFU,
+ 0xFFFFFFFFU, 0xFFFFFFFFU, 0xFFFFFFFFU, 0xFFFFFFFFU,
+ 0xFFFFFFFFU, 0xFFFFFFFFU, 0xFFFFFFFFU, 0xFFFFFFFFU,
+ 0xFFFFFFFFU, 0xFFFFFFFFU, 0xFFFFFFFFU, 0xFFFFFFFFU,
+};
+/*
+ * An IRQ mapping table. Less space than switch statement.
+ */
+static const int lemac_irqs[] = { IRQ5, IRQ10, IRQ11, IRQ15 };
+
+/*
+ * Some tuning/monitoring variables.
+ */
+unsigned lemac_deftxmax = 16; /* see lemac_max above */
+unsigned lemac_txnospc = 0; /* total # of tranmit starvations */
+
+unsigned lemac_tne_intrs = 0; /* total # of tranmit done intrs */
+unsigned lemac_rne_intrs = 0; /* total # of receive done intrs */
+unsigned lemac_txd_intrs = 0; /* total # of tranmit error intrs */
+unsigned lemac_rxd_intrs = 0; /* total # of receive error intrs */
+
+
+static int
+lemac_probe(
+ le_softc_t *sc,
+ const le_board_t *bd,
+ int *msize)
+{
+ int irq, portval, cksum;
+ long membase;
+
+ LE_OUTB(sc, LEMAC_REG_IOP, LEMAC_IOP_EEINIT);
+ DELAY(LEMAC_EEP_DELAY);
+
+ /*
+ * Read Ethernet address if card is present.
+ */
+ if (le_read_macaddr(sc, LEMAC_REG_APD, 0) < 0)
+ return 0;
+
+ MEMCPY(sc->le_ac.ac_enaddr, sc->le_hwaddr, 6);
+ /*
+ * Clear interrupts and set IRQ.
+ */
+
+ portval = LE_INB(sc, LEMAC_REG_IC) & LEMAC_IC_IRQMSK;
+ irq = lemac_irqs[portval >> 5];
+ LE_OUTB(sc, LEMAC_REG_IC, portval);
+
+ /*
+ * Make sure settings match.
+ */
+
+ if (irq != sc->le_irq) {
+ printf("%s%d: lemac configuration error: expected IRQ 0x%x actual 0x%x\n",
+ LE_IFP(sc)->if_name, LE_IFP(sc)->if_unit, sc->le_irq, irq);
+ return 0;
+ }
+
+ /*
+ * Try to reset the unit
+ */
+ sc->lemac_memmode = 2;
+ lemac_reset(LE_IFP(sc)->if_unit, 0);
+ if ((sc->le_flags & IFF_UP) == 0)
+ return 0;
+
+ /*
+ * Check for correct memory base configuration.
+ */
+ if (vtophys(sc->le_membase) != sc->lemac_membase) {
+ printf("%s%d: lemac configuration error: expected iomem 0x%x actual 0x%x\n",
+ LE_IFP(sc)->if_name, LE_IFP(sc)->if_unit,
+ vtophys(sc->le_membase), sc->lemac_membase);
+ return 0;
+ }
+
+ LE_IFP(sc)->if_init = lemac_init;
+ LE_IFP(sc)->if_start = lemac_start;
+ LE_IFP(sc)->if_reset = lemac_reset;
+ sc->le_prodname = sc->lemac_prodname;
+ sc->le_mctbl = sc->lemac_mctbl;
+ sc->le_mcmask = (1 << LEMAC_MCTBL_BITS) - 1;
+ sc->lemac_txmax = lemac_deftxmax;
+ *msize = 2048;
+ le_intrvec[LE_IFP(sc)->if_unit] = lemac_intr;
+
+ return LEMAC_IOSPACE;
+}
+
+/*
+ * Do a hard reset of the board;
+ */
+static void
+lemac_reset(
+ int unit,
+ int dummy)
+{
+ le_softc_t *sc = &le_softc[unit];
+ int portval, cksum;
+
+ /*
+ * Initialize board..
+ */
+
+ sc->le_flags &= IFF_UP;
+ LE_IFP(sc)->if_flags &= ~IFF_OACTIVE;
+ LEMAC_INTR_DISABLE(sc);
+
+ LE_OUTB(sc, LEMAC_REG_IOP, LEMAC_IOP_EEINIT);
+ DELAY(LEMAC_EEP_DELAY);
+
+ /* Disable Interrupts */
+ /* LE_OUTB(sc, LEMAC_REG_IC, LE_INB(sc, LEMAC_REG_IC) & ICR_IRQ_SEL); */
+
+ /*
+ * Read EEPROM information. NOTE - the placement of this function
+ * is important because functions hereafter may rely on information
+ * read from the EEPROM.
+ */
+ if ((cksum = lemac_read_eeprom(sc)) != LEMAC_EEP_CKSUM) {
+ printf("%s%d: reset: EEPROM checksum failed (0x%x)\n",
+ LE_IFP(sc)->if_unit, LE_IFP(sc)->if_unit, cksum);
+ return;
+ }
+
+ /*
+ * Force to 2K mode if not already configured.
+ */
+
+ portval = LE_INB(sc, LEMAC_REG_MBR);
+ if (!LEMAC_2K_MODE(portval)) {
+ if (LEMAC_64K_MODE(portval)) {
+ portval = (((portval * 2) & 0xF) << 4);
+ sc->lemac_memmode = 64;
+ } else if (LEMAC_32K_MODE(portval)) {
+ portval = ((portval & 0xF) << 4);
+ sc->lemac_memmode = 32;
+ }
+ LE_OUTB(sc, LEMAC_REG_MBR, portval);
+ }
+ sc->lemac_membase = portval * (2 * 1024) + (512 * 1024);
+
+ /*
+ * Initialize Free Memory Queue, Init mcast table with broadcast.
+ */
+
+ lemac_init_adapmem(sc);
+ sc->le_flags |= IFF_UP;
+ return;
+}
+
+static void
+lemac_init(
+ int unit)
+{
+ le_softc_t *sc = &le_softc[unit];
+ int regval, s;
+
+ if ((sc->le_flags & IFF_UP) == 0)
+ return;
+
+ s = splimp();
+
+ /*
+ * If the interface has the up flag
+ */
+ if (LE_IFP(sc)->if_flags & IFF_UP) {
+ int saved_cs = LE_INB(sc, LEMAC_REG_CS);
+ LE_OUTB(sc, LEMAC_REG_CS, saved_cs | (LEMAC_CS_TXD | LEMAC_CS_RXD));
+ LE_OUTB(sc, LEMAC_REG_PA0, sc->le_ac.ac_enaddr[0]);
+ LE_OUTB(sc, LEMAC_REG_PA1, sc->le_ac.ac_enaddr[1]);
+ LE_OUTB(sc, LEMAC_REG_PA2, sc->le_ac.ac_enaddr[2]);
+ LE_OUTB(sc, LEMAC_REG_PA3, sc->le_ac.ac_enaddr[3]);
+ LE_OUTB(sc, LEMAC_REG_PA4, sc->le_ac.ac_enaddr[4]);
+ LE_OUTB(sc, LEMAC_REG_PA5, sc->le_ac.ac_enaddr[5]);
+
+ LE_OUTB(sc, LEMAC_REG_IC, LE_INB(sc, LEMAC_REG_IC) | LEMAC_IC_IE);
+
+ if (LE_IFP(sc)->if_flags & IFF_PROMISC) {
+ LE_OUTB(sc, LEMAC_REG_CS, LEMAC_CS_MCE | LEMAC_CS_PME);
+ } else {
+ LEMAC_INTR_DISABLE(sc);
+ le_multi_filter(sc);
+ LE_OUTB(sc, LEMAC_REG_MPN, 0);
+ if ((sc->le_flags | LE_IFP(sc)->if_flags) & IFF_ALLMULTI) {
+ MEMCPY(&sc->le_membase[LEMAC_MCTBL_OFF], lemac_allmulti_mctbl, sizeof(lemac_allmulti_mctbl));
+ } else {
+ MEMCPY(&sc->le_membase[LEMAC_MCTBL_OFF], sc->lemac_mctbl, sizeof(sc->lemac_mctbl));
+ }
+ LE_OUTB(sc, LEMAC_REG_CS, LEMAC_CS_MCE);
+ }
+
+ LE_OUTB(sc, LEMAC_REG_CTL, LE_INB(sc, LEMAC_REG_CTL) ^ LEMAC_CTL_LED);
+
+ LEMAC_INTR_ENABLE(sc);
+ LE_IFP(sc)->if_flags |= IFF_RUNNING;
+ } else {
+ LE_OUTB(sc, LEMAC_REG_CS, LEMAC_CS_RXD|LEMAC_CS_TXD);
+
+ LEMAC_INTR_DISABLE(sc);
+ LE_IFP(sc)->if_flags &= ~IFF_RUNNING;
+ }
+ splx(s);
+}
+
+/*
+ * What to do upon receipt of an interrupt.
+ */
+static void
+lemac_intr(
+ le_softc_t *sc)
+{
+ int cs_value;
+
+ LEMAC_INTR_DISABLE(sc); /* Mask interrupts */
+
+ /*
+ * Determine cause of interrupt. Receive events take
+ * priority over Transmit.
+ */
+
+ cs_value = LE_INB(sc, LEMAC_REG_CS);
+
+ /*
+ * Check for Receive Queue not being empty.
+ * Check for Transmit Done Queue not being empty.
+ */
+
+ if (cs_value & LEMAC_CS_RNE)
+ lemac_rne_intr(sc);
+ if (cs_value & LEMAC_CS_TNE)
+ lemac_tne_intr(sc);
+
+ /*
+ * Check for Transmitter Disabled.
+ * Check for Receiver Disabled.
+ */
+
+ if (cs_value & LEMAC_CS_TXD)
+ lemac_txd_intr(sc, cs_value);
+ if (cs_value & LEMAC_CS_RXD)
+ lemac_rxd_intr(sc, cs_value);
+
+ /*
+ * Toggle LED and unmask interrupts.
+ */
+
+ LE_OUTB(sc, LEMAC_REG_CTL, LE_INB(sc, LEMAC_REG_CTL) ^ LEMAC_CTL_LED);
+ LEMAC_INTR_ENABLE(sc); /* Unmask interrupts */
+}
+
+static void
+lemac_rne_intr(
+ le_softc_t *sc)
+{
+ int rxcount, rxlen, rxpg;
+ struct mbuf *m;
+ u_char *rxptr;
+
+ lemac_rne_intrs++;
+ rxcount = LE_INB(sc, LEMAC_REG_RQC);
+ while (rxcount--) {
+ rxpg = LE_INB(sc, LEMAC_REG_RQ);
+ LE_OUTB(sc, LEMAC_REG_MPN, rxpg);
+
+ rxptr = sc->le_membase;
+ LE_IFP(sc)->if_ipackets++;
+ if (*rxptr & LEMAC_RX_OK) {
+
+ /*
+ * Get receive length - subtract out checksum.
+ */
+
+ rxlen = ((*(u_int *)rxptr >> 8) & 0x7FF) - 4;
+ le_input(sc, rxptr + sizeof(u_int), rxlen, rxlen, NULL);
+ } else { /* end if (*rxptr & LEMAC_RX_OK) */
+ LE_IFP(sc)->if_ierrors++;
+ }
+next:
+ LE_OUTB(sc, LEMAC_REG_FMQ, rxpg); /* Return this page to Free Memory Queue */
+ } /* end while (recv_count--) */
+
+ return;
+}
+
+static void
+lemac_rxd_intr(
+ le_softc_t *sc,
+ unsigned cs_value)
+{
+ /*
+ * Handle CS_RXD (Receiver disabled) here.
+ *
+ * Check Free Memory Queue Count. If not equal to zero
+ * then just turn Receiver back on. If it is equal to
+ * zero then check to see if transmitter is disabled.
+ * Process transmit TXD loop once more. If all else
+ * fails then do software init (0xC0 to EEPROM Init)
+ * and rebuild Free Memory Queue.
+ */
+
+ lemac_rxd_intrs++;
+
+ /*
+ * Re-enable Receiver.
+ */
+
+ cs_value &= ~LEMAC_CS_RXD;
+ LE_OUTB(sc, LEMAC_REG_CS, cs_value);
+
+ if (LE_INB(sc, LEMAC_REG_FMC) > 0)
+ return;
+
+ if (cs_value & LEMAC_CS_TXD)
+ lemac_txd_intr(sc, cs_value);
+
+ if ((LE_INB(sc, LEMAC_REG_CS) & LEMAC_CS_RXD) == 0)
+ return;
+
+ printf("%s%d: fatal RXD error, attempting recovery\n",
+ LE_IFP(sc)->if_name, LE_IFP(sc)->if_unit);
+
+ lemac_reset(LE_IFP(sc)->if_unit, 0);
+ if (sc->le_flags & IFF_UP) {
+ lemac_init(LE_IFP(sc)->if_unit);
+ return;
+ }
+
+ /*
+ * Error during initializion. Mark card as disabled.
+ */
+ printf("%s%d: recovery failed -- board disabled\n",
+ LE_IFP(sc)->if_name, LE_IFP(sc)->if_unit);
+ return;
+}
+
+static void
+lemac_start(
+ struct ifnet *ifp)
+{
+ le_softc_t *sc = (le_softc_t *) ifp;
+ struct ifqueue *ifq = &ifp->if_snd;
+
+ if ((ifp->if_flags & IFF_RUNNING) == 0)
+ return;
+
+ LEMAC_INTR_DISABLE(sc);
+
+ while (ifq->ifq_head != NULL) {
+ struct mbuf *m;
+ int tx_pg;
+ u_int txhdr, txoff;
+
+ if (LE_INB(sc, LEMAC_REG_TQC) >= sc->lemac_txmax) {
+ ifp->if_flags |= IFF_OACTIVE;
+ break;
+ }
+
+ tx_pg = LE_INB(sc, LEMAC_REG_FMQ); /* get free memory page */
+ /*
+ * Check for good transmit page.
+ */
+ if (tx_pg == 0 || tx_pg > sc->lemac_lastpage) {
+ lemac_txnospc++;
+ ifp->if_flags |= IFF_OACTIVE;
+ break;
+ }
+
+ IF_DEQUEUE(ifq, m);
+ LE_OUTB(sc, LEMAC_REG_MPN, tx_pg); /* Shift 2K window. */
+
+ /*
+ * The first four bytes of each transmit buffer are for
+ * control information. The first byte is the control
+ * byte, then the length (why not word aligned??), then
+ * the off to the buffer.
+ */
+
+ txoff = (mtod(m, u_int) & (sizeof(u_long) - 1)) + LEMAC_TX_HDRSZ;
+ txhdr = sc->lemac_txctl | (m->m_pkthdr.len << 8) | (txoff << 24);
+ *(u_int *) sc->le_membase = txhdr;
+
+ /*
+ * Copy the packet to the board
+ */
+
+ m_copydata(m, 0, m->m_pkthdr.len, sc->le_membase + txoff);
+
+ LE_OUTB(sc, LEMAC_REG_TQ, tx_pg); /* tell chip to transmit this packet */
+ m_freem(m); /* free the mbuf */
+ }
+ LEMAC_INTR_ENABLE(sc);
+}
+
+static void
+lemac_tne_intr(
+ le_softc_t *sc)
+{
+ int txsts, txcount = LE_INB(sc, LEMAC_REG_TDC);
+
+ lemac_tne_intrs++;
+ while (txcount--) {
+ txsts = LE_INB(sc, LEMAC_REG_TDQ);
+ LE_IFP(sc)->if_opackets++; /* another one done */
+ if ((txsts & LEMAC_TDQ_COL) != LEMAC_TDQ_NOCOL)
+ LE_IFP(sc)->if_collisions++;
+ }
+ LE_IFP(sc)->if_flags &= ~IFF_OACTIVE;
+ lemac_start(LE_IFP(sc));
+}
+
+static void
+lemac_txd_intr(
+ le_softc_t *sc,
+ unsigned cs_value)
+{
+ /*
+ * Read transmit status, remove transmit buffer from
+ * transmit queue and place on free memory queue,
+ * then reset transmitter.
+ * Increment appropriate counters.
+ */
+
+ lemac_txd_intrs++;
+ LE_IFP(sc)->if_oerrors++;
+ if (LE_INB(sc, LEMAC_REG_TS) & LEMAC_TS_ECL)
+ LE_IFP(sc)->if_collisions++;
+ LE_IFP(sc)->if_flags &= ~IFF_OACTIVE;
+
+ LE_OUTB(sc, LEMAC_REG_FMQ, LE_INB(sc, LEMAC_REG_TQ));
+ /* Get Page number and write it back out */
+
+ LE_OUTB(sc, LEMAC_REG_CS, cs_value & ~LEMAC_CS_TXD);
+ /* Turn back on transmitter */
+ return;
+}
+
+static int
+lemac_read_eeprom(
+ le_softc_t *sc)
+{
+ int word_off, cksum;
+
+ u_char *ep;
+
+ cksum = 0;
+ ep = sc->lemac_eeprom;
+ for (word_off = 0; word_off < LEMAC_EEP_SIZE / 2; word_off++) {
+ LE_OUTB(sc, LEMAC_REG_PI1, word_off);
+ LE_OUTB(sc, LEMAC_REG_IOP, LEMAC_IOP_EEREAD);
+
+ DELAY(LEMAC_EEP_DELAY);
+
+ *ep = LE_INB(sc, LEMAC_REG_EE1); cksum += *ep++;
+ *ep = LE_INB(sc, LEMAC_REG_EE2); cksum += *ep++;
+ }
+
+ /*
+ * Set up Transmit Control Byte for use later during transmit.
+ */
+
+ sc->lemac_txctl |= LEMAC_TX_FLAGS;
+
+ if ((sc->lemac_eeprom[LEMAC_EEP_SWFLAGS] & LEMAC_EEP_SW_SQE) == 0)
+ sc->lemac_txctl &= ~LEMAC_TX_SQE;
+
+ if (sc->lemac_eeprom[LEMAC_EEP_SWFLAGS] & LEMAC_EEP_SW_LAB)
+ sc->lemac_txctl |= LEMAC_TX_LAB;
+
+ MEMCPY(sc->lemac_prodname, &sc->lemac_eeprom[LEMAC_EEP_PRDNM], LEMAC_EEP_PRDNMSZ);
+ sc->lemac_prodname[LEMAC_EEP_PRDNMSZ] = '\0';
+
+ return cksum % 256;
+}
+
+static void
+lemac_init_adapmem(
+ le_softc_t *sc)
+{
+ int pg, conf;
+
+ conf = LE_INB(sc, LEMAC_REG_CNF);
+
+ if ((sc->lemac_eeprom[LEMAC_EEP_SETUP] & LEMAC_EEP_ST_DRAM) == 0) {
+ sc->lemac_lastpage = 63;
+ conf &= ~LEMAC_CNF_DRAM;
+ } else {
+ sc->lemac_lastpage = 127;
+ conf |= LEMAC_CNF_DRAM;
+ }
+
+ LE_OUTB(sc, LEMAC_REG_CNF, conf);
+
+ for (pg = 1; pg <= sc->lemac_lastpage; pg++)
+ LE_OUTB(sc, LEMAC_REG_FMQ, pg);
+
+ return;
+}
+#endif /* !defined(LE_NOLEMAC) */
+
+#if !defined(LE_NOLANCE)
+/*
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ *
+ * Start of DEPCA (DE200/DE201/DE202/DE422 etal) support.
+ *
+ */
+static int depca_probe(le_softc_t *sc, const le_board_t *bd, int *msize);
+static void depca_intr(le_softc_t *sc);
+static int lance_init_adapmem(le_softc_t *sc);
+static int lance_init_ring(le_softc_t *sc, ln_ring_t *rp, lance_ring_t *ri,
+ unsigned ndescs, unsigned bufoffset,
+ unsigned descoffset);
+static void lance_init(int unit);
+static void lance_reset(int unit, int dummy);
+static void lance_intr(le_softc_t *sc);
+static int lance_rx_intr(le_softc_t *sc);
+static void lance_start(struct ifnet *ifp);
+static int lance_tx_intr(le_softc_t *sc);
+
+#define LN_BUFSIZE /* 380 */ 304 /* 1520 / 4 */
+#define LN_TXDESC_RATIO 2048
+#define LN_DESC_MAX 128
+
+#if LN_DOSTATS
+struct {
+ unsigned lance_rx_misses;
+ unsigned lance_rx_badcrc;
+ unsigned lance_rx_badalign;
+ unsigned lance_rx_badframe;
+ unsigned lance_rx_buferror;
+ unsigned lance_tx_deferred;
+ unsigned lance_tx_single_collisions;
+ unsigned lance_tx_multiple_collisions;
+ unsigned lance_tx_excessive_collisions;
+ unsigned lance_tx_late_collisions;
+
+ unsigned lance_memory_errors;
+ unsigned lance_inits;
+ unsigned lance_tx_intrs;
+ unsigned lance_tx_nospc[2];
+ unsigned lance_tx_drains[2];
+ unsigned lance_tx_orphaned;
+ unsigned lance_tx_adoptions;
+ unsigned lance_tx_emptied;
+ unsigned lance_tx_deftxint;
+ unsigned lance_tx_buferror;
+ unsigned lance_high_txoutptr;
+ unsigned lance_low_txheapsize;
+ unsigned lance_low_txfree;
+ unsigned lance_tx_intr_hidescs;
+ /* unsigned lance_tx_intr_descs[LN_DESC_MAX]; */
+
+ unsigned lance_rx_intrs;
+ unsigned lance_rx_badsop;
+ unsigned lance_rx_contig;
+ unsigned lance_rx_noncontig;
+ unsigned lance_rx_intr_hidescs;
+ unsigned lance_rx_ndescs[4096 / LN_BUFSIZE];
+ /* unsigned lance_rx_intr_descs[LN_DESC_MAX]; */
+} lance_stats;
+
+#define LN_STAT(stat) (lance_stats.lance_ ## stat)
+#define LN_MINSTAT(stat, val) (LN_STAT(stat > (val)) ? LN_STAT(stat = (val)) : 0)
+#define LN_MAXSTAT(stat, val) (LN_STAT(stat < (val)) ? LN_STAT(stat = (val)) : 0)
+
+#else
+#define LN_STAT(stat) 0
+#define LN_MINSTAT(stat, val) 0
+#define LN_MAXSTAT(stat, val) 0
+#endif
+
+#define LN_SELCSR(sc, csrno) (LE_OUTW(sc, sc->lance_rap, csrno))
+#define LN_INQCSR(sc) (LE_INW(sc, sc->lance_rap))
+
+#define LN_WRCSR(sc, val) (LE_OUTW(sc, sc->lance_rdp, val))
+#define LN_RDCSR(sc) (LE_INW(sc, sc->lance_rdp))
+
+
+#define LN_ZERO(sc, vaddr, len) bzero(vaddr, len)
+#define LN_COPYTO(sc, from, to, len) bcopy(from, to, len)
+
+#define LN_SETFLAG(sc, vaddr, val) \
+ (((volatile u_char *) vaddr)[3] = (val))
+
+#define LN_PUTDESC(sc, desc, vaddr) \
+ (((volatile u_short *) vaddr)[0] = ((u_short *) desc)[0], \
+ ((volatile u_short *) vaddr)[2] = ((u_short *) desc)[2], \
+ ((volatile u_short *) vaddr)[1] = ((u_short *) desc)[1])
+
+/*
+ * Only get the descriptor flags and length/status. All else
+ * read-only.
+ */
+#define LN_GETDESC(sc, desc, vaddr) \
+ (((u_short *) desc)[1] = ((volatile u_short *) vaddr)[1], \
+ ((u_short *) desc)[3] = ((volatile u_short *) vaddr)[3])
+
+
+/*
+ * These definitions are specific to the DEC "DEPCA-style" NICs.
+ * (DEPCA, DE10x, DE20[012], DE422)
+ *
+ */
+#define DEPCA_REG_NICSR 0 /* (RW;16) NI Control / Status */
+#define DEPCA_REG_RDP 4 /* (RW:16) LANCE RDP (data) register */
+#define DEPCA_REG_RAP 6 /* (RW:16) LANCE RAP (address) register */
+#define DEPCA_REG_ADDRROM 12 /* (R : 8) DEPCA Ethernet Address ROM */
+#define DEPCA_IOSPACE 16 /* DEPCAs use 16 bytes of IO space */
+
+#define DEPCA_NICSR_LED 0x0001 /* Light the LED on the back of the DEPCA */
+#define DEPCA_NICSR_ENABINTR 0x0002 /* Enable Interrupts */
+#define DEPCA_NICSR_MASKINTR 0x0004 /* Mask Interrupts */
+#define DEPCA_NICSR_AAC 0x0008 /* Address Counter Clear */
+#define DEPCA_NICSR_REMOTEBOOT 0x0010 /* Remote Boot Enabled (ignored) */
+#define DEPCA_NICSR_32KRAM 0x0020 /* DEPCA LANCE RAM size 64K (C) / 32K (S) */
+#define DEPCA_NICSR_LOW32K 0x0040 /* Bank Select (A15 = !This Bit) */
+#define DEPCA_NICSR_SHE 0x0080 /* Shared RAM Enabled (ie hide ROM) */
+#define DEPCA_NICSR_BOOTTMO 0x0100 /* Remote Boot Timeout (ignored) */
+
+#define DEPCA_RDNICSR(sc) (LE_INW(sc, DEPCA_REG_NICSR))
+#define DEPCA_WRNICSR(sc, val) (LE_OUTW(sc, DEPCA_REG_NICSR, val))
+
+#define DEPCA_IDSTR_OFFSET 0xC006 /* ID String Offset */
+
+#define DEPCA_REG_EISAID 0x80
+#define DEPCA_EISAID_MASK 0xf0ffffff
+#define DEPCA_EISAID_DE422 0x2042A310
+
+typedef enum {
+ DEPCA_CLASSIC,
+ DEPCA_DE100, DEPCA_DE101,
+ DEPCA_EE100,
+ DEPCA_DE200, DEPCA_DE201, DEPCA_DE202,
+ DEPCA_DE422,
+ DEPCA_UNKNOWN
+} depca_t;
+
+static const char *depca_signatures[] = {
+ "DEPCA",
+ "DE100", "DE101",
+ "EE100",
+ "DE200", "DE201", "DE202",
+ "DE422",
+ NULL
+};
+
+static int
+depca_probe(
+ le_softc_t *sc,
+ const le_board_t *bd,
+ int *msize)
+{
+ unsigned nicsr, idx, idstr_offset = DEPCA_IDSTR_OFFSET;
+
+ /*
+ * Find out how memory we are dealing with. Adjust
+ * the ID string offset approriately if we are at
+ * 32K. Make sure the ROM is enabled.
+ */
+ nicsr = DEPCA_RDNICSR(sc);
+ nicsr &= ~(DEPCA_NICSR_SHE|DEPCA_NICSR_LED|DEPCA_NICSR_ENABINTR);
+
+ if (nicsr & DEPCA_NICSR_32KRAM) {
+ /*
+ * Make we are going to read the upper
+ * 32K so we do read the ROM.
+ */
+ sc->lance_ramsize = 32 * 1024;
+ nicsr &= ~DEPCA_NICSR_LOW32K;
+ sc->lance_ramoffset = 32 * 1024;
+ idstr_offset -= sc->lance_ramsize;
+ } else {
+ sc->lance_ramsize = 64 * 1024;
+ sc->lance_ramoffset = 0;
+ }
+ DEPCA_WRNICSR(sc, nicsr);
+
+ sc->le_prodname = NULL;
+ for (idx = 0; depca_signatures[idx] != NULL; idx++) {
+ if (bcmp(depca_signatures[idx], sc->le_membase + idstr_offset, 5) == 0) {
+ sc->le_prodname = depca_signatures[idx];
+ break;
+ }
+ }
+
+ if (sc->le_prodname == NULL) {
+ /*
+ * Try to get the EISA device if it's a DE422.
+ */
+ if (sc->le_iobase > 0x1000 && (sc->le_iobase & 0x0F00) == 0x0C00
+ && (LE_INL(sc, DEPCA_REG_EISAID) & DEPCA_EISAID_MASK)
+ == DEPCA_EISAID_DE422) {
+ sc->le_prodname = "DE422";
+ } else {
+ return 0;
+ }
+ }
+ /*
+ * Try to read the address ROM.
+ * Stop the LANCE, reset the Address ROM Counter (AAC),
+ * read the NICSR to "clock" in the reset, and then
+ * re-enable the Address ROM Counter. Now read the
+ * address ROM.
+ */
+ sc->lance_rdp = DEPCA_REG_RDP;
+ sc->lance_rap = DEPCA_REG_RAP;
+ sc->lance_csr3 = LN_CSR3_ALE;
+ sc->le_mctbl = sc->lance_initb.ln_multi_mask;
+ sc->le_mcmask = LN_MC_MASK;
+ LN_SELCSR(sc, LN_CSR0);
+ LN_WRCSR(sc, LN_CSR0_STOP);
+
+ if (idx < DEPCA_DE200) {
+ DEPCA_WRNICSR(sc, DEPCA_RDNICSR(sc) & ~DEPCA_NICSR_AAC);
+ DEPCA_WRNICSR(sc, DEPCA_RDNICSR(sc) | DEPCA_NICSR_AAC);
+ }
+
+ if (le_read_macaddr(sc, DEPCA_REG_ADDRROM, 0) < 0)
+ return 0;
+
+ MEMCPY(sc->le_ac.ac_enaddr, sc->le_hwaddr, 6);
+ /*
+ * Renable shared RAM.
+ */
+ DEPCA_WRNICSR(sc, DEPCA_RDNICSR(sc) | DEPCA_NICSR_SHE);
+
+ le_intrvec[LE_IFP(sc)->if_unit] = depca_intr;
+ if (!lance_init_adapmem(sc))
+ return 0;
+
+ DEPCA_WRNICSR(sc, DEPCA_NICSR_SHE | DEPCA_NICSR_ENABINTR);
+ lance_reset(LE_IFP(sc)->if_unit, 0);
+
+ LN_STAT(low_txfree = sc->lance_txinfo.ri_max);
+ LN_STAT(low_txheapsize = 0xFFFFFFFF);
+ LE_IFP(sc)->if_reset = lance_reset;
+ LE_IFP(sc)->if_init = lance_init;
+ LE_IFP(sc)->if_start = lance_start;
+ *msize = sc->lance_ramsize;
+ return DEPCA_IOSPACE;
+}
+
+static void
+depca_intr(
+ le_softc_t *sc)
+{
+ DEPCA_WRNICSR(sc, DEPCA_RDNICSR(sc) ^ DEPCA_NICSR_LED);
+ lance_intr(sc);
+}
+
+/*
+ * Here's as good a place to describe our paritioning of the
+ * LANCE shared RAM space. (NOTE: this driver does not yet support
+ * the concept of a LANCE being able to DMA).
+ *
+ * First is the 24 (00:23) bytes for LANCE Initialization Block
+ * Next are the recieve descriptors. The number is calculated from
+ * how many LN_BUFSIZE buffers we can allocate (this number must
+ * be a power of 2). Next are the transmit descriptors. The amount
+ * of transmit descriptors is derived from the size of the RAM
+ * divided by 1K. Now come the receive buffers (one for each receive
+ * descriptor). Finally is the transmit heap. (no fixed buffers are
+ * allocated so as to make the most use of the limited space).
+ */
+static int
+lance_init_adapmem(
+ le_softc_t *sc)
+{
+ lance_addr_t rxbufoffset;
+ lance_addr_t rxdescoffset, txdescoffset;
+ unsigned rxdescs, txdescs;
+
+ /*
+ * First calculate how many descriptors we heap.
+ * Note this assumes the ramsize is a power of two.
+ */
+ sc->lance_rxbufsize = LN_BUFSIZE;
+ rxdescs = 1;
+ while (rxdescs * sc->lance_rxbufsize < sc->lance_ramsize)
+ rxdescs *= 2;
+ rxdescs /= 2;
+ if (rxdescs > LN_DESC_MAX) {
+ sc->lance_rxbufsize *= rxdescs / LN_DESC_MAX;
+ rxdescs = LN_DESC_MAX;
+ }
+ txdescs = sc->lance_ramsize / LN_TXDESC_RATIO;
+ if (txdescs > LN_DESC_MAX)
+ txdescs = LN_DESC_MAX;
+
+ /*
+ * Now calculate where everything goes in memory
+ */
+ rxdescoffset = sizeof(ln_initb_t);
+ txdescoffset = rxdescoffset + sizeof(ln_desc_t) * rxdescs;
+ rxbufoffset = txdescoffset + sizeof(ln_desc_t) * txdescs;
+
+ sc->le_mctbl = (le_mcbits_t *) sc->lance_initb.ln_multi_mask;
+ /*
+ * Remember these for debugging.
+ */
+ sc->lance_raminitb = (ln_initb_t *) sc->le_membase;
+ sc->lance_ramdesc = (ln_desc_t *) (sc->le_membase + rxdescoffset);
+
+ /*
+ * Initialize the rings.
+ */
+ if (!lance_init_ring(sc, &sc->lance_initb.ln_rxring, &sc->lance_rxinfo,
+ rxdescs, rxbufoffset, rxdescoffset))
+ return 0;
+ sc->lance_rxinfo.ri_heap = rxbufoffset;
+ sc->lance_rxinfo.ri_heapend = rxbufoffset + sc->lance_rxbufsize * rxdescs;
+
+ if (!lance_init_ring(sc, &sc->lance_initb.ln_txring, &sc->lance_txinfo,
+ txdescs, 0, txdescoffset))
+ return 0;
+ sc->lance_txinfo.ri_heap = sc->lance_rxinfo.ri_heapend;
+ sc->lance_txinfo.ri_heapend = sc->lance_ramsize;
+
+ /*
+ * Set CSR1 and CSR2 to the address of the init block (which
+ * for us is always 0.
+ */
+ sc->lance_csr1 = LN_ADDR_LO(0 + sc->lance_ramoffset);
+ sc->lance_csr2 = LN_ADDR_HI(0 + sc->lance_ramoffset);
+ return 1;
+}
+
+static int
+lance_init_ring(
+ le_softc_t *sc,
+ ln_ring_t *rp,
+ lance_ring_t *ri,
+ unsigned ndescs,
+ lance_addr_t bufoffset,
+ lance_addr_t descoffset)
+{
+ lance_descinfo_t *di;
+
+ /*
+ * Initialize the ring pointer in the LANCE InitBlock
+ */
+ rp->r_addr_lo = LN_ADDR_LO(descoffset + sc->lance_ramoffset);
+ rp->r_addr_hi = LN_ADDR_HI(descoffset + sc->lance_ramoffset);
+ rp->r_log2_size = ffs(ndescs) - 1;
+
+ /*
+ * Allocate the ring entry descriptors and initialize
+ * our ring information data structure. All these are
+ * our copies and do not live in the LANCE RAM.
+ */
+ ri->ri_first = (lance_descinfo_t *) malloc(ndescs * sizeof(*di), M_DEVBUF, M_NOWAIT);
+ if (ri->ri_first == NULL) {
+ printf("lance_init_ring: malloc(%d) failed\n", ndescs * sizeof(*di));
+ return 0;
+ }
+ ri->ri_free = ri->ri_max = ndescs;
+ ri->ri_last = ri->ri_first + ri->ri_max;
+ for (di = ri->ri_first; di < ri->ri_last; di++) {
+ di->di_addr = sc->le_membase + descoffset;
+ di->di_mbuf = NULL;
+ if (bufoffset) {
+ di->di_bufaddr = bufoffset;
+ di->di_buflen = sc->lance_rxbufsize;
+ bufoffset += sc->lance_rxbufsize;
+ }
+ descoffset += sizeof(ln_desc_t);
+ }
+ return 1;
+}
+
+static void
+lance_dumpcsrs(
+ le_softc_t *sc,
+ const char *id)
+{
+ printf("%s%d: %s: nicsr=%04x",
+ LE_IFP(sc)->if_name, LE_IFP(sc)->if_unit,
+ id, DEPCA_RDNICSR(sc));
+ LN_SELCSR(sc, LN_CSR0); printf(" csr0=%04x", LN_RDCSR(sc));
+ LN_SELCSR(sc, LN_CSR1); printf(" csr1=%04x", LN_RDCSR(sc));
+ LN_SELCSR(sc, LN_CSR2); printf(" csr2=%04x", LN_RDCSR(sc));
+ LN_SELCSR(sc, LN_CSR3); printf(" csr3=%04x\n", LN_RDCSR(sc));
+ LN_SELCSR(sc, LN_CSR0);
+}
+
+static void
+lance_reset(
+ int unit,
+ int dummy)
+{
+ le_softc_t *sc = &le_softc[unit];
+ register int cnt, csr;
+
+ /* lance_dumpcsrs(sc, "lance_reset: start"); */
+
+ LN_WRCSR(sc, LN_RDCSR(sc) & ~LN_CSR0_ENABINTR);
+ LN_WRCSR(sc, LN_CSR0_STOP);
+ DELAY(100);
+
+ sc->le_flags &= ~IFF_UP;
+ LE_IFP(sc)->if_flags &= ~(IFF_UP|IFF_RUNNING);
+
+ le_multi_filter(sc); /* initialize the multicast table */
+ if ((sc->le_flags | LE_IFP(sc)->if_flags) & IFF_ALLMULTI) {
+ sc->lance_initb.ln_multi_mask[0] = 0xFFFFU;
+ sc->lance_initb.ln_multi_mask[1] = 0xFFFFU;
+ sc->lance_initb.ln_multi_mask[2] = 0xFFFFU;
+ sc->lance_initb.ln_multi_mask[3] = 0xFFFFU;
+ }
+ sc->lance_initb.ln_physaddr[0] = ((u_short *) sc->le_ac.ac_enaddr)[0];
+ sc->lance_initb.ln_physaddr[1] = ((u_short *) sc->le_ac.ac_enaddr)[1];
+ sc->lance_initb.ln_physaddr[2] = ((u_short *) sc->le_ac.ac_enaddr)[2];
+ if (LE_IFP(sc)->if_flags & IFF_PROMISC) {
+ sc->lance_initb.ln_mode |= LN_MODE_PROMISC;
+ } else {
+ sc->lance_initb.ln_mode &= ~LN_MODE_PROMISC;
+ }
+ /*
+ * We force the init block to be at the start
+ * of the LANCE's RAM buffer.
+ */
+ LN_COPYTO(sc, &sc->lance_initb, sc->le_membase, sizeof(sc->lance_initb));
+ LN_SELCSR(sc, LN_CSR1); LN_WRCSR(sc, sc->lance_csr1);
+ LN_SELCSR(sc, LN_CSR2); LN_WRCSR(sc, sc->lance_csr2);
+ LN_SELCSR(sc, LN_CSR3); LN_WRCSR(sc, sc->lance_csr3);
+
+ /* lance_dumpcsrs(sc, "lance_reset: preinit"); */
+
+ /*
+ * clear INITDONE and INIT the chip
+ */
+ LN_SELCSR(sc, LN_CSR0);
+ LN_WRCSR(sc, LN_CSR0_INIT|LN_CSR0_INITDONE);
+
+ csr = 0;
+ cnt = 100;
+ while (cnt-- > 0) {
+ if (((csr = LN_RDCSR(sc)) & LN_CSR0_INITDONE) != 0)
+ break;
+ DELAY(10000);
+ }
+
+ if ((csr & LN_CSR0_INITDONE) == 0) { /* make sure we got out okay */
+ lance_dumpcsrs(sc, "lance_reset: reset failure");
+ } else {
+ /* lance_dumpcsrs(sc, "lance_reset: end"); */
+ LE_IFP(sc)->if_flags |= IFF_UP;
+ sc->le_flags |= IFF_UP;
+ }
+}
+
+static void
+lance_init(
+ int unit)
+{
+ le_softc_t *sc = &le_softc[unit];
+ lance_ring_t *ri;
+ lance_descinfo_t *di;
+ ln_desc_t desc;
+
+ LN_STAT(inits++);
+ if (LE_IFP(sc)->if_flags & IFF_RUNNING) {
+ lance_reset(unit, 0);
+ lance_tx_intr(sc);
+ /*
+ * If we were running, requeue any pending transmits.
+ */
+ ri = &sc->lance_txinfo;
+ di = ri->ri_nextout;
+ while (ri->ri_free < ri->ri_max) {
+ if (--di == ri->ri_first)
+ di = ri->ri_nextout - 1;
+ if (di->di_mbuf == NULL)
+ break;
+ IF_PREPEND(&LE_IFP(sc)->if_snd, di->di_mbuf);
+ di->di_mbuf = NULL;
+ ri->ri_free++;
+ }
+ } else {
+ lance_reset(unit, 0);
+ }
+
+ /*
+ * Reset the transmit ring. Make sure we own all the buffers.
+ * Also reset the transmit heap.
+ */
+ LE_IFP(sc)->if_flags &= ~IFF_OACTIVE;
+ ri = &sc->lance_txinfo;
+ for (di = ri->ri_first; di < ri->ri_last; di++) {
+ if (di->di_mbuf != NULL) {
+ m_freem(di->di_mbuf);
+ di->di_mbuf = NULL;
+ }
+ desc.d_flag = 0;
+ desc.d_addr_lo = LN_ADDR_LO(ri->ri_heap + sc->lance_ramoffset);
+ desc.d_addr_hi = LN_ADDR_HI(ri->ri_heap + sc->lance_ramoffset);
+ desc.d_buflen = 0;
+ LN_PUTDESC(sc, &desc, di->di_addr);
+ }
+ ri->ri_nextin = ri->ri_nextout = ri->ri_first;
+ ri->ri_free = ri->ri_max;
+ ri->ri_outptr = ri->ri_heap;
+ ri->ri_outsize = ri->ri_heapend - ri->ri_heap;
+
+ ri = &sc->lance_rxinfo;
+ desc.d_flag = LN_DFLAG_OWNER;
+ desc.d_buflen = 0 - sc->lance_rxbufsize;
+ for (di = ri->ri_first; di < ri->ri_last; di++) {
+ desc.d_addr_lo = LN_ADDR_LO(di->di_bufaddr + sc->lance_ramoffset);
+ desc.d_addr_hi = LN_ADDR_HI(di->di_bufaddr + sc->lance_ramoffset);
+ LN_PUTDESC(sc, &desc, di->di_addr);
+ }
+ ri->ri_nextin = ri->ri_nextout = ri->ri_first;
+ ri->ri_outptr = ri->ri_heap;
+ ri->ri_outsize = ri->ri_heapend - ri->ri_heap;
+ ri->ri_free = 0;
+
+ if (LE_IFP(sc)->if_flags & IFF_UP) {
+ LE_IFP(sc)->if_flags |= IFF_RUNNING;
+ LN_WRCSR(sc, LN_CSR0_START|LN_CSR0_INITDONE|LN_CSR0_ENABINTR);
+ /* lance_dumpcsrs(sc, "lance_init: up"); */
+ lance_start(LE_IFP(sc));
+ } else {
+ /* lance_dumpcsrs(sc, "lance_init: down"); */
+ LE_IFP(sc)->if_flags &= ~IFF_RUNNING;
+ }
+}
+
+static void
+lance_intr(
+ le_softc_t *sc)
+{
+ unsigned oldcsr;
+
+ oldcsr = LN_RDCSR(sc);
+ oldcsr &= ~LN_CSR0_ENABINTR;
+ LN_WRCSR(sc, oldcsr);
+ LN_WRCSR(sc, LN_CSR0_ENABINTR);
+
+ if (oldcsr & LN_CSR0_ERRSUM) {
+ if (oldcsr & LN_CSR0_MISS) {
+ /*
+ * LN_CSR0_MISS is signaled when the LANCE receiver
+ * loses a packet because it doesn't own a receive
+ * descriptor. Rev. D LANCE chips, which are no
+ * longer used, require a chip reset as described
+ * below.
+ */
+ LN_STAT(rx_misses++);
+ }
+ if (oldcsr & LN_CSR0_MEMERROR) {
+ LN_STAT(memory_errors++);
+ if (oldcsr & (LN_CSR0_RXON|LN_CSR0_TXON)) {
+ lance_init(LE_IFP(sc)->if_unit);
+ return;
+ }
+ }
+ }
+
+ if ((oldcsr & LN_CSR0_RXINT) && lance_rx_intr(sc)) {
+ lance_init(LE_IFP(sc)->if_unit);
+ return;
+ }
+
+ if (oldcsr & LN_CSR0_TXINT) {
+ if (lance_tx_intr(sc))
+ lance_start(LE_IFP(sc));
+ }
+
+ if (oldcsr == (LN_CSR0_PENDINTR|LN_CSR0_RXON|LN_CSR0_TXON))
+ printf("%s%d: lance_intr: stray interrupt\n",
+ LE_IFP(sc)->if_name, LE_IFP(sc)->if_unit);
+}
+
+static int
+lance_rx_intr(
+ le_softc_t *sc)
+{
+ lance_ring_t *ri = &sc->lance_rxinfo;
+ lance_descinfo_t *eop;
+ ln_desc_t desc;
+ int ndescs, total_len, rxdescs;
+
+ LN_STAT(rx_intrs++);
+
+ for (rxdescs = 0;;) {
+ /*
+ * Now to try to find the end of this packet chain.
+ */
+ for (ndescs = 1, eop = ri->ri_nextin;; ndescs++) {
+ /*
+ * If we don't own this descriptor, the packet ain't
+ * all here so return because we are done.
+ */
+ LN_GETDESC(sc, &desc, eop->di_addr);
+ if (desc.d_flag & LN_DFLAG_OWNER)
+ return 0;
+ /*
+ * In case we have missed a packet and gotten the
+ * LANCE confused, make sure we are pointing at the
+ * start of a packet. If we aren't, something is really
+ * strange so reinit the LANCE.
+ */
+ if (desc.d_flag & LN_DFLAG_RxBUFERROR) {
+ LN_STAT(rx_buferror++);
+ return 1;
+ }
+ if ((desc.d_flag & LN_DFLAG_SOP) && eop != ri->ri_nextin) {
+ LN_STAT(rx_badsop++);
+ return 1;
+ }
+ if (desc.d_flag & LN_DFLAG_EOP)
+ break;
+ if (++eop == ri->ri_last)
+ eop = ri->ri_first;
+ }
+
+ total_len = (desc.d_status & LN_DSTS_RxLENMASK) - 4;
+ if ((desc.d_flag & LN_DFLAG_RxERRSUM) == 0) {
+ /*
+ * Valid Packet -- If the SOP is less than or equal to the EOP
+ * or the length is less than the receive buffer size, then the
+ * packet is contiguous in memory and can be copied in one shot.
+ * Otherwise we need to copy two segments to get the entire
+ * packet.
+ */
+ if (ri->ri_nextin <= eop || total_len <= ri->ri_heapend - ri->ri_nextin->di_bufaddr) {
+ le_input(sc, sc->le_membase + ri->ri_nextin->di_bufaddr,
+ total_len, total_len, NULL);
+ LN_STAT(rx_contig++);
+ } else {
+ le_input(sc, sc->le_membase + ri->ri_nextin->di_bufaddr,
+ total_len,
+ ri->ri_heapend - ri->ri_nextin->di_bufaddr,
+ sc->le_membase + ri->ri_first->di_bufaddr);
+ LN_STAT(rx_noncontig++);
+ }
+ } else {
+ /*
+ * If the packet is bad, increment the
+ * counters.
+ */
+ LE_IFP(sc)->if_ierrors++;
+ if (desc.d_flag & LN_DFLAG_RxBADCRC)
+ LN_STAT(rx_badcrc++);
+ if (desc.d_flag & LN_DFLAG_RxOVERFLOW)
+ LN_STAT(rx_badalign++);
+ if (desc.d_flag & LN_DFLAG_RxFRAMING)
+ LN_STAT(rx_badframe++);
+ }
+ LE_IFP(sc)->if_ipackets++;
+ LN_STAT(rx_ndescs[ndescs-1]++);
+ rxdescs += ndescs;
+ while (ndescs-- > 0) {
+ LN_SETFLAG(sc, ri->ri_nextin->di_addr, LN_DFLAG_OWNER);
+ if (++ri->ri_nextin == ri->ri_last)
+ ri->ri_nextin = ri->ri_first;
+ }
+ }
+ /* LN_STAT(rx_intr_descs[rxdescs]++); */
+ LN_MAXSTAT(rx_intr_hidescs, rxdescs);
+
+ return 0;
+}
+
+static void
+lance_start(
+ struct ifnet *ifp)
+{
+ le_softc_t *sc = (le_softc_t *) ifp;
+ struct ifqueue *ifq = &ifp->if_snd;
+ lance_ring_t *ri = &sc->lance_txinfo;
+ lance_descinfo_t *di;
+ ln_desc_t desc;
+ unsigned len, slop;
+ struct mbuf *m, *m0;
+ caddr_t bp;
+
+ if ((ifp->if_flags & IFF_RUNNING) == 0)
+ return;
+
+ for (;;) {
+ IF_DEQUEUE(ifq, m);
+ if (m == NULL)
+ break;
+
+ /*
+ * Make the packet meets the minimum size for Ethernet.
+ * The slop is so that we also use an even number of longwards.
+ */
+ len = ETHERMIN + sizeof(struct ether_header);
+ if (m->m_pkthdr.len > len)
+ len = m->m_pkthdr.len;
+
+ slop = (8 - len) & 3;
+ /*
+ * If there are no free ring entries (there must be always
+ * one owned by the host), or there's not enough space for
+ * this packet, or this packet would wrap around the end
+ * of LANCE RAM then wait for the transmits to empty for
+ * space and ring entries to become available.
+ */
+ if (ri->ri_free == 1 || len + slop > ri->ri_outsize) {
+ /*
+ * Try to see if we can free up anything off the transit ring.
+ */
+ if (lance_tx_intr(sc) > 0) {
+ LN_STAT(tx_drains[0]++);
+ IF_PREPEND(ifq, m);
+ continue;
+ }
+ LN_STAT(tx_nospc[0]++);
+ break;
+ }
+
+ if (len + slop > ri->ri_heapend - ri->ri_outptr) {
+ /*
+ * Since the packet won't fit in the end of the transmit
+ * heap, see if there is space at the beginning of the transmit
+ * heap. If not, try again when there is space.
+ */
+ LN_STAT(tx_orphaned++);
+ slop += ri->ri_heapend - ri->ri_outptr;
+ if (len + slop > ri->ri_outsize) {
+ LN_STAT(tx_nospc[1]++);
+ break;
+ }
+ /*
+ * Point to the beginning of the heap
+ */
+ ri->ri_outptr = ri->ri_heap;
+ LN_STAT(tx_adoptions++);
+ }
+
+ /*
+ * Initialize the descriptor (saving the buffer address,
+ * buffer length, and mbuf) and write the packet out
+ * to the board.
+ */
+ di = ri->ri_nextout;
+ di->di_bufaddr = ri->ri_outptr;
+ di->di_buflen = len + slop;
+ di->di_mbuf = m;
+ bp = sc->le_membase + di->di_bufaddr;
+ for (m0 = m; m0 != NULL; m0 = m0->m_next) {
+ LN_COPYTO(sc, mtod(m0, caddr_t), bp, m0->m_len);
+ bp += m0->m_len;
+ }
+ /*
+ * Zero out the remainder if needed (< ETHERMIN).
+ */
+ if (m->m_pkthdr.len < len)
+ LN_ZERO(sc, bp, len - m->m_pkthdr.len);
+
+ /*
+ * Finally, copy out the descriptor and tell the
+ * LANCE to transmit!.
+ */
+ desc.d_buflen = 0 - len;
+ desc.d_addr_lo = LN_ADDR_LO(di->di_bufaddr + sc->lance_ramoffset);
+ desc.d_addr_hi = LN_ADDR_HI(di->di_bufaddr + sc->lance_ramoffset);
+ desc.d_flag = LN_DFLAG_SOP|LN_DFLAG_EOP|LN_DFLAG_OWNER;
+ LN_PUTDESC(sc, &desc, di->di_addr);
+ LN_WRCSR(sc, LN_CSR0_TXDEMAND|LN_CSR0_ENABINTR);
+
+ /*
+ * Do our bookkeeping with our transmit heap.
+ * (if we wrap, point back to the beginning).
+ */
+ ri->ri_outptr += di->di_buflen;
+ ri->ri_outsize -= di->di_buflen;
+ LN_MAXSTAT(high_txoutptr, ri->ri_outptr);
+ LN_MINSTAT(low_txheapsize, ri->ri_outsize);
+
+ if (ri->ri_outptr == ri->ri_heapend)
+ ri->ri_outptr = ri->ri_heap;
+
+ ri->ri_free--;
+ if (++ri->ri_nextout == ri->ri_last)
+ ri->ri_nextout = ri->ri_first;
+ LN_MINSTAT(low_txfree, ri->ri_free);
+ }
+ if (m != NULL) {
+ ifp->if_flags |= IFF_OACTIVE;
+ IF_PREPEND(ifq, m);
+ }
+}
+
+static int
+lance_tx_intr(
+ le_softc_t *sc)
+{
+ lance_ring_t *ri = &sc->lance_txinfo;
+ unsigned xmits;
+
+ LN_STAT(tx_intrs++);
+ for (xmits = 0; ri->ri_free < ri->ri_max; ) {
+ ln_desc_t desc;
+
+ LN_GETDESC(sc, &desc, ri->ri_nextin->di_addr);
+ if (desc.d_flag & LN_DFLAG_OWNER)
+ break;
+
+ if (desc.d_flag & (LN_DFLAG_TxONECOLL|LN_DFLAG_TxMULTCOLL))
+ LE_IFP(sc)->if_collisions++;
+ if (desc.d_flag & LN_DFLAG_TxDEFERRED)
+ LN_STAT(tx_deferred++);
+ if (desc.d_flag & LN_DFLAG_TxONECOLL)
+ LN_STAT(tx_single_collisions++);
+ if (desc.d_flag & LN_DFLAG_TxMULTCOLL)
+ LN_STAT(tx_multiple_collisions++);
+
+ if (desc.d_flag & LN_DFLAG_TxERRSUM) {
+ if (desc.d_status & (LN_DSTS_TxUNDERFLOW|LN_DSTS_TxBUFERROR|
+ LN_DSTS_TxEXCCOLL|LN_DSTS_TxLATECOLL)) {
+ if (desc.d_status & LN_DSTS_TxEXCCOLL) {
+ unsigned tdr;
+ LN_STAT(tx_excessive_collisions++);
+ if ((tdr = (desc.d_status & LN_DSTS_TxTDRMASK)) > 0) {
+ tdr *= 100;
+ printf("%s%d: lance: warning: excessive collisions: TDR %dns (%d-%dm)\n",
+ LE_IFP(sc)->if_name, LE_IFP(sc)->if_unit,
+ tdr, (tdr*99)/1000, (tdr*117)/1000);
+ }
+ }
+ if (desc.d_status & LN_DSTS_TxBUFERROR)
+ LN_STAT(tx_buferror++);
+ LE_IFP(sc)->if_oerrors++;
+ if ((desc.d_status & LN_DSTS_TxLATECOLL) == 0) {
+ lance_init(LE_IFP(sc)->if_unit);
+ return 0;
+ } else {
+ LN_STAT(tx_late_collisions++);
+ }
+ }
+ }
+ m_freem(ri->ri_nextin->di_mbuf);
+ ri->ri_nextin->di_mbuf = NULL;
+ LE_IFP(sc)->if_opackets++;
+ ri->ri_free++;
+ ri->ri_outsize += ri->ri_nextin->di_buflen;
+ if (++ri->ri_nextin == ri->ri_last)
+ ri->ri_nextin = ri->ri_first;
+ LE_IFP(sc)->if_flags &= ~IFF_OACTIVE;
+ xmits++;
+ }
+ if (ri->ri_free == ri->ri_max)
+ LN_STAT(tx_emptied++);
+ /* LN_STAT(tx_intr_descs[xmits]++); */
+ LN_MAXSTAT(tx_intr_hidescs, xmits);
+ return xmits;
+}
+#endif /* !defined(LE_NOLANCE) */
+#endif /* NLE > 0 */
OpenPOWER on IntegriCloud