if_ex.c revision 66440
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 * 2750477Speter * $FreeBSD: head/sys/dev/ex/if_ex.c 66440 2000-09-29 03:58:06Z imp $ 2852286Smdodd * 2952286Smdodd * MAINTAINER: Matthew N. Dodd <winter@jurai.net> 3052286Smdodd * <mdodd@FreeBSD.org> 3121769Sjkh */ 3221769Sjkh 3321769Sjkh/* 3429877Smsmith * Intel EtherExpress Pro/10, Pro/10+ Ethernet driver 3521769Sjkh * 3621769Sjkh * Revision history: 3721769Sjkh * 3821769Sjkh * 30-Oct-1996: first beta version. Inet and BPF supported, but no multicast. 3921769Sjkh */ 4021769Sjkh 4121769Sjkh#include <sys/param.h> 4221769Sjkh#include <sys/systm.h> 4352286Smdodd#include <sys/kernel.h> 4424204Sbde#include <sys/sockio.h> 4521769Sjkh#include <sys/mbuf.h> 4621769Sjkh#include <sys/socket.h> 4721769Sjkh 4852286Smdodd#include <sys/module.h> 4952286Smdodd#include <sys/bus.h> 5052286Smdodd 5152286Smdodd#include <machine/bus.h> 5252286Smdodd#include <machine/resource.h> 5352286Smdodd#include <sys/rman.h> 5452286Smdodd 5557987Smdodd#include <net/if.h> 5657987Smdodd#include <net/if_arp.h> 5757987Smdodd#include <net/if_media.h> 5850026Smdodd#include <net/ethernet.h> 5957987Smdodd#include <net/bpf.h> 6021769Sjkh 6150026Smdodd#include <netinet/in.h> 6250026Smdodd#include <netinet/if_ether.h> 6350026Smdodd 6421769Sjkh#include <machine/clock.h> 6521769Sjkh 6652286Smdodd#include <isa/isavar.h> 6752286Smdodd#include <isa/pnpvar.h> 6852286Smdodd 6955953Speter#include <dev/ex/if_exreg.h> 7059816Smdodd#include <dev/ex/if_exvar.h> 7121769Sjkh 7221769Sjkh#ifdef EXDEBUG 7352286Smdodd# define Start_End 1 7452286Smdodd# define Rcvd_Pkts 2 7552286Smdodd# define Sent_Pkts 4 7652286Smdodd# define Status 8 7721769Sjkhstatic int debug_mask = 0; 7821769Sjkhstatic int exintr_count = 0; 7952286Smdodd# define DODEBUG(level, action) if (level & debug_mask) action 8021769Sjkh#else 8152286Smdodd# define DODEBUG(level, action) 8221769Sjkh#endif 8321769Sjkh 8459816Smdoddchar irq2eemap[] = 8552286Smdodd { -1, -1, 0, 1, -1, 2, -1, -1, -1, 0, 3, 4, -1, -1, -1, -1 }; 8659816Smdoddu_char ee2irqmap[] = 8752286Smdodd { 9, 3, 5, 10, 11, 0, 0, 0 }; 8859816Smdodd 8959816Smdoddchar plus_irq2eemap[] = 9052286Smdodd { -1, -1, -1, 0, 1, 2, -1, 3, -1, 4, 5, 6, 7, -1, -1, -1 }; 9159816Smdoddu_char plus_ee2irqmap[] = 9252286Smdodd { 3, 4, 5, 7, 9, 10, 11, 12 }; 9321769Sjkh 9452286Smdodd/* Network Interface Functions */ 9552286Smdoddstatic void ex_init __P((void *)); 9652286Smdoddstatic void ex_start __P((struct ifnet *)); 9752286Smdoddstatic int ex_ioctl __P((struct ifnet *, u_long, caddr_t)); 9852286Smdoddstatic void ex_watchdog __P((struct ifnet *)); 9921769Sjkh 10057987Smdodd/* ifmedia Functions */ 10157987Smdoddstatic int ex_ifmedia_upd __P((struct ifnet *)); 10257987Smdoddstatic void ex_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); 10357987Smdodd 10459816Smdoddstatic int ex_get_media __P((u_int32_t iobase)); 10559816Smdodd 10666440Simp void ex_stop __P((struct ex_softc *)); 10752286Smdoddstatic void ex_reset __P((struct ex_softc *)); 10852286Smdodd 10952286Smdoddstatic void ex_tx_intr __P((struct ex_softc *)); 11052286Smdoddstatic void ex_rx_intr __P((struct ex_softc *)); 11152286Smdodd 11259816Smdoddint 11359816Smdoddlook_for_card (u_int32_t iobase) 11421769Sjkh{ 11521769Sjkh int count1, count2; 11621769Sjkh 11721769Sjkh /* 11821769Sjkh * Check for the i82595 signature, and check that the round robin 11921769Sjkh * counter actually advances. 12021769Sjkh */ 12121769Sjkh if (((count1 = inb(iobase + ID_REG)) & Id_Mask) != Id_Sig) 12221769Sjkh return(0); 12321769Sjkh count2 = inb(iobase + ID_REG); 12421769Sjkh count2 = inb(iobase + ID_REG); 12521769Sjkh count2 = inb(iobase + ID_REG); 12652286Smdodd 12721769Sjkh return((count2 & Counter_bits) == ((count1 + 0xc0) & Counter_bits)); 12821769Sjkh} 12921769Sjkh 13059816Smdoddvoid 13152286Smdoddex_get_address (u_int32_t iobase, u_char *enaddr) 13221769Sjkh{ 13352286Smdodd u_int16_t eaddr_tmp; 13421769Sjkh 13552286Smdodd eaddr_tmp = eeprom_read(iobase, EE_Eth_Addr_Lo); 13652286Smdodd enaddr[5] = eaddr_tmp & 0xff; 13752286Smdodd enaddr[4] = eaddr_tmp >> 8; 13852286Smdodd eaddr_tmp = eeprom_read(iobase, EE_Eth_Addr_Mid); 13952286Smdodd enaddr[3] = eaddr_tmp & 0xff; 14052286Smdodd enaddr[2] = eaddr_tmp >> 8; 14152286Smdodd eaddr_tmp = eeprom_read(iobase, EE_Eth_Addr_Hi); 14252286Smdodd enaddr[1] = eaddr_tmp & 0xff; 14352286Smdodd enaddr[0] = eaddr_tmp >> 8; 14452286Smdodd 14552286Smdodd return; 14652286Smdodd} 14721769Sjkh 14859816Smdoddint 14952286Smdoddex_card_type (u_char *enaddr) 15052286Smdodd{ 15152286Smdodd if ((enaddr[0] == 0x00) && (enaddr[1] == 0xA0) && (enaddr[2] == 0xC9)) 15252286Smdodd return (CARD_TYPE_EX_10_PLUS); 15352286Smdodd 15452286Smdodd return (CARD_TYPE_EX_10); 15552286Smdodd} 15652286Smdodd 15755882Smdodd/* 15859816Smdodd * Caller is responsible for eventually calling 15959816Smdodd * ex_release_resources() on failure. 16055882Smdodd */ 16159816Smdoddint 16259816Smdoddex_alloc_resources (device_t dev) 16355882Smdodd{ 16459816Smdodd struct ex_softc * sc = device_get_softc(dev); 16559816Smdodd int error = 0; 16655882Smdodd 16759816Smdodd sc->ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->ioport_rid, 16859816Smdodd 0, ~0, 1, RF_ACTIVE); 16959816Smdodd if (!sc->ioport) { 17059816Smdodd device_printf(dev, "No I/O space?!\n"); 17159816Smdodd error = ENOMEM; 17259816Smdodd goto bad; 17359816Smdodd } 17457989Smdodd 17559816Smdodd sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irq_rid, 17659816Smdodd 0, ~0, 1, RF_ACTIVE); 17755882Smdodd 17859816Smdodd if (!sc->irq) { 17959816Smdodd device_printf(dev, "No IRQ?!\n"); 18059816Smdodd error = ENOMEM; 18159816Smdodd goto bad; 18255882Smdodd } 18355882Smdodd 18459816Smdoddbad: 18559816Smdodd return (error); 18655882Smdodd} 18755882Smdodd 18859816Smdoddvoid 18959816Smdoddex_release_resources (device_t dev) 19052286Smdodd{ 19159816Smdodd struct ex_softc * sc = device_get_softc(dev); 19252286Smdodd 19359816Smdodd if (sc->ih) { 19459816Smdodd bus_teardown_intr(dev, sc->irq, sc->ih); 19559816Smdodd sc->ih = NULL; 19652286Smdodd } 19752286Smdodd 19859816Smdodd if (sc->ioport) { 19959816Smdodd bus_release_resource(dev, SYS_RES_IOPORT, 20059816Smdodd sc->ioport_rid, sc->ioport); 20159816Smdodd sc->ioport = NULL; 20252286Smdodd } 20352286Smdodd 20459816Smdodd if (sc->irq) { 20559816Smdodd bus_release_resource(dev, SYS_RES_IRQ, 20659816Smdodd sc->irq_rid, sc->irq); 20759816Smdodd sc->irq = NULL; 20857989Smdodd } 20957989Smdodd 21059816Smdodd return; 21152286Smdodd} 21252286Smdodd 21359816Smdoddint 21459816Smdoddex_attach(device_t dev) 21552286Smdodd{ 21652286Smdodd struct ex_softc * sc = device_get_softc(dev); 21752286Smdodd struct ifnet * ifp = &sc->arpcom.ac_if; 21857987Smdodd struct ifmedia * ifm; 21952286Smdodd int unit = device_get_unit(dev); 22057987Smdodd u_int16_t temp; 22152286Smdodd 22229877Smsmith /* work out which set of irq <-> internal tables to use */ 22352286Smdodd if (ex_card_type(sc->arpcom.ac_enaddr) == CARD_TYPE_EX_10_PLUS) { 22429877Smsmith sc->irq2ee = plus_irq2eemap; 22529877Smsmith sc->ee2irq = plus_ee2irqmap; 22652286Smdodd } else { 22729877Smsmith sc->irq2ee = irq2eemap; 22829877Smsmith sc->ee2irq = ee2irqmap; 22929877Smsmith } 23029877Smsmith 23121769Sjkh sc->mem_size = CARD_RAM_SIZE; /* XXX This should be read from the card itself. */ 23221769Sjkh 23321769Sjkh /* 23421769Sjkh * Initialize the ifnet structure. 23521769Sjkh */ 23621769Sjkh ifp->if_softc = sc; 23721769Sjkh ifp->if_unit = unit; 23821769Sjkh ifp->if_name = "ex"; 23955883Smdodd ifp->if_mtu = ETHERMTU; 24055883Smdodd ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST /* XXX not done yet. | IFF_MULTICAST */; 24121769Sjkh ifp->if_output = ether_output; 24221769Sjkh ifp->if_start = ex_start; 24321769Sjkh ifp->if_ioctl = ex_ioctl; 24421769Sjkh ifp->if_watchdog = ex_watchdog; 24555883Smdodd ifp->if_init = ex_init; 24655883Smdodd ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; 24721769Sjkh 24857987Smdodd ifmedia_init(&sc->ifmedia, 0, ex_ifmedia_upd, ex_ifmedia_sts); 24957987Smdodd 25057987Smdodd temp = eeprom_read(sc->iobase, EE_W5); 25157987Smdodd if (temp & EE_W5_PORT_TPE) 25257987Smdodd ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); 25357987Smdodd if (temp & EE_W5_PORT_BNC) 25457987Smdodd ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_2, 0, NULL); 25557987Smdodd if (temp & EE_W5_PORT_AUI) 25657987Smdodd ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_5, 0, NULL); 25757987Smdodd 25857987Smdodd ifmedia_set(&sc->ifmedia, ex_get_media(sc->iobase)); 25957987Smdodd 26057987Smdodd ifm = &sc->ifmedia; 26157987Smdodd ifm->ifm_media = ifm->ifm_cur->ifm_media; 26257987Smdodd ex_ifmedia_upd(ifp); 26357987Smdodd 26421769Sjkh /* 26521769Sjkh * Attach the interface. 26621769Sjkh */ 26763090Sarchie ether_ifattach(ifp, ETHER_BPF_SUPPORTED); 26821769Sjkh 26955883Smdodd device_printf(sc->dev, "Ethernet address %6D\n", 27055883Smdodd sc->arpcom.ac_enaddr, ":"); 27152286Smdodd 27252286Smdodd return(0); 27321769Sjkh} 27421769Sjkh 27555883Smdoddstatic void 27655883Smdoddex_init(void *xsc) 27721769Sjkh{ 27852286Smdodd struct ex_softc * sc = (struct ex_softc *) xsc; 27952286Smdodd struct ifnet * ifp = &sc->arpcom.ac_if; 28052286Smdodd int s; 28152286Smdodd int i; 28252286Smdodd register int iobase = sc->iobase; 28352286Smdodd unsigned short temp_reg; 28421769Sjkh 28521769Sjkh DODEBUG(Start_End, printf("ex_init%d: start\n", ifp->if_unit);); 28621769Sjkh 28752286Smdodd if (ifp->if_addrhead.tqh_first == NULL) { 28852286Smdodd return; 28952286Smdodd } 29021769Sjkh s = splimp(); 29159816Smdodd ifp->if_timer = 0; 29221769Sjkh 29321769Sjkh /* 29421769Sjkh * Load the ethernet address into the card. 29521769Sjkh */ 29621769Sjkh outb(iobase + CMD_REG, Bank2_Sel); 29721769Sjkh temp_reg = inb(iobase + EEPROM_REG); 29852286Smdodd if (temp_reg & Trnoff_Enable) { 29952286Smdodd outb(iobase + EEPROM_REG, temp_reg & ~Trnoff_Enable); 30052286Smdodd } 30152286Smdodd for (i = 0; i < ETHER_ADDR_LEN; i++) { 30252286Smdodd outb(iobase + I_ADDR_REG0 + i, sc->arpcom.ac_enaddr[i]); 30352286Smdodd } 30421769Sjkh /* 30521769Sjkh * - Setup transmit chaining and discard bad received frames. 30621769Sjkh * - Match broadcast. 30721769Sjkh * - Clear test mode. 30821769Sjkh * - Set receiving mode. 30921769Sjkh * - Set IRQ number. 31021769Sjkh */ 31121769Sjkh outb(iobase + REG1, inb(iobase + REG1) | Tx_Chn_Int_Md | Tx_Chn_ErStp | Disc_Bad_Fr); 31221769Sjkh outb(iobase + REG2, inb(iobase + REG2) | No_SA_Ins | RX_CRC_InMem); 31326545Sgibbs outb(iobase + REG3, inb(iobase + REG3) & 0x3f /* XXX constants. */ ); 31421769Sjkh outb(iobase + CMD_REG, Bank1_Sel); 31529877Smsmith outb(iobase + INT_NO_REG, (inb(iobase + INT_NO_REG) & 0xf8) | sc->irq2ee[sc->irq_no]); 31621769Sjkh 31721769Sjkh /* 31821769Sjkh * Divide the available memory in the card into rcv and xmt buffers. 31921769Sjkh * By default, I use the first 3/4 of the memory for the rcv buffer, 32021769Sjkh * and the remaining 1/4 of the memory for the xmt buffer. 32121769Sjkh */ 32221769Sjkh sc->rx_mem_size = sc->mem_size * 3 / 4; 32321769Sjkh sc->tx_mem_size = sc->mem_size - sc->rx_mem_size; 32421769Sjkh sc->rx_lower_limit = 0x0000; 32521769Sjkh sc->rx_upper_limit = sc->rx_mem_size - 2; 32621769Sjkh sc->tx_lower_limit = sc->rx_mem_size; 32721769Sjkh sc->tx_upper_limit = sc->mem_size - 2; 32821769Sjkh outb(iobase + RCV_LOWER_LIMIT_REG, sc->rx_lower_limit >> 8); 32921769Sjkh outb(iobase + RCV_UPPER_LIMIT_REG, sc->rx_upper_limit >> 8); 33021769Sjkh outb(iobase + XMT_LOWER_LIMIT_REG, sc->tx_lower_limit >> 8); 33121769Sjkh outb(iobase + XMT_UPPER_LIMIT_REG, sc->tx_upper_limit >> 8); 33221769Sjkh 33321769Sjkh /* 33421769Sjkh * Enable receive and transmit interrupts, and clear any pending int. 33521769Sjkh */ 33621769Sjkh outb(iobase + REG1, inb(iobase + REG1) | TriST_INT); 33721769Sjkh outb(iobase + CMD_REG, Bank0_Sel); 33821769Sjkh outb(iobase + MASK_REG, All_Int & ~(Rx_Int | Tx_Int)); 33921769Sjkh outb(iobase + STATUS_REG, All_Int); 34021769Sjkh 34121769Sjkh /* 34221769Sjkh * Initialize receive and transmit ring buffers. 34321769Sjkh */ 34421769Sjkh outw(iobase + RCV_BAR, sc->rx_lower_limit); 34521769Sjkh sc->rx_head = sc->rx_lower_limit; 34626545Sgibbs outw(iobase + RCV_STOP_REG, sc->rx_upper_limit | 0xfe); 34721769Sjkh outw(iobase + XMT_BAR, sc->tx_lower_limit); 34821769Sjkh sc->tx_head = sc->tx_tail = sc->tx_lower_limit; 34921769Sjkh 35021769Sjkh ifp->if_flags |= IFF_RUNNING; 35121769Sjkh ifp->if_flags &= ~IFF_OACTIVE; 35221769Sjkh DODEBUG(Status, printf("OIDLE init\n");); 35321769Sjkh 35421769Sjkh /* 35521769Sjkh * Final reset of the board, and enable operation. 35621769Sjkh */ 35721769Sjkh outb(iobase + CMD_REG, Sel_Reset_CMD); 35821769Sjkh DELAY(2); 35921769Sjkh outb(iobase + CMD_REG, Rcv_Enable_CMD); 36021769Sjkh 36121769Sjkh ex_start(ifp); 36221769Sjkh splx(s); 36321769Sjkh 36421769Sjkh DODEBUG(Start_End, printf("ex_init%d: finish\n", ifp->if_unit);); 36521769Sjkh} 36621769Sjkh 36721769Sjkh 36855883Smdoddstatic void 36952286Smdoddex_start(struct ifnet *ifp) 37021769Sjkh{ 37152286Smdodd struct ex_softc * sc = ifp->if_softc; 37252286Smdodd int iobase = sc->iobase; 37352286Smdodd int i, s, len, data_len, avail, dest, next; 37452286Smdodd unsigned char tmp16[2]; 37552286Smdodd struct mbuf * opkt; 37652286Smdodd struct mbuf * m; 37721769Sjkh 37852286Smdodd DODEBUG(Start_End, printf("ex_start%d: start\n", unit);); 37921769Sjkh 38052286Smdodd s = splimp(); 38121769Sjkh 38252286Smdodd /* 38352286Smdodd * Main loop: send outgoing packets to network card until there are no 38452286Smdodd * more packets left, or the card cannot accept any more yet. 38552286Smdodd */ 38652286Smdodd while (((opkt = ifp->if_snd.ifq_head) != NULL) && 38752286Smdodd !(ifp->if_flags & IFF_OACTIVE)) { 38821769Sjkh 38952286Smdodd /* 39052286Smdodd * Ensure there is enough free transmit buffer space for 39152286Smdodd * this packet, including its header. Note: the header 39252286Smdodd * cannot wrap around the end of the transmit buffer and 39352286Smdodd * must be kept together, so we allow space for twice the 39452286Smdodd * length of the header, just in case. 39552286Smdodd */ 39621769Sjkh 39752286Smdodd for (len = 0, m = opkt; m != NULL; m = m->m_next) { 39852286Smdodd len += m->m_len; 39952286Smdodd } 40052286Smdodd 40152286Smdodd data_len = len; 40252286Smdodd 40352286Smdodd DODEBUG(Sent_Pkts, printf("1. Sending packet with %d data bytes. ", data_len);); 40452286Smdodd 40552286Smdodd if (len & 1) { 40652286Smdodd len += XMT_HEADER_LEN + 1; 40752286Smdodd } else { 40852286Smdodd len += XMT_HEADER_LEN; 40952286Smdodd } 41052286Smdodd 41152286Smdodd if ((i = sc->tx_tail - sc->tx_head) >= 0) { 41252286Smdodd avail = sc->tx_mem_size - i; 41352286Smdodd } else { 41452286Smdodd avail = -i; 41552286Smdodd } 41652286Smdodd 41752286Smdodd DODEBUG(Sent_Pkts, printf("i=%d, avail=%d\n", i, avail);); 41852286Smdodd 41952286Smdodd if (avail >= len + XMT_HEADER_LEN) { 42052286Smdodd IF_DEQUEUE(&ifp->if_snd, opkt); 42152286Smdodd 42221769Sjkh#ifdef EX_PSA_INTR 42352286Smdodd /* 42452286Smdodd * Disable rx and tx interrupts, to avoid corruption 42552286Smdodd * of the host address register by interrupt service 42652286Smdodd * routines. 42752286Smdodd * XXX Is this necessary with splimp() enabled? 42852286Smdodd */ 42952286Smdodd outb(iobase + MASK_REG, All_Int); 43021769Sjkh#endif 43121769Sjkh 43252286Smdodd /* 43352286Smdodd * Compute the start and end addresses of this 43452286Smdodd * frame in the tx buffer. 43552286Smdodd */ 43652286Smdodd dest = sc->tx_tail; 43752286Smdodd next = dest + len; 43821769Sjkh 43952286Smdodd if (next > sc->tx_upper_limit) { 44052286Smdodd if ((sc->tx_upper_limit + 2 - sc->tx_tail) <= 44152286Smdodd XMT_HEADER_LEN) { 44252286Smdodd dest = sc->tx_lower_limit; 44352286Smdodd next = dest + len; 44455881Smdodd } else { 44555881Smdodd next = sc->tx_lower_limit + 44655881Smdodd next - sc->tx_upper_limit - 2; 44752286Smdodd } 44852286Smdodd } 44921769Sjkh 45052286Smdodd /* 45152286Smdodd * Build the packet frame in the card's ring buffer. 45252286Smdodd */ 45352286Smdodd DODEBUG(Sent_Pkts, printf("2. dest=%d, next=%d. ", dest, next);); 45421769Sjkh 45552286Smdodd outw(iobase + HOST_ADDR_REG, dest); 45652286Smdodd outw(iobase + IO_PORT_REG, Transmit_CMD); 45752286Smdodd outw(iobase + IO_PORT_REG, 0); 45852286Smdodd outw(iobase + IO_PORT_REG, next); 45952286Smdodd outw(iobase + IO_PORT_REG, data_len); 46021769Sjkh 46152286Smdodd /* 46252286Smdodd * Output the packet data to the card. Ensure all 46352286Smdodd * transfers are 16-bit wide, even if individual 46452286Smdodd * mbufs have odd length. 46552286Smdodd */ 46621769Sjkh 46752286Smdodd for (m = opkt, i = 0; m != NULL; m = m->m_next) { 46852286Smdodd DODEBUG(Sent_Pkts, printf("[%d]", m->m_len);); 46952286Smdodd if (i) { 47052286Smdodd tmp16[1] = *(mtod(m, caddr_t)); 47152286Smdodd outsw(iobase + IO_PORT_REG, tmp16, 1); 47252286Smdodd } 47352286Smdodd outsw(iobase + IO_PORT_REG, 47452286Smdodd mtod(m, caddr_t) + i, (m->m_len - i) / 2); 47552286Smdodd 47652286Smdodd if ((i = (m->m_len - i) & 1) != 0) { 47752286Smdodd tmp16[0] = *(mtod(m, caddr_t) + 47852286Smdodd m->m_len - 1); 47952286Smdodd } 48052286Smdodd } 48152286Smdodd if (i) { 48252286Smdodd outsw(iobase + IO_PORT_REG, tmp16, 1); 48355881Smdodd } 48452286Smdodd 48555881Smdodd /* 48655881Smdodd * If there were other frames chained, update the 48755881Smdodd * chain in the last one. 48855881Smdodd */ 48955881Smdodd if (sc->tx_head != sc->tx_tail) { 49055881Smdodd if (sc->tx_tail != dest) { 49152286Smdodd outw(iobase + HOST_ADDR_REG, 49255881Smdodd sc->tx_last + XMT_Chain_Point); 49355881Smdodd outw(iobase + IO_PORT_REG, dest); 49452286Smdodd } 49555881Smdodd outw(iobase + HOST_ADDR_REG, 49655881Smdodd sc->tx_last + XMT_Byte_Count); 49755881Smdodd i = inw(iobase + IO_PORT_REG); 49855881Smdodd outw(iobase + HOST_ADDR_REG, 49955881Smdodd sc->tx_last + XMT_Byte_Count); 50055881Smdodd outw(iobase + IO_PORT_REG, i | Ch_bit); 50155881Smdodd } 50255881Smdodd 50355881Smdodd /* 50455881Smdodd * Resume normal operation of the card: 50555881Smdodd * - Make a dummy read to flush the DRAM write 50655881Smdodd * pipeline. 50755881Smdodd * - Enable receive and transmit interrupts. 50855881Smdodd * - Send Transmit or Resume_XMT command, as 50955881Smdodd * appropriate. 51055881Smdodd */ 51155881Smdodd inw(iobase + IO_PORT_REG); 51221769Sjkh#ifdef EX_PSA_INTR 51355881Smdodd outb(iobase + MASK_REG, All_Int & ~(Rx_Int | Tx_Int)); 51421769Sjkh#endif 51555881Smdodd if (sc->tx_head == sc->tx_tail) { 51655881Smdodd outw(iobase + XMT_BAR, dest); 51755881Smdodd outb(iobase + CMD_REG, Transmit_CMD); 51855881Smdodd sc->tx_head = dest; 51955881Smdodd DODEBUG(Sent_Pkts, printf("Transmit\n");); 52052286Smdodd } else { 52155881Smdodd outb(iobase + CMD_REG, Resume_XMT_List_CMD); 52255881Smdodd DODEBUG(Sent_Pkts, printf("Resume\n");); 52352286Smdodd } 52455881Smdodd 52555881Smdodd sc->tx_last = dest; 52655881Smdodd sc->tx_tail = next; 52755881Smdodd 52855881Smdodd if (ifp->if_bpf != NULL) { 52955881Smdodd bpf_mtap(ifp, opkt); 53055881Smdodd } 53155881Smdodd 53255881Smdodd ifp->if_timer = 2; 53355881Smdodd ifp->if_opackets++; 53455881Smdodd m_freem(opkt); 53555881Smdodd } else { 53655881Smdodd ifp->if_flags |= IFF_OACTIVE; 53755881Smdodd DODEBUG(Status, printf("OACTIVE start\n");); 53852286Smdodd } 53921769Sjkh } 54021769Sjkh 54152286Smdodd splx(s); 54221769Sjkh 54352286Smdodd DODEBUG(Start_End, printf("ex_start%d: finish\n", unit);); 54421769Sjkh} 54521769Sjkh 54666440Simpvoid 54752286Smdoddex_stop(struct ex_softc *sc) 54821769Sjkh{ 54952286Smdodd int iobase = sc->iobase; 55021769Sjkh 55152286Smdodd DODEBUG(Start_End, printf("ex_stop%d: start\n", unit);); 55221769Sjkh 55352286Smdodd /* 55452286Smdodd * Disable card operation: 55552286Smdodd * - Disable the interrupt line. 55652286Smdodd * - Flush transmission and disable reception. 55752286Smdodd * - Mask and clear all interrupts. 55852286Smdodd * - Reset the 82595. 55952286Smdodd */ 56052286Smdodd outb(iobase + CMD_REG, Bank1_Sel); 56152286Smdodd outb(iobase + REG1, inb(iobase + REG1) & ~TriST_INT); 56252286Smdodd outb(iobase + CMD_REG, Bank0_Sel); 56352286Smdodd outb(iobase + CMD_REG, Rcv_Stop); 56452286Smdodd sc->tx_head = sc->tx_tail = sc->tx_lower_limit; 56552286Smdodd 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. */ 56652286Smdodd outb(iobase + MASK_REG, All_Int); 56752286Smdodd outb(iobase + STATUS_REG, All_Int); 56852286Smdodd outb(iobase + CMD_REG, Reset_CMD); 56952286Smdodd DELAY(200); 57021769Sjkh 57152286Smdodd DODEBUG(Start_End, printf("ex_stop%d: finish\n", unit);); 57252286Smdodd 57352286Smdodd return; 57421769Sjkh} 57521769Sjkh 57659816Smdoddvoid 57755883Smdoddex_intr(void *arg) 57821769Sjkh{ 57952286Smdodd struct ex_softc * sc = (struct ex_softc *)arg; 58052286Smdodd struct ifnet * ifp = &sc->arpcom.ac_if; 58152286Smdodd int iobase = sc->iobase; 58252286Smdodd int int_status, send_pkts; 58321769Sjkh 58455883Smdodd DODEBUG(Start_End, printf("ex_intr%d: start\n", unit);); 58521769Sjkh 58621769Sjkh#ifdef EXDEBUG 58721769Sjkh if (++exintr_count != 1) 58821769Sjkh printf("WARNING: nested interrupt (%d). Mail the author.\n", exintr_count); 58921769Sjkh#endif 59021769Sjkh 59152286Smdodd send_pkts = 0; 59252286Smdodd while ((int_status = inb(iobase + STATUS_REG)) & (Tx_Int | Rx_Int)) { 59352286Smdodd if (int_status & Rx_Int) { 59452286Smdodd outb(iobase + STATUS_REG, Rx_Int); 59521769Sjkh 59652286Smdodd ex_rx_intr(sc); 59752286Smdodd } else if (int_status & Tx_Int) { 59852286Smdodd outb(iobase + STATUS_REG, Tx_Int); 59921769Sjkh 60052286Smdodd ex_tx_intr(sc); 60152286Smdodd send_pkts = 1; 60252286Smdodd } 60352286Smdodd } 60421769Sjkh 60552286Smdodd /* 60652286Smdodd * If any packet has been transmitted, and there are queued packets to 60752286Smdodd * be sent, attempt to send more packets to the network card. 60852286Smdodd */ 60952286Smdodd 61052286Smdodd if (send_pkts && (ifp->if_snd.ifq_head != NULL)) { 61152286Smdodd ex_start(ifp); 61252286Smdodd } 61352286Smdodd 61421769Sjkh#ifdef EXDEBUG 61521769Sjkh exintr_count--; 61621769Sjkh#endif 61721769Sjkh 61855883Smdodd DODEBUG(Start_End, printf("ex_intr%d: finish\n", unit);); 61952286Smdodd 62052286Smdodd return; 62121769Sjkh} 62221769Sjkh 62352286Smdoddstatic void 62452286Smdoddex_tx_intr(struct ex_softc *sc) 62521769Sjkh{ 62652286Smdodd struct ifnet * ifp = &sc->arpcom.ac_if; 62752286Smdodd int iobase = sc->iobase; 62852286Smdodd int tx_status; 62921769Sjkh 63052286Smdodd DODEBUG(Start_End, printf("ex_tx_intr%d: start\n", unit);); 63121769Sjkh 63252286Smdodd /* 63352286Smdodd * - Cancel the watchdog. 63452286Smdodd * For all packets transmitted since last transmit interrupt: 63552286Smdodd * - Advance chain pointer to next queued packet. 63652286Smdodd * - Update statistics. 63752286Smdodd */ 63821769Sjkh 63952286Smdodd ifp->if_timer = 0; 64021769Sjkh 64152286Smdodd while (sc->tx_head != sc->tx_tail) { 64252286Smdodd outw(iobase + HOST_ADDR_REG, sc->tx_head); 64321769Sjkh 64452286Smdodd if (! inw(iobase + IO_PORT_REG) & Done_bit) 64552286Smdodd break; 64621769Sjkh 64752286Smdodd tx_status = inw(iobase + IO_PORT_REG); 64852286Smdodd sc->tx_head = inw(iobase + IO_PORT_REG); 64952286Smdodd 65052286Smdodd if (tx_status & TX_OK_bit) { 65152286Smdodd ifp->if_opackets++; 65252286Smdodd } else { 65352286Smdodd ifp->if_oerrors++; 65452286Smdodd } 65552286Smdodd 65652286Smdodd ifp->if_collisions += tx_status & No_Collisions_bits; 65752286Smdodd } 65852286Smdodd 65952286Smdodd /* 66052286Smdodd * The card should be ready to accept more packets now. 66152286Smdodd */ 66252286Smdodd 66352286Smdodd ifp->if_flags &= ~IFF_OACTIVE; 66452286Smdodd 66552286Smdodd DODEBUG(Status, printf("OIDLE tx_intr\n");); 66652286Smdodd DODEBUG(Start_End, printf("ex_tx_intr%d: finish\n", unit);); 66752286Smdodd 66852286Smdodd return; 66921769Sjkh} 67021769Sjkh 67152286Smdoddstatic void 67252286Smdoddex_rx_intr(struct ex_softc *sc) 67321769Sjkh{ 67452286Smdodd struct ifnet * ifp = &sc->arpcom.ac_if; 67552286Smdodd int iobase = sc->iobase; 67652286Smdodd int rx_status; 67752286Smdodd int pkt_len; 67852286Smdodd int QQQ; 67952286Smdodd struct mbuf * m; 68052286Smdodd struct mbuf * ipkt; 68152286Smdodd struct ether_header * eh; 68221769Sjkh 68352286Smdodd DODEBUG(Start_End, printf("ex_rx_intr%d: start\n", unit);); 68421769Sjkh 68552286Smdodd /* 68652286Smdodd * For all packets received since last receive interrupt: 68752286Smdodd * - If packet ok, read it into a new mbuf and queue it to interface, 68852286Smdodd * updating statistics. 68952286Smdodd * - If packet bad, just discard it, and update statistics. 69052286Smdodd * Finally, advance receive stop limit in card's memory to new location. 69152286Smdodd */ 69221769Sjkh 69352286Smdodd outw(iobase + HOST_ADDR_REG, sc->rx_head); 69421769Sjkh 69552286Smdodd while (inw(iobase + IO_PORT_REG) == RCV_Done) { 69652286Smdodd 69752286Smdodd rx_status = inw(iobase + IO_PORT_REG); 69852286Smdodd sc->rx_head = inw(iobase + IO_PORT_REG); 69952286Smdodd QQQ = pkt_len = inw(iobase + IO_PORT_REG); 70052286Smdodd 70152286Smdodd if (rx_status & RCV_OK_bit) { 70252286Smdodd MGETHDR(m, M_DONTWAIT, MT_DATA); 70352286Smdodd ipkt = m; 70452286Smdodd if (ipkt == NULL) { 70552286Smdodd ifp->if_iqdrops++; 70652286Smdodd } else { 70752286Smdodd ipkt->m_pkthdr.rcvif = ifp; 70852286Smdodd ipkt->m_pkthdr.len = pkt_len; 70952286Smdodd ipkt->m_len = MHLEN; 71052286Smdodd 71152286Smdodd while (pkt_len > 0) { 71252286Smdodd if (pkt_len > MINCLSIZE) { 71352286Smdodd MCLGET(m, M_DONTWAIT); 71452286Smdodd if (m->m_flags & M_EXT) { 71552286Smdodd m->m_len = MCLBYTES; 71652286Smdodd } else { 71752286Smdodd m_freem(ipkt); 71852286Smdodd ifp->if_iqdrops++; 71952286Smdodd goto rx_another; 72052286Smdodd } 72152286Smdodd } 72252286Smdodd m->m_len = min(m->m_len, pkt_len); 72352286Smdodd 72421769Sjkh /* 72521769Sjkh * NOTE: I'm assuming that all mbufs allocated are of even length, 72621769Sjkh * except for the last one in an odd-length packet. 72721769Sjkh */ 72852286Smdodd 72952286Smdodd insw(iobase + IO_PORT_REG, 73052286Smdodd mtod(m, caddr_t), m->m_len / 2); 73152286Smdodd 73252286Smdodd if (m->m_len & 1) { 73352286Smdodd *(mtod(m, caddr_t) + m->m_len - 1) = inb(iobase + IO_PORT_REG); 73452286Smdodd } 73552286Smdodd pkt_len -= m->m_len; 73652286Smdodd 73752286Smdodd if (pkt_len > 0) { 73852286Smdodd MGET(m->m_next, M_DONTWAIT, MT_DATA); 73952286Smdodd if (m->m_next == NULL) { 74052286Smdodd m_freem(ipkt); 74152286Smdodd ifp->if_iqdrops++; 74252286Smdodd goto rx_another; 74352286Smdodd } 74452286Smdodd m = m->m_next; 74552286Smdodd m->m_len = MLEN; 74652286Smdodd } 74752286Smdodd } 74852286Smdodd eh = mtod(ipkt, struct ether_header *); 74952286Smdodd#ifdef EXDEBUG 75052286Smdodd if (debug_mask & Rcvd_Pkts) { 75152286Smdodd if ((eh->ether_dhost[5] != 0xff) || (eh->ether_dhost[0] != 0xff)) { 75252286Smdodd printf("Receive packet with %d data bytes: %6D -> ", QQQ, eh->ether_shost, ":"); 75352286Smdodd printf("%6D\n", eh->ether_dhost, ":"); 75452286Smdodd } /* QQQ */ 75521769Sjkh } 75621769Sjkh#endif 75752286Smdodd m_adj(ipkt, sizeof(struct ether_header)); 75852286Smdodd ether_input(ifp, eh, ipkt); 75952286Smdodd ifp->if_ipackets++; 76052286Smdodd } 76152286Smdodd } else { 76252286Smdodd ifp->if_ierrors++; 76321769Sjkh } 76452286Smdodd outw(iobase + HOST_ADDR_REG, sc->rx_head); 76552286Smdoddrx_another: ; 76621769Sjkh } 76721769Sjkh 76852286Smdodd if (sc->rx_head < sc->rx_lower_limit + 2) 76952286Smdodd outw(iobase + RCV_STOP_REG, sc->rx_upper_limit); 77052286Smdodd else 77152286Smdodd outw(iobase + RCV_STOP_REG, sc->rx_head - 2); 77252286Smdodd 77352286Smdodd DODEBUG(Start_End, printf("ex_rx_intr%d: finish\n", unit);); 77452286Smdodd 77552286Smdodd return; 77621769Sjkh} 77721769Sjkh 77821769Sjkh 77955883Smdoddstatic int 78055883Smdoddex_ioctl(register struct ifnet *ifp, u_long cmd, caddr_t data) 78121769Sjkh{ 78252286Smdodd struct ex_softc * sc = ifp->if_softc; 78357987Smdodd struct ifreq * ifr = (struct ifreq *)data; 78452286Smdodd int s; 78552286Smdodd int error = 0; 78621769Sjkh 78752286Smdodd DODEBUG(Start_End, printf("ex_ioctl%d: start ", ifp->if_unit);); 78821769Sjkh 78952286Smdodd s = splimp(); 79021769Sjkh 79152286Smdodd switch(cmd) { 79252286Smdodd case SIOCSIFADDR: 79352286Smdodd case SIOCGIFADDR: 79452286Smdodd case SIOCSIFMTU: 79552286Smdodd error = ether_ioctl(ifp, cmd, data); 79652286Smdodd break; 79721769Sjkh 79852286Smdodd case SIOCSIFFLAGS: 79952286Smdodd DODEBUG(Start_End, printf("SIOCSIFFLAGS");); 80052286Smdodd if ((ifp->if_flags & IFF_UP) == 0 && 80152286Smdodd (ifp->if_flags & IFF_RUNNING)) { 80252286Smdodd 80352286Smdodd ifp->if_flags &= ~IFF_RUNNING; 80452286Smdodd ex_stop(sc); 80552286Smdodd } else { 80652286Smdodd ex_init(sc); 80752286Smdodd } 80852286Smdodd break; 80921769Sjkh#ifdef NODEF 81052286Smdodd case SIOCGHWADDR: 81152286Smdodd DODEBUG(Start_End, printf("SIOCGHWADDR");); 81252286Smdodd bcopy((caddr_t)sc->sc_addr, (caddr_t)&ifr->ifr_data, 81352286Smdodd sizeof(sc->sc_addr)); 81452286Smdodd break; 81521769Sjkh#endif 81652286Smdodd case SIOCADDMULTI: 81752286Smdodd DODEBUG(Start_End, printf("SIOCADDMULTI");); 81852286Smdodd case SIOCDELMULTI: 81952286Smdodd DODEBUG(Start_End, printf("SIOCDELMULTI");); 82052286Smdodd /* XXX Support not done yet. */ 82152286Smdodd error = EINVAL; 82252286Smdodd break; 82357987Smdodd case SIOCSIFMEDIA: 82457987Smdodd case SIOCGIFMEDIA: 82557987Smdodd error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, cmd); 82657987Smdodd break; 82752286Smdodd default: 82852286Smdodd DODEBUG(Start_End, printf("unknown");); 82952286Smdodd error = EINVAL; 83052286Smdodd } 83121769Sjkh 83252286Smdodd splx(s); 83321769Sjkh 83452286Smdodd DODEBUG(Start_End, printf("\nex_ioctl%d: finish\n", ifp->if_unit);); 83552286Smdodd 83652286Smdodd return(error); 83721769Sjkh} 83821769Sjkh 83921769Sjkh 84052286Smdoddstatic void 84152286Smdoddex_reset(struct ex_softc *sc) 84221769Sjkh{ 84352286Smdodd int s; 84421769Sjkh 84552286Smdodd DODEBUG(Start_End, printf("ex_reset%d: start\n", unit);); 84621769Sjkh 84752286Smdodd s = splimp(); 84821769Sjkh 84952286Smdodd ex_stop(sc); 85052286Smdodd ex_init(sc); 85121769Sjkh 85252286Smdodd splx(s); 85321769Sjkh 85452286Smdodd DODEBUG(Start_End, printf("ex_reset%d: finish\n", unit);); 85552286Smdodd 85652286Smdodd return; 85721769Sjkh} 85821769Sjkh 85952286Smdoddstatic void 86052286Smdoddex_watchdog(struct ifnet *ifp) 86121769Sjkh{ 86252286Smdodd struct ex_softc * sc = ifp->if_softc; 86321769Sjkh 86452286Smdodd DODEBUG(Start_End, printf("ex_watchdog%d: start\n", ifp->if_unit);); 86521769Sjkh 86652286Smdodd ifp->if_flags &= ~IFF_OACTIVE; 86721769Sjkh 86852286Smdodd DODEBUG(Status, printf("OIDLE watchdog\n");); 86952286Smdodd 87052286Smdodd ifp->if_oerrors++; 87152286Smdodd ex_reset(sc); 87252286Smdodd ex_start(ifp); 87352286Smdodd 87452286Smdodd DODEBUG(Start_End, printf("ex_watchdog%d: finish\n", ifp->if_unit);); 87552286Smdodd 87652286Smdodd return; 87721769Sjkh} 87821769Sjkh 87957987Smdoddstatic int 88059816Smdoddex_get_media (u_int32_t iobase) 88159816Smdodd{ 88259816Smdodd int tmp; 88359816Smdodd 88459816Smdodd outb(iobase + CMD_REG, Bank2_Sel); 88559816Smdodd tmp = inb(iobase + REG3); 88659816Smdodd outb(iobase + CMD_REG, Bank0_Sel); 88759816Smdodd 88859816Smdodd if (tmp & TPE_bit) 88959816Smdodd return(IFM_ETHER|IFM_10_T); 89059816Smdodd if (tmp & BNC_bit) 89159816Smdodd return(IFM_ETHER|IFM_10_2); 89259816Smdodd 89359816Smdodd return (IFM_ETHER|IFM_10_5); 89459816Smdodd} 89559816Smdodd 89659816Smdoddstatic int 89757987Smdoddex_ifmedia_upd (ifp) 89857987Smdodd struct ifnet * ifp; 89957987Smdodd{ 90057987Smdodd struct ex_softc * sc = ifp->if_softc; 90121769Sjkh 90257987Smdodd return (0); 90357987Smdodd} 90457987Smdodd 90557987Smdoddstatic void 90657987Smdoddex_ifmedia_sts(ifp, ifmr) 90757987Smdodd struct ifnet * ifp; 90857987Smdodd struct ifmediareq * ifmr; 90957987Smdodd{ 91057987Smdodd struct ex_softc * sc = ifp->if_softc; 91157987Smdodd 91257987Smdodd ifmr->ifm_active = ex_get_media(sc->iobase); 91357987Smdodd 91457987Smdodd return; 91557987Smdodd} 91657987Smdodd 91759816Smdoddu_short 91859816Smdoddeeprom_read(u_int32_t iobase, int location) 91921769Sjkh{ 92021769Sjkh int i; 92121769Sjkh u_short data = 0; 92221769Sjkh int ee_addr; 92321769Sjkh int read_cmd = location | EE_READ_CMD; 92421769Sjkh short ctrl_val = EECS; 92521769Sjkh 92621769Sjkh ee_addr = iobase + EEPROM_REG; 92721769Sjkh outb(iobase + CMD_REG, Bank2_Sel); 92821769Sjkh outb(ee_addr, EECS); 92921769Sjkh for (i = 8; i >= 0; i--) { 93021769Sjkh short outval = (read_cmd & (1 << i)) ? ctrl_val | EEDI : ctrl_val; 93121769Sjkh outb(ee_addr, outval); 93221769Sjkh outb(ee_addr, outval | EESK); 93321769Sjkh DELAY(3); 93421769Sjkh outb(ee_addr, outval); 93521769Sjkh DELAY(2); 93621769Sjkh } 93721769Sjkh outb(ee_addr, ctrl_val); 93821769Sjkh 93921769Sjkh for (i = 16; i > 0; i--) { 94021769Sjkh outb(ee_addr, ctrl_val | EESK); 94121769Sjkh DELAY(3); 94221769Sjkh data = (data << 1) | ((inb(ee_addr) & EEDO) ? 1 : 0); 94321769Sjkh outb(ee_addr, ctrl_val); 94421769Sjkh DELAY(2); 94521769Sjkh } 94621769Sjkh 94721769Sjkh ctrl_val &= ~EECS; 94821769Sjkh outb(ee_addr, ctrl_val | EESK); 94921769Sjkh DELAY(3); 95021769Sjkh outb(ee_addr, ctrl_val); 95121769Sjkh DELAY(2); 95221769Sjkh outb(iobase + CMD_REG, Bank0_Sel); 95321769Sjkh return(data); 95421769Sjkh} 955