1581Srgrimes/*- 2581Srgrimes * Copyright (c) 1992, 1993, University of Vermont and State 3581Srgrimes * Agricultural College. 4581Srgrimes * Copyright (c) 1992, 1993, Garrett A. Wollman. 5581Srgrimes * 6581Srgrimes * Portions: 7581Srgrimes * Copyright (c) 1990, 1991, William F. Jolitz 8581Srgrimes * Copyright (c) 1990, The Regents of the University of California 9581Srgrimes * 102281Swollman * 3Com 3C507 support: 112281Swollman * Copyright (c) 1993, 1994, Charles M. Hannum 122281Swollman * 1324909Sgibbs * EtherExpress 16 support: 1424909Sgibbs * Copyright (c) 1993, 1994, 1995, Rodney W. Grimes 1524909Sgibbs * Copyright (c) 1997, Aaron C. Smith 1624909Sgibbs * 17581Srgrimes * All rights reserved. 18581Srgrimes * 19581Srgrimes * Redistribution and use in source and binary forms, with or without 20581Srgrimes * modification, are permitted provided that the following conditions 21581Srgrimes * are met: 22581Srgrimes * 1. Redistributions of source code must retain the above copyright 23581Srgrimes * notice, this list of conditions and the following disclaimer. 24581Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 25581Srgrimes * notice, this list of conditions and the following disclaimer in the 26581Srgrimes * documentation and/or other materials provided with the distribution. 27581Srgrimes * 3. All advertising materials mentioning features or use of this software 28581Srgrimes * must display the following acknowledgement: 29581Srgrimes * This product includes software developed by the University of 3024909Sgibbs * Vermont and State Agricultural College and Garrett A. Wollman, by 3124909Sgibbs * William F. Jolitz, by the University of California, Berkeley, 3224909Sgibbs * Lawrence Berkeley Laboratory, and their contributors, by 3324909Sgibbs * Charles M. Hannum, by Rodney W. Grimes, and by Aaron C. Smith. 34581Srgrimes * 4. Neither the names of the Universities nor the names of the authors 35581Srgrimes * may be used to endorse or promote products derived from this software 36581Srgrimes * without specific prior written permission. 37581Srgrimes * 38581Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 39581Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 40581Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 41581Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR AUTHORS BE LIABLE 42581Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 43581Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 44581Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 45581Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 46581Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 47581Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 48581Srgrimes * SUCH DAMAGE. 49581Srgrimes * 5059812Smdodd * MAINTAINER: Matthew N. Dodd <winter@jurai.net> 51581Srgrimes */ 52581Srgrimes 53119418Sobrien#include <sys/cdefs.h> 54119418Sobrien__FBSDID("$FreeBSD: releng/11.0/sys/dev/ie/if_ie.c 276750 2015-01-06 12:59:37Z rwatson $"); 55119418Sobrien 56581Srgrimes/* 57581Srgrimes * Intel 82586 Ethernet chip 58581Srgrimes * Register, bit, and structure definitions. 59581Srgrimes * 60581Srgrimes * Written by GAW with reference to the Clarkson Packet Driver code for this 61581Srgrimes * chip written by Russ Nelson and others. 62581Srgrimes * 6326996Sgibbs * Intel EtherExpress 16 support from if_ix.c, written by Rodney W. Grimes. 64581Srgrimes */ 65581Srgrimes 66581Srgrimes/* 67581Srgrimes * The i82586 is a very versatile chip, found in many implementations. 68581Srgrimes * Programming this chip is mostly the same, but certain details differ 698876Srgrimes * from card to card. This driver is written so that different cards 7024909Sgibbs * can be automatically detected at run-time. 71581Srgrimes */ 72581Srgrimes 73581Srgrimes/* 74112765Smdodd * Mode of operation: 75112765Smdodd * 76112765Smdodd * We run the 82586 in a standard Ethernet mode. We keep NFRAMES 77112765Smdodd * received frame descriptors around for the receiver to use, and 78112765Smdodd * NRXBUFS associated receive buffer descriptors, both in a circular 79112765Smdodd * list. Whenever a frame is received, we rotate both lists as 80112765Smdodd * necessary. (The 586 treats both lists as a simple queue.) We also 81112765Smdodd * keep a transmit command around so that packets can be sent off 82112765Smdodd * quickly. 83112765Smdodd * 84112765Smdodd * We configure the adapter in AL-LOC = 1 mode, which means that the 85112765Smdodd * Ethernet/802.3 MAC header is placed at the beginning of the receive 86112765Smdodd * buffer rather than being split off into various fields in the RFD. 87112765Smdodd * This also means that we must include this header in the transmit 88112765Smdodd * buffer as well. 89112765Smdodd * 90112765Smdodd * By convention, all transmit commands, and only transmit commands, 91112765Smdodd * shall have the I (IE_CMD_INTR) bit set in the command. This way, 92112765Smdodd * when an interrupt arrives at ieintr(), it is immediately possible 93112765Smdodd * to tell what precisely caused it. ANY OTHER command-sending routines 94112765Smdodd * should run at splimp(), and should post an acknowledgement to every 95112765Smdodd * interrupt they generate. 96112765Smdodd * 97112765Smdodd * The 82586 has a 24-bit address space internally, and the adaptor's 98112765Smdodd * memory is located at the top of this region. However, the value 99112765Smdodd * we are given in configuration is normally the *bottom* of the adaptor 100112765Smdodd * RAM. So, we must go through a few gyrations to come up with a 101112765Smdodd * kernel virtual address which represents the actual beginning of the 102112765Smdodd * 586 address space. First, we autosize the RAM by running through 103112765Smdodd * several possible sizes and trying to initialize the adapter under 104112765Smdodd * the assumption that the selected size is correct. Then, knowing 105112765Smdodd * the correct RAM size, we set up our pointers in the softc `iomem' 106112765Smdodd * represents the computed base of the 586 address space. `iomembot' 107112765Smdodd * represents the actual configured base of adapter RAM. Finally, 108112765Smdodd * `iosize' represents the calculated size of 586 RAM. Then, when 109112765Smdodd * laying out commands, we use the interval [iomembot, iomembot + 110112765Smdodd * iosize); to make 24-pointers, we subtract iomem, and to make 111112765Smdodd * 16-pointers, we subtract iomem and and with 0xffff. 112112765Smdodd */ 113581Srgrimes 1142056Swollman#include <sys/param.h> 1152056Swollman#include <sys/systm.h> 11650135Smsmith#include <sys/eventhandler.h> 11710080Sbde#include <sys/kernel.h> 11829024Sbde#include <sys/malloc.h> 1192056Swollman#include <sys/mbuf.h> 1202056Swollman#include <sys/socket.h> 12124204Sbde#include <sys/sockio.h> 1222056Swollman#include <sys/syslog.h> 123581Srgrimes 124112790Smdodd#include <sys/module.h> 125112790Smdodd#include <sys/bus.h> 126112790Smdodd 127112790Smdodd#include <machine/bus.h> 128112790Smdodd#include <machine/resource.h> 129112790Smdodd#include <sys/rman.h> 130112790Smdodd 13150026Smdodd#include <net/ethernet.h> 1322056Swollman#include <net/if.h> 133257176Sglebius#include <net/if_var.h> 1342056Swollman#include <net/if_types.h> 1352056Swollman#include <net/if_dl.h> 136581Srgrimes 13750026Smdodd#include <netinet/in.h> 13850026Smdodd#include <netinet/if_ether.h> 13950026Smdodd 14079077Simp#include <dev/ic/i82586.h> 141112790Smdodd#include <dev/ie/if_ievar.h> 14255953Speter#include <dev/ie/if_iereg.h> 14355953Speter#include <dev/ie/if_ie507.h> 14455953Speter#include <dev/ie/if_iee16.h> 1452268Sats#include <i386/isa/elink.h> 146581Srgrimes 1472056Swollman#include <net/bpf.h> 148581Srgrimes 149581Srgrimes#ifdef DEBUG 15026996Sgibbs#define IED_RINT 0x01 15126996Sgibbs#define IED_TINT 0x02 15226996Sgibbs#define IED_RNR 0x04 15326996Sgibbs#define IED_CNA 0x08 15426996Sgibbs#define IED_READFRAME 0x10 15533181Seivindstatic int ie_debug = IED_RNR; 15626996Sgibbs 157581Srgrimes#endif 158581Srgrimes 15926996Sgibbs#define IE_BUF_LEN ETHER_MAX_LEN /* length of transmit buffer */ 160581Srgrimes 161230808Spluknet/* XXX this driver uses `volatile' and `caddr_t' to a fault. */ 162230808Spluknettypedef volatile char *v_caddr_t; /* core address, pointer to volatile */ 163230808Spluknet 164581Srgrimes/* Forward declaration */ 165581Srgrimesstruct ie_softc; 166581Srgrimes 167112734Smdoddstatic void ieinit (void *); 168179491Sjhbstatic void ieinit_locked (struct ie_softc *); 169112734Smdoddstatic void ie_stop (struct ie_softc *); 170112734Smdoddstatic int ieioctl (struct ifnet *, u_long, caddr_t); 171112734Smdoddstatic void iestart (struct ifnet *); 172179491Sjhbstatic void iestart_locked (struct ifnet *); 17324909Sgibbs 174112734Smdoddstatic __inline void 175112734Smdodd ee16_interrupt_enable (struct ie_softc *); 17624909Sgibbs 177112734Smdoddstatic __inline void 178112734Smdodd ie_ack (struct ie_softc *, u_int); 179112734Smdoddstatic void iereset (struct ie_softc *); 180112734Smdoddstatic void ie_readframe (struct ie_softc *, int); 181112734Smdoddstatic void ie_drop_packet_buffer (struct ie_softc *); 182112734Smdoddstatic int command_and_wait (struct ie_softc *, 183112734Smdodd int, void volatile *, int); 184112734Smdoddstatic void run_tdr (struct ie_softc *, 185112734Smdodd volatile struct ie_tdr_cmd *); 186112734Smdoddstatic int ierint (struct ie_softc *); 187112734Smdoddstatic int ietint (struct ie_softc *); 188112734Smdoddstatic int iernr (struct ie_softc *); 189112734Smdoddstatic void start_receiver (struct ie_softc *); 190112734Smdoddstatic __inline int 191112734Smdodd ieget (struct ie_softc *, struct mbuf **); 192112734Smdoddstatic v_caddr_t setup_rfa (struct ie_softc *, v_caddr_t); 193112734Smdoddstatic int mc_setup (struct ie_softc *); 194112734Smdoddstatic void ie_mc_reset (struct ie_softc *); 195581Srgrimes 196581Srgrimes#ifdef DEBUG 197112734Smdoddstatic void print_rbd (volatile struct ie_recv_buf_desc * rbd); 19833181Seivindstatic int in_ierint = 0; 19933181Seivindstatic int in_ietint = 0; 200581Srgrimes#endif 201581Srgrimes 20212724Sphkstatic const char *ie_hardware_names[] = { 203112790Smdodd "None", 20426996Sgibbs "StarLAN 10", 20526996Sgibbs "EN100", 20626996Sgibbs "StarLAN Fiber", 20726996Sgibbs "3C507", 20826996Sgibbs "NI5210", 20926996Sgibbs "EtherExpress 16", 21026996Sgibbs "Unknown" 211581Srgrimes}; 212581Srgrimes 2138876Srgrimes/* 214112765Smdodd * sizeof(iscp) == 1+1+2+4 == 8 215112765Smdodd * sizeof(scb) == 2+2+2+2+2+2+2+2 == 16 216112765Smdodd * NFRAMES * sizeof(rfd) == NFRAMES*(2+2+2+2+6+6+2+2) == NFRAMES*24 == 384 217112765Smdodd * sizeof(xmit_cmd) == 2+2+2+2+6+2 == 18 218112765Smdodd * sizeof(transmit buffer) == 1512 219112765Smdodd * sizeof(transmit buffer desc) == 8 220112765Smdodd * ----- 221112765Smdodd * 1946 222112765Smdodd * 223112765Smdodd * NRXBUFS * sizeof(rbd) == NRXBUFS*(2+2+4+2+2) == NRXBUFS*12 224112765Smdodd * NRXBUFS * IE_RBUF_SIZE == NRXBUFS*256 225112765Smdodd * 226112765Smdodd * NRXBUFS should be (16384 - 1946) / (256 + 12) == 14438 / 268 == 53 227112765Smdodd * 228112765Smdodd * With NRXBUFS == 48, this leaves us 1574 bytes for another command or 229112765Smdodd * more buffers. Another transmit command would be 18+8+1512 == 1538 230112765Smdodd * ---just barely fits! 231112765Smdodd * 232112765Smdodd * Obviously all these would have to be reduced for smaller memory sizes. 233112765Smdodd * With a larger memory, it would be possible to roughly double the number 234112765Smdodd * of both transmit and receive buffers. 235112765Smdodd */ 236581Srgrimes 237112783Smdodd#define NFRAMES 4 /* number of receive frames */ 238112783Smdodd#define NRXBUFS 24 /* number of buffers to allocate */ 239112783Smdodd#define IE_RBUF_SIZE 256 /* size of each buffer, MUST BE POWER OF TWO */ 240112783Smdodd#define NTXBUFS 1 /* number of transmit commands */ 241112783Smdodd#define IE_TBUF_SIZE ETHER_MAX_LEN /* size of transmit buffer */ 242581Srgrimes 24338232Sbde#define MK_24(base, ptr) ((caddr_t)((uintptr_t)ptr - (uintptr_t)base)) 24438232Sbde#define MK_16(base, ptr) ((u_short)(uintptr_t)MK_24(base, ptr)) 245581Srgrimes 246181134Sjhbvoid 247181134Sjhbee16_shutdown(struct ie_softc *sc) 24824909Sgibbs{ 24924909Sgibbs 250112734Smdodd ee16_reset_586(sc); 251112734Smdodd outb(PORT(sc) + IEE16_ECTRL, IEE16_RESET_ASIC); 252112734Smdodd outb(PORT(sc) + IEE16_ECTRL, 0); 25324909Sgibbs} 25424909Sgibbs 255581Srgrimes/* 256581Srgrimes * Taken almost exactly from Bill's if_is.c, then modified beyond recognition. 257581Srgrimes */ 258112790Smdoddint 259112790Smdoddie_attach(device_t dev) 260581Srgrimes{ 261112790Smdodd struct ie_softc * sc; 262112790Smdodd struct ifnet * ifp; 263112790Smdodd size_t allocsize; 264179491Sjhb int error, factor; 265581Srgrimes 266112790Smdodd sc = device_get_softc(dev); 267147256Sbrooks ifp = sc->ifp = if_alloc(IFT_ETHER); 268147256Sbrooks if (ifp == NULL) { 269147256Sbrooks device_printf(sc->dev, "can not if_alloc()\n"); 270147256Sbrooks return (ENOSPC); 271147256Sbrooks } 27240565Sbde 273112790Smdodd sc->dev = dev; 274179491Sjhb mtx_init(&sc->lock, device_get_nameunit(dev), MTX_NETWORK_LOCK, 275179491Sjhb MTX_DEF); 276112790Smdodd 27726996Sgibbs /* 27826996Sgibbs * based on the amount of memory we have, allocate our tx and rx 27926996Sgibbs * resources. 28026996Sgibbs */ 281112790Smdodd factor = rman_get_size(sc->mem_res) / 8192; 282112734Smdodd sc->nframes = factor * NFRAMES; 283112734Smdodd sc->nrxbufs = factor * NRXBUFS; 284112734Smdodd sc->ntxbufs = factor * NTXBUFS; 28525971Sgibbs 28626996Sgibbs /* 28726996Sgibbs * Since all of these guys are arrays of pointers, allocate as one 28826996Sgibbs * big chunk and dole out accordingly. 28926996Sgibbs */ 290112734Smdodd allocsize = sizeof(void *) * (sc->nframes 291112734Smdodd + (sc->nrxbufs * 2) 292112734Smdodd + (sc->ntxbufs * 3)); 293112734Smdodd sc->rframes = (volatile struct ie_recv_frame_desc **) malloc(allocsize, 29426996Sgibbs M_DEVBUF, 29526996Sgibbs M_NOWAIT); 296147256Sbrooks if (sc->rframes == NULL) { 297179491Sjhb mtx_destroy(&sc->lock); 298112790Smdodd return (ENXIO); 299147256Sbrooks } 300112734Smdodd sc->rbuffs = 301112734Smdodd (volatile struct ie_recv_buf_desc **)&sc->rframes[sc->nframes]; 302112734Smdodd sc->cbuffs = (volatile u_char **)&sc->rbuffs[sc->nrxbufs]; 303112734Smdodd sc->xmit_cmds = 304112734Smdodd (volatile struct ie_xmit_cmd **)&sc->cbuffs[sc->nrxbufs]; 305112734Smdodd sc->xmit_buffs = 306112734Smdodd (volatile struct ie_xmit_buf **)&sc->xmit_cmds[sc->ntxbufs]; 307112734Smdodd sc->xmit_cbuffs = (volatile u_char **)&sc->xmit_buffs[sc->ntxbufs]; 30825971Sgibbs 309112790Smdodd if (bootverbose) 310112790Smdodd device_printf(sc->dev, "hardware type %s, revision %d\n", 311112790Smdodd ie_hardware_names[sc->hard_type], sc->hard_vers + 1); 312112790Smdodd 313112734Smdodd ifp->if_softc = sc; 314121816Sbrooks if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 315179491Sjhb ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 31626996Sgibbs ifp->if_start = iestart; 31726996Sgibbs ifp->if_ioctl = ieioctl; 31850084Smdodd ifp->if_init = ieinit; 319207554Ssobomax IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); 3208876Srgrimes 321147256Sbrooks ether_ifattach(ifp, sc->enaddr); 322179491Sjhb 323179491Sjhb error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET | INTR_MPSAFE, 324179491Sjhb NULL, ie_intr, sc, &sc->irq_ih); 325179491Sjhb if (error) { 326179491Sjhb device_printf(dev, "Unable to register interrupt handler\n"); 327179491Sjhb mtx_destroy(&sc->lock); 328179491Sjhb return (error); 329179491Sjhb } 330179491Sjhb 331112790Smdodd return (0); 332581Srgrimes} 333581Srgrimes 334117877Sphkstatic __inline void 335117877Sphkie_ack(struct ie_softc *sc, u_int mask) 336117877Sphk{ 337117877Sphk 338117877Sphk sc->scb->ie_command = sc->scb->ie_status & mask; 339117877Sphk (*sc->ie_chan_attn) (sc); 340117877Sphk} 341117877Sphk 342581Srgrimes/* 343581Srgrimes * What to do upon receipt of an interrupt. 344581Srgrimes */ 345112790Smdoddvoid 346112790Smdoddie_intr(void *xsc) 347581Srgrimes{ 348112790Smdodd struct ie_softc *sc = (struct ie_softc *)xsc; 349112734Smdodd u_short status; 350581Srgrimes 351179491Sjhb IE_LOCK(sc); 352179491Sjhb 35326996Sgibbs /* Clear the interrupt latch on the 3C507. */ 354112734Smdodd if (sc->hard_type == IE_3C507 355112734Smdodd && (inb(PORT(sc) + IE507_CTRL) & EL_CTRL_INTL)) 356112734Smdodd outb(PORT(sc) + IE507_ICTRL, 1); 35724909Sgibbs 35826996Sgibbs /* disable interrupts on the EE16. */ 359112734Smdodd if (sc->hard_type == IE_EE16) 360112734Smdodd outb(PORT(sc) + IEE16_IRQ, sc->irq_encoded); 36124909Sgibbs 362112734Smdodd status = sc->scb->ie_status; 363581Srgrimes 364581Srgrimesloop: 36525971Sgibbs 36626996Sgibbs /* Don't ack interrupts which we didn't receive */ 367112734Smdodd ie_ack(sc, IE_ST_WHENCE & status); 36825971Sgibbs 36926996Sgibbs if (status & (IE_ST_RECV | IE_ST_RNR)) { 370581Srgrimes#ifdef DEBUG 37126996Sgibbs in_ierint++; 37226996Sgibbs if (ie_debug & IED_RINT) 373179435Sjhb if_printf(sc->ifp, "rint\n"); 374581Srgrimes#endif 375112734Smdodd ierint(sc); 376581Srgrimes#ifdef DEBUG 37726996Sgibbs in_ierint--; 378581Srgrimes#endif 37926996Sgibbs } 38026996Sgibbs if (status & IE_ST_DONE) { 381581Srgrimes#ifdef DEBUG 38226996Sgibbs in_ietint++; 38326996Sgibbs if (ie_debug & IED_TINT) 384179435Sjhb if_printf(sc->ifp, "tint\n"); 385581Srgrimes#endif 386112734Smdodd ietint(sc); 387581Srgrimes#ifdef DEBUG 38826996Sgibbs in_ietint--; 389581Srgrimes#endif 39026996Sgibbs } 39126996Sgibbs if (status & IE_ST_RNR) { 392581Srgrimes#ifdef DEBUG 39326996Sgibbs if (ie_debug & IED_RNR) 394179435Sjhb if_printf(sc->ifp, "rnr\n"); 395581Srgrimes#endif 396112734Smdodd iernr(sc); 39726996Sgibbs } 398581Srgrimes#ifdef DEBUG 399112734Smdodd if ((status & IE_ST_ALLDONE) && (ie_debug & IED_CNA)) 400179435Sjhb if_printf(sc->ifp, "cna\n"); 401581Srgrimes#endif 402581Srgrimes 403112734Smdodd if ((status = sc->scb->ie_status) & IE_ST_WHENCE) 40426996Sgibbs goto loop; 405581Srgrimes 40626996Sgibbs /* Clear the interrupt latch on the 3C507. */ 407112734Smdodd if (sc->hard_type == IE_3C507) 408112734Smdodd outb(PORT(sc) + IE507_ICTRL, 1); 40924909Sgibbs 41026996Sgibbs /* enable interrupts on the EE16. */ 411112734Smdodd if (sc->hard_type == IE_EE16) 412112734Smdodd outb(PORT(sc) + IEE16_IRQ, sc->irq_encoded | IEE16_IRQ_ENABLE); 413179491Sjhb IE_UNLOCK(sc); 414581Srgrimes} 415581Srgrimes 416581Srgrimes/* 417581Srgrimes * Process a received-frame interrupt. 418581Srgrimes */ 41924909Sgibbsstatic int 420112734Smdoddierint(struct ie_softc *sc) 421581Srgrimes{ 42226996Sgibbs int i, status; 42326996Sgibbs static int timesthru = 1024; 424581Srgrimes 425112734Smdodd i = sc->rfhead; 42626996Sgibbs while (1) { 427112734Smdodd status = sc->rframes[i]->ie_fd_status; 428581Srgrimes 42926996Sgibbs if ((status & IE_FD_COMPLETE) && (status & IE_FD_OK)) { 430271820Sglebius if_inc_counter(sc->ifp, IFCOUNTER_IPACKETS, 1); 43126996Sgibbs if (!--timesthru) { 432271820Sglebius if_inc_counter(sc->ifp, IFCOUNTER_IERRORS, 433112734Smdodd sc->scb->ie_err_crc + 434112734Smdodd sc->scb->ie_err_align + 435112734Smdodd sc->scb->ie_err_resource + 436271820Sglebius sc->scb->ie_err_overrun); 437112734Smdodd sc->scb->ie_err_crc = 0; 438112734Smdodd sc->scb->ie_err_align = 0; 439112734Smdodd sc->scb->ie_err_resource = 0; 440112734Smdodd sc->scb->ie_err_overrun = 0; 44126996Sgibbs timesthru = 1024; 44226996Sgibbs } 443112734Smdodd ie_readframe(sc, i); 44426996Sgibbs } else { 44526996Sgibbs if (status & IE_FD_RNR) { 446112734Smdodd if (!(sc->scb->ie_status & IE_RU_READY)) { 447112734Smdodd sc->rframes[0]->ie_fd_next = 448112734Smdodd MK_16(MEM(sc), sc->rbuffs[0]); 449112734Smdodd sc->scb->ie_recv_list = 450112734Smdodd MK_16(MEM(sc), sc->rframes[0]); 451112734Smdodd command_and_wait(sc, IE_RU_START, 0, 0); 45226996Sgibbs } 45326996Sgibbs } 45426996Sgibbs break; 45526996Sgibbs } 456112734Smdodd i = (i + 1) % sc->nframes; 457581Srgrimes } 45826996Sgibbs return (0); 459581Srgrimes} 460581Srgrimes 461581Srgrimes/* 462581Srgrimes * Process a command-complete interrupt. These are only generated by 46326996Sgibbs * the transmission of frames. This routine is deceptively simple, since 464581Srgrimes * most of the real work is done by iestart(). 465581Srgrimes */ 46626996Sgibbsstatic int 467112734Smdoddietint(struct ie_softc *sc) 468581Srgrimes{ 469179416Sjhb struct ifnet *ifp = sc->ifp; 47026996Sgibbs int status; 47126996Sgibbs int i; 472581Srgrimes 473179416Sjhb ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 474581Srgrimes 475112734Smdodd for (i = 0; i < sc->xmit_count; i++) { 476112734Smdodd status = sc->xmit_cmds[i]->ie_xmit_status; 477581Srgrimes 47826996Sgibbs if (status & IE_XS_LATECOLL) { 479179416Sjhb if_printf(ifp, "late collision\n"); 480271820Sglebius if_inc_counter(ifp, IFCOUNTER_COLLISIONS, 1); 481271820Sglebius if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 48226996Sgibbs } else if (status & IE_XS_NOCARRIER) { 483179416Sjhb if_printf(ifp, "no carrier\n"); 484271820Sglebius if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 48526996Sgibbs } else if (status & IE_XS_LOSTCTS) { 486179416Sjhb if_printf(ifp, "lost CTS\n"); 487271820Sglebius if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 48826996Sgibbs } else if (status & IE_XS_UNDERRUN) { 489179416Sjhb if_printf(ifp, "DMA underrun\n"); 490271820Sglebius if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 49126996Sgibbs } else if (status & IE_XS_EXCMAX) { 492179416Sjhb if_printf(ifp, "too many collisions\n"); 493271820Sglebius if_inc_counter(ifp, IFCOUNTER_COLLISIONS, 16); 494271820Sglebius if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 49526996Sgibbs } else { 496271820Sglebius if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 497271820Sglebius if_inc_counter(ifp, IFCOUNTER_COLLISIONS, status & IE_XS_MAXCOLL); 49826996Sgibbs } 49926996Sgibbs } 500112734Smdodd sc->xmit_count = 0; 501581Srgrimes 50226996Sgibbs /* 50326996Sgibbs * If multicast addresses were added or deleted while we were 50426996Sgibbs * transmitting, ie_mc_reset() set the want_mcsetup flag indicating 50526996Sgibbs * that we should do it. 50626996Sgibbs */ 507112734Smdodd if (sc->want_mcsetup) { 508112734Smdodd mc_setup(sc); 509112734Smdodd sc->want_mcsetup = 0; 51026996Sgibbs } 51126996Sgibbs /* Wish I knew why this seems to be necessary... */ 512112734Smdodd sc->xmit_cmds[0]->ie_xmit_status |= IE_STAT_COMPL; 513581Srgrimes 514179491Sjhb iestart_locked(ifp); 51526996Sgibbs return (0); /* shouldn't be necessary */ 516581Srgrimes} 517581Srgrimes 518581Srgrimes/* 519581Srgrimes * Process a receiver-not-ready interrupt. I believe that we get these 520581Srgrimes * when there aren't enough buffers to go around. For now (FIXME), we 521581Srgrimes * just restart the receiver, and hope everything's ok. 522581Srgrimes */ 52326996Sgibbsstatic int 524112734Smdoddiernr(struct ie_softc *sc) 525581Srgrimes{ 526581Srgrimes#ifdef doesnt_work 527112734Smdodd setup_rfa(sc, (v_caddr_t) sc->rframes[0]); 528581Srgrimes 529112734Smdodd sc->scb->ie_recv_list = MK_16(MEM(sc), sc->rframes[0]); 530112734Smdodd command_and_wait(sc, IE_RU_START, 0, 0); 531581Srgrimes#else 53226996Sgibbs /* This doesn't work either, but it doesn't hang either. */ 533112734Smdodd command_and_wait(sc, IE_RU_DISABLE, 0, 0); /* just in case */ 534112734Smdodd setup_rfa(sc, (v_caddr_t) sc->rframes[0]); /* ignore cast-qual */ 535581Srgrimes 536112734Smdodd sc->scb->ie_recv_list = MK_16(MEM(sc), sc->rframes[0]); 537112734Smdodd command_and_wait(sc, IE_RU_START, 0, 0); /* was ENABLE */ 538581Srgrimes 539581Srgrimes#endif 540112734Smdodd ie_ack(sc, IE_ST_WHENCE); 541581Srgrimes 542271820Sglebius if_inc_counter(sc->ifp, IFCOUNTER_IERRORS, 1); 54326996Sgibbs return (0); 544581Srgrimes} 545581Srgrimes 546581Srgrimes/* 547581Srgrimes * Compare two Ether/802 addresses for equality, inlined and 54826996Sgibbs * unrolled for speed. I'd love to have an inline assembler 549581Srgrimes * version of this... 550581Srgrimes */ 55135210Sbdestatic __inline int 55226996Sgibbsether_equal(u_char * one, u_char * two) 55326996Sgibbs{ 55426996Sgibbs if (one[0] != two[0]) 55526996Sgibbs return (0); 55626996Sgibbs if (one[1] != two[1]) 55726996Sgibbs return (0); 55826996Sgibbs if (one[2] != two[2]) 55926996Sgibbs return (0); 56026996Sgibbs if (one[3] != two[3]) 56126996Sgibbs return (0); 56226996Sgibbs if (one[4] != two[4]) 56326996Sgibbs return (0); 56426996Sgibbs if (one[5] != two[5]) 56526996Sgibbs return (0); 56626996Sgibbs return 1; 567581Srgrimes} 568581Srgrimes 569581Srgrimes/* 57060536Sarchie * Determine quickly whether we should bother reading in this packet. 57160536Sarchie * This depends on whether BPF and/or bridging is enabled, whether we 57260536Sarchie * are receiving multicast address, and whether promiscuous mode is enabled. 57360536Sarchie * We assume that if IFF_PROMISC is set, then *somebody* wants to see 57460536Sarchie * all incoming packets. 575581Srgrimes */ 57635210Sbdestatic __inline int 577112734Smdoddcheck_eh(struct ie_softc *sc, struct ether_header *eh) 57826996Sgibbs{ 57960536Sarchie /* Optimize the common case: normal operation. We've received 58060536Sarchie either a unicast with our dest or a multicast packet. */ 581112734Smdodd if (sc->promisc == 0) { 58260536Sarchie int i; 583581Srgrimes 58460536Sarchie /* If not multicast, it's definitely for us */ 58560536Sarchie if ((eh->ether_dhost[0] & 1) == 0) 58626996Sgibbs return (1); 587581Srgrimes 58860536Sarchie /* Accept broadcasts (loose but fast check) */ 58960536Sarchie if (eh->ether_dhost[0] == 0xff) 59026996Sgibbs return (1); 591581Srgrimes 59260536Sarchie /* Compare against our multicast addresses */ 593112734Smdodd for (i = 0; i < sc->mcast_count; i++) { 59426996Sgibbs if (ether_equal(eh->ether_dhost, 595112734Smdodd (u_char *)&sc->mcast_addrs[i])) 59626996Sgibbs return (1); 59726996Sgibbs } 59860536Sarchie return (0); 59960536Sarchie } 60060536Sarchie 60160536Sarchie /* Always accept packets when in promiscuous mode */ 602112734Smdodd if ((sc->promisc & IFF_PROMISC) != 0) 60326996Sgibbs return (1); 604581Srgrimes 60560536Sarchie /* Always accept packets directed at us */ 606152315Sru if (ether_equal(eh->ether_dhost, IF_LLADDR(sc->ifp))) 60726996Sgibbs return (1); 608581Srgrimes 60960536Sarchie /* Must have IFF_ALLMULTI but not IFF_PROMISC set. The chip is 61060536Sarchie actually in promiscuous mode, so discard unicast packets. */ 61160536Sarchie return((eh->ether_dhost[0] & 1) != 0); 612581Srgrimes} 613581Srgrimes 614581Srgrimes/* 615581Srgrimes * We want to isolate the bits that have meaning... This assumes that 616581Srgrimes * IE_RBUF_SIZE is an even power of two. If somehow the act_len exceeds 617581Srgrimes * the size of the buffer, then we are screwed anyway. 618581Srgrimes */ 61935210Sbdestatic __inline int 620112734Smdoddie_buflen(struct ie_softc *sc, int head) 62126996Sgibbs{ 622112734Smdodd return (sc->rbuffs[head]->ie_rbd_actual 62326996Sgibbs & (IE_RBUF_SIZE | (IE_RBUF_SIZE - 1))); 624581Srgrimes} 625581Srgrimes 62635210Sbdestatic __inline int 627112734Smdoddie_packet_len(struct ie_softc *sc) 62826996Sgibbs{ 62926996Sgibbs int i; 630112734Smdodd int head = sc->rbhead; 63126996Sgibbs int acc = 0; 632581Srgrimes 63326996Sgibbs do { 634112734Smdodd if (!(sc->rbuffs[sc->rbhead]->ie_rbd_actual & IE_RBD_USED)) { 635581Srgrimes#ifdef DEBUG 636112734Smdodd print_rbd(sc->rbuffs[sc->rbhead]); 637581Srgrimes#endif 63826996Sgibbs log(LOG_ERR, 639179416Sjhb "%s: receive descriptors out of sync at %d\n", 640179416Sjhb sc->ifp->if_xname, sc->rbhead); 641112734Smdodd iereset(sc); 64226996Sgibbs return (-1); 64326996Sgibbs } 644112734Smdodd i = sc->rbuffs[head]->ie_rbd_actual & IE_RBD_LAST; 645581Srgrimes 646112734Smdodd acc += ie_buflen(sc, head); 647112734Smdodd head = (head + 1) % sc->nrxbufs; 64826996Sgibbs } while (!i); 649581Srgrimes 65026996Sgibbs return (acc); 651581Srgrimes} 652581Srgrimes 653581Srgrimes/* 654581Srgrimes * Read data off the interface, and turn it into an mbuf chain. 655581Srgrimes * 656581Srgrimes * This code is DRAMATICALLY different from the previous version; this 657581Srgrimes * version tries to allocate the entire mbuf chain up front, given the 658581Srgrimes * length of the data available. This enables us to allocate mbuf 659581Srgrimes * clusters in many situations where before we would have had a long 660581Srgrimes * chain of partially-full mbufs. This should help to speed up the 661581Srgrimes * operation considerably. (Provided that it works, of course.) 662581Srgrimes */ 66335210Sbdestatic __inline int 664112734Smdoddieget(struct ie_softc *sc, struct mbuf **mp) 665581Srgrimes{ 666112720Smdodd struct ether_header eh; 66726996Sgibbs struct mbuf *m, *top, **mymp; 66826996Sgibbs int offset; 66926996Sgibbs int totlen, resid; 67026996Sgibbs int thismboff; 67126996Sgibbs int head; 672581Srgrimes 673112734Smdodd totlen = ie_packet_len(sc); 67426996Sgibbs if (totlen <= 0) 67526996Sgibbs return (-1); 676581Srgrimes 67726996Sgibbs /* 67826996Sgibbs * Snarf the Ethernet header. 67926996Sgibbs */ 680179416Sjhb bcopy(sc->cbuffs[sc->rbhead], &eh, sizeof(struct ether_header)); 68126996Sgibbs /* ignore cast-qual warning here */ 682581Srgrimes 68326996Sgibbs /* 68426996Sgibbs * As quickly as possible, check if this packet is for us. If not, 68526996Sgibbs * don't waste a single cycle copying the rest of the packet in. 68626996Sgibbs * This is only a consideration when FILTER is defined; i.e., when 68726996Sgibbs * we are either running BPF or doing multicasting. 68826996Sgibbs */ 689112734Smdodd if (!check_eh(sc, &eh)) { 690112734Smdodd ie_drop_packet_buffer(sc); 69126996Sgibbs return (-1); 69226996Sgibbs } 693581Srgrimes 694243857Sglebius MGETHDR(m, M_NOWAIT, MT_DATA); 695112720Smdodd if (!m) { 696112734Smdodd ie_drop_packet_buffer(sc); 697112720Smdodd return (-1); 698112720Smdodd } 699106937Ssam 700106937Ssam *mp = m; 701147256Sbrooks m->m_pkthdr.rcvif = sc->ifp; 702112720Smdodd m->m_len = MHLEN; 703112720Smdodd resid = m->m_pkthdr.len = totlen; 704112720Smdodd top = 0; 705106937Ssam 70626996Sgibbs mymp = ⊤ 707581Srgrimes 70826996Sgibbs /* 70926996Sgibbs * This loop goes through and allocates mbufs for all the data we 71026996Sgibbs * will be copying in. It does not actually do the copying yet. 71126996Sgibbs */ 71226996Sgibbs do { /* while(resid > 0) */ 71326996Sgibbs /* 71426996Sgibbs * Try to allocate an mbuf to hold the data that we have. 71526996Sgibbs * If we already allocated one, just get another one and 71626996Sgibbs * stick it on the end (eventually). If we don't already 71726996Sgibbs * have one, try to allocate an mbuf cluster big enough to 71826996Sgibbs * hold the whole packet, if we think it's reasonable, or a 71926996Sgibbs * single mbuf which may or may not be big enough. Got that? 72026996Sgibbs */ 72126996Sgibbs if (top) { 722243857Sglebius MGET(m, M_NOWAIT, MT_DATA); 72326996Sgibbs if (!m) { 72426996Sgibbs m_freem(top); 725112734Smdodd ie_drop_packet_buffer(sc); 72626996Sgibbs return (-1); 72726996Sgibbs } 72826996Sgibbs m->m_len = MLEN; 72926996Sgibbs } 73026996Sgibbs if (resid >= MINCLSIZE) { 731276750Srwatson if (MCLGET(m, M_NOWAIT)) 73226996Sgibbs m->m_len = min(resid, MCLBYTES); 73326996Sgibbs } else { 73426996Sgibbs if (resid < m->m_len) { 73526996Sgibbs if (!top && resid + max_linkhdr <= m->m_len) 73626996Sgibbs m->m_data += max_linkhdr; 73726996Sgibbs m->m_len = resid; 73826996Sgibbs } 73926996Sgibbs } 74026996Sgibbs resid -= m->m_len; 74126996Sgibbs *mymp = m; 74226996Sgibbs mymp = &m->m_next; 74326996Sgibbs } while (resid > 0); 744581Srgrimes 745112720Smdodd resid = totlen; /* remaining data */ 746112720Smdodd offset = 0; /* packet offset */ 747112720Smdodd thismboff = 0; /* offset in m */ 748112720Smdodd 749106937Ssam m = top; /* current mbuf */ 750112734Smdodd head = sc->rbhead; /* current rx buffer */ 751581Srgrimes 75226996Sgibbs /* 75326996Sgibbs * Now we take the mbuf chain (hopefully only one mbuf most of the 75426996Sgibbs * time) and stuff the data into it. There are no possible failures 75526996Sgibbs * at or after this point. 75626996Sgibbs */ 75726996Sgibbs while (resid > 0) { /* while there's stuff left */ 758112734Smdodd int thislen = ie_buflen(sc, head) - offset; 759581Srgrimes 76026996Sgibbs /* 76126996Sgibbs * If too much data for the current mbuf, then fill the 76226996Sgibbs * current one up, go to the next one, and try again. 76326996Sgibbs */ 76426996Sgibbs if (thislen > m->m_len - thismboff) { 76526996Sgibbs int newlen = m->m_len - thismboff; 766581Srgrimes 767112734Smdodd bcopy((v_caddr_t) (sc->cbuffs[head] + offset), 768112734Smdodd mtod(m, caddr_t) +thismboff, (unsigned) newlen); 76926996Sgibbs /* ignore cast-qual warning */ 77026996Sgibbs m = m->m_next; 771106937Ssam thismboff = 0; /* new mbuf, so no offset */ 77226996Sgibbs offset += newlen; /* we are now this far into 77326996Sgibbs * the packet */ 77426996Sgibbs resid -= newlen; /* so there is this much left 77526996Sgibbs * to get */ 77626996Sgibbs continue; 77726996Sgibbs } 77826996Sgibbs /* 77926996Sgibbs * If there is more than enough space in the mbuf to hold 78026996Sgibbs * the contents of this buffer, copy everything in, advance 78126996Sgibbs * pointers, and so on. 78226996Sgibbs */ 78326996Sgibbs if (thislen < m->m_len - thismboff) { 784112734Smdodd bcopy((v_caddr_t) (sc->cbuffs[head] + offset), 78526996Sgibbs mtod(m, caddr_t) +thismboff, (unsigned) thislen); 78626996Sgibbs thismboff += thislen; /* we are this far into the 78726996Sgibbs * mbuf */ 78826996Sgibbs resid -= thislen; /* and this much is left */ 78926996Sgibbs goto nextbuf; 79026996Sgibbs } 79126996Sgibbs /* 79226996Sgibbs * Otherwise, there is exactly enough space to put this 79326996Sgibbs * buffer's contents into the current mbuf. Do the 79426996Sgibbs * combination of the above actions. 79526996Sgibbs */ 796112734Smdodd bcopy((v_caddr_t) (sc->cbuffs[head] + offset), 79726996Sgibbs mtod(m, caddr_t) + thismboff, (unsigned) thislen); 79826996Sgibbs m = m->m_next; 79926996Sgibbs thismboff = 0; /* new mbuf, start at the beginning */ 80026996Sgibbs resid -= thislen; /* and we are this far through */ 801581Srgrimes 80226996Sgibbs /* 80326996Sgibbs * Advance all the pointers. We can get here from either of 80426996Sgibbs * the last two cases, but never the first. 80526996Sgibbs */ 806581Srgrimesnextbuf: 80726996Sgibbs offset = 0; 808112734Smdodd sc->rbuffs[head]->ie_rbd_actual = 0; 809112734Smdodd sc->rbuffs[head]->ie_rbd_length |= IE_RBD_LAST; 810112734Smdodd sc->rbhead = head = (head + 1) % sc->nrxbufs; 811112734Smdodd sc->rbuffs[sc->rbtail]->ie_rbd_length &= ~IE_RBD_LAST; 812112734Smdodd sc->rbtail = (sc->rbtail + 1) % sc->nrxbufs; 81326996Sgibbs } 814581Srgrimes 81526996Sgibbs /* 81626996Sgibbs * Unless something changed strangely while we were doing the copy, 81726996Sgibbs * we have now copied everything in from the shared memory. This 81826996Sgibbs * means that we are done. 81926996Sgibbs */ 82026996Sgibbs return (0); 821581Srgrimes} 822581Srgrimes 823581Srgrimes/* 824581Srgrimes * Read frame NUM from unit UNIT (pre-cached as IE). 825581Srgrimes * 826581Srgrimes * This routine reads the RFD at NUM, and copies in the buffers from 827581Srgrimes * the list of RBD, then rotates the RBD and RFD lists so that the receiver 828581Srgrimes * doesn't start complaining. Trailers are DROPPED---there's no point 82926996Sgibbs * in wasting time on confusing code to deal with them. Hopefully, 830581Srgrimes * this machine will never ARP for trailers anyway. 831581Srgrimes */ 83226996Sgibbsstatic void 833112734Smdoddie_readframe(struct ie_softc *sc, int num/* frame number to read */) 834581Srgrimes{ 835147256Sbrooks struct ifnet *ifp = sc->ifp; 83626996Sgibbs struct ie_recv_frame_desc rfd; 83726996Sgibbs struct mbuf *m = 0; 838106937Ssam#ifdef DEBUG 839106937Ssam struct ether_header *eh; 840106937Ssam#endif 84126996Sgibbs 842112734Smdodd bcopy((v_caddr_t) (sc->rframes[num]), &rfd, 84326996Sgibbs sizeof(struct ie_recv_frame_desc)); 844581Srgrimes 84526996Sgibbs /* 84626996Sgibbs * Immediately advance the RFD list, since we we have copied ours 84726996Sgibbs * now. 84826996Sgibbs */ 849112734Smdodd sc->rframes[num]->ie_fd_status = 0; 850112734Smdodd sc->rframes[num]->ie_fd_last |= IE_FD_LAST; 851112734Smdodd sc->rframes[sc->rftail]->ie_fd_last &= ~IE_FD_LAST; 852112734Smdodd sc->rftail = (sc->rftail + 1) % sc->nframes; 853112734Smdodd sc->rfhead = (sc->rfhead + 1) % sc->nframes; 854581Srgrimes 85526996Sgibbs if (rfd.ie_fd_status & IE_FD_OK) { 856112734Smdodd if (ieget(sc, &m)) { 857271820Sglebius if_inc_counter(sc->ifp, IFCOUNTER_IERRORS, 1); /* this counts as an 85826996Sgibbs * error */ 85926996Sgibbs return; 86026996Sgibbs } 86126996Sgibbs } 862581Srgrimes#ifdef DEBUG 863106937Ssam eh = mtod(m, struct ether_header *); 86426996Sgibbs if (ie_debug & IED_READFRAME) { 865179416Sjhb if_printf(ifp, "frame from ether %6D type %x\n", 866106937Ssam eh->ether_shost, ":", (unsigned) eh->ether_type); 86726996Sgibbs } 868106937Ssam if (ntohs(eh->ether_type) > ETHERTYPE_TRAIL 869106937Ssam && ntohs(eh->ether_type) < (ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER)) 87026996Sgibbs printf("received trailer!\n"); 871581Srgrimes#endif 872581Srgrimes 87326996Sgibbs if (!m) 87426996Sgibbs return; 875581Srgrimes 87626996Sgibbs /* 87726996Sgibbs * Finally pass this packet up to higher layers. 87826996Sgibbs */ 879179491Sjhb IE_UNLOCK(sc); 880106937Ssam (*ifp->if_input)(ifp, m); 881179491Sjhb IE_LOCK(sc); 882581Srgrimes} 883581Srgrimes 88426996Sgibbsstatic void 885112734Smdoddie_drop_packet_buffer(struct ie_softc *sc) 88626996Sgibbs{ 88726996Sgibbs int i; 888581Srgrimes 88926996Sgibbs do { 89026996Sgibbs /* 89126996Sgibbs * This means we are somehow out of sync. So, we reset the 89226996Sgibbs * adapter. 89326996Sgibbs */ 894112734Smdodd if (!(sc->rbuffs[sc->rbhead]->ie_rbd_actual & IE_RBD_USED)) { 895581Srgrimes#ifdef DEBUG 896112734Smdodd print_rbd(sc->rbuffs[sc->rbhead]); 897581Srgrimes#endif 898179416Sjhb log(LOG_ERR, "%s: receive descriptors out of sync at %d\n", 899179416Sjhb sc->ifp->if_xname, sc->rbhead); 900112734Smdodd iereset(sc); 90126996Sgibbs return; 90226996Sgibbs } 903112734Smdodd i = sc->rbuffs[sc->rbhead]->ie_rbd_actual & IE_RBD_LAST; 904581Srgrimes 905112734Smdodd sc->rbuffs[sc->rbhead]->ie_rbd_length |= IE_RBD_LAST; 906112734Smdodd sc->rbuffs[sc->rbhead]->ie_rbd_actual = 0; 907112734Smdodd sc->rbhead = (sc->rbhead + 1) % sc->nrxbufs; 908112734Smdodd sc->rbuffs[sc->rbtail]->ie_rbd_length &= ~IE_RBD_LAST; 909112734Smdodd sc->rbtail = (sc->rbtail + 1) % sc->nrxbufs; 91026996Sgibbs } while (!i); 911581Srgrimes} 912581Srgrimes 913581Srgrimes 914581Srgrimes/* 915581Srgrimes * Start transmission on an interface. 916581Srgrimes */ 917798Swollmanstatic void 91826996Sgibbsiestart(struct ifnet *ifp) 919581Srgrimes{ 920112734Smdodd struct ie_softc *sc = ifp->if_softc; 921179491Sjhb 922179491Sjhb IE_LOCK(sc); 923179491Sjhb iestart_locked(ifp); 924179491Sjhb IE_UNLOCK(sc); 925179491Sjhb} 926179491Sjhb 927179491Sjhbstatic void 928179491Sjhbiestart_locked(struct ifnet *ifp) 929179491Sjhb{ 930179491Sjhb struct ie_softc *sc = ifp->if_softc; 93126996Sgibbs struct mbuf *m0, *m; 93243314Sdillon volatile unsigned char *buffer; 93326996Sgibbs u_short len; 934581Srgrimes 93526996Sgibbs /* 93626996Sgibbs * This is not really volatile, in this routine, but it makes gcc 93726996Sgibbs * happy. 93826996Sgibbs */ 939112734Smdodd volatile u_short *bptr = &sc->scb->ie_command_list; 940581Srgrimes 941148887Srwatson if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) 94226996Sgibbs return; 943148887Srwatson if (ifp->if_drv_flags & IFF_DRV_OACTIVE) 94426996Sgibbs return; 945581Srgrimes 94626996Sgibbs do { 947147256Sbrooks IF_DEQUEUE(&sc->ifp->if_snd, m); 94826996Sgibbs if (!m) 94926996Sgibbs break; 950581Srgrimes 951272266Smelifaro BPF_MTAP(ifp, m); 952272266Smelifaro 953112734Smdodd buffer = sc->xmit_cbuffs[sc->xmit_count]; 95426996Sgibbs len = 0; 955581Srgrimes 95626996Sgibbs for (m0 = m; m && len < IE_BUF_LEN; m = m->m_next) { 95726996Sgibbs bcopy(mtod(m, caddr_t), buffer, m->m_len); 95826996Sgibbs buffer += m->m_len; 95926996Sgibbs len += m->m_len; 96026996Sgibbs } 961581Srgrimes 96226996Sgibbs m_freem(m0); 96326996Sgibbs len = max(len, ETHER_MIN_LEN); 96426996Sgibbs 965112734Smdodd sc->xmit_buffs[sc->xmit_count]->ie_xmit_flags = 96626996Sgibbs IE_XMIT_LAST|len; 967112734Smdodd sc->xmit_buffs[sc->xmit_count]->ie_xmit_next = 0xffff; 968112734Smdodd sc->xmit_buffs[sc->xmit_count]->ie_xmit_buf = 969112734Smdodd MK_24(sc->iomem, sc->xmit_cbuffs[sc->xmit_count]); 970581Srgrimes 971112734Smdodd sc->xmit_cmds[sc->xmit_count]->com.ie_cmd_cmd = IE_CMD_XMIT; 972112734Smdodd sc->xmit_cmds[sc->xmit_count]->ie_xmit_status = 0; 973112734Smdodd sc->xmit_cmds[sc->xmit_count]->ie_xmit_desc = 974112734Smdodd MK_16(sc->iomem, sc->xmit_buffs[sc->xmit_count]); 975581Srgrimes 976112734Smdodd *bptr = MK_16(sc->iomem, sc->xmit_cmds[sc->xmit_count]); 977112734Smdodd bptr = &sc->xmit_cmds[sc->xmit_count]->com.ie_cmd_link; 978112734Smdodd sc->xmit_count++; 979112734Smdodd } while (sc->xmit_count < sc->ntxbufs); 980581Srgrimes 98126996Sgibbs /* 98226996Sgibbs * If we queued up anything for transmission, send it. 98326996Sgibbs */ 984112734Smdodd if (sc->xmit_count) { 985112734Smdodd sc->xmit_cmds[sc->xmit_count - 1]->com.ie_cmd_cmd |= 98626996Sgibbs IE_CMD_LAST | IE_CMD_INTR; 987581Srgrimes 98826996Sgibbs /* 98926996Sgibbs * By passing the command pointer as a null, we tell 99026996Sgibbs * command_and_wait() to pretend that this isn't an action 99126996Sgibbs * command. I wish I understood what was happening here. 99226996Sgibbs */ 993112734Smdodd command_and_wait(sc, IE_CU_START, 0, 0); 994148887Srwatson ifp->if_drv_flags |= IFF_DRV_OACTIVE; 99526996Sgibbs } 99626996Sgibbs return; 997581Srgrimes} 998581Srgrimes 999581Srgrimes/* 1000581Srgrimes * Check to see if there's an 82586 out there. 1001581Srgrimes */ 1002112790Smdoddint 1003112790Smdoddcheck_ie_present(struct ie_softc *sc) 1004581Srgrimes{ 100526996Sgibbs volatile struct ie_sys_conf_ptr *scp; 100626996Sgibbs volatile struct ie_int_sys_conf_ptr *iscp; 100726996Sgibbs volatile struct ie_sys_ctl_block *scb; 100826996Sgibbs u_long realbase; 1009581Srgrimes 1010112790Smdodd realbase = (uintptr_t) sc->iomembot + sc->iosize - (1 << 24); 1011581Srgrimes 101238232Sbde scp = (volatile struct ie_sys_conf_ptr *) (uintptr_t) 101338232Sbde (realbase + IE_SCP_ADDR); 101443314Sdillon bzero((volatile char *) scp, sizeof *scp); 10158876Srgrimes 101626996Sgibbs /* 101726996Sgibbs * First we put the ISCP at the bottom of memory; this tests to make 101826996Sgibbs * sure that our idea of the size of memory is the same as the 101926996Sgibbs * controller's. This is NOT where the ISCP will be in normal 102026996Sgibbs * operation. 102126996Sgibbs */ 1022112790Smdodd iscp = (volatile struct ie_int_sys_conf_ptr *) sc->iomembot; 102343314Sdillon bzero((volatile char *)iscp, sizeof *iscp); 1024581Srgrimes 1025112790Smdodd scb = (volatile struct ie_sys_ctl_block *) sc->iomembot; 102643314Sdillon bzero((volatile char *)scb, sizeof *scb); 1027581Srgrimes 1028112734Smdodd scp->ie_bus_use = sc->bus_use; /* 8-bit or 16-bit */ 102947108Sbde scp->ie_iscp_ptr = (caddr_t) (uintptr_t) 103047108Sbde ((volatile char *) iscp - (volatile char *) (uintptr_t) realbase); 1031581Srgrimes 103226996Sgibbs iscp->ie_busy = 1; 103326996Sgibbs iscp->ie_scb_offset = MK_16(realbase, scb) + 256; 1034581Srgrimes 1035112734Smdodd (*sc->ie_reset_586) (sc); 1036112734Smdodd (*sc->ie_chan_attn) (sc); 1037581Srgrimes 103826996Sgibbs DELAY(100); /* wait a while... */ 1039581Srgrimes 104026996Sgibbs if (iscp->ie_busy) { 104126996Sgibbs return (0); 104226996Sgibbs } 104326996Sgibbs /* 104426996Sgibbs * Now relocate the ISCP to its real home, and reset the controller 104526996Sgibbs * again. 104626996Sgibbs */ 104738232Sbde iscp = (void *) Align((caddr_t) (uintptr_t) 104838232Sbde (realbase + IE_SCP_ADDR - 104938232Sbde sizeof(struct ie_int_sys_conf_ptr))); 105043314Sdillon bzero((volatile char *) iscp, sizeof *iscp); /* ignore cast-qual */ 1051581Srgrimes 105247108Sbde scp->ie_iscp_ptr = (caddr_t) (uintptr_t) 105347108Sbde ((volatile char *) iscp - (volatile char *) (uintptr_t) realbase); 1054581Srgrimes 105526996Sgibbs iscp->ie_busy = 1; 105626996Sgibbs iscp->ie_scb_offset = MK_16(realbase, scb); 1057581Srgrimes 1058112734Smdodd (*sc->ie_reset_586) (sc); 1059112734Smdodd (*sc->ie_chan_attn) (sc); 1060581Srgrimes 106126996Sgibbs DELAY(100); 1062581Srgrimes 106326996Sgibbs if (iscp->ie_busy) { 106426996Sgibbs return (0); 106526996Sgibbs } 1066112734Smdodd sc->iomem = (caddr_t) (uintptr_t) realbase; 1067581Srgrimes 1068112734Smdodd sc->iscp = iscp; 1069112734Smdodd sc->scb = scb; 1070581Srgrimes 107126996Sgibbs /* 107226996Sgibbs * Acknowledge any interrupts we may have caused... 107326996Sgibbs */ 1074112734Smdodd ie_ack(sc, IE_ST_WHENCE); 1075581Srgrimes 107626996Sgibbs return (1); 1077581Srgrimes} 1078581Srgrimes 1079112790Smdoddvoid 1080112734Smdoddel_reset_586(struct ie_softc *sc) 10812268Sats{ 1082112734Smdodd outb(PORT(sc) + IE507_CTRL, EL_CTRL_RESET); 108326996Sgibbs DELAY(100); 1084112734Smdodd outb(PORT(sc) + IE507_CTRL, EL_CTRL_NORMAL); 108526996Sgibbs DELAY(100); 10862268Sats} 10872268Sats 1088112790Smdoddvoid 1089112734Smdoddsl_reset_586(struct ie_softc *sc) 1090581Srgrimes{ 1091112734Smdodd outb(PORT(sc) + IEATT_RESET, 0); 1092581Srgrimes} 1093581Srgrimes 1094112790Smdoddvoid 1095112734Smdoddee16_reset_586(struct ie_softc *sc) 109624909Sgibbs{ 1097112734Smdodd outb(PORT(sc) + IEE16_ECTRL, IEE16_RESET_586); 109824909Sgibbs DELAY(100); 1099112734Smdodd outb(PORT(sc) + IEE16_ECTRL, 0); 110024909Sgibbs DELAY(100); 110124909Sgibbs} 110224909Sgibbs 1103112790Smdoddvoid 1104112734Smdoddel_chan_attn(struct ie_softc *sc) 11052268Sats{ 1106112734Smdodd outb(PORT(sc) + IE507_ATTN, 1); 11072268Sats} 11082268Sats 1109112790Smdoddvoid 1110112734Smdoddsl_chan_attn(struct ie_softc *sc) 1111581Srgrimes{ 1112112734Smdodd outb(PORT(sc) + IEATT_ATTN, 0); 1113581Srgrimes} 1114581Srgrimes 1115112790Smdoddvoid 1116112734Smdoddee16_chan_attn(struct ie_softc *sc) 111724909Sgibbs{ 1118112734Smdodd outb(PORT(sc) + IEE16_ATTN, 0); 111924909Sgibbs} 112024909Sgibbs 112135210Sbdestatic __inline void 112226996Sgibbsee16_interrupt_enable(struct ie_softc *sc) 112324909Sgibbs{ 112424909Sgibbs DELAY(100); 112524909Sgibbs outb(sc->port + IEE16_IRQ, sc->irq_encoded | IEE16_IRQ_ENABLE); 112624909Sgibbs DELAY(100); 112724909Sgibbs} 112824909Sgibbs 1129112790Smdoddvoid 1130112734Smdoddsl_read_ether(struct ie_softc *sc, unsigned char *addr) 1131581Srgrimes{ 113226996Sgibbs int i; 1133581Srgrimes 113426996Sgibbs for (i = 0; i < 6; i++) 1135112734Smdodd addr[i] = inb(PORT(sc) + i); 1136581Srgrimes} 1137581Srgrimes 1138798Swollmanstatic void 1139112734Smdoddiereset(struct ie_softc *sc) 1140581Srgrimes{ 1141179416Sjhb struct ifnet *ifp = sc->ifp; 1142581Srgrimes 1143179416Sjhb if_printf(ifp, "reset\n"); 1144179491Sjhb ie_stop(sc); 1145581Srgrimes 114626996Sgibbs /* 114726996Sgibbs * Stop i82586 dead in its tracks. 114826996Sgibbs */ 1149112734Smdodd if (command_and_wait(sc, IE_RU_ABORT | IE_CU_ABORT, 0, 0)) 1150179416Sjhb if_printf(ifp, "abort commands timed out\n"); 1151581Srgrimes 1152112734Smdodd if (command_and_wait(sc, IE_RU_DISABLE | IE_CU_STOP, 0, 0)) 1153179416Sjhb if_printf(ifp, "disable commands timed out\n"); 1154581Srgrimes 1155581Srgrimes#ifdef notdef 1156112790Smdodd if (!check_ie_present(sc)) 115726996Sgibbs panic("ie disappeared!"); 1158581Srgrimes#endif 1159581Srgrimes 1160179491Sjhb if (ifp->if_flags & IFF_UP) 1161179491Sjhb ieinit_locked(sc); 11628876Srgrimes 116326996Sgibbs return; 1164581Srgrimes} 1165581Srgrimes 1166581Srgrimes/* 1167581Srgrimes * Send a command to the controller and wait for it to either 1168581Srgrimes * complete or be accepted, depending on the command. If the 1169581Srgrimes * command pointer is null, then pretend that the command is 1170581Srgrimes * not an action command. If the command pointer is not null, 1171581Srgrimes * and the command is an action command, wait for 1172581Srgrimes * ((volatile struct ie_cmd_common *)pcmd)->ie_cmd_status & MASK 1173581Srgrimes * to become true. 1174581Srgrimes */ 117526996Sgibbsstatic int 1176112734Smdoddcommand_and_wait(struct ie_softc *sc, int cmd, volatile void *pcmd, int mask) 1177581Srgrimes{ 117826996Sgibbs volatile struct ie_cmd_common *cc = pcmd; 1179179491Sjhb int i; 1180581Srgrimes 1181112734Smdodd sc->scb->ie_command = (u_short) cmd; 11828876Srgrimes 118326996Sgibbs if (IE_ACTION_COMMAND(cmd) && pcmd) { 1184112734Smdodd (*sc->ie_chan_attn) (sc); 1185179491Sjhb 118626996Sgibbs /* 118726996Sgibbs * Now spin-lock waiting for status. This is not a very 118826996Sgibbs * nice thing to do, but I haven't figured out how, or 118926996Sgibbs * indeed if, we can put the process waiting for action to 119026996Sgibbs * sleep. (We may be getting called through some other 119126996Sgibbs * timeout running in the kernel.) 1192179491Sjhb * 1193179491Sjhb * According to the packet driver, the minimum timeout 1194179491Sjhb * should be .369 seconds, which we round up to .37. 119526996Sgibbs */ 1196179491Sjhb for (i = 0; i < 370; i++) { 1197179491Sjhb if (cc->ie_cmd_status & mask) 1198179491Sjhb return (0); 1199179491Sjhb DELAY(1000); 120026996Sgibbs } 1201581Srgrimes 1202179491Sjhb return (1); 120326996Sgibbs } else { 12048876Srgrimes 120526996Sgibbs /* 120626996Sgibbs * Otherwise, just wait for the command to be accepted. 120726996Sgibbs */ 1208112734Smdodd (*sc->ie_chan_attn) (sc); 1209581Srgrimes 1210112734Smdodd while (sc->scb->ie_command); /* spin lock */ 1211581Srgrimes 121226996Sgibbs return (0); 121326996Sgibbs } 1214581Srgrimes} 1215581Srgrimes 1216581Srgrimes/* 1217581Srgrimes * Run the time-domain reflectometer... 1218581Srgrimes */ 121926996Sgibbsstatic void 1220112734Smdoddrun_tdr(struct ie_softc *sc, volatile struct ie_tdr_cmd *cmd) 1221581Srgrimes{ 122226996Sgibbs int result; 1223581Srgrimes 122426996Sgibbs cmd->com.ie_cmd_status = 0; 122526996Sgibbs cmd->com.ie_cmd_cmd = IE_CMD_TDR | IE_CMD_LAST; 122626996Sgibbs cmd->com.ie_cmd_link = 0xffff; 122726996Sgibbs cmd->ie_tdr_time = 0; 1228581Srgrimes 1229112734Smdodd sc->scb->ie_command_list = MK_16(MEM(sc), cmd); 123026996Sgibbs cmd->ie_tdr_time = 0; 1231581Srgrimes 1232112734Smdodd if (command_and_wait(sc, IE_CU_START, cmd, IE_STAT_COMPL)) 123326996Sgibbs result = 0x2000; 123426996Sgibbs else 123526996Sgibbs result = cmd->ie_tdr_time; 1236581Srgrimes 1237112734Smdodd ie_ack(sc, IE_ST_WHENCE); 1238581Srgrimes 123926996Sgibbs if (result & IE_TDR_SUCCESS) 124026996Sgibbs return; 1241581Srgrimes 124226996Sgibbs if (result & IE_TDR_XCVR) { 1243179416Sjhb if_printf(sc->ifp, "transceiver problem\n"); 124426996Sgibbs } else if (result & IE_TDR_OPEN) { 1245179416Sjhb if_printf(sc->ifp, "TDR detected an open %d clocks away\n", 124626996Sgibbs result & IE_TDR_TIME); 124726996Sgibbs } else if (result & IE_TDR_SHORT) { 1248179416Sjhb if_printf(sc->ifp, "TDR detected a short %d clocks away\n", 124926996Sgibbs result & IE_TDR_TIME); 125026996Sgibbs } else { 1251179416Sjhb if_printf(sc->ifp, "TDR returned unknown status %x\n", result); 125226996Sgibbs } 1253581Srgrimes} 1254581Srgrimes 125526996Sgibbsstatic void 1256112734Smdoddstart_receiver(struct ie_softc *sc) 1257581Srgrimes{ 1258581Srgrimes 1259112734Smdodd sc->scb->ie_recv_list = MK_16(MEM(sc), sc->rframes[0]); 1260112734Smdodd command_and_wait(sc, IE_RU_START, 0, 0); 1261581Srgrimes 1262112734Smdodd ie_ack(sc, IE_ST_WHENCE); 1263581Srgrimes} 1264581Srgrimes 1265581Srgrimes/* 1266581Srgrimes * Here is a helper routine for iernr() and ieinit(). This sets up 1267581Srgrimes * the RFA. 1268581Srgrimes */ 126943314Sdillonstatic v_caddr_t 1270112734Smdoddsetup_rfa(struct ie_softc *sc, v_caddr_t ptr) 127126996Sgibbs{ 127243314Sdillon volatile struct ie_recv_frame_desc *rfd = (volatile void *)ptr; 127326996Sgibbs volatile struct ie_recv_buf_desc *rbd; 127426996Sgibbs int i; 1275581Srgrimes 127626996Sgibbs /* First lay them out */ 1277112734Smdodd for (i = 0; i < sc->nframes; i++) { 1278112734Smdodd sc->rframes[i] = rfd; 127943314Sdillon bzero((volatile char *) rfd, sizeof *rfd); /* ignore cast-qual */ 128026996Sgibbs rfd++; 128126996Sgibbs } 1282581Srgrimes 128347108Sbde ptr = Alignvol(rfd); /* ignore cast-qual */ 12848876Srgrimes 128526996Sgibbs /* Now link them together */ 1286112734Smdodd for (i = 0; i < sc->nframes; i++) { 1287112734Smdodd sc->rframes[i]->ie_fd_next = 1288112734Smdodd MK_16(MEM(sc), sc->rframes[(i + 1) % sc->nframes]); 128926996Sgibbs } 12908876Srgrimes 129126996Sgibbs /* Finally, set the EOL bit on the last one. */ 1292112734Smdodd sc->rframes[sc->nframes - 1]->ie_fd_last |= IE_FD_LAST; 1293581Srgrimes 129426996Sgibbs /* 129526996Sgibbs * Now lay out some buffers for the incoming frames. Note that we 129626996Sgibbs * set aside a bit of slop in each buffer, to make sure that we have 129726996Sgibbs * enough space to hold a single frame in every buffer. 129826996Sgibbs */ 129943314Sdillon rbd = (volatile void *) ptr; 1300581Srgrimes 1301112734Smdodd for (i = 0; i < sc->nrxbufs; i++) { 1302112734Smdodd sc->rbuffs[i] = rbd; 130343314Sdillon bzero((volatile char *)rbd, sizeof *rbd); 130447108Sbde ptr = Alignvol(ptr + sizeof *rbd); 130526996Sgibbs rbd->ie_rbd_length = IE_RBUF_SIZE; 1306112734Smdodd rbd->ie_rbd_buffer = MK_24(MEM(sc), ptr); 1307112734Smdodd sc->cbuffs[i] = (volatile void *) ptr; 130826996Sgibbs ptr += IE_RBUF_SIZE; 130943314Sdillon rbd = (volatile void *) ptr; 131026996Sgibbs } 13118876Srgrimes 131226996Sgibbs /* Now link them together */ 1313112734Smdodd for (i = 0; i < sc->nrxbufs; i++) { 1314112734Smdodd sc->rbuffs[i]->ie_rbd_next = 1315112734Smdodd MK_16(MEM(sc), sc->rbuffs[(i + 1) % sc->nrxbufs]); 131626996Sgibbs } 13178876Srgrimes 131826996Sgibbs /* Tag EOF on the last one */ 1319112734Smdodd sc->rbuffs[sc->nrxbufs - 1]->ie_rbd_length |= IE_RBD_LAST; 1320581Srgrimes 132126996Sgibbs /* 132226996Sgibbs * We use the head and tail pointers on receive to keep track of the 132326996Sgibbs * order in which RFDs and RBDs are used. 132426996Sgibbs */ 1325112734Smdodd sc->rfhead = 0; 1326112734Smdodd sc->rftail = sc->nframes - 1; 1327112734Smdodd sc->rbhead = 0; 1328112734Smdodd sc->rbtail = sc->nrxbufs - 1; 1329581Srgrimes 1330112734Smdodd sc->scb->ie_recv_list = MK_16(MEM(sc), sc->rframes[0]); 1331112734Smdodd sc->rframes[0]->ie_fd_buf_desc = MK_16(MEM(sc), sc->rbuffs[0]); 1332581Srgrimes 133347108Sbde ptr = Alignvol(ptr); 133426996Sgibbs return (ptr); 1335581Srgrimes} 1336581Srgrimes 1337581Srgrimes/* 1338581Srgrimes * Run the multicast setup command. 1339581Srgrimes */ 134026996Sgibbsstatic int 1341112734Smdoddmc_setup(struct ie_softc *sc) 134226996Sgibbs{ 1343112734Smdodd volatile struct ie_mcast_cmd *cmd = (volatile void *)sc->xmit_cbuffs[0]; 13448876Srgrimes 134526996Sgibbs cmd->com.ie_cmd_status = 0; 134626996Sgibbs cmd->com.ie_cmd_cmd = IE_CMD_MCAST | IE_CMD_LAST; 134726996Sgibbs cmd->com.ie_cmd_link = 0xffff; 13488876Srgrimes 134926996Sgibbs /* ignore cast-qual */ 1350112734Smdodd bcopy((v_caddr_t) sc->mcast_addrs, (v_caddr_t) cmd->ie_mcast_addrs, 1351112734Smdodd sc->mcast_count * sizeof *sc->mcast_addrs); 1352581Srgrimes 1353112734Smdodd cmd->ie_mcast_bytes = sc->mcast_count * 6; /* grrr... */ 13548876Srgrimes 1355112734Smdodd sc->scb->ie_command_list = MK_16(MEM(sc), cmd); 1356112734Smdodd if (command_and_wait(sc, IE_CU_START, cmd, IE_STAT_COMPL) 135726996Sgibbs || !(cmd->com.ie_cmd_status & IE_STAT_OK)) { 1358179416Sjhb if_printf(sc->ifp, "multicast address setup command failed\n"); 135926996Sgibbs return (0); 136026996Sgibbs } 136126996Sgibbs return (1); 1362581Srgrimes} 1363581Srgrimes 1364581Srgrimes/* 1365581Srgrimes * This routine takes the environment generated by check_ie_present() 1366581Srgrimes * and adds to it all the other structures we need to operate the adapter. 1367581Srgrimes * This includes executing the CONFIGURE, IA-SETUP, and MC-SETUP commands, 1368581Srgrimes * starting the receiver unit, and clearing interrupts. 1369581Srgrimes */ 1370798Swollmanstatic void 137150084Smdoddieinit(xsc) 137250084Smdodd void *xsc; 1373581Srgrimes{ 1374112734Smdodd struct ie_softc *sc = xsc; 1375179491Sjhb 1376179491Sjhb IE_LOCK(sc); 1377179491Sjhb ieinit_locked(sc); 1378179491Sjhb IE_UNLOCK(sc); 1379179491Sjhb} 1380179491Sjhb 1381179491Sjhbstatic void 1382179491Sjhbieinit_locked(struct ie_softc *sc) 1383179491Sjhb{ 1384179416Sjhb struct ifnet *ifp = sc->ifp; 1385112734Smdodd volatile struct ie_sys_ctl_block *scb = sc->scb; 1386112734Smdodd caddr_t ptr; 138726996Sgibbs int i; 1388581Srgrimes 138947108Sbde ptr = Alignvol((volatile char *) scb + sizeof *scb); 1390581Srgrimes 139126996Sgibbs /* 139226996Sgibbs * Send the configure command first. 139326996Sgibbs */ 139426996Sgibbs { 139543314Sdillon volatile struct ie_config_cmd *cmd = (volatile void *) ptr; 1396581Srgrimes 1397112734Smdodd ie_setup_config(cmd, sc->promisc, 1398112734Smdodd sc->hard_type == IE_STARLAN10); 139926996Sgibbs cmd->com.ie_cmd_status = 0; 140026996Sgibbs cmd->com.ie_cmd_cmd = IE_CMD_CONFIG | IE_CMD_LAST; 140126996Sgibbs cmd->com.ie_cmd_link = 0xffff; 1402581Srgrimes 1403112734Smdodd scb->ie_command_list = MK_16(MEM(sc), cmd); 1404581Srgrimes 1405112734Smdodd if (command_and_wait(sc, IE_CU_START, cmd, IE_STAT_COMPL) 140626996Sgibbs || !(cmd->com.ie_cmd_status & IE_STAT_OK)) { 1407179416Sjhb if_printf(ifp, "configure command failed\n"); 140826996Sgibbs return; 140926996Sgibbs } 141026996Sgibbs } 141126996Sgibbs /* 141226996Sgibbs * Now send the Individual Address Setup command. 141326996Sgibbs */ 141426996Sgibbs { 141543314Sdillon volatile struct ie_iasetup_cmd *cmd = (volatile void *) ptr; 1416581Srgrimes 141726996Sgibbs cmd->com.ie_cmd_status = 0; 141826996Sgibbs cmd->com.ie_cmd_cmd = IE_CMD_IASETUP | IE_CMD_LAST; 141926996Sgibbs cmd->com.ie_cmd_link = 0xffff; 1420581Srgrimes 1421179416Sjhb bcopy((volatile char *)IF_LLADDR(ifp), 142243314Sdillon (volatile char *)&cmd->ie_address, sizeof cmd->ie_address); 1423112734Smdodd scb->ie_command_list = MK_16(MEM(sc), cmd); 1424112734Smdodd if (command_and_wait(sc, IE_CU_START, cmd, IE_STAT_COMPL) 142526996Sgibbs || !(cmd->com.ie_cmd_status & IE_STAT_OK)) { 1426179416Sjhb if_printf(ifp, "individual address " 1427179416Sjhb "setup command failed\n"); 142826996Sgibbs return; 142926996Sgibbs } 143026996Sgibbs } 1431581Srgrimes 143226996Sgibbs /* 143326996Sgibbs * Now run the time-domain reflectometer. 143426996Sgibbs */ 1435112734Smdodd run_tdr(sc, (volatile void *) ptr); 1436581Srgrimes 143726996Sgibbs /* 143826996Sgibbs * Acknowledge any interrupts we have generated thus far. 143926996Sgibbs */ 1440112734Smdodd ie_ack(sc, IE_ST_WHENCE); 1441581Srgrimes 144226996Sgibbs /* 144326996Sgibbs * Set up the RFA. 144426996Sgibbs */ 1445112734Smdodd ptr = setup_rfa(sc, ptr); 1446581Srgrimes 144726996Sgibbs /* 144826996Sgibbs * Finally, the transmit command and buffer are the last little bit 144926996Sgibbs * of work. 145026996Sgibbs */ 1451581Srgrimes 145226996Sgibbs /* transmit command buffers */ 1453112734Smdodd for (i = 0; i < sc->ntxbufs; i++) { 1454112734Smdodd sc->xmit_cmds[i] = (volatile void *) ptr; 1455112734Smdodd ptr += sizeof *sc->xmit_cmds[i]; 145647108Sbde ptr = Alignvol(ptr); 1457112734Smdodd sc->xmit_buffs[i] = (volatile void *)ptr; 1458112734Smdodd ptr += sizeof *sc->xmit_buffs[i]; 145947108Sbde ptr = Alignvol(ptr); 146026996Sgibbs } 1461581Srgrimes 146226996Sgibbs /* transmit buffers */ 1463112734Smdodd for (i = 0; i < sc->ntxbufs - 1; i++) { 1464112734Smdodd sc->xmit_cbuffs[i] = (volatile void *)ptr; 146526996Sgibbs ptr += IE_BUF_LEN; 146647108Sbde ptr = Alignvol(ptr); 146726996Sgibbs } 1468112734Smdodd sc->xmit_cbuffs[sc->ntxbufs - 1] = (volatile void *) ptr; 1469581Srgrimes 1470112734Smdodd for (i = 1; i < sc->ntxbufs; i++) { 1471112734Smdodd bzero((v_caddr_t) sc->xmit_cmds[i], sizeof *sc->xmit_cmds[i]); 1472112734Smdodd bzero((v_caddr_t) sc->xmit_buffs[i], sizeof *sc->xmit_buffs[i]); 147326996Sgibbs } 1474581Srgrimes 147526996Sgibbs /* 147626996Sgibbs * This must be coordinated with iestart() and ietint(). 147726996Sgibbs */ 1478112734Smdodd sc->xmit_cmds[0]->ie_xmit_status = IE_STAT_COMPL; 1479581Srgrimes 148026996Sgibbs /* take the ee16 out of loopback */ 1481112734Smdodd if (sc->hard_type == IE_EE16) { 148226996Sgibbs u_int8_t bart_config; 1483581Srgrimes 1484112734Smdodd bart_config = inb(PORT(sc) + IEE16_CONFIG); 148526996Sgibbs bart_config &= ~IEE16_BART_LOOPBACK; 148626996Sgibbs /* inb doesn't get bit! */ 148726996Sgibbs bart_config |= IEE16_BART_MCS16_TEST; 1488112734Smdodd outb(PORT(sc) + IEE16_CONFIG, bart_config); 1489112734Smdodd ee16_interrupt_enable(sc); 1490112734Smdodd ee16_chan_attn(sc); 149126996Sgibbs } 1492179416Sjhb ifp->if_drv_flags |= IFF_DRV_RUNNING; /* tell higher levels 149326996Sgibbs * we're here */ 1494179416Sjhb ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 1495112788Smdodd 1496112734Smdodd start_receiver(sc); 149725971Sgibbs 149826996Sgibbs return; 1499581Srgrimes} 1500581Srgrimes 150126996Sgibbsstatic void 1502112734Smdoddie_stop(struct ie_softc *sc) 1503581Srgrimes{ 1504179491Sjhb struct ifnet *ifp = sc->ifp; 1505179491Sjhb 1506179491Sjhb ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 1507112734Smdodd command_and_wait(sc, IE_RU_DISABLE, 0, 0); 1508581Srgrimes} 1509581Srgrimes 1510798Swollmanstatic int 151136735Sdfrieioctl(struct ifnet *ifp, u_long command, caddr_t data) 1512581Srgrimes{ 1513179491Sjhb int error = 0; 1514112734Smdodd struct ie_softc *sc = ifp->if_softc; 1515581Srgrimes 151626996Sgibbs switch (command) { 151726996Sgibbs case SIOCSIFFLAGS: 151826996Sgibbs /* 151926996Sgibbs * Note that this device doesn't have an "all multicast" 152026996Sgibbs * mode, so we must turn on promiscuous mode and do the 152126996Sgibbs * filtering manually. 152226996Sgibbs */ 1523179491Sjhb IE_LOCK(sc); 152426996Sgibbs if ((ifp->if_flags & IFF_UP) == 0 && 1525148887Srwatson (ifp->if_drv_flags & IFF_DRV_RUNNING)) { 1526112734Smdodd ie_stop(sc); 152726996Sgibbs } else if ((ifp->if_flags & IFF_UP) && 1528148887Srwatson (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 1529112734Smdodd sc->promisc = 153026996Sgibbs ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI); 1531179558Sjhb ieinit_locked(sc); 1532112734Smdodd } else if (sc->promisc ^ 153326996Sgibbs (ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI))) { 1534112734Smdodd sc->promisc = 153526996Sgibbs ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI); 1536179558Sjhb ieinit_locked(sc); 153726996Sgibbs } 1538179491Sjhb IE_UNLOCK(sc); 153926996Sgibbs break; 1540581Srgrimes 154126996Sgibbs case SIOCADDMULTI: 154226996Sgibbs case SIOCDELMULTI: 154326996Sgibbs /* 154426996Sgibbs * Update multicast listeners 154526996Sgibbs */ 154626996Sgibbs /* reset multicast filtering */ 1547179491Sjhb IE_LOCK(sc); 1548112734Smdodd ie_mc_reset(sc); 1549179491Sjhb IE_UNLOCK(sc); 155026996Sgibbs error = 0; 155126996Sgibbs break; 1552581Srgrimes 155326996Sgibbs default: 1554106937Ssam error = ether_ioctl(ifp, command, data); 1555106937Ssam break; 155626996Sgibbs } 1557581Srgrimes 155826996Sgibbs return (error); 1559581Srgrimes} 1560581Srgrimes 156126996Sgibbsstatic void 1562112734Smdoddie_mc_reset(struct ie_softc *sc) 156326996Sgibbs{ 156426996Sgibbs struct ifmultiaddr *ifma; 1565581Srgrimes 156626996Sgibbs /* 156726996Sgibbs * Step through the list of addresses. 156826996Sgibbs */ 1569112734Smdodd sc->mcast_count = 0; 1570195049Srwatson if_maddr_rlock(sc->ifp); 1571147256Sbrooks TAILQ_FOREACH(ifma, &sc->ifp->if_multiaddrs, ifma_link) { 157226996Sgibbs if (ifma->ifma_addr->sa_family != AF_LINK) 157326996Sgibbs continue; 1574581Srgrimes 157526996Sgibbs /* XXX - this is broken... */ 1576112734Smdodd if (sc->mcast_count >= MAXMCAST) { 1577147256Sbrooks sc->ifp->if_flags |= IFF_ALLMULTI; 1578179491Sjhb if (sc->ifp->if_flags & IFF_UP) 1579179491Sjhb ieinit_locked(sc); 158026996Sgibbs goto setflag; 158126996Sgibbs } 158226996Sgibbs bcopy(LLADDR((struct sockaddr_dl *) ifma->ifma_addr), 1583112734Smdodd &(sc->mcast_addrs[sc->mcast_count]), 6); 1584112734Smdodd sc->mcast_count++; 158526996Sgibbs } 1586195049Srwatson if_maddr_runlock(sc->ifp); 158721666Swollman 1588581Srgrimessetflag: 1589112734Smdodd sc->want_mcsetup = 1; 1590581Srgrimes} 1591581Srgrimes 1592581Srgrimes 1593581Srgrimes#ifdef DEBUG 159433181Seivindstatic void 159526996Sgibbsprint_rbd(volatile struct ie_recv_buf_desc * rbd) 159626996Sgibbs{ 159738224Sbde printf("RBD at %p:\n" 159838224Sbde "actual %04x, next %04x, buffer %p\n" 159926996Sgibbs "length %04x, mbz %04x\n", 160043314Sdillon (volatile void *) rbd, 160137618Sbde rbd->ie_rbd_actual, rbd->ie_rbd_next, 160237618Sbde (void *) rbd->ie_rbd_buffer, 160326996Sgibbs rbd->ie_rbd_length, rbd->mbz); 1604581Srgrimes} 1605581Srgrimes 160626996Sgibbs#endif /* DEBUG */ 1607112790Smdodd 1608112790Smdoddint 1609112790Smdoddie_alloc_resources (device_t dev) 1610112790Smdodd{ 1611112790Smdodd struct ie_softc * sc; 1612112790Smdodd int error; 1613112790Smdodd 1614112790Smdodd error = 0; 1615112790Smdodd sc = device_get_softc(dev); 1616112790Smdodd 1617127135Snjl sc->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &sc->io_rid, 1618127135Snjl RF_ACTIVE); 1619112790Smdodd if (!sc->io_res) { 1620112790Smdodd device_printf(dev, "No I/O space?!\n"); 1621112790Smdodd error = ENOMEM; 1622112790Smdodd goto bad; 1623112790Smdodd } 1624112790Smdodd sc->io_bt = rman_get_bustag(sc->io_res); 1625112790Smdodd sc->io_bh = rman_get_bushandle(sc->io_res); 1626112790Smdodd 1627127135Snjl sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, 1628127135Snjl RF_ACTIVE); 1629112790Smdodd if (!sc->mem_res) { 1630112790Smdodd device_printf(dev, "No Memory!\n"); 1631112790Smdodd error = ENOMEM; 1632112790Smdodd goto bad; 1633112790Smdodd } 1634112790Smdodd sc->mem_bt = rman_get_bustag(sc->mem_res); 1635112790Smdodd sc->mem_bh = rman_get_bushandle(sc->mem_res); 1636112790Smdodd 1637127135Snjl sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid, 1638127135Snjl RF_ACTIVE); 1639112790Smdodd if (!sc->irq_res) { 1640112790Smdodd device_printf(dev, "No IRQ!\n"); 1641112790Smdodd error = ENOMEM; 1642112790Smdodd goto bad; 1643112790Smdodd } 1644112790Smdodd 1645112790Smdodd sc->port = rman_get_start(sc->io_res); /* XXX hack */ 1646112790Smdodd sc->iomembot = rman_get_virtual(sc->mem_res); 1647112790Smdodd sc->iosize = rman_get_size(sc->mem_res); 1648112790Smdodd 1649112790Smdodd return (0); 1650112790Smdoddbad: 1651112790Smdodd return (error); 1652112790Smdodd} 1653112790Smdodd 1654112790Smdoddvoid 1655112790Smdoddie_release_resources (device_t dev) 1656112790Smdodd{ 1657112790Smdodd struct ie_softc * sc; 1658112790Smdodd 1659112790Smdodd sc = device_get_softc(dev); 1660112790Smdodd 1661112790Smdodd if (sc->irq_ih) 1662112790Smdodd bus_teardown_intr(dev, sc->irq_res, sc->irq_ih); 1663179491Sjhb if (sc->rframes) 1664179491Sjhb free(sc->rframes, M_DEVBUF); 1665112790Smdodd if (sc->io_res) 1666112790Smdodd bus_release_resource(dev, SYS_RES_IOPORT, 1667112790Smdodd sc->io_rid, sc->io_res); 1668112790Smdodd if (sc->irq_res) 1669112790Smdodd bus_release_resource(dev, SYS_RES_IRQ, 1670112790Smdodd sc->irq_rid, sc->irq_res); 1671112790Smdodd if (sc->mem_res) 1672112790Smdodd bus_release_resource(dev, SYS_RES_MEMORY, 1673112790Smdodd sc->mem_rid, sc->mem_res); 1674150215Sru if (sc->ifp) 1675150215Sru if_free(sc->ifp); 1676112790Smdodd 1677112790Smdodd return; 1678112790Smdodd} 1679112790Smdodd 1680112790Smdoddint 1681112790Smdoddie_detach (device_t dev) 1682112790Smdodd{ 1683112790Smdodd struct ie_softc * sc; 1684112790Smdodd struct ifnet * ifp; 1685112790Smdodd 1686112790Smdodd sc = device_get_softc(dev); 1687147256Sbrooks ifp = sc->ifp; 1688112790Smdodd 1689179491Sjhb IE_LOCK(sc); 1690112790Smdodd if (sc->hard_type == IE_EE16) 1691181134Sjhb ee16_shutdown(sc); 1692112790Smdodd 1693112790Smdodd ie_stop(sc); 1694179491Sjhb IE_UNLOCK(sc); 1695112790Smdodd ether_ifdetach(ifp); 1696112790Smdodd ie_release_resources(dev); 1697179491Sjhb mtx_destroy(&sc->lock); 1698112790Smdodd 1699112790Smdodd return (0); 1700112790Smdodd} 1701