if_ex.c revision 50026
121769Sjkh/* 221769Sjkh * Copyright (c) 1996, Javier Mart�n Rueda (jmrueda@diatel.upm.es) 321769Sjkh * All rights reserved. 421769Sjkh * 521769Sjkh * Redistribution and use in source and binary forms, with or without 621769Sjkh * modification, are permitted provided that the following conditions 721769Sjkh * are met: 821769Sjkh * 1. Redistributions of source code must retain the above copyright 921769Sjkh * notice unmodified, this list of conditions, and the following 1021769Sjkh * disclaimer. 1121769Sjkh * 2. Redistributions in binary form must reproduce the above copyright 1221769Sjkh * notice, this list of conditions and the following disclaimer in the 1321769Sjkh * documentation and/or other materials provided with the distribution. 1421769Sjkh * 1521769Sjkh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1621769Sjkh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1721769Sjkh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1821769Sjkh * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1921769Sjkh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2021769Sjkh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2121769Sjkh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2221769Sjkh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2321769Sjkh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2421769Sjkh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2521769Sjkh * SUCH DAMAGE. 2629877Smsmith * 2750026Smdodd * $Id: if_ex.c,v 1.17 1999/08/18 06:11:58 mdodd Exp $ 2821769Sjkh */ 2921769Sjkh 3021769Sjkh/* 3129877Smsmith * Intel EtherExpress Pro/10, Pro/10+ Ethernet driver 3221769Sjkh * 3321769Sjkh * Revision history: 3421769Sjkh * 3521769Sjkh * 30-Oct-1996: first beta version. Inet and BPF supported, but no multicast. 3621769Sjkh */ 3721769Sjkh 3821769Sjkh#include "ex.h" 3921769Sjkh#if NEX > 0 4048645Sdes#include "bpf.h" 4132350Seivind#include "opt_inet.h" 4231742Seivind#include "opt_ipx.h" 4321769Sjkh 4421769Sjkh#include <sys/param.h> 4521769Sjkh#include <sys/systm.h> 4621769Sjkh#include <sys/conf.h> 4724204Sbde#include <sys/sockio.h> 4821769Sjkh#include <sys/mbuf.h> 4921769Sjkh#include <sys/socket.h> 5021769Sjkh 5150026Smdodd#include <net/ethernet.h> 5221769Sjkh#include <net/if.h> 5321769Sjkh 5450026Smdodd#include <netinet/in.h> 5550026Smdodd#include <netinet/if_ether.h> 5650026Smdodd 5748645Sdes#if NBPF > 0 5821769Sjkh#include <net/bpf.h> 5921769Sjkh#endif 6021769Sjkh 6121769Sjkh#include <machine/clock.h> 6221769Sjkh 6321769Sjkh#include <i386/isa/isa_device.h> 6421769Sjkh#include <i386/isa/if_exreg.h> 6521769Sjkh 6621769Sjkh#ifdef EXDEBUG 6721769Sjkh#define Start_End 1 6821769Sjkh#define Rcvd_Pkts 2 6921769Sjkh#define Sent_Pkts 4 7021769Sjkh#define Status 8 7121769Sjkhstatic int debug_mask = 0; 7221769Sjkhstatic int exintr_count = 0; 7321769Sjkh#define DODEBUG(level, action) if (level & debug_mask) action 7421769Sjkh#else 7521769Sjkh#define DODEBUG(level, action) 7621769Sjkh#endif 7721769Sjkh 7821769Sjkh#define Conn_BNC 1 7921769Sjkh#define Conn_TPE 2 8021769Sjkh#define Conn_AUI 3 8121769Sjkh 8221769Sjkhstruct ex_softc { 8321769Sjkh struct arpcom arpcom; /* Ethernet common data */ 8421769Sjkh u_int iobase; /* I/O base address. */ 8521769Sjkh u_short connector; /* Connector type. */ 8621769Sjkh u_short irq_no; /* IRQ number. */ 8729877Smsmith char *irq2ee; /* irq <-> internal representation conversion */ 8829877Smsmith u_char *ee2irq; 8921769Sjkh u_int mem_size; /* Total memory size, in bytes. */ 9021769Sjkh u_int rx_mem_size; /* Rx memory size (by default, first 3/4 of total memory). */ 9121769Sjkh u_int rx_lower_limit, rx_upper_limit; /* Lower and upper limits of receive buffer. */ 9221769Sjkh u_int rx_head; /* Head of receive ring buffer. */ 9321769Sjkh u_int tx_mem_size; /* Tx memory size (by default, last quarter of total memory). */ 9421769Sjkh u_int tx_lower_limit, tx_upper_limit; /* Lower and upper limits of transmit buffer. */ 9521769Sjkh u_int tx_head, tx_tail; /* Head and tail of transmit ring buffer. */ 9621769Sjkh u_int tx_last; /* Pointer to beginning of last frame in the chain. */ 9721769Sjkh}; 9821769Sjkh 9921769Sjkhstatic struct ex_softc ex_sc[NEX]; /* XXX would it be better to malloc(3) the memory? */ 10021769Sjkh 10129877Smsmithstatic char irq2eemap[] = { -1, -1, 0, 1, -1, 2, -1, -1, -1, 0, 3, 4, -1, -1, -1, -1 }; 10229877Smsmithstatic u_char ee2irqmap[] = { 9, 3, 5, 10, 11, 0, 0, 0 }; 10329877Smsmithstatic char plus_irq2eemap[] = { -1, -1, -1, 0, 1, 2, -1, 3, -1, 4, 5, 6, 7, -1, -1, -1 }; 10429877Smsmithstatic u_char plus_ee2irqmap[] = { 3, 4, 5, 7, 9, 10, 11, 12 }; 10521769Sjkh 10621769Sjkhstatic int ex_probe __P((struct isa_device *)); 10721769Sjkhstatic int ex_attach __P((struct isa_device *)); 10821769Sjkhstatic void ex_init __P((void *)); 10921769Sjkhstatic void ex_start __P((struct ifnet *)); 11021769Sjkhstatic void ex_stop __P((int)); 11140565Sbdestatic ointhand2_t exintr; 11236735Sdfrstatic int ex_ioctl __P((struct ifnet *, u_long, caddr_t)); 11321769Sjkhstatic void ex_reset __P((int)); 11421769Sjkhstatic void ex_watchdog __P((struct ifnet *)); 11521769Sjkh 11621769Sjkhstatic u_short eeprom_read __P((int, int)); 11721769Sjkhstatic int look_for_card __P((u_int)); 11821769Sjkhstatic void ex_tx_intr __P((int)); 11921769Sjkhstatic void ex_rx_intr __P((int)); 12021769Sjkh 12121769Sjkhstruct isa_driver exdriver = { 12221769Sjkh ex_probe, 12321769Sjkh ex_attach, 12421769Sjkh "ex", 12521769Sjkh 0 12621769Sjkh}; 12721769Sjkh 12821769Sjkhstatic int look_for_card(u_int iobase) 12921769Sjkh{ 13021769Sjkh int count1, count2; 13121769Sjkh 13221769Sjkh /* 13321769Sjkh * Check for the i82595 signature, and check that the round robin 13421769Sjkh * counter actually advances. 13521769Sjkh */ 13621769Sjkh if (((count1 = inb(iobase + ID_REG)) & Id_Mask) != Id_Sig) 13721769Sjkh return(0); 13821769Sjkh count2 = inb(iobase + ID_REG); 13921769Sjkh count2 = inb(iobase + ID_REG); 14021769Sjkh count2 = inb(iobase + ID_REG); 14121769Sjkh return((count2 & Counter_bits) == ((count1 + 0xc0) & Counter_bits)); 14221769Sjkh} 14321769Sjkh 14421769Sjkh 14521769Sjkhint ex_probe(struct isa_device *dev) 14621769Sjkh{ 14721769Sjkh int unit = dev->id_unit; 14821769Sjkh struct ex_softc *sc = &ex_sc[unit]; 14921769Sjkh u_int iobase; 15021769Sjkh u_short eaddr_tmp; 15121769Sjkh int tmp; 15221769Sjkh 15321769Sjkh DODEBUG(Start_End, printf("ex_probe%d: start\n", unit);); 15421769Sjkh 15521769Sjkh /* 15621769Sjkh * If an I/O address was supplied in the configuration file, probe only 15721769Sjkh * that. Otherwise, cycle through the predefined set of possible addresses. 15821769Sjkh */ 15921769Sjkh if (dev->id_iobase != -1) { 16021769Sjkh if (! look_for_card(iobase = dev->id_iobase)) 16121769Sjkh return(0); 16221769Sjkh } 16321769Sjkh else { 16421769Sjkh for (iobase = 0x200; iobase < 0x3a0; iobase += 0x10) 16521769Sjkh if (look_for_card(iobase)) 16621769Sjkh break; 16721769Sjkh if (iobase >= 0x3a0) 16821769Sjkh return(0); 16921769Sjkh else 17021769Sjkh dev->id_iobase = iobase; 17121769Sjkh } 17221769Sjkh 17321769Sjkh /* 17421769Sjkh * Reset the card. 17521769Sjkh */ 17621769Sjkh outb(iobase + CMD_REG, Reset_CMD); 17729313Smsmith DELAY(400); 17821769Sjkh 17921769Sjkh /* 18021769Sjkh * Fill in several fields of the softc structure: 18121769Sjkh * - I/O base address. 18221769Sjkh * - Hardware Ethernet address. 18321769Sjkh * - IRQ number (if not supplied in config file, read it from EEPROM). 18421769Sjkh * - Connector type. 18521769Sjkh */ 18621769Sjkh sc->iobase = iobase; 18721769Sjkh eaddr_tmp = eeprom_read(iobase, EE_Eth_Addr_Lo); 18821769Sjkh sc->arpcom.ac_enaddr[5] = eaddr_tmp & 0xff; 18921769Sjkh sc->arpcom.ac_enaddr[4] = eaddr_tmp >> 8; 19021769Sjkh eaddr_tmp = eeprom_read(iobase, EE_Eth_Addr_Mid); 19121769Sjkh sc->arpcom.ac_enaddr[3] = eaddr_tmp & 0xff; 19221769Sjkh sc->arpcom.ac_enaddr[2] = eaddr_tmp >> 8; 19321769Sjkh eaddr_tmp = eeprom_read(iobase, EE_Eth_Addr_Hi); 19421769Sjkh sc->arpcom.ac_enaddr[1] = eaddr_tmp & 0xff; 19521769Sjkh sc->arpcom.ac_enaddr[0] = eaddr_tmp >> 8; 19621769Sjkh tmp = eeprom_read(iobase, EE_IRQ_No) & IRQ_No_Mask; 19729877Smsmith 19829877Smsmith /* work out which set of irq <-> internal tables to use */ 19929877Smsmith if (sc->arpcom.ac_enaddr[0] == 0x00 && 20029877Smsmith sc->arpcom.ac_enaddr[1] == 0xA0 && 20129877Smsmith sc->arpcom.ac_enaddr[2] == 0xC9) { /* it's a 10+ */ 20229877Smsmith sc->irq2ee = plus_irq2eemap; 20329877Smsmith sc->ee2irq = plus_ee2irqmap; 20429877Smsmith } else { /* it's an ordinary 10 */ 20529877Smsmith sc->irq2ee = irq2eemap; 20629877Smsmith sc->ee2irq = ee2irqmap; 20729877Smsmith } 20829877Smsmith 20921769Sjkh if (dev->id_irq > 0) { 21029877Smsmith if (sc->ee2irq[tmp] != ffs(dev->id_irq) - 1) 21129877Smsmith printf("ex%d: WARNING: board's EEPROM is configured for IRQ %d, using %d\n", unit, sc->ee2irq[tmp], ffs(dev->id_irq) - 1); 21221769Sjkh sc->irq_no = ffs(dev->id_irq) - 1; 21321769Sjkh } 21421769Sjkh else { 21529877Smsmith sc->irq_no = sc->ee2irq[tmp]; 21621769Sjkh dev->id_irq = 1 << sc->irq_no; 21721769Sjkh } 21821769Sjkh if (sc->irq_no == 0) { 21921769Sjkh printf("ex%d: invalid IRQ.\n", unit); 22021769Sjkh return(0); 22121769Sjkh } 22221769Sjkh outb(iobase + CMD_REG, Bank2_Sel); 22321769Sjkh tmp = inb(iobase + REG3); 22421769Sjkh if (tmp & TPE_bit) 22521769Sjkh sc->connector = Conn_TPE; 22621769Sjkh else if (tmp & BNC_bit) 22721769Sjkh sc->connector = Conn_BNC; 22821769Sjkh else 22921769Sjkh sc->connector = Conn_AUI; 23021769Sjkh sc->mem_size = CARD_RAM_SIZE; /* XXX This should be read from the card itself. */ 23121769Sjkh 23221769Sjkh outb(iobase + CMD_REG, Bank0_Sel); 23321769Sjkh 23421769Sjkh DODEBUG(Start_End, printf("ex_probe%d: finish\n", unit);); 23521769Sjkh return(EX_IOSIZE); 23621769Sjkh} 23721769Sjkh 23821769Sjkh 23921769Sjkhint ex_attach(struct isa_device *dev) 24021769Sjkh{ 24121769Sjkh int unit = dev->id_unit; 24221769Sjkh struct ex_softc *sc = &ex_sc[unit]; 24321769Sjkh struct ifnet *ifp = &sc->arpcom.ac_if; 24421769Sjkh 24521769Sjkh DODEBUG(Start_End, printf("ex_attach%d: start\n", unit);); 24621769Sjkh 24740565Sbde dev->id_ointr = exintr; 24840565Sbde 24921769Sjkh /* 25021769Sjkh * Initialize the ifnet structure. 25121769Sjkh */ 25221769Sjkh ifp->if_softc = sc; 25321769Sjkh ifp->if_unit = unit; 25421769Sjkh ifp->if_name = "ex"; 25521769Sjkh ifp->if_init = ex_init; 25621769Sjkh ifp->if_output = ether_output; 25721769Sjkh ifp->if_start = ex_start; 25821769Sjkh ifp->if_ioctl = ex_ioctl; 25921769Sjkh ifp->if_watchdog = ex_watchdog; 26021769Sjkh ifp->if_mtu = ETHERMTU; 26121769Sjkh ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST /* XXX not done yet. | IFF_MULTICAST */; 26221769Sjkh 26321769Sjkh /* 26421769Sjkh * Attach the interface. 26521769Sjkh */ 26621769Sjkh if_attach(ifp); 26721769Sjkh ether_ifattach(ifp); 26821769Sjkh 26929313Smsmith if (sc->arpcom.ac_enaddr[0] == 0x00 && 27029313Smsmith sc->arpcom.ac_enaddr[1] == 0xA0 && 27129313Smsmith sc->arpcom.ac_enaddr[2] == 0xC9) { 27229313Smsmith printf("ex%d: Intel EtherExpress Pro/10+, address %6D, connector ", dev->id_unit, sc->arpcom.ac_enaddr, ":"); 27329313Smsmith } else { 27429313Smsmith printf("ex%d: Intel EtherExpress Pro/10, address %6D, connector ", dev->id_unit, sc->arpcom.ac_enaddr, ":"); 27529313Smsmith } 27621769Sjkh switch(sc->connector) { 27721769Sjkh case Conn_TPE: printf("TPE\n"); break; 27821769Sjkh case Conn_BNC: printf("BNC\n"); break; 27921769Sjkh case Conn_AUI: printf("AUI\n"); break; 28021769Sjkh default: printf("???\n"); 28121769Sjkh } 28221769Sjkh 28321769Sjkh /* 28421769Sjkh * If BPF is in the kernel, call the attach for it 28521769Sjkh */ 28648645Sdes#if NBPF > 0 28721769Sjkh bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); 28821769Sjkh#endif 28921769Sjkh DODEBUG(Start_End, printf("ex_attach%d: finish\n", unit);); 29046347Speter sc->arpcom.ac_if.if_snd.ifq_maxlen = ifqmaxlen; 29121769Sjkh return(1); 29221769Sjkh} 29321769Sjkh 29421769Sjkh 29521769Sjkhvoid ex_init(void *xsc) 29621769Sjkh{ 29721769Sjkh register struct ex_softc *sc = (struct ex_softc *) xsc; 29821769Sjkh struct ifnet *ifp = &sc->arpcom.ac_if; 29921769Sjkh int s, i; 30021769Sjkh register int iobase = sc->iobase; 30121769Sjkh unsigned short temp_reg; 30221769Sjkh 30321769Sjkh DODEBUG(Start_End, printf("ex_init%d: start\n", ifp->if_unit);); 30421769Sjkh 30521769Sjkh if (ifp->if_addrhead.tqh_first == NULL) 30621769Sjkh return; 30721769Sjkh s = splimp(); 30821769Sjkh sc->arpcom.ac_if.if_timer = 0; 30921769Sjkh 31021769Sjkh /* 31121769Sjkh * Load the ethernet address into the card. 31221769Sjkh */ 31321769Sjkh outb(iobase + CMD_REG, Bank2_Sel); 31421769Sjkh temp_reg = inb(iobase + EEPROM_REG); 31521769Sjkh if (temp_reg & Trnoff_Enable) 31621769Sjkh outb(iobase + EEPROM_REG, temp_reg & ~Trnoff_Enable); 31721769Sjkh for (i = 0; i < ETHER_ADDR_LEN; i++) 31821769Sjkh outb(iobase + I_ADDR_REG0 + i, sc->arpcom.ac_enaddr[i]); 31921769Sjkh /* 32021769Sjkh * - Setup transmit chaining and discard bad received frames. 32121769Sjkh * - Match broadcast. 32221769Sjkh * - Clear test mode. 32321769Sjkh * - Set receiving mode. 32421769Sjkh * - Set IRQ number. 32521769Sjkh */ 32621769Sjkh outb(iobase + REG1, inb(iobase + REG1) | Tx_Chn_Int_Md | Tx_Chn_ErStp | Disc_Bad_Fr); 32721769Sjkh outb(iobase + REG2, inb(iobase + REG2) | No_SA_Ins | RX_CRC_InMem); 32826545Sgibbs outb(iobase + REG3, inb(iobase + REG3) & 0x3f /* XXX constants. */ ); 32921769Sjkh outb(iobase + CMD_REG, Bank1_Sel); 33029877Smsmith outb(iobase + INT_NO_REG, (inb(iobase + INT_NO_REG) & 0xf8) | sc->irq2ee[sc->irq_no]); 33121769Sjkh 33221769Sjkh /* 33321769Sjkh * Divide the available memory in the card into rcv and xmt buffers. 33421769Sjkh * By default, I use the first 3/4 of the memory for the rcv buffer, 33521769Sjkh * and the remaining 1/4 of the memory for the xmt buffer. 33621769Sjkh */ 33721769Sjkh sc->rx_mem_size = sc->mem_size * 3 / 4; 33821769Sjkh sc->tx_mem_size = sc->mem_size - sc->rx_mem_size; 33921769Sjkh sc->rx_lower_limit = 0x0000; 34021769Sjkh sc->rx_upper_limit = sc->rx_mem_size - 2; 34121769Sjkh sc->tx_lower_limit = sc->rx_mem_size; 34221769Sjkh sc->tx_upper_limit = sc->mem_size - 2; 34321769Sjkh outb(iobase + RCV_LOWER_LIMIT_REG, sc->rx_lower_limit >> 8); 34421769Sjkh outb(iobase + RCV_UPPER_LIMIT_REG, sc->rx_upper_limit >> 8); 34521769Sjkh outb(iobase + XMT_LOWER_LIMIT_REG, sc->tx_lower_limit >> 8); 34621769Sjkh outb(iobase + XMT_UPPER_LIMIT_REG, sc->tx_upper_limit >> 8); 34721769Sjkh 34821769Sjkh /* 34921769Sjkh * Enable receive and transmit interrupts, and clear any pending int. 35021769Sjkh */ 35121769Sjkh outb(iobase + REG1, inb(iobase + REG1) | TriST_INT); 35221769Sjkh outb(iobase + CMD_REG, Bank0_Sel); 35321769Sjkh outb(iobase + MASK_REG, All_Int & ~(Rx_Int | Tx_Int)); 35421769Sjkh outb(iobase + STATUS_REG, All_Int); 35521769Sjkh 35621769Sjkh /* 35721769Sjkh * Initialize receive and transmit ring buffers. 35821769Sjkh */ 35921769Sjkh outw(iobase + RCV_BAR, sc->rx_lower_limit); 36021769Sjkh sc->rx_head = sc->rx_lower_limit; 36126545Sgibbs outw(iobase + RCV_STOP_REG, sc->rx_upper_limit | 0xfe); 36221769Sjkh outw(iobase + XMT_BAR, sc->tx_lower_limit); 36321769Sjkh sc->tx_head = sc->tx_tail = sc->tx_lower_limit; 36421769Sjkh 36521769Sjkh ifp->if_flags |= IFF_RUNNING; 36621769Sjkh ifp->if_flags &= ~IFF_OACTIVE; 36721769Sjkh DODEBUG(Status, printf("OIDLE init\n");); 36821769Sjkh 36921769Sjkh /* 37021769Sjkh * Final reset of the board, and enable operation. 37121769Sjkh */ 37221769Sjkh outb(iobase + CMD_REG, Sel_Reset_CMD); 37321769Sjkh DELAY(2); 37421769Sjkh outb(iobase + CMD_REG, Rcv_Enable_CMD); 37521769Sjkh 37621769Sjkh ex_start(ifp); 37721769Sjkh splx(s); 37821769Sjkh 37921769Sjkh DODEBUG(Start_End, printf("ex_init%d: finish\n", ifp->if_unit);); 38021769Sjkh} 38121769Sjkh 38221769Sjkh 38321769Sjkhvoid ex_start(struct ifnet *ifp) 38421769Sjkh{ 38521769Sjkh int unit = ifp->if_unit; 38621769Sjkh register struct ex_softc *sc = &ex_sc[unit]; 38721769Sjkh register int iobase = sc->iobase; 38821769Sjkh int i, s, len, data_len, avail, dest, next; 38931016Sphk unsigned char tmp16[2]; 39021769Sjkh struct mbuf *opkt; 39121769Sjkh register struct mbuf *m; 39221769Sjkh 39321769Sjkh DODEBUG(Start_End, printf("ex_start%d: start\n", unit);); 39421769Sjkh 39521769Sjkh s = splimp(); 39621769Sjkh 39721769Sjkh /* 39821769Sjkh * Main loop: send outgoing packets to network card until there are no 39921769Sjkh * more packets left, or the card cannot accept any more yet. 40021769Sjkh */ 40121769Sjkh while (((opkt = ifp->if_snd.ifq_head) != NULL) && ! (ifp->if_flags & IFF_OACTIVE)) { 40221769Sjkh 40321769Sjkh /* 40421769Sjkh * Ensure there is enough free transmit buffer space for this packet, 40521769Sjkh * including its header. Note: the header cannot wrap around the end of 40621769Sjkh * the transmit buffer and must be kept together, so we allow space for 40721769Sjkh * twice the length of the header, just in case. 40821769Sjkh */ 40921769Sjkh for (len = 0, m = opkt; m != NULL; m = m->m_next) 41021769Sjkh len += m->m_len; 41121769Sjkh data_len = len; 41221769Sjkh DODEBUG(Sent_Pkts, printf("1. Sending packet with %d data bytes. ", data_len);); 41321769Sjkh if (len & 1) 41421769Sjkh len += XMT_HEADER_LEN + 1; 41521769Sjkh else 41621769Sjkh len += XMT_HEADER_LEN; 41721769Sjkh if ((i = sc->tx_tail - sc->tx_head) >= 0) 41821769Sjkh avail = sc->tx_mem_size - i; 41921769Sjkh else 42021769Sjkh avail = -i; 42121769Sjkh DODEBUG(Sent_Pkts, printf("i=%d, avail=%d\n", i, avail);); 42221769Sjkh if (avail >= len + XMT_HEADER_LEN) { 42321769Sjkh IF_DEQUEUE(&ifp->if_snd, opkt); 42421769Sjkh 42521769Sjkh#ifdef EX_PSA_INTR 42621769Sjkh /* 42721769Sjkh * Disable rx and tx interrupts, to avoid corruption of the host 42821769Sjkh * address register by interrupt service routines. XXX Is this necessary with splimp() enabled? 42921769Sjkh */ 43021769Sjkh outb(iobase + MASK_REG, All_Int); 43121769Sjkh#endif 43221769Sjkh 43321769Sjkh /* 43421769Sjkh * Compute the start and end addresses of this frame in the tx buffer. 43521769Sjkh */ 43621769Sjkh dest = sc->tx_tail; 43721769Sjkh next = dest + len; 43821769Sjkh if (next > sc->tx_upper_limit) { 43921769Sjkh if ((sc->tx_upper_limit + 2 - sc->tx_tail) <= XMT_HEADER_LEN) { 44021769Sjkh dest = sc->tx_lower_limit; 44121769Sjkh next = dest + len; 44221769Sjkh } 44321769Sjkh else 44421769Sjkh next = sc->tx_lower_limit + next - sc->tx_upper_limit - 2; 44521769Sjkh } 44621769Sjkh 44721769Sjkh /* 44821769Sjkh * Build the packet frame in the card's ring buffer. 44921769Sjkh */ 45021769Sjkh DODEBUG(Sent_Pkts, printf("2. dest=%d, next=%d. ", dest, next);); 45121769Sjkh outw(iobase + HOST_ADDR_REG, dest); 45221769Sjkh outw(iobase + IO_PORT_REG, Transmit_CMD); 45321769Sjkh outw(iobase + IO_PORT_REG, 0); 45421769Sjkh outw(iobase + IO_PORT_REG, next); 45521769Sjkh outw(iobase + IO_PORT_REG, data_len); 45621769Sjkh 45721769Sjkh /* 45821769Sjkh * Output the packet data to the card. Ensure all transfers are 45921769Sjkh * 16-bit wide, even if individual mbufs have odd length. 46021769Sjkh */ 46121769Sjkh 46221769Sjkh for (m = opkt, i = 0; m != NULL; m = m->m_next) { 46321769Sjkh DODEBUG(Sent_Pkts, printf("[%d]", m->m_len);); 46421769Sjkh if (i) { 46521769Sjkh tmp16[1] = *(mtod(m, caddr_t)); 46621769Sjkh outsw(iobase + IO_PORT_REG, tmp16, 1); 46721769Sjkh } 46821769Sjkh outsw(iobase + IO_PORT_REG, mtod(m, caddr_t) + i, (m->m_len - i) / 2); 46943314Sdillon if ((i = (m->m_len - i) & 1) != 0) 47021769Sjkh tmp16[0] = *(mtod(m, caddr_t) + m->m_len - 1); 47121769Sjkh } 47221769Sjkh if (i) 47321769Sjkh outsw(iobase + IO_PORT_REG, tmp16, 1); 47421769Sjkh 47521769Sjkh /* 47621769Sjkh * If there were other frames chained, update the chain in the last one. 47721769Sjkh */ 47821769Sjkh if (sc->tx_head != sc->tx_tail) { 47921769Sjkh if (sc->tx_tail != dest) { 48021769Sjkh outw(iobase + HOST_ADDR_REG, sc->tx_last + XMT_Chain_Point); 48121769Sjkh outw(iobase + IO_PORT_REG, dest); 48221769Sjkh } 48321769Sjkh outw(iobase + HOST_ADDR_REG, sc->tx_last + XMT_Byte_Count); 48421769Sjkh i = inw(iobase + IO_PORT_REG); 48521769Sjkh outw(iobase + HOST_ADDR_REG, sc->tx_last + XMT_Byte_Count); 48621769Sjkh outw(iobase + IO_PORT_REG, i | Ch_bit); 48721769Sjkh } 48821769Sjkh 48921769Sjkh /* 49021769Sjkh * Resume normal operation of the card: 49121769Sjkh * - Make a dummy read to flush the DRAM write pipeline. 49221769Sjkh * - Enable receive and transmit interrupts. 49321769Sjkh * - Send Transmit or Resume_XMT command, as appropriate. 49421769Sjkh */ 49521769Sjkh inw(iobase + IO_PORT_REG); 49621769Sjkh#ifdef EX_PSA_INTR 49721769Sjkh outb(iobase + MASK_REG, All_Int & ~(Rx_Int | Tx_Int)); 49821769Sjkh#endif 49921769Sjkh if (sc->tx_head == sc->tx_tail) { 50021769Sjkh outw(iobase + XMT_BAR, dest); 50121769Sjkh outb(iobase + CMD_REG, Transmit_CMD); 50221769Sjkh sc->tx_head = dest; 50321769Sjkh DODEBUG(Sent_Pkts, printf("Transmit\n");); 50421769Sjkh } 50521769Sjkh else { 50621769Sjkh outb(iobase + CMD_REG, Resume_XMT_List_CMD); 50721769Sjkh DODEBUG(Sent_Pkts, printf("Resume\n");); 50821769Sjkh } 50921769Sjkh sc->tx_last = dest; 51021769Sjkh sc->tx_tail = next; 51121769Sjkh 51248645Sdes#if NBPF > 0 51321769Sjkh if (ifp->if_bpf != NULL) 51421769Sjkh bpf_mtap(ifp, opkt); 51521769Sjkh#endif 51621769Sjkh ifp->if_timer = 2; 51721769Sjkh ifp->if_opackets++; 51821769Sjkh m_freem(opkt); 51921769Sjkh } 52021769Sjkh else { 52121769Sjkh ifp->if_flags |= IFF_OACTIVE; 52221769Sjkh DODEBUG(Status, printf("OACTIVE start\n");); 52321769Sjkh } 52421769Sjkh } 52521769Sjkh 52621769Sjkh splx(s); 52721769Sjkh 52821769Sjkh DODEBUG(Start_End, printf("ex_start%d: finish\n", unit);); 52921769Sjkh} 53021769Sjkh 53121769Sjkh 53221769Sjkhvoid ex_stop(int unit) 53321769Sjkh{ 53421769Sjkh struct ex_softc *sc = &ex_sc[unit]; 53521769Sjkh int iobase = sc->iobase; 53621769Sjkh 53721769Sjkh DODEBUG(Start_End, printf("ex_stop%d: start\n", unit);); 53821769Sjkh 53921769Sjkh /* 54021769Sjkh * Disable card operation: 54121769Sjkh * - Disable the interrupt line. 54221769Sjkh * - Flush transmission and disable reception. 54321769Sjkh * - Mask and clear all interrupts. 54421769Sjkh * - Reset the 82595. 54521769Sjkh */ 54621769Sjkh outb(iobase + CMD_REG, Bank1_Sel); 54721769Sjkh outb(iobase + REG1, inb(iobase + REG1) & ~TriST_INT); 54821769Sjkh outb(iobase + CMD_REG, Bank0_Sel); 54921769Sjkh outb(iobase + CMD_REG, Rcv_Stop); 55021769Sjkh sc->tx_head = sc->tx_tail = sc->tx_lower_limit; 55121769Sjkh sc->tx_last = 0; /* XXX I think these two lines are not necessary, because ex_init will always be called again to reinit the interface. */ 55221769Sjkh outb(iobase + MASK_REG, All_Int); 55321769Sjkh outb(iobase + STATUS_REG, All_Int); 55421769Sjkh outb(iobase + CMD_REG, Reset_CMD); 55521769Sjkh DELAY(200); 55621769Sjkh 55721769Sjkh DODEBUG(Start_End, printf("ex_stop%d: finish\n", unit);); 55821769Sjkh} 55921769Sjkh 56021769Sjkh 56140565Sbdestatic void exintr(int unit) 56221769Sjkh{ 56321769Sjkh struct ex_softc *sc = &ex_sc[unit]; 56421769Sjkh struct ifnet *ifp = &sc->arpcom.ac_if; 56521769Sjkh int iobase = sc->iobase; 56631016Sphk int int_status, send_pkts; 56721769Sjkh 56821769Sjkh DODEBUG(Start_End, printf("exintr%d: start\n", unit);); 56921769Sjkh 57021769Sjkh#ifdef EXDEBUG 57121769Sjkh if (++exintr_count != 1) 57221769Sjkh printf("WARNING: nested interrupt (%d). Mail the author.\n", exintr_count); 57321769Sjkh#endif 57421769Sjkh 57521769Sjkh send_pkts = 0; 57621769Sjkh while ((int_status = inb(iobase + STATUS_REG)) & (Tx_Int | Rx_Int)) { 57721769Sjkh if (int_status & Rx_Int) { 57821769Sjkh outb(iobase + STATUS_REG, Rx_Int); 57921769Sjkh ex_rx_intr(unit); 58021769Sjkh } 58121769Sjkh else if (int_status & Tx_Int) { 58221769Sjkh outb(iobase + STATUS_REG, Tx_Int); 58321769Sjkh ex_tx_intr(unit); 58421769Sjkh send_pkts = 1; 58521769Sjkh } 58621769Sjkh } 58721769Sjkh 58821769Sjkh /* 58921769Sjkh * If any packet has been transmitted, and there are queued packets to 59021769Sjkh * be sent, attempt to send more packets to the network card. 59121769Sjkh */ 59221769Sjkh 59321769Sjkh if (send_pkts && (ifp->if_snd.ifq_head != NULL)) 59421769Sjkh ex_start(ifp); 59521769Sjkh 59621769Sjkh#ifdef EXDEBUG 59721769Sjkh exintr_count--; 59821769Sjkh#endif 59921769Sjkh 60021769Sjkh DODEBUG(Start_End, printf("exintr%d: finish\n", unit);); 60121769Sjkh} 60221769Sjkh 60321769Sjkh 60421769Sjkhvoid ex_tx_intr(int unit) 60521769Sjkh{ 60621769Sjkh register struct ex_softc *sc = &ex_sc[unit]; 60721769Sjkh register struct ifnet *ifp = &sc->arpcom.ac_if; 60821769Sjkh register int iobase = sc->iobase; 60921769Sjkh int tx_status; 61021769Sjkh 61121769Sjkh DODEBUG(Start_End, printf("ex_tx_intr%d: start\n", unit);); 61221769Sjkh 61321769Sjkh /* 61421769Sjkh * - Cancel the watchdog. 61521769Sjkh * For all packets transmitted since last transmit interrupt: 61621769Sjkh * - Advance chain pointer to next queued packet. 61721769Sjkh * - Update statistics. 61821769Sjkh */ 61921769Sjkh 62021769Sjkh ifp->if_timer = 0; 62121769Sjkh while (sc->tx_head != sc->tx_tail) { 62221769Sjkh outw(iobase + HOST_ADDR_REG, sc->tx_head); 62321769Sjkh if (! inw(iobase + IO_PORT_REG) & Done_bit) 62421769Sjkh break; 62521769Sjkh tx_status = inw(iobase + IO_PORT_REG); 62621769Sjkh sc->tx_head = inw(iobase + IO_PORT_REG); 62721769Sjkh if (tx_status & TX_OK_bit) 62821769Sjkh ifp->if_opackets++; 62921769Sjkh else 63021769Sjkh ifp->if_oerrors++; 63121769Sjkh ifp->if_collisions += tx_status & No_Collisions_bits; 63221769Sjkh } 63321769Sjkh 63421769Sjkh /* 63521769Sjkh * The card should be ready to accept more packets now. 63621769Sjkh */ 63721769Sjkh 63821769Sjkh ifp->if_flags &= ~IFF_OACTIVE; 63921769Sjkh DODEBUG(Status, printf("OIDLE tx_intr\n");); 64021769Sjkh 64121769Sjkh DODEBUG(Start_End, printf("ex_tx_intr%d: finish\n", unit);); 64221769Sjkh} 64321769Sjkh 64421769Sjkh 64521769Sjkhvoid ex_rx_intr(int unit) 64621769Sjkh{ 64721769Sjkh register struct ex_softc *sc = &ex_sc[unit]; 64821769Sjkh register struct ifnet *ifp = &sc->arpcom.ac_if; 64921769Sjkh register int iobase = sc->iobase; 65021769Sjkh int rx_status, pkt_len, QQQ; 65121769Sjkh register struct mbuf *m, *ipkt; 65221769Sjkh struct ether_header *eh; 65321769Sjkh 65421769Sjkh DODEBUG(Start_End, printf("ex_rx_intr%d: start\n", unit);); 65521769Sjkh 65621769Sjkh /* 65721769Sjkh * For all packets received since last receive interrupt: 65821769Sjkh * - If packet ok, read it into a new mbuf and queue it to interface, 65921769Sjkh * updating statistics. 66021769Sjkh * - If packet bad, just discard it, and update statistics. 66121769Sjkh * Finally, advance receive stop limit in card's memory to new location. 66221769Sjkh */ 66321769Sjkh 66421769Sjkh outw(iobase + HOST_ADDR_REG, sc->rx_head); 66521769Sjkh while (inw(iobase + IO_PORT_REG) == RCV_Done) { 66621769Sjkh rx_status = inw(iobase + IO_PORT_REG); 66721769Sjkh sc->rx_head = inw(iobase + IO_PORT_REG); 66821769Sjkh QQQ = pkt_len = inw(iobase + IO_PORT_REG); 66921769Sjkh if (rx_status & RCV_OK_bit) { 67021769Sjkh MGETHDR(m, M_DONTWAIT, MT_DATA); 67121769Sjkh ipkt = m; 67221769Sjkh if (ipkt == NULL) 67321769Sjkh ifp->if_iqdrops++; 67421769Sjkh else { 67521769Sjkh ipkt->m_pkthdr.rcvif = ifp; 67621769Sjkh ipkt->m_pkthdr.len = pkt_len; 67721769Sjkh ipkt->m_len = MHLEN; 67821769Sjkh while (pkt_len > 0) { 67921769Sjkh if (pkt_len > MINCLSIZE) { 68021769Sjkh MCLGET(m, M_DONTWAIT); 68121769Sjkh if (m->m_flags & M_EXT) 68221769Sjkh m->m_len = MCLBYTES; 68321769Sjkh else { 68421769Sjkh m_freem(ipkt); 68521769Sjkh ifp->if_iqdrops++; 68621769Sjkh goto rx_another; 68721769Sjkh } 68821769Sjkh } 68921769Sjkh m->m_len = min(m->m_len, pkt_len); 69021769Sjkh 69121769Sjkh /* 69221769Sjkh * NOTE: I'm assuming that all mbufs allocated are of even length, 69321769Sjkh * except for the last one in an odd-length packet. 69421769Sjkh */ 69521769Sjkh insw(iobase + IO_PORT_REG, mtod(m, caddr_t), m->m_len / 2); 69621769Sjkh if (m->m_len & 1) 69721769Sjkh *(mtod(m, caddr_t) + m->m_len - 1) = inb(iobase + IO_PORT_REG); 69821769Sjkh pkt_len -= m->m_len; 69921769Sjkh if (pkt_len > 0) { 70021769Sjkh MGET(m->m_next, M_DONTWAIT, MT_DATA); 70121769Sjkh if (m->m_next == NULL) { 70221769Sjkh m_freem(ipkt); 70321769Sjkh ifp->if_iqdrops++; 70421769Sjkh goto rx_another; 70521769Sjkh } 70621769Sjkh m = m->m_next; 70721769Sjkh m->m_len = MLEN; 70821769Sjkh } 70921769Sjkh } 71021769Sjkh eh = mtod(ipkt, struct ether_header *); 71121769Sjkh#ifdef EXDEBUG 71221769Sjkh if (debug_mask & Rcvd_Pkts) { 71321769Sjkh if ((eh->ether_dhost[5] != 0xff) || (eh->ether_dhost[0] != 0xff)) { 71421769Sjkh printf("Receive packet with %d data bytes: %6D -> ", QQQ, eh->ether_shost, ":"); 71521769Sjkh printf("%6D\n", eh->ether_dhost, ":"); 71621769Sjkh } /* QQQ */ 71721769Sjkh } 71821769Sjkh#endif 71948645Sdes#if NBPF > 0 72021769Sjkh if (ifp->if_bpf != NULL) { 72121769Sjkh bpf_mtap(ifp, ipkt); 72221769Sjkh 72321769Sjkh /* 72421769Sjkh * Note that the interface cannot be in promiscuous mode if there are 72521769Sjkh * no BPF listeners. And if we are in promiscuous mode, we have to 72621769Sjkh * check if this packet is really ours. 72721769Sjkh */ 72821769Sjkh if ((ifp->if_flags & IFF_PROMISC) && 72921769Sjkh (eh->ether_dhost[0] & 1) == 0 && 73021769Sjkh bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, sizeof(eh->ether_dhost)) != 0 && 73121769Sjkh bcmp(eh->ether_dhost, etherbroadcastaddr, sizeof(eh->ether_dhost)) != 0) { 73221769Sjkh m_freem(ipkt); 73321769Sjkh goto rx_another; 73421769Sjkh } 73521769Sjkh } 73621769Sjkh#endif 73721769Sjkh m_adj(ipkt, sizeof(struct ether_header)); 73821769Sjkh ether_input(ifp, eh, ipkt); 73921769Sjkh ifp->if_ipackets++; 74021769Sjkh } 74121769Sjkh } 74221769Sjkh else 74321769Sjkh ifp->if_ierrors++; 74421769Sjkh outw(iobase + HOST_ADDR_REG, sc->rx_head); 74527125Sbde rx_another: ; 74621769Sjkh } 74721769Sjkh if (sc->rx_head < sc->rx_lower_limit + 2) 74821769Sjkh outw(iobase + RCV_STOP_REG, sc->rx_upper_limit); 74921769Sjkh else 75021769Sjkh outw(iobase + RCV_STOP_REG, sc->rx_head - 2); 75121769Sjkh 75221769Sjkh DODEBUG(Start_End, printf("ex_rx_intr%d: finish\n", unit);); 75321769Sjkh} 75421769Sjkh 75521769Sjkh 75636735Sdfrint ex_ioctl(register struct ifnet *ifp, u_long cmd, caddr_t data) 75721769Sjkh{ 75821769Sjkh struct ex_softc *sc = &ex_sc[ifp->if_unit]; 75921769Sjkh int s, error = 0; 76021769Sjkh 76121769Sjkh DODEBUG(Start_End, printf("ex_ioctl%d: start ", ifp->if_unit);); 76221769Sjkh 76321769Sjkh s = splimp(); 76421769Sjkh 76521769Sjkh switch(cmd) { 76621769Sjkh case SIOCSIFADDR: 76750002Smdodd case SIOCGIFADDR: 76850002Smdodd case SIOCSIFMTU: 76950026Smdodd error = ether_ioctl(ifp, cmd, data); 77021769Sjkh break; 77121769Sjkh 77221769Sjkh case SIOCSIFFLAGS: 77321769Sjkh DODEBUG(Start_End, printf("SIOCSIFFLAGS");); 77421769Sjkh if ((ifp->if_flags & IFF_UP) == 0 && ifp->if_flags & IFF_RUNNING) { 77521769Sjkh ifp->if_flags &= ~IFF_RUNNING; 77621769Sjkh ex_stop(ifp->if_unit); 77721769Sjkh } 77821769Sjkh else 77921769Sjkh ex_init(sc); 78021769Sjkh break; 78121769Sjkh#ifdef NODEF 78221769Sjkh case SIOCGHWADDR: 78321769Sjkh DODEBUG(Start_End, printf("SIOCGHWADDR");); 78421769Sjkh bcopy((caddr_t) sc->sc_addr, (caddr_t) &ifr->ifr_data, sizeof(sc->sc_addr)); 78521769Sjkh break; 78621769Sjkh#endif 78721769Sjkh case SIOCADDMULTI: 78821769Sjkh DODEBUG(Start_End, printf("SIOCADDMULTI");); 78921769Sjkh case SIOCDELMULTI: 79021769Sjkh DODEBUG(Start_End, printf("SIOCDELMULTI");); 79121769Sjkh /* XXX Support not done yet. */ 79221769Sjkh error = EINVAL; 79321769Sjkh break; 79421769Sjkh default: 79521769Sjkh DODEBUG(Start_End, printf("unknown");); 79621769Sjkh error = EINVAL; 79721769Sjkh } 79821769Sjkh 79921769Sjkh splx(s); 80021769Sjkh 80121769Sjkh DODEBUG(Start_End, printf("\nex_ioctl%d: finish\n", ifp->if_unit);); 80221769Sjkh return(error); 80321769Sjkh} 80421769Sjkh 80521769Sjkh 80621769Sjkhvoid ex_reset(int unit) 80721769Sjkh{ 80821769Sjkh struct ex_softc *sc = &ex_sc[unit]; 80921769Sjkh int s; 81021769Sjkh 81121769Sjkh DODEBUG(Start_End, printf("ex_reset%d: start\n", unit);); 81221769Sjkh 81321769Sjkh s = splimp(); 81421769Sjkh 81521769Sjkh ex_stop(unit); 81621769Sjkh ex_init(sc); 81721769Sjkh 81821769Sjkh splx(s); 81921769Sjkh 82021769Sjkh DODEBUG(Start_End, printf("ex_reset%d: finish\n", unit);); 82121769Sjkh} 82221769Sjkh 82321769Sjkh 82421769Sjkhvoid ex_watchdog(struct ifnet *ifp) 82521769Sjkh{ 82621769Sjkh 82721769Sjkh DODEBUG(Start_End, printf("ex_watchdog%d: start\n", ifp->if_unit);); 82821769Sjkh 82921769Sjkh ifp->if_flags &= ~IFF_OACTIVE; 83021769Sjkh DODEBUG(Status, printf("OIDLE watchdog\n");); 83121769Sjkh ifp->if_oerrors++; 83221769Sjkh ex_reset(ifp->if_unit); 83321769Sjkh ex_start(ifp); 83421769Sjkh 83521769Sjkh DODEBUG(Start_End, printf("ex_watchdog%d: finish\n", ifp->if_unit);); 83621769Sjkh} 83721769Sjkh 83821769Sjkh 83921769Sjkhstatic u_short eeprom_read(int iobase, int location) 84021769Sjkh{ 84121769Sjkh int i; 84221769Sjkh u_short data = 0; 84321769Sjkh int ee_addr; 84421769Sjkh int read_cmd = location | EE_READ_CMD; 84521769Sjkh short ctrl_val = EECS; 84621769Sjkh 84721769Sjkh ee_addr = iobase + EEPROM_REG; 84821769Sjkh outb(iobase + CMD_REG, Bank2_Sel); 84921769Sjkh outb(ee_addr, EECS); 85021769Sjkh for (i = 8; i >= 0; i--) { 85121769Sjkh short outval = (read_cmd & (1 << i)) ? ctrl_val | EEDI : ctrl_val; 85221769Sjkh outb(ee_addr, outval); 85321769Sjkh outb(ee_addr, outval | EESK); 85421769Sjkh DELAY(3); 85521769Sjkh outb(ee_addr, outval); 85621769Sjkh DELAY(2); 85721769Sjkh } 85821769Sjkh outb(ee_addr, ctrl_val); 85921769Sjkh 86021769Sjkh for (i = 16; i > 0; i--) { 86121769Sjkh outb(ee_addr, ctrl_val | EESK); 86221769Sjkh DELAY(3); 86321769Sjkh data = (data << 1) | ((inb(ee_addr) & EEDO) ? 1 : 0); 86421769Sjkh outb(ee_addr, ctrl_val); 86521769Sjkh DELAY(2); 86621769Sjkh } 86721769Sjkh 86821769Sjkh ctrl_val &= ~EECS; 86921769Sjkh outb(ee_addr, ctrl_val | EESK); 87021769Sjkh DELAY(3); 87121769Sjkh outb(ee_addr, ctrl_val); 87221769Sjkh DELAY(2); 87321769Sjkh outb(iobase + CMD_REG, Bank0_Sel); 87421769Sjkh return(data); 87521769Sjkh} 87621769Sjkh 87721769Sjkh#endif /* NEX > 0 */ 878