diff options
author | dg <dg@FreeBSD.org> | 1994-08-12 06:51:12 +0000 |
---|---|---|
committer | dg <dg@FreeBSD.org> | 1994-08-12 06:51:12 +0000 |
commit | cc490038a0f6ece1aa559fbd7f0f2ff39eecbf5a (patch) | |
tree | c656aff1523b520b6a5e7f3897ad11ef631a40bc | |
parent | 0bf5c587d2b0703e87888f837c671426497a4ca1 (diff) | |
download | FreeBSD-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.i386 | 3 | ||||
-rw-r--r-- | sys/i386/conf/files.i386 | 3 | ||||
-rw-r--r-- | sys/i386/isa/ic/am7990.h | 109 | ||||
-rw-r--r-- | sys/i386/isa/ic/lemac.h | 177 | ||||
-rw-r--r-- | sys/i386/isa/if_le.c | 2144 |
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 */ |