if_ie.c revision 147256
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: head/sys/dev/ie/if_ie.c 147256 2005-06-10 16:49:24Z brooks $"); 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> 1332056Swollman#include <net/if_types.h> 1342056Swollman#include <net/if_dl.h> 135581Srgrimes 13650026Smdodd#include <netinet/in.h> 13750026Smdodd#include <netinet/if_ether.h> 13850026Smdodd 13979077Simp#include <dev/ic/i82586.h> 140112790Smdodd#include <dev/ie/if_ievar.h> 14155953Speter#include <dev/ie/if_iereg.h> 14255953Speter#include <dev/ie/if_ie507.h> 14355953Speter#include <dev/ie/if_iee16.h> 1442268Sats#include <i386/isa/elink.h> 145581Srgrimes 1462056Swollman#include <net/bpf.h> 147581Srgrimes 148581Srgrimes#ifdef DEBUG 14926996Sgibbs#define IED_RINT 0x01 15026996Sgibbs#define IED_TINT 0x02 15126996Sgibbs#define IED_RNR 0x04 15226996Sgibbs#define IED_CNA 0x08 15326996Sgibbs#define IED_READFRAME 0x10 15433181Seivindstatic int ie_debug = IED_RNR; 15526996Sgibbs 156581Srgrimes#endif 157581Srgrimes 15826996Sgibbs#define IE_BUF_LEN ETHER_MAX_LEN /* length of transmit buffer */ 159581Srgrimes 160581Srgrimes/* Forward declaration */ 161581Srgrimesstruct ie_softc; 162581Srgrimes 163112734Smdoddstatic void ieinit (void *); 164112734Smdoddstatic void ie_stop (struct ie_softc *); 165112734Smdoddstatic int ieioctl (struct ifnet *, u_long, caddr_t); 166112734Smdoddstatic void iestart (struct ifnet *); 16724909Sgibbs 168112734Smdoddstatic __inline void 169112734Smdodd ee16_interrupt_enable (struct ie_softc *); 170112734Smdoddstatic void ee16_eeprom_outbits (struct ie_softc *, int, int); 171112734Smdoddstatic void ee16_eeprom_clock (struct ie_softc *, int); 172112734Smdoddstatic u_short ee16_read_eeprom (struct ie_softc *, int); 173112734Smdoddstatic int ee16_eeprom_inbits (struct ie_softc *); 174112734Smdoddstatic void ee16_shutdown (void *, int); 17524909Sgibbs 176112734Smdoddstatic __inline void 177112734Smdodd ie_ack (struct ie_softc *, u_int); 178112734Smdoddstatic void iereset (struct ie_softc *); 179112734Smdoddstatic void ie_readframe (struct ie_softc *, int); 180112734Smdoddstatic void ie_drop_packet_buffer (struct ie_softc *); 181112734Smdoddstatic void find_ie_mem_size (struct ie_softc *); 182112734Smdoddstatic void chan_attn_timeout (void *); 183112734Smdoddstatic int command_and_wait (struct ie_softc *, 184112734Smdodd int, void volatile *, int); 185112734Smdoddstatic void run_tdr (struct ie_softc *, 186112734Smdodd volatile struct ie_tdr_cmd *); 187112734Smdoddstatic int ierint (struct ie_softc *); 188112734Smdoddstatic int ietint (struct ie_softc *); 189112734Smdoddstatic int iernr (struct ie_softc *); 190112734Smdoddstatic void start_receiver (struct ie_softc *); 191112734Smdoddstatic __inline int 192112734Smdodd ieget (struct ie_softc *, struct mbuf **); 193112734Smdoddstatic v_caddr_t setup_rfa (struct ie_softc *, v_caddr_t); 194112734Smdoddstatic int mc_setup (struct ie_softc *); 195112734Smdoddstatic void ie_mc_reset (struct ie_softc *); 196581Srgrimes 197581Srgrimes#ifdef DEBUG 198112734Smdoddstatic void print_rbd (volatile struct ie_recv_buf_desc * rbd); 19933181Seivindstatic int in_ierint = 0; 20033181Seivindstatic int in_ietint = 0; 201581Srgrimes#endif 202581Srgrimes 20312724Sphkstatic const char *ie_hardware_names[] = { 204112790Smdodd "None", 20526996Sgibbs "StarLAN 10", 20626996Sgibbs "EN100", 20726996Sgibbs "StarLAN Fiber", 20826996Sgibbs "3C507", 20926996Sgibbs "NI5210", 21026996Sgibbs "EtherExpress 16", 21126996Sgibbs "Unknown" 212581Srgrimes}; 213581Srgrimes 2148876Srgrimes/* 215112765Smdodd * sizeof(iscp) == 1+1+2+4 == 8 216112765Smdodd * sizeof(scb) == 2+2+2+2+2+2+2+2 == 16 217112765Smdodd * NFRAMES * sizeof(rfd) == NFRAMES*(2+2+2+2+6+6+2+2) == NFRAMES*24 == 384 218112765Smdodd * sizeof(xmit_cmd) == 2+2+2+2+6+2 == 18 219112765Smdodd * sizeof(transmit buffer) == 1512 220112765Smdodd * sizeof(transmit buffer desc) == 8 221112765Smdodd * ----- 222112765Smdodd * 1946 223112765Smdodd * 224112765Smdodd * NRXBUFS * sizeof(rbd) == NRXBUFS*(2+2+4+2+2) == NRXBUFS*12 225112765Smdodd * NRXBUFS * IE_RBUF_SIZE == NRXBUFS*256 226112765Smdodd * 227112765Smdodd * NRXBUFS should be (16384 - 1946) / (256 + 12) == 14438 / 268 == 53 228112765Smdodd * 229112765Smdodd * With NRXBUFS == 48, this leaves us 1574 bytes for another command or 230112765Smdodd * more buffers. Another transmit command would be 18+8+1512 == 1538 231112765Smdodd * ---just barely fits! 232112765Smdodd * 233112765Smdodd * Obviously all these would have to be reduced for smaller memory sizes. 234112765Smdodd * With a larger memory, it would be possible to roughly double the number 235112765Smdodd * of both transmit and receive buffers. 236112765Smdodd */ 237581Srgrimes 238112783Smdodd#define NFRAMES 4 /* number of receive frames */ 239112783Smdodd#define NRXBUFS 24 /* number of buffers to allocate */ 240112783Smdodd#define IE_RBUF_SIZE 256 /* size of each buffer, MUST BE POWER OF TWO */ 241112783Smdodd#define NTXBUFS 1 /* number of transmit commands */ 242112783Smdodd#define IE_TBUF_SIZE ETHER_MAX_LEN /* size of transmit buffer */ 243581Srgrimes 24438232Sbde#define MK_24(base, ptr) ((caddr_t)((uintptr_t)ptr - (uintptr_t)base)) 24538232Sbde#define MK_16(base, ptr) ((u_short)(uintptr_t)MK_24(base, ptr)) 246581Srgrimes 24724909Sgibbsstatic void 248112734Smdoddee16_shutdown(void *xsc, int howto) 24924909Sgibbs{ 250112734Smdodd struct ie_softc *sc = (struct ie_softc *)xsc; 25124909Sgibbs 252112734Smdodd ee16_reset_586(sc); 253112734Smdodd outb(PORT(sc) + IEE16_ECTRL, IEE16_RESET_ASIC); 254112734Smdodd outb(PORT(sc) + IEE16_ECTRL, 0); 25524909Sgibbs} 25624909Sgibbs 257581Srgrimes/* 258581Srgrimes * Taken almost exactly from Bill's if_is.c, then modified beyond recognition. 259581Srgrimes */ 260112790Smdoddint 261112790Smdoddie_attach(device_t dev) 262581Srgrimes{ 263112790Smdodd struct ie_softc * sc; 264112790Smdodd struct ifnet * ifp; 265112790Smdodd size_t allocsize; 266112790Smdodd int factor; 267581Srgrimes 268112790Smdodd sc = device_get_softc(dev); 269147256Sbrooks ifp = sc->ifp = if_alloc(IFT_ETHER); 270147256Sbrooks if (ifp == NULL) { 271147256Sbrooks device_printf(sc->dev, "can not if_alloc()\n"); 272147256Sbrooks return (ENOSPC); 273147256Sbrooks } 27440565Sbde 275112790Smdodd sc->dev = dev; 276112790Smdodd sc->unit = device_get_unit(dev); 277112790Smdodd 27826996Sgibbs /* 27926996Sgibbs * based on the amount of memory we have, allocate our tx and rx 28026996Sgibbs * resources. 28126996Sgibbs */ 282112790Smdodd factor = rman_get_size(sc->mem_res) / 8192; 283112734Smdodd sc->nframes = factor * NFRAMES; 284112734Smdodd sc->nrxbufs = factor * NRXBUFS; 285112734Smdodd sc->ntxbufs = factor * NTXBUFS; 28625971Sgibbs 28726996Sgibbs /* 28826996Sgibbs * Since all of these guys are arrays of pointers, allocate as one 28926996Sgibbs * big chunk and dole out accordingly. 29026996Sgibbs */ 291112734Smdodd allocsize = sizeof(void *) * (sc->nframes 292112734Smdodd + (sc->nrxbufs * 2) 293112734Smdodd + (sc->ntxbufs * 3)); 294112734Smdodd sc->rframes = (volatile struct ie_recv_frame_desc **) malloc(allocsize, 29526996Sgibbs M_DEVBUF, 29626996Sgibbs M_NOWAIT); 297147256Sbrooks if (sc->rframes == NULL) { 298147256Sbrooks if_free(ifp); 299112790Smdodd return (ENXIO); 300147256Sbrooks } 301112734Smdodd sc->rbuffs = 302112734Smdodd (volatile struct ie_recv_buf_desc **)&sc->rframes[sc->nframes]; 303112734Smdodd sc->cbuffs = (volatile u_char **)&sc->rbuffs[sc->nrxbufs]; 304112734Smdodd sc->xmit_cmds = 305112734Smdodd (volatile struct ie_xmit_cmd **)&sc->cbuffs[sc->nrxbufs]; 306112734Smdodd sc->xmit_buffs = 307112734Smdodd (volatile struct ie_xmit_buf **)&sc->xmit_cmds[sc->ntxbufs]; 308112734Smdodd sc->xmit_cbuffs = (volatile u_char **)&sc->xmit_buffs[sc->ntxbufs]; 30925971Sgibbs 310112790Smdodd if (bootverbose) 311112790Smdodd device_printf(sc->dev, "hardware type %s, revision %d\n", 312112790Smdodd ie_hardware_names[sc->hard_type], sc->hard_vers + 1); 313112790Smdodd 314112734Smdodd ifp->if_softc = sc; 315121816Sbrooks if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 31626996Sgibbs ifp->if_mtu = ETHERMTU; 317133689Srwatson ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST | 318133689Srwatson IFF_NEEDSGIANT; 31926996Sgibbs ifp->if_start = iestart; 32026996Sgibbs ifp->if_ioctl = ieioctl; 32150084Smdodd ifp->if_init = ieinit; 322112720Smdodd ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; 3238876Srgrimes 324112734Smdodd if (sc->hard_type == IE_EE16) 32550107Smsmith EVENTHANDLER_REGISTER(shutdown_post_sync, ee16_shutdown, 326112734Smdodd sc, SHUTDOWN_PRI_DEFAULT); 32724909Sgibbs 328147256Sbrooks ether_ifattach(ifp, sc->enaddr); 329112790Smdodd return (0); 330581Srgrimes} 331581Srgrimes 332117877Sphkstatic __inline void 333117877Sphkie_ack(struct ie_softc *sc, u_int mask) 334117877Sphk{ 335117877Sphk 336117877Sphk sc->scb->ie_command = sc->scb->ie_status & mask; 337117877Sphk (*sc->ie_chan_attn) (sc); 338117877Sphk} 339117877Sphk 340581Srgrimes/* 341581Srgrimes * What to do upon receipt of an interrupt. 342581Srgrimes */ 343112790Smdoddvoid 344112790Smdoddie_intr(void *xsc) 345581Srgrimes{ 346112790Smdodd struct ie_softc *sc = (struct ie_softc *)xsc; 347112734Smdodd u_short status; 348581Srgrimes 34926996Sgibbs /* Clear the interrupt latch on the 3C507. */ 350112734Smdodd if (sc->hard_type == IE_3C507 351112734Smdodd && (inb(PORT(sc) + IE507_CTRL) & EL_CTRL_INTL)) 352112734Smdodd outb(PORT(sc) + IE507_ICTRL, 1); 35324909Sgibbs 35426996Sgibbs /* disable interrupts on the EE16. */ 355112734Smdodd if (sc->hard_type == IE_EE16) 356112734Smdodd outb(PORT(sc) + IEE16_IRQ, sc->irq_encoded); 35724909Sgibbs 358112734Smdodd status = sc->scb->ie_status; 359581Srgrimes 360581Srgrimesloop: 36125971Sgibbs 36226996Sgibbs /* Don't ack interrupts which we didn't receive */ 363112734Smdodd ie_ack(sc, IE_ST_WHENCE & status); 36425971Sgibbs 36526996Sgibbs if (status & (IE_ST_RECV | IE_ST_RNR)) { 366581Srgrimes#ifdef DEBUG 36726996Sgibbs in_ierint++; 36826996Sgibbs if (ie_debug & IED_RINT) 369112734Smdodd printf("ie%d: rint\n", sc->unit); 370581Srgrimes#endif 371112734Smdodd ierint(sc); 372581Srgrimes#ifdef DEBUG 37326996Sgibbs in_ierint--; 374581Srgrimes#endif 37526996Sgibbs } 37626996Sgibbs if (status & IE_ST_DONE) { 377581Srgrimes#ifdef DEBUG 37826996Sgibbs in_ietint++; 37926996Sgibbs if (ie_debug & IED_TINT) 380112734Smdodd printf("ie%d: tint\n", sc->unit); 381581Srgrimes#endif 382112734Smdodd ietint(sc); 383581Srgrimes#ifdef DEBUG 38426996Sgibbs in_ietint--; 385581Srgrimes#endif 38626996Sgibbs } 38726996Sgibbs if (status & IE_ST_RNR) { 388581Srgrimes#ifdef DEBUG 38926996Sgibbs if (ie_debug & IED_RNR) 390112734Smdodd printf("ie%d: rnr\n", sc->unit); 391581Srgrimes#endif 392112734Smdodd iernr(sc); 39326996Sgibbs } 394581Srgrimes#ifdef DEBUG 395112734Smdodd if ((status & IE_ST_ALLDONE) && (ie_debug & IED_CNA)) 396112734Smdodd printf("ie%d: cna\n", sc->unit); 397581Srgrimes#endif 398581Srgrimes 399112734Smdodd if ((status = sc->scb->ie_status) & IE_ST_WHENCE) 40026996Sgibbs goto loop; 401581Srgrimes 40226996Sgibbs /* Clear the interrupt latch on the 3C507. */ 403112734Smdodd if (sc->hard_type == IE_3C507) 404112734Smdodd outb(PORT(sc) + IE507_ICTRL, 1); 40524909Sgibbs 40626996Sgibbs /* enable interrupts on the EE16. */ 407112734Smdodd if (sc->hard_type == IE_EE16) 408112734Smdodd outb(PORT(sc) + IEE16_IRQ, sc->irq_encoded | IEE16_IRQ_ENABLE); 40924909Sgibbs 410581Srgrimes} 411581Srgrimes 412581Srgrimes/* 413581Srgrimes * Process a received-frame interrupt. 414581Srgrimes */ 41524909Sgibbsstatic int 416112734Smdoddierint(struct ie_softc *sc) 417581Srgrimes{ 41826996Sgibbs int i, status; 41926996Sgibbs static int timesthru = 1024; 420581Srgrimes 421112734Smdodd i = sc->rfhead; 42226996Sgibbs while (1) { 423112734Smdodd status = sc->rframes[i]->ie_fd_status; 424581Srgrimes 42526996Sgibbs if ((status & IE_FD_COMPLETE) && (status & IE_FD_OK)) { 426147256Sbrooks sc->ifp->if_ipackets++; 42726996Sgibbs if (!--timesthru) { 428147256Sbrooks sc->ifp->if_ierrors += 429112734Smdodd sc->scb->ie_err_crc + 430112734Smdodd sc->scb->ie_err_align + 431112734Smdodd sc->scb->ie_err_resource + 432112734Smdodd sc->scb->ie_err_overrun; 433112734Smdodd sc->scb->ie_err_crc = 0; 434112734Smdodd sc->scb->ie_err_align = 0; 435112734Smdodd sc->scb->ie_err_resource = 0; 436112734Smdodd sc->scb->ie_err_overrun = 0; 43726996Sgibbs timesthru = 1024; 43826996Sgibbs } 439112734Smdodd ie_readframe(sc, i); 44026996Sgibbs } else { 44126996Sgibbs if (status & IE_FD_RNR) { 442112734Smdodd if (!(sc->scb->ie_status & IE_RU_READY)) { 443112734Smdodd sc->rframes[0]->ie_fd_next = 444112734Smdodd MK_16(MEM(sc), sc->rbuffs[0]); 445112734Smdodd sc->scb->ie_recv_list = 446112734Smdodd MK_16(MEM(sc), sc->rframes[0]); 447112734Smdodd command_and_wait(sc, IE_RU_START, 0, 0); 44826996Sgibbs } 44926996Sgibbs } 45026996Sgibbs break; 45126996Sgibbs } 452112734Smdodd i = (i + 1) % sc->nframes; 453581Srgrimes } 45426996Sgibbs return (0); 455581Srgrimes} 456581Srgrimes 457581Srgrimes/* 458581Srgrimes * Process a command-complete interrupt. These are only generated by 45926996Sgibbs * the transmission of frames. This routine is deceptively simple, since 460581Srgrimes * most of the real work is done by iestart(). 461581Srgrimes */ 46226996Sgibbsstatic int 463112734Smdoddietint(struct ie_softc *sc) 464581Srgrimes{ 46526996Sgibbs int status; 46626996Sgibbs int i; 467581Srgrimes 468147256Sbrooks sc->ifp->if_timer = 0; 469147256Sbrooks sc->ifp->if_flags &= ~IFF_OACTIVE; 470581Srgrimes 471112734Smdodd for (i = 0; i < sc->xmit_count; i++) { 472112734Smdodd status = sc->xmit_cmds[i]->ie_xmit_status; 473581Srgrimes 47426996Sgibbs if (status & IE_XS_LATECOLL) { 475112734Smdodd printf("ie%d: late collision\n", sc->unit); 476147256Sbrooks sc->ifp->if_collisions++; 477147256Sbrooks sc->ifp->if_oerrors++; 47826996Sgibbs } else if (status & IE_XS_NOCARRIER) { 479112734Smdodd printf("ie%d: no carrier\n", sc->unit); 480147256Sbrooks sc->ifp->if_oerrors++; 48126996Sgibbs } else if (status & IE_XS_LOSTCTS) { 482112734Smdodd printf("ie%d: lost CTS\n", sc->unit); 483147256Sbrooks sc->ifp->if_oerrors++; 48426996Sgibbs } else if (status & IE_XS_UNDERRUN) { 485112734Smdodd printf("ie%d: DMA underrun\n", sc->unit); 486147256Sbrooks sc->ifp->if_oerrors++; 48726996Sgibbs } else if (status & IE_XS_EXCMAX) { 488112734Smdodd printf("ie%d: too many collisions\n", sc->unit); 489147256Sbrooks sc->ifp->if_collisions += 16; 490147256Sbrooks sc->ifp->if_oerrors++; 49126996Sgibbs } else { 492147256Sbrooks sc->ifp->if_opackets++; 493147256Sbrooks sc->ifp->if_collisions += status & IE_XS_MAXCOLL; 49426996Sgibbs } 49526996Sgibbs } 496112734Smdodd sc->xmit_count = 0; 497581Srgrimes 49826996Sgibbs /* 49926996Sgibbs * If multicast addresses were added or deleted while we were 50026996Sgibbs * transmitting, ie_mc_reset() set the want_mcsetup flag indicating 50126996Sgibbs * that we should do it. 50226996Sgibbs */ 503112734Smdodd if (sc->want_mcsetup) { 504112734Smdodd mc_setup(sc); 505112734Smdodd sc->want_mcsetup = 0; 50626996Sgibbs } 50726996Sgibbs /* Wish I knew why this seems to be necessary... */ 508112734Smdodd sc->xmit_cmds[0]->ie_xmit_status |= IE_STAT_COMPL; 509581Srgrimes 510147256Sbrooks iestart(sc->ifp); 51126996Sgibbs return (0); /* shouldn't be necessary */ 512581Srgrimes} 513581Srgrimes 514581Srgrimes/* 515581Srgrimes * Process a receiver-not-ready interrupt. I believe that we get these 516581Srgrimes * when there aren't enough buffers to go around. For now (FIXME), we 517581Srgrimes * just restart the receiver, and hope everything's ok. 518581Srgrimes */ 51926996Sgibbsstatic int 520112734Smdoddiernr(struct ie_softc *sc) 521581Srgrimes{ 522581Srgrimes#ifdef doesnt_work 523112734Smdodd setup_rfa(sc, (v_caddr_t) sc->rframes[0]); 524581Srgrimes 525112734Smdodd sc->scb->ie_recv_list = MK_16(MEM(sc), sc->rframes[0]); 526112734Smdodd command_and_wait(sc, IE_RU_START, 0, 0); 527581Srgrimes#else 52826996Sgibbs /* This doesn't work either, but it doesn't hang either. */ 529112734Smdodd command_and_wait(sc, IE_RU_DISABLE, 0, 0); /* just in case */ 530112734Smdodd setup_rfa(sc, (v_caddr_t) sc->rframes[0]); /* ignore cast-qual */ 531581Srgrimes 532112734Smdodd sc->scb->ie_recv_list = MK_16(MEM(sc), sc->rframes[0]); 533112734Smdodd command_and_wait(sc, IE_RU_START, 0, 0); /* was ENABLE */ 534581Srgrimes 535581Srgrimes#endif 536112734Smdodd ie_ack(sc, IE_ST_WHENCE); 537581Srgrimes 538147256Sbrooks sc->ifp->if_ierrors++; 53926996Sgibbs return (0); 540581Srgrimes} 541581Srgrimes 542581Srgrimes/* 543581Srgrimes * Compare two Ether/802 addresses for equality, inlined and 54426996Sgibbs * unrolled for speed. I'd love to have an inline assembler 545581Srgrimes * version of this... 546581Srgrimes */ 54735210Sbdestatic __inline int 54826996Sgibbsether_equal(u_char * one, u_char * two) 54926996Sgibbs{ 55026996Sgibbs if (one[0] != two[0]) 55126996Sgibbs return (0); 55226996Sgibbs if (one[1] != two[1]) 55326996Sgibbs return (0); 55426996Sgibbs if (one[2] != two[2]) 55526996Sgibbs return (0); 55626996Sgibbs if (one[3] != two[3]) 55726996Sgibbs return (0); 55826996Sgibbs if (one[4] != two[4]) 55926996Sgibbs return (0); 56026996Sgibbs if (one[5] != two[5]) 56126996Sgibbs return (0); 56226996Sgibbs return 1; 563581Srgrimes} 564581Srgrimes 565581Srgrimes/* 56660536Sarchie * Determine quickly whether we should bother reading in this packet. 56760536Sarchie * This depends on whether BPF and/or bridging is enabled, whether we 56860536Sarchie * are receiving multicast address, and whether promiscuous mode is enabled. 56960536Sarchie * We assume that if IFF_PROMISC is set, then *somebody* wants to see 57060536Sarchie * all incoming packets. 571581Srgrimes */ 57235210Sbdestatic __inline int 573112734Smdoddcheck_eh(struct ie_softc *sc, struct ether_header *eh) 57426996Sgibbs{ 57560536Sarchie /* Optimize the common case: normal operation. We've received 57660536Sarchie either a unicast with our dest or a multicast packet. */ 577112734Smdodd if (sc->promisc == 0) { 57860536Sarchie int i; 579581Srgrimes 58060536Sarchie /* If not multicast, it's definitely for us */ 58160536Sarchie if ((eh->ether_dhost[0] & 1) == 0) 58226996Sgibbs return (1); 583581Srgrimes 58460536Sarchie /* Accept broadcasts (loose but fast check) */ 58560536Sarchie if (eh->ether_dhost[0] == 0xff) 58626996Sgibbs return (1); 587581Srgrimes 58860536Sarchie /* Compare against our multicast addresses */ 589112734Smdodd for (i = 0; i < sc->mcast_count; i++) { 59026996Sgibbs if (ether_equal(eh->ether_dhost, 591112734Smdodd (u_char *)&sc->mcast_addrs[i])) 59226996Sgibbs return (1); 59326996Sgibbs } 59460536Sarchie return (0); 59560536Sarchie } 59660536Sarchie 59760536Sarchie /* Always accept packets when in promiscuous mode */ 598112734Smdodd if ((sc->promisc & IFF_PROMISC) != 0) 59926996Sgibbs return (1); 600581Srgrimes 60160536Sarchie /* Always accept packets directed at us */ 602147256Sbrooks if (ether_equal(eh->ether_dhost, IFP2ENADDR(sc->ifp))) 60326996Sgibbs return (1); 604581Srgrimes 60560536Sarchie /* Must have IFF_ALLMULTI but not IFF_PROMISC set. The chip is 60660536Sarchie actually in promiscuous mode, so discard unicast packets. */ 60760536Sarchie return((eh->ether_dhost[0] & 1) != 0); 608581Srgrimes} 609581Srgrimes 610581Srgrimes/* 611581Srgrimes * We want to isolate the bits that have meaning... This assumes that 612581Srgrimes * IE_RBUF_SIZE is an even power of two. If somehow the act_len exceeds 613581Srgrimes * the size of the buffer, then we are screwed anyway. 614581Srgrimes */ 61535210Sbdestatic __inline int 616112734Smdoddie_buflen(struct ie_softc *sc, int head) 61726996Sgibbs{ 618112734Smdodd return (sc->rbuffs[head]->ie_rbd_actual 61926996Sgibbs & (IE_RBUF_SIZE | (IE_RBUF_SIZE - 1))); 620581Srgrimes} 621581Srgrimes 62235210Sbdestatic __inline int 623112734Smdoddie_packet_len(struct ie_softc *sc) 62426996Sgibbs{ 62526996Sgibbs int i; 626112734Smdodd int head = sc->rbhead; 62726996Sgibbs int acc = 0; 628581Srgrimes 62926996Sgibbs do { 630112734Smdodd if (!(sc->rbuffs[sc->rbhead]->ie_rbd_actual & IE_RBD_USED)) { 631581Srgrimes#ifdef DEBUG 632112734Smdodd print_rbd(sc->rbuffs[sc->rbhead]); 633581Srgrimes#endif 63426996Sgibbs log(LOG_ERR, 63526996Sgibbs "ie%d: receive descriptors out of sync at %d\n", 636112734Smdodd sc->unit, sc->rbhead); 637112734Smdodd iereset(sc); 63826996Sgibbs return (-1); 63926996Sgibbs } 640112734Smdodd i = sc->rbuffs[head]->ie_rbd_actual & IE_RBD_LAST; 641581Srgrimes 642112734Smdodd acc += ie_buflen(sc, head); 643112734Smdodd head = (head + 1) % sc->nrxbufs; 64426996Sgibbs } while (!i); 645581Srgrimes 64626996Sgibbs return (acc); 647581Srgrimes} 648581Srgrimes 649581Srgrimes/* 650581Srgrimes * Read data off the interface, and turn it into an mbuf chain. 651581Srgrimes * 652581Srgrimes * This code is DRAMATICALLY different from the previous version; this 653581Srgrimes * version tries to allocate the entire mbuf chain up front, given the 654581Srgrimes * length of the data available. This enables us to allocate mbuf 655581Srgrimes * clusters in many situations where before we would have had a long 656581Srgrimes * chain of partially-full mbufs. This should help to speed up the 657581Srgrimes * operation considerably. (Provided that it works, of course.) 658581Srgrimes */ 65935210Sbdestatic __inline int 660112734Smdoddieget(struct ie_softc *sc, struct mbuf **mp) 661581Srgrimes{ 662112720Smdodd struct ether_header eh; 66326996Sgibbs struct mbuf *m, *top, **mymp; 66426996Sgibbs int offset; 66526996Sgibbs int totlen, resid; 66626996Sgibbs int thismboff; 66726996Sgibbs int head; 668581Srgrimes 669112734Smdodd totlen = ie_packet_len(sc); 67026996Sgibbs if (totlen <= 0) 67126996Sgibbs return (-1); 672581Srgrimes 67326996Sgibbs /* 67426996Sgibbs * Snarf the Ethernet header. 67526996Sgibbs */ 676112734Smdodd bcopy((caddr_t)sc->cbuffs[sc->rbhead], &eh, sizeof(struct ether_header)); 67726996Sgibbs /* ignore cast-qual warning here */ 678581Srgrimes 67926996Sgibbs /* 68026996Sgibbs * As quickly as possible, check if this packet is for us. If not, 68126996Sgibbs * don't waste a single cycle copying the rest of the packet in. 68226996Sgibbs * This is only a consideration when FILTER is defined; i.e., when 68326996Sgibbs * we are either running BPF or doing multicasting. 68426996Sgibbs */ 685112734Smdodd if (!check_eh(sc, &eh)) { 686112734Smdodd ie_drop_packet_buffer(sc); 687147256Sbrooks sc->ifp->if_ierrors--; /* just this case, it's not an 68826996Sgibbs * error 68926996Sgibbs */ 69026996Sgibbs return (-1); 69126996Sgibbs } 692581Srgrimes 693112720Smdodd MGETHDR(m, M_DONTWAIT, MT_DATA); 694112720Smdodd if (!m) { 695112734Smdodd ie_drop_packet_buffer(sc); 696112720Smdodd /* XXXX if_ierrors++; */ 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) { 722111119Simp MGET(m, M_DONTWAIT, 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) { 731111119Simp MCLGET(m, M_DONTWAIT); 73226996Sgibbs if (m->m_flags & M_EXT) 73326996Sgibbs m->m_len = min(resid, MCLBYTES); 73426996Sgibbs } else { 73526996Sgibbs if (resid < m->m_len) { 73626996Sgibbs if (!top && resid + max_linkhdr <= m->m_len) 73726996Sgibbs m->m_data += max_linkhdr; 73826996Sgibbs m->m_len = resid; 73926996Sgibbs } 74026996Sgibbs } 74126996Sgibbs resid -= m->m_len; 74226996Sgibbs *mymp = m; 74326996Sgibbs mymp = &m->m_next; 74426996Sgibbs } while (resid > 0); 745581Srgrimes 746112720Smdodd resid = totlen; /* remaining data */ 747112720Smdodd offset = 0; /* packet offset */ 748112720Smdodd thismboff = 0; /* offset in m */ 749112720Smdodd 750106937Ssam m = top; /* current mbuf */ 751112734Smdodd head = sc->rbhead; /* current rx buffer */ 752581Srgrimes 75326996Sgibbs /* 75426996Sgibbs * Now we take the mbuf chain (hopefully only one mbuf most of the 75526996Sgibbs * time) and stuff the data into it. There are no possible failures 75626996Sgibbs * at or after this point. 75726996Sgibbs */ 75826996Sgibbs while (resid > 0) { /* while there's stuff left */ 759112734Smdodd int thislen = ie_buflen(sc, head) - offset; 760581Srgrimes 76126996Sgibbs /* 76226996Sgibbs * If too much data for the current mbuf, then fill the 76326996Sgibbs * current one up, go to the next one, and try again. 76426996Sgibbs */ 76526996Sgibbs if (thislen > m->m_len - thismboff) { 76626996Sgibbs int newlen = m->m_len - thismboff; 767581Srgrimes 768112734Smdodd bcopy((v_caddr_t) (sc->cbuffs[head] + offset), 769112734Smdodd mtod(m, caddr_t) +thismboff, (unsigned) newlen); 77026996Sgibbs /* ignore cast-qual warning */ 77126996Sgibbs m = m->m_next; 772106937Ssam thismboff = 0; /* new mbuf, so no offset */ 77326996Sgibbs offset += newlen; /* we are now this far into 77426996Sgibbs * the packet */ 77526996Sgibbs resid -= newlen; /* so there is this much left 77626996Sgibbs * to get */ 77726996Sgibbs continue; 77826996Sgibbs } 77926996Sgibbs /* 78026996Sgibbs * If there is more than enough space in the mbuf to hold 78126996Sgibbs * the contents of this buffer, copy everything in, advance 78226996Sgibbs * pointers, and so on. 78326996Sgibbs */ 78426996Sgibbs if (thislen < m->m_len - thismboff) { 785112734Smdodd bcopy((v_caddr_t) (sc->cbuffs[head] + offset), 78626996Sgibbs mtod(m, caddr_t) +thismboff, (unsigned) thislen); 78726996Sgibbs thismboff += thislen; /* we are this far into the 78826996Sgibbs * mbuf */ 78926996Sgibbs resid -= thislen; /* and this much is left */ 79026996Sgibbs goto nextbuf; 79126996Sgibbs } 79226996Sgibbs /* 79326996Sgibbs * Otherwise, there is exactly enough space to put this 79426996Sgibbs * buffer's contents into the current mbuf. Do the 79526996Sgibbs * combination of the above actions. 79626996Sgibbs */ 797112734Smdodd bcopy((v_caddr_t) (sc->cbuffs[head] + offset), 79826996Sgibbs mtod(m, caddr_t) + thismboff, (unsigned) thislen); 79926996Sgibbs m = m->m_next; 80026996Sgibbs thismboff = 0; /* new mbuf, start at the beginning */ 80126996Sgibbs resid -= thislen; /* and we are this far through */ 802581Srgrimes 80326996Sgibbs /* 80426996Sgibbs * Advance all the pointers. We can get here from either of 80526996Sgibbs * the last two cases, but never the first. 80626996Sgibbs */ 807581Srgrimesnextbuf: 80826996Sgibbs offset = 0; 809112734Smdodd sc->rbuffs[head]->ie_rbd_actual = 0; 810112734Smdodd sc->rbuffs[head]->ie_rbd_length |= IE_RBD_LAST; 811112734Smdodd sc->rbhead = head = (head + 1) % sc->nrxbufs; 812112734Smdodd sc->rbuffs[sc->rbtail]->ie_rbd_length &= ~IE_RBD_LAST; 813112734Smdodd sc->rbtail = (sc->rbtail + 1) % sc->nrxbufs; 81426996Sgibbs } 815581Srgrimes 81626996Sgibbs /* 81726996Sgibbs * Unless something changed strangely while we were doing the copy, 81826996Sgibbs * we have now copied everything in from the shared memory. This 81926996Sgibbs * means that we are done. 82026996Sgibbs */ 82126996Sgibbs return (0); 822581Srgrimes} 823581Srgrimes 824581Srgrimes/* 825581Srgrimes * Read frame NUM from unit UNIT (pre-cached as IE). 826581Srgrimes * 827581Srgrimes * This routine reads the RFD at NUM, and copies in the buffers from 828581Srgrimes * the list of RBD, then rotates the RBD and RFD lists so that the receiver 829581Srgrimes * doesn't start complaining. Trailers are DROPPED---there's no point 83026996Sgibbs * in wasting time on confusing code to deal with them. Hopefully, 831581Srgrimes * this machine will never ARP for trailers anyway. 832581Srgrimes */ 83326996Sgibbsstatic void 834112734Smdoddie_readframe(struct ie_softc *sc, int num/* frame number to read */) 835581Srgrimes{ 836147256Sbrooks struct ifnet *ifp = sc->ifp; 83726996Sgibbs struct ie_recv_frame_desc rfd; 83826996Sgibbs struct mbuf *m = 0; 839106937Ssam#ifdef DEBUG 840106937Ssam struct ether_header *eh; 841106937Ssam#endif 84226996Sgibbs 843112734Smdodd bcopy((v_caddr_t) (sc->rframes[num]), &rfd, 84426996Sgibbs sizeof(struct ie_recv_frame_desc)); 845581Srgrimes 84626996Sgibbs /* 84726996Sgibbs * Immediately advance the RFD list, since we we have copied ours 84826996Sgibbs * now. 84926996Sgibbs */ 850112734Smdodd sc->rframes[num]->ie_fd_status = 0; 851112734Smdodd sc->rframes[num]->ie_fd_last |= IE_FD_LAST; 852112734Smdodd sc->rframes[sc->rftail]->ie_fd_last &= ~IE_FD_LAST; 853112734Smdodd sc->rftail = (sc->rftail + 1) % sc->nframes; 854112734Smdodd sc->rfhead = (sc->rfhead + 1) % sc->nframes; 855581Srgrimes 85626996Sgibbs if (rfd.ie_fd_status & IE_FD_OK) { 857112734Smdodd if (ieget(sc, &m)) { 858147256Sbrooks sc->ifp->if_ierrors++; /* this counts as an 85926996Sgibbs * error */ 86026996Sgibbs return; 86126996Sgibbs } 86226996Sgibbs } 863581Srgrimes#ifdef DEBUG 864106937Ssam eh = mtod(m, struct ether_header *); 86526996Sgibbs if (ie_debug & IED_READFRAME) { 866112734Smdodd printf("ie%d: frame from ether %6D type %x\n", sc->unit, 867106937Ssam eh->ether_shost, ":", (unsigned) eh->ether_type); 86826996Sgibbs } 869106937Ssam if (ntohs(eh->ether_type) > ETHERTYPE_TRAIL 870106937Ssam && ntohs(eh->ether_type) < (ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER)) 87126996Sgibbs printf("received trailer!\n"); 872581Srgrimes#endif 873581Srgrimes 87426996Sgibbs if (!m) 87526996Sgibbs return; 876581Srgrimes 87726996Sgibbs /* 87826996Sgibbs * Finally pass this packet up to higher layers. 87926996Sgibbs */ 880106937Ssam (*ifp->if_input)(ifp, m); 881581Srgrimes} 882581Srgrimes 88326996Sgibbsstatic void 884112734Smdoddie_drop_packet_buffer(struct ie_softc *sc) 88526996Sgibbs{ 88626996Sgibbs int i; 887581Srgrimes 88826996Sgibbs do { 88926996Sgibbs /* 89026996Sgibbs * This means we are somehow out of sync. So, we reset the 89126996Sgibbs * adapter. 89226996Sgibbs */ 893112734Smdodd if (!(sc->rbuffs[sc->rbhead]->ie_rbd_actual & IE_RBD_USED)) { 894581Srgrimes#ifdef DEBUG 895112734Smdodd print_rbd(sc->rbuffs[sc->rbhead]); 896581Srgrimes#endif 89726996Sgibbs log(LOG_ERR, "ie%d: receive descriptors out of sync at %d\n", 898112734Smdodd sc->unit, sc->rbhead); 899112734Smdodd iereset(sc); 90026996Sgibbs return; 90126996Sgibbs } 902112734Smdodd i = sc->rbuffs[sc->rbhead]->ie_rbd_actual & IE_RBD_LAST; 903581Srgrimes 904112734Smdodd sc->rbuffs[sc->rbhead]->ie_rbd_length |= IE_RBD_LAST; 905112734Smdodd sc->rbuffs[sc->rbhead]->ie_rbd_actual = 0; 906112734Smdodd sc->rbhead = (sc->rbhead + 1) % sc->nrxbufs; 907112734Smdodd sc->rbuffs[sc->rbtail]->ie_rbd_length &= ~IE_RBD_LAST; 908112734Smdodd sc->rbtail = (sc->rbtail + 1) % sc->nrxbufs; 90926996Sgibbs } while (!i); 910581Srgrimes} 911581Srgrimes 912581Srgrimes 913581Srgrimes/* 914581Srgrimes * Start transmission on an interface. 915581Srgrimes */ 916798Swollmanstatic void 91726996Sgibbsiestart(struct ifnet *ifp) 918581Srgrimes{ 919112734Smdodd struct ie_softc *sc = ifp->if_softc; 92026996Sgibbs struct mbuf *m0, *m; 92143314Sdillon volatile unsigned char *buffer; 92226996Sgibbs u_short len; 923581Srgrimes 92426996Sgibbs /* 92526996Sgibbs * This is not really volatile, in this routine, but it makes gcc 92626996Sgibbs * happy. 92726996Sgibbs */ 928112734Smdodd volatile u_short *bptr = &sc->scb->ie_command_list; 929581Srgrimes 93026996Sgibbs if (!(ifp->if_flags & IFF_RUNNING)) 93126996Sgibbs return; 93226996Sgibbs if (ifp->if_flags & IFF_OACTIVE) 93326996Sgibbs return; 934581Srgrimes 93526996Sgibbs do { 936147256Sbrooks IF_DEQUEUE(&sc->ifp->if_snd, m); 93726996Sgibbs if (!m) 93826996Sgibbs break; 939581Srgrimes 940112734Smdodd buffer = sc->xmit_cbuffs[sc->xmit_count]; 94126996Sgibbs len = 0; 942581Srgrimes 94326996Sgibbs for (m0 = m; m && len < IE_BUF_LEN; m = m->m_next) { 94426996Sgibbs bcopy(mtod(m, caddr_t), buffer, m->m_len); 94526996Sgibbs buffer += m->m_len; 94626996Sgibbs len += m->m_len; 94726996Sgibbs } 948581Srgrimes 94926996Sgibbs m_freem(m0); 95026996Sgibbs len = max(len, ETHER_MIN_LEN); 95126996Sgibbs 95226996Sgibbs /* 95326996Sgibbs * See if bpf is listening on this interface, let it see the 95426996Sgibbs * packet before we commit it to the wire. 95526996Sgibbs */ 956147256Sbrooks BPF_TAP(sc->ifp, 957112734Smdodd (void *)sc->xmit_cbuffs[sc->xmit_count], len); 958581Srgrimes 959112734Smdodd sc->xmit_buffs[sc->xmit_count]->ie_xmit_flags = 96026996Sgibbs IE_XMIT_LAST|len; 961112734Smdodd sc->xmit_buffs[sc->xmit_count]->ie_xmit_next = 0xffff; 962112734Smdodd sc->xmit_buffs[sc->xmit_count]->ie_xmit_buf = 963112734Smdodd MK_24(sc->iomem, sc->xmit_cbuffs[sc->xmit_count]); 964581Srgrimes 965112734Smdodd sc->xmit_cmds[sc->xmit_count]->com.ie_cmd_cmd = IE_CMD_XMIT; 966112734Smdodd sc->xmit_cmds[sc->xmit_count]->ie_xmit_status = 0; 967112734Smdodd sc->xmit_cmds[sc->xmit_count]->ie_xmit_desc = 968112734Smdodd MK_16(sc->iomem, sc->xmit_buffs[sc->xmit_count]); 969581Srgrimes 970112734Smdodd *bptr = MK_16(sc->iomem, sc->xmit_cmds[sc->xmit_count]); 971112734Smdodd bptr = &sc->xmit_cmds[sc->xmit_count]->com.ie_cmd_link; 972112734Smdodd sc->xmit_count++; 973112734Smdodd } while (sc->xmit_count < sc->ntxbufs); 974581Srgrimes 97526996Sgibbs /* 97626996Sgibbs * If we queued up anything for transmission, send it. 97726996Sgibbs */ 978112734Smdodd if (sc->xmit_count) { 979112734Smdodd sc->xmit_cmds[sc->xmit_count - 1]->com.ie_cmd_cmd |= 98026996Sgibbs IE_CMD_LAST | IE_CMD_INTR; 981581Srgrimes 98226996Sgibbs /* 98326996Sgibbs * By passing the command pointer as a null, we tell 98426996Sgibbs * command_and_wait() to pretend that this isn't an action 98526996Sgibbs * command. I wish I understood what was happening here. 98626996Sgibbs */ 987112734Smdodd command_and_wait(sc, IE_CU_START, 0, 0); 98826996Sgibbs ifp->if_flags |= IFF_OACTIVE; 98926996Sgibbs } 99026996Sgibbs return; 991581Srgrimes} 992581Srgrimes 993581Srgrimes/* 994581Srgrimes * Check to see if there's an 82586 out there. 995581Srgrimes */ 996112790Smdoddint 997112790Smdoddcheck_ie_present(struct ie_softc *sc) 998581Srgrimes{ 99926996Sgibbs volatile struct ie_sys_conf_ptr *scp; 100026996Sgibbs volatile struct ie_int_sys_conf_ptr *iscp; 100126996Sgibbs volatile struct ie_sys_ctl_block *scb; 100226996Sgibbs u_long realbase; 100326996Sgibbs int s; 1004581Srgrimes 100526996Sgibbs s = splimp(); 1006581Srgrimes 1007112790Smdodd realbase = (uintptr_t) sc->iomembot + sc->iosize - (1 << 24); 1008581Srgrimes 100938232Sbde scp = (volatile struct ie_sys_conf_ptr *) (uintptr_t) 101038232Sbde (realbase + IE_SCP_ADDR); 101143314Sdillon bzero((volatile char *) scp, sizeof *scp); 10128876Srgrimes 101326996Sgibbs /* 101426996Sgibbs * First we put the ISCP at the bottom of memory; this tests to make 101526996Sgibbs * sure that our idea of the size of memory is the same as the 101626996Sgibbs * controller's. This is NOT where the ISCP will be in normal 101726996Sgibbs * operation. 101826996Sgibbs */ 1019112790Smdodd iscp = (volatile struct ie_int_sys_conf_ptr *) sc->iomembot; 102043314Sdillon bzero((volatile char *)iscp, sizeof *iscp); 1021581Srgrimes 1022112790Smdodd scb = (volatile struct ie_sys_ctl_block *) sc->iomembot; 102343314Sdillon bzero((volatile char *)scb, sizeof *scb); 1024581Srgrimes 1025112734Smdodd scp->ie_bus_use = sc->bus_use; /* 8-bit or 16-bit */ 102647108Sbde scp->ie_iscp_ptr = (caddr_t) (uintptr_t) 102747108Sbde ((volatile char *) iscp - (volatile char *) (uintptr_t) realbase); 1028581Srgrimes 102926996Sgibbs iscp->ie_busy = 1; 103026996Sgibbs iscp->ie_scb_offset = MK_16(realbase, scb) + 256; 1031581Srgrimes 1032112734Smdodd (*sc->ie_reset_586) (sc); 1033112734Smdodd (*sc->ie_chan_attn) (sc); 1034581Srgrimes 103526996Sgibbs DELAY(100); /* wait a while... */ 1036581Srgrimes 103726996Sgibbs if (iscp->ie_busy) { 103826996Sgibbs splx(s); 103926996Sgibbs return (0); 104026996Sgibbs } 104126996Sgibbs /* 104226996Sgibbs * Now relocate the ISCP to its real home, and reset the controller 104326996Sgibbs * again. 104426996Sgibbs */ 104538232Sbde iscp = (void *) Align((caddr_t) (uintptr_t) 104638232Sbde (realbase + IE_SCP_ADDR - 104738232Sbde sizeof(struct ie_int_sys_conf_ptr))); 104843314Sdillon bzero((volatile char *) iscp, sizeof *iscp); /* ignore cast-qual */ 1049581Srgrimes 105047108Sbde scp->ie_iscp_ptr = (caddr_t) (uintptr_t) 105147108Sbde ((volatile char *) iscp - (volatile char *) (uintptr_t) realbase); 1052581Srgrimes 105326996Sgibbs iscp->ie_busy = 1; 105426996Sgibbs iscp->ie_scb_offset = MK_16(realbase, scb); 1055581Srgrimes 1056112734Smdodd (*sc->ie_reset_586) (sc); 1057112734Smdodd (*sc->ie_chan_attn) (sc); 1058581Srgrimes 105926996Sgibbs DELAY(100); 1060581Srgrimes 106126996Sgibbs if (iscp->ie_busy) { 106226996Sgibbs splx(s); 106326996Sgibbs return (0); 106426996Sgibbs } 1065112734Smdodd sc->iomem = (caddr_t) (uintptr_t) realbase; 1066581Srgrimes 1067112734Smdodd sc->iscp = iscp; 1068112734Smdodd sc->scb = scb; 1069581Srgrimes 107026996Sgibbs /* 107126996Sgibbs * Acknowledge any interrupts we may have caused... 107226996Sgibbs */ 1073112734Smdodd ie_ack(sc, IE_ST_WHENCE); 107426996Sgibbs splx(s); 1075581Srgrimes 107626996Sgibbs return (1); 1077581Srgrimes} 1078581Srgrimes 1079581Srgrimes/* 1080581Srgrimes * Divine the memory size of ie board UNIT. 1081581Srgrimes * Better hope there's nothing important hiding just below the ie card... 1082581Srgrimes */ 108326996Sgibbsstatic void 1084112734Smdoddfind_ie_mem_size(struct ie_softc *sc) 1085581Srgrimes{ 108626996Sgibbs unsigned size; 1087581Srgrimes 1088112734Smdodd sc->iosize = 0; 1089581Srgrimes 109026996Sgibbs for (size = 65536; size >= 8192; size -= 8192) { 1091112790Smdodd if (check_ie_present(sc)) { 109226996Sgibbs return; 109326996Sgibbs } 109426996Sgibbs } 1095581Srgrimes 109626996Sgibbs return; 1097581Srgrimes} 1098581Srgrimes 1099112790Smdoddvoid 1100112734Smdoddel_reset_586(struct ie_softc *sc) 11012268Sats{ 1102112734Smdodd outb(PORT(sc) + IE507_CTRL, EL_CTRL_RESET); 110326996Sgibbs DELAY(100); 1104112734Smdodd outb(PORT(sc) + IE507_CTRL, EL_CTRL_NORMAL); 110526996Sgibbs DELAY(100); 11062268Sats} 11072268Sats 1108112790Smdoddvoid 1109112734Smdoddsl_reset_586(struct ie_softc *sc) 1110581Srgrimes{ 1111112734Smdodd outb(PORT(sc) + IEATT_RESET, 0); 1112581Srgrimes} 1113581Srgrimes 1114112790Smdoddvoid 1115112734Smdoddee16_reset_586(struct ie_softc *sc) 111624909Sgibbs{ 1117112734Smdodd outb(PORT(sc) + IEE16_ECTRL, IEE16_RESET_586); 111824909Sgibbs DELAY(100); 1119112734Smdodd outb(PORT(sc) + IEE16_ECTRL, 0); 112024909Sgibbs DELAY(100); 112124909Sgibbs} 112224909Sgibbs 1123112790Smdoddvoid 1124112734Smdoddel_chan_attn(struct ie_softc *sc) 11252268Sats{ 1126112734Smdodd outb(PORT(sc) + IE507_ATTN, 1); 11272268Sats} 11282268Sats 1129112790Smdoddvoid 1130112734Smdoddsl_chan_attn(struct ie_softc *sc) 1131581Srgrimes{ 1132112734Smdodd outb(PORT(sc) + IEATT_ATTN, 0); 1133581Srgrimes} 1134581Srgrimes 1135112790Smdoddvoid 1136112734Smdoddee16_chan_attn(struct ie_softc *sc) 113724909Sgibbs{ 1138112734Smdodd outb(PORT(sc) + IEE16_ATTN, 0); 113924909Sgibbs} 114024909Sgibbs 1141112790Smdoddu_short 114226996Sgibbsee16_read_eeprom(struct ie_softc *sc, int location) 114324909Sgibbs{ 114426996Sgibbs int ectrl, edata; 114524909Sgibbs 114624909Sgibbs ectrl = inb(sc->port + IEE16_ECTRL); 114724909Sgibbs ectrl &= IEE16_ECTRL_MASK; 114824909Sgibbs ectrl |= IEE16_ECTRL_EECS; 114924909Sgibbs outb(sc->port + IEE16_ECTRL, ectrl); 115024909Sgibbs 115124909Sgibbs ee16_eeprom_outbits(sc, IEE16_EEPROM_READ, IEE16_EEPROM_OPSIZE1); 115224909Sgibbs ee16_eeprom_outbits(sc, location, IEE16_EEPROM_ADDR_SIZE); 115324909Sgibbs edata = ee16_eeprom_inbits(sc); 115424909Sgibbs ectrl = inb(sc->port + IEE16_ECTRL); 115524909Sgibbs ectrl &= ~(IEE16_RESET_ASIC | IEE16_ECTRL_EEDI | IEE16_ECTRL_EECS); 115624909Sgibbs outb(sc->port + IEE16_ECTRL, ectrl); 115724909Sgibbs ee16_eeprom_clock(sc, 1); 115824909Sgibbs ee16_eeprom_clock(sc, 0); 115924909Sgibbs return edata; 116024909Sgibbs} 116124909Sgibbs 1162104094Sphkstatic void 116326996Sgibbsee16_eeprom_outbits(struct ie_softc *sc, int edata, int count) 116424909Sgibbs{ 116526996Sgibbs int ectrl, i; 116624909Sgibbs 116724909Sgibbs ectrl = inb(sc->port + IEE16_ECTRL); 116824909Sgibbs ectrl &= ~IEE16_RESET_ASIC; 116924909Sgibbs for (i = count - 1; i >= 0; i--) { 117024909Sgibbs ectrl &= ~IEE16_ECTRL_EEDI; 117124909Sgibbs if (edata & (1 << i)) { 117224909Sgibbs ectrl |= IEE16_ECTRL_EEDI; 117324909Sgibbs } 117424909Sgibbs outb(sc->port + IEE16_ECTRL, ectrl); 117524909Sgibbs DELAY(1); /* eeprom data must be setup for 0.4 uSec */ 117624909Sgibbs ee16_eeprom_clock(sc, 1); 117724909Sgibbs ee16_eeprom_clock(sc, 0); 117824909Sgibbs } 117924909Sgibbs ectrl &= ~IEE16_ECTRL_EEDI; 118024909Sgibbs outb(sc->port + IEE16_ECTRL, ectrl); 118124909Sgibbs DELAY(1); /* eeprom data must be held for 0.4 uSec */ 118224909Sgibbs} 118324909Sgibbs 1184104094Sphkstatic int 118526996Sgibbsee16_eeprom_inbits(struct ie_softc *sc) 118624909Sgibbs{ 118726996Sgibbs int ectrl, edata, i; 118824909Sgibbs 118924909Sgibbs ectrl = inb(sc->port + IEE16_ECTRL); 119024909Sgibbs ectrl &= ~IEE16_RESET_ASIC; 119124909Sgibbs for (edata = 0, i = 0; i < 16; i++) { 119224909Sgibbs edata = edata << 1; 119324909Sgibbs ee16_eeprom_clock(sc, 1); 119424909Sgibbs ectrl = inb(sc->port + IEE16_ECTRL); 119524909Sgibbs if (ectrl & IEE16_ECTRL_EEDO) { 119624909Sgibbs edata |= 1; 119724909Sgibbs } 119824909Sgibbs ee16_eeprom_clock(sc, 0); 119924909Sgibbs } 120024909Sgibbs return (edata); 120124909Sgibbs} 120224909Sgibbs 1203104094Sphkstatic void 120426996Sgibbsee16_eeprom_clock(struct ie_softc *sc, int state) 120524909Sgibbs{ 120626996Sgibbs int ectrl; 120724909Sgibbs 120824909Sgibbs ectrl = inb(sc->port + IEE16_ECTRL); 120924909Sgibbs ectrl &= ~(IEE16_RESET_ASIC | IEE16_ECTRL_EESK); 121024909Sgibbs if (state) { 121124909Sgibbs ectrl |= IEE16_ECTRL_EESK; 121224909Sgibbs } 121324909Sgibbs outb(sc->port + IEE16_ECTRL, ectrl); 121424909Sgibbs DELAY(9); /* EESK must be stable for 8.38 uSec */ 121524909Sgibbs} 121624909Sgibbs 121735210Sbdestatic __inline void 121826996Sgibbsee16_interrupt_enable(struct ie_softc *sc) 121924909Sgibbs{ 122024909Sgibbs DELAY(100); 122124909Sgibbs outb(sc->port + IEE16_IRQ, sc->irq_encoded | IEE16_IRQ_ENABLE); 122224909Sgibbs DELAY(100); 122324909Sgibbs} 122424909Sgibbs 1225112790Smdoddvoid 1226112734Smdoddsl_read_ether(struct ie_softc *sc, unsigned char *addr) 1227581Srgrimes{ 122826996Sgibbs int i; 1229581Srgrimes 123026996Sgibbs for (i = 0; i < 6; i++) 1231112734Smdodd addr[i] = inb(PORT(sc) + i); 1232581Srgrimes} 1233581Srgrimes 1234798Swollmanstatic void 1235112734Smdoddiereset(struct ie_softc *sc) 1236581Srgrimes{ 123726996Sgibbs int s = splimp(); 1238581Srgrimes 1239112734Smdodd printf("ie%d: reset\n", sc->unit); 1240147256Sbrooks sc->ifp->if_flags &= ~IFF_UP; 1241147256Sbrooks ieioctl(sc->ifp, SIOCSIFFLAGS, 0); 1242581Srgrimes 124326996Sgibbs /* 124426996Sgibbs * Stop i82586 dead in its tracks. 124526996Sgibbs */ 1246112734Smdodd if (command_and_wait(sc, IE_RU_ABORT | IE_CU_ABORT, 0, 0)) 1247112734Smdodd printf("ie%d: abort commands timed out\n", sc->unit); 1248581Srgrimes 1249112734Smdodd if (command_and_wait(sc, IE_RU_DISABLE | IE_CU_STOP, 0, 0)) 1250112734Smdodd printf("ie%d: disable commands timed out\n", sc->unit); 1251581Srgrimes 1252581Srgrimes#ifdef notdef 1253112790Smdodd if (!check_ie_present(sc)) 125426996Sgibbs panic("ie disappeared!"); 1255581Srgrimes#endif 1256581Srgrimes 1257147256Sbrooks sc->ifp->if_flags |= IFF_UP; 1258147256Sbrooks ieioctl(sc->ifp, SIOCSIFFLAGS, 0); 12598876Srgrimes 126026996Sgibbs splx(s); 126126996Sgibbs return; 1262581Srgrimes} 1263581Srgrimes 1264581Srgrimes/* 1265581Srgrimes * This is called if we time out. 1266581Srgrimes */ 1267798Swollmanstatic void 126826996Sgibbschan_attn_timeout(void *rock) 1269581Srgrimes{ 127026996Sgibbs *(int *) rock = 1; 1271581Srgrimes} 1272581Srgrimes 1273581Srgrimes/* 1274581Srgrimes * Send a command to the controller and wait for it to either 1275581Srgrimes * complete or be accepted, depending on the command. If the 1276581Srgrimes * command pointer is null, then pretend that the command is 1277581Srgrimes * not an action command. If the command pointer is not null, 1278581Srgrimes * and the command is an action command, wait for 1279581Srgrimes * ((volatile struct ie_cmd_common *)pcmd)->ie_cmd_status & MASK 1280581Srgrimes * to become true. 1281581Srgrimes */ 128226996Sgibbsstatic int 1283112734Smdoddcommand_and_wait(struct ie_softc *sc, int cmd, volatile void *pcmd, int mask) 1284581Srgrimes{ 128526996Sgibbs volatile struct ie_cmd_common *cc = pcmd; 128626996Sgibbs volatile int timedout = 0; 128729677Sgibbs struct callout_handle ch; 1288581Srgrimes 1289112734Smdodd sc->scb->ie_command = (u_short) cmd; 12908876Srgrimes 129126996Sgibbs if (IE_ACTION_COMMAND(cmd) && pcmd) { 1292112734Smdodd (*sc->ie_chan_attn) (sc); 1293581Srgrimes 129426996Sgibbs /* 129526996Sgibbs * According to the packet driver, the minimum timeout 129626996Sgibbs * should be .369 seconds, which we round up to .37. 129726996Sgibbs */ 129829677Sgibbs ch = timeout(chan_attn_timeout, (caddr_t)&timedout, 129929677Sgibbs 37 * hz / 100); 130026996Sgibbs /* ignore cast-qual */ 1301581Srgrimes 130226996Sgibbs /* 130326996Sgibbs * Now spin-lock waiting for status. This is not a very 130426996Sgibbs * nice thing to do, but I haven't figured out how, or 130526996Sgibbs * indeed if, we can put the process waiting for action to 130626996Sgibbs * sleep. (We may be getting called through some other 130726996Sgibbs * timeout running in the kernel.) 130826996Sgibbs */ 130926996Sgibbs while (1) { 131026996Sgibbs if ((cc->ie_cmd_status & mask) || timedout) 131126996Sgibbs break; 131226996Sgibbs } 1313581Srgrimes 131429677Sgibbs untimeout(chan_attn_timeout, (caddr_t)&timedout, ch); 131526996Sgibbs /* ignore cast-qual */ 1316581Srgrimes 131726996Sgibbs return (timedout); 131826996Sgibbs } else { 13198876Srgrimes 132026996Sgibbs /* 132126996Sgibbs * Otherwise, just wait for the command to be accepted. 132226996Sgibbs */ 1323112734Smdodd (*sc->ie_chan_attn) (sc); 1324581Srgrimes 1325112734Smdodd while (sc->scb->ie_command); /* spin lock */ 1326581Srgrimes 132726996Sgibbs return (0); 132826996Sgibbs } 1329581Srgrimes} 1330581Srgrimes 1331581Srgrimes/* 1332581Srgrimes * Run the time-domain reflectometer... 1333581Srgrimes */ 133426996Sgibbsstatic void 1335112734Smdoddrun_tdr(struct ie_softc *sc, volatile struct ie_tdr_cmd *cmd) 1336581Srgrimes{ 133726996Sgibbs int result; 1338581Srgrimes 133926996Sgibbs cmd->com.ie_cmd_status = 0; 134026996Sgibbs cmd->com.ie_cmd_cmd = IE_CMD_TDR | IE_CMD_LAST; 134126996Sgibbs cmd->com.ie_cmd_link = 0xffff; 134226996Sgibbs cmd->ie_tdr_time = 0; 1343581Srgrimes 1344112734Smdodd sc->scb->ie_command_list = MK_16(MEM(sc), cmd); 134526996Sgibbs cmd->ie_tdr_time = 0; 1346581Srgrimes 1347112734Smdodd if (command_and_wait(sc, IE_CU_START, cmd, IE_STAT_COMPL)) 134826996Sgibbs result = 0x2000; 134926996Sgibbs else 135026996Sgibbs result = cmd->ie_tdr_time; 1351581Srgrimes 1352112734Smdodd ie_ack(sc, IE_ST_WHENCE); 1353581Srgrimes 135426996Sgibbs if (result & IE_TDR_SUCCESS) 135526996Sgibbs return; 1356581Srgrimes 135726996Sgibbs if (result & IE_TDR_XCVR) { 1358112734Smdodd printf("ie%d: transceiver problem\n", sc->unit); 135926996Sgibbs } else if (result & IE_TDR_OPEN) { 1360112734Smdodd printf("ie%d: TDR detected an open %d clocks away\n", sc->unit, 136126996Sgibbs result & IE_TDR_TIME); 136226996Sgibbs } else if (result & IE_TDR_SHORT) { 1363112734Smdodd printf("ie%d: TDR detected a short %d clocks away\n", sc->unit, 136426996Sgibbs result & IE_TDR_TIME); 136526996Sgibbs } else { 1366112734Smdodd printf("ie%d: TDR returned unknown status %x\n", sc->unit, result); 136726996Sgibbs } 1368581Srgrimes} 1369581Srgrimes 137026996Sgibbsstatic void 1371112734Smdoddstart_receiver(struct ie_softc *sc) 1372581Srgrimes{ 137326996Sgibbs int s = splimp(); 1374581Srgrimes 1375112734Smdodd sc->scb->ie_recv_list = MK_16(MEM(sc), sc->rframes[0]); 1376112734Smdodd command_and_wait(sc, IE_RU_START, 0, 0); 1377581Srgrimes 1378112734Smdodd ie_ack(sc, IE_ST_WHENCE); 1379581Srgrimes 138026996Sgibbs splx(s); 1381581Srgrimes} 1382581Srgrimes 1383581Srgrimes/* 1384581Srgrimes * Here is a helper routine for iernr() and ieinit(). This sets up 1385581Srgrimes * the RFA. 1386581Srgrimes */ 138743314Sdillonstatic v_caddr_t 1388112734Smdoddsetup_rfa(struct ie_softc *sc, v_caddr_t ptr) 138926996Sgibbs{ 139043314Sdillon volatile struct ie_recv_frame_desc *rfd = (volatile void *)ptr; 139126996Sgibbs volatile struct ie_recv_buf_desc *rbd; 139226996Sgibbs int i; 1393581Srgrimes 139426996Sgibbs /* First lay them out */ 1395112734Smdodd for (i = 0; i < sc->nframes; i++) { 1396112734Smdodd sc->rframes[i] = rfd; 139743314Sdillon bzero((volatile char *) rfd, sizeof *rfd); /* ignore cast-qual */ 139826996Sgibbs rfd++; 139926996Sgibbs } 1400581Srgrimes 140147108Sbde ptr = Alignvol(rfd); /* ignore cast-qual */ 14028876Srgrimes 140326996Sgibbs /* Now link them together */ 1404112734Smdodd for (i = 0; i < sc->nframes; i++) { 1405112734Smdodd sc->rframes[i]->ie_fd_next = 1406112734Smdodd MK_16(MEM(sc), sc->rframes[(i + 1) % sc->nframes]); 140726996Sgibbs } 14088876Srgrimes 140926996Sgibbs /* Finally, set the EOL bit on the last one. */ 1410112734Smdodd sc->rframes[sc->nframes - 1]->ie_fd_last |= IE_FD_LAST; 1411581Srgrimes 141226996Sgibbs /* 141326996Sgibbs * Now lay out some buffers for the incoming frames. Note that we 141426996Sgibbs * set aside a bit of slop in each buffer, to make sure that we have 141526996Sgibbs * enough space to hold a single frame in every buffer. 141626996Sgibbs */ 141743314Sdillon rbd = (volatile void *) ptr; 1418581Srgrimes 1419112734Smdodd for (i = 0; i < sc->nrxbufs; i++) { 1420112734Smdodd sc->rbuffs[i] = rbd; 142143314Sdillon bzero((volatile char *)rbd, sizeof *rbd); 142247108Sbde ptr = Alignvol(ptr + sizeof *rbd); 142326996Sgibbs rbd->ie_rbd_length = IE_RBUF_SIZE; 1424112734Smdodd rbd->ie_rbd_buffer = MK_24(MEM(sc), ptr); 1425112734Smdodd sc->cbuffs[i] = (volatile void *) ptr; 142626996Sgibbs ptr += IE_RBUF_SIZE; 142743314Sdillon rbd = (volatile void *) ptr; 142826996Sgibbs } 14298876Srgrimes 143026996Sgibbs /* Now link them together */ 1431112734Smdodd for (i = 0; i < sc->nrxbufs; i++) { 1432112734Smdodd sc->rbuffs[i]->ie_rbd_next = 1433112734Smdodd MK_16(MEM(sc), sc->rbuffs[(i + 1) % sc->nrxbufs]); 143426996Sgibbs } 14358876Srgrimes 143626996Sgibbs /* Tag EOF on the last one */ 1437112734Smdodd sc->rbuffs[sc->nrxbufs - 1]->ie_rbd_length |= IE_RBD_LAST; 1438581Srgrimes 143926996Sgibbs /* 144026996Sgibbs * We use the head and tail pointers on receive to keep track of the 144126996Sgibbs * order in which RFDs and RBDs are used. 144226996Sgibbs */ 1443112734Smdodd sc->rfhead = 0; 1444112734Smdodd sc->rftail = sc->nframes - 1; 1445112734Smdodd sc->rbhead = 0; 1446112734Smdodd sc->rbtail = sc->nrxbufs - 1; 1447581Srgrimes 1448112734Smdodd sc->scb->ie_recv_list = MK_16(MEM(sc), sc->rframes[0]); 1449112734Smdodd sc->rframes[0]->ie_fd_buf_desc = MK_16(MEM(sc), sc->rbuffs[0]); 1450581Srgrimes 145147108Sbde ptr = Alignvol(ptr); 145226996Sgibbs return (ptr); 1453581Srgrimes} 1454581Srgrimes 1455581Srgrimes/* 1456581Srgrimes * Run the multicast setup command. 1457581Srgrimes * Call at splimp(). 1458581Srgrimes */ 145926996Sgibbsstatic int 1460112734Smdoddmc_setup(struct ie_softc *sc) 146126996Sgibbs{ 1462112734Smdodd volatile struct ie_mcast_cmd *cmd = (volatile void *)sc->xmit_cbuffs[0]; 14638876Srgrimes 146426996Sgibbs cmd->com.ie_cmd_status = 0; 146526996Sgibbs cmd->com.ie_cmd_cmd = IE_CMD_MCAST | IE_CMD_LAST; 146626996Sgibbs cmd->com.ie_cmd_link = 0xffff; 14678876Srgrimes 146826996Sgibbs /* ignore cast-qual */ 1469112734Smdodd bcopy((v_caddr_t) sc->mcast_addrs, (v_caddr_t) cmd->ie_mcast_addrs, 1470112734Smdodd sc->mcast_count * sizeof *sc->mcast_addrs); 1471581Srgrimes 1472112734Smdodd cmd->ie_mcast_bytes = sc->mcast_count * 6; /* grrr... */ 14738876Srgrimes 1474112734Smdodd sc->scb->ie_command_list = MK_16(MEM(sc), cmd); 1475112734Smdodd if (command_and_wait(sc, IE_CU_START, cmd, IE_STAT_COMPL) 147626996Sgibbs || !(cmd->com.ie_cmd_status & IE_STAT_OK)) { 1477112734Smdodd printf("ie%d: multicast address setup command failed\n", sc->unit); 147826996Sgibbs return (0); 147926996Sgibbs } 148026996Sgibbs return (1); 1481581Srgrimes} 1482581Srgrimes 1483581Srgrimes/* 1484581Srgrimes * This routine takes the environment generated by check_ie_present() 1485581Srgrimes * and adds to it all the other structures we need to operate the adapter. 1486581Srgrimes * This includes executing the CONFIGURE, IA-SETUP, and MC-SETUP commands, 1487581Srgrimes * starting the receiver unit, and clearing interrupts. 1488581Srgrimes * 1489581Srgrimes * THIS ROUTINE MUST BE CALLED AT splimp() OR HIGHER. 1490581Srgrimes */ 1491798Swollmanstatic void 149250084Smdoddieinit(xsc) 149350084Smdodd void *xsc; 1494581Srgrimes{ 1495112734Smdodd struct ie_softc *sc = xsc; 1496112734Smdodd volatile struct ie_sys_ctl_block *scb = sc->scb; 1497112734Smdodd caddr_t ptr; 149826996Sgibbs int i; 1499112734Smdodd int unit = sc->unit; 1500581Srgrimes 150147108Sbde ptr = Alignvol((volatile char *) scb + sizeof *scb); 1502581Srgrimes 150326996Sgibbs /* 150426996Sgibbs * Send the configure command first. 150526996Sgibbs */ 150626996Sgibbs { 150743314Sdillon volatile struct ie_config_cmd *cmd = (volatile void *) ptr; 1508581Srgrimes 1509112734Smdodd ie_setup_config(cmd, sc->promisc, 1510112734Smdodd sc->hard_type == IE_STARLAN10); 151126996Sgibbs cmd->com.ie_cmd_status = 0; 151226996Sgibbs cmd->com.ie_cmd_cmd = IE_CMD_CONFIG | IE_CMD_LAST; 151326996Sgibbs cmd->com.ie_cmd_link = 0xffff; 1514581Srgrimes 1515112734Smdodd scb->ie_command_list = MK_16(MEM(sc), cmd); 1516581Srgrimes 1517112734Smdodd if (command_and_wait(sc, IE_CU_START, cmd, IE_STAT_COMPL) 151826996Sgibbs || !(cmd->com.ie_cmd_status & IE_STAT_OK)) { 151950086Smdodd printf("ie%d: configure command failed\n", unit); 152026996Sgibbs return; 152126996Sgibbs } 152226996Sgibbs } 152326996Sgibbs /* 152426996Sgibbs * Now send the Individual Address Setup command. 152526996Sgibbs */ 152626996Sgibbs { 152743314Sdillon volatile struct ie_iasetup_cmd *cmd = (volatile void *) ptr; 1528581Srgrimes 152926996Sgibbs cmd->com.ie_cmd_status = 0; 153026996Sgibbs cmd->com.ie_cmd_cmd = IE_CMD_IASETUP | IE_CMD_LAST; 153126996Sgibbs cmd->com.ie_cmd_link = 0xffff; 1532581Srgrimes 1533147256Sbrooks bcopy((volatile char *)IFP2ENADDR(sc->ifp), 153443314Sdillon (volatile char *)&cmd->ie_address, sizeof cmd->ie_address); 1535112734Smdodd scb->ie_command_list = MK_16(MEM(sc), cmd); 1536112734Smdodd if (command_and_wait(sc, IE_CU_START, cmd, IE_STAT_COMPL) 153726996Sgibbs || !(cmd->com.ie_cmd_status & IE_STAT_OK)) { 153826996Sgibbs printf("ie%d: individual address " 1539112734Smdodd "setup command failed\n", sc->unit); 154026996Sgibbs return; 154126996Sgibbs } 154226996Sgibbs } 1543581Srgrimes 154426996Sgibbs /* 154526996Sgibbs * Now run the time-domain reflectometer. 154626996Sgibbs */ 1547112734Smdodd run_tdr(sc, (volatile void *) ptr); 1548581Srgrimes 154926996Sgibbs /* 155026996Sgibbs * Acknowledge any interrupts we have generated thus far. 155126996Sgibbs */ 1552112734Smdodd ie_ack(sc, IE_ST_WHENCE); 1553581Srgrimes 155426996Sgibbs /* 155526996Sgibbs * Set up the RFA. 155626996Sgibbs */ 1557112734Smdodd ptr = setup_rfa(sc, ptr); 1558581Srgrimes 155926996Sgibbs /* 156026996Sgibbs * Finally, the transmit command and buffer are the last little bit 156126996Sgibbs * of work. 156226996Sgibbs */ 1563581Srgrimes 156426996Sgibbs /* transmit command buffers */ 1565112734Smdodd for (i = 0; i < sc->ntxbufs; i++) { 1566112734Smdodd sc->xmit_cmds[i] = (volatile void *) ptr; 1567112734Smdodd ptr += sizeof *sc->xmit_cmds[i]; 156847108Sbde ptr = Alignvol(ptr); 1569112734Smdodd sc->xmit_buffs[i] = (volatile void *)ptr; 1570112734Smdodd ptr += sizeof *sc->xmit_buffs[i]; 157147108Sbde ptr = Alignvol(ptr); 157226996Sgibbs } 1573581Srgrimes 157426996Sgibbs /* transmit buffers */ 1575112734Smdodd for (i = 0; i < sc->ntxbufs - 1; i++) { 1576112734Smdodd sc->xmit_cbuffs[i] = (volatile void *)ptr; 157726996Sgibbs ptr += IE_BUF_LEN; 157847108Sbde ptr = Alignvol(ptr); 157926996Sgibbs } 1580112734Smdodd sc->xmit_cbuffs[sc->ntxbufs - 1] = (volatile void *) ptr; 1581581Srgrimes 1582112734Smdodd for (i = 1; i < sc->ntxbufs; i++) { 1583112734Smdodd bzero((v_caddr_t) sc->xmit_cmds[i], sizeof *sc->xmit_cmds[i]); 1584112734Smdodd bzero((v_caddr_t) sc->xmit_buffs[i], sizeof *sc->xmit_buffs[i]); 158526996Sgibbs } 1586581Srgrimes 158726996Sgibbs /* 158826996Sgibbs * This must be coordinated with iestart() and ietint(). 158926996Sgibbs */ 1590112734Smdodd sc->xmit_cmds[0]->ie_xmit_status = IE_STAT_COMPL; 1591581Srgrimes 159226996Sgibbs /* take the ee16 out of loopback */ 1593112734Smdodd if (sc->hard_type == IE_EE16) { 159426996Sgibbs u_int8_t bart_config; 1595581Srgrimes 1596112734Smdodd bart_config = inb(PORT(sc) + IEE16_CONFIG); 159726996Sgibbs bart_config &= ~IEE16_BART_LOOPBACK; 159826996Sgibbs /* inb doesn't get bit! */ 159926996Sgibbs bart_config |= IEE16_BART_MCS16_TEST; 1600112734Smdodd outb(PORT(sc) + IEE16_CONFIG, bart_config); 1601112734Smdodd ee16_interrupt_enable(sc); 1602112734Smdodd ee16_chan_attn(sc); 160326996Sgibbs } 1604147256Sbrooks sc->ifp->if_flags |= IFF_RUNNING; /* tell higher levels 160526996Sgibbs * we're here */ 1606147256Sbrooks sc->ifp->if_flags &= ~IFF_OACTIVE; 1607112788Smdodd 1608112734Smdodd start_receiver(sc); 160925971Sgibbs 161026996Sgibbs return; 1611581Srgrimes} 1612581Srgrimes 161326996Sgibbsstatic void 1614112734Smdoddie_stop(struct ie_softc *sc) 1615581Srgrimes{ 1616112734Smdodd command_and_wait(sc, IE_RU_DISABLE, 0, 0); 1617581Srgrimes} 1618581Srgrimes 1619798Swollmanstatic int 162036735Sdfrieioctl(struct ifnet *ifp, u_long command, caddr_t data) 1621581Srgrimes{ 162226996Sgibbs int s, error = 0; 1623112734Smdodd struct ie_softc *sc = ifp->if_softc; 1624581Srgrimes 162526996Sgibbs s = splimp(); 1626581Srgrimes 162726996Sgibbs switch (command) { 162826996Sgibbs case SIOCSIFFLAGS: 162926996Sgibbs /* 163026996Sgibbs * Note that this device doesn't have an "all multicast" 163126996Sgibbs * mode, so we must turn on promiscuous mode and do the 163226996Sgibbs * filtering manually. 163326996Sgibbs */ 163426996Sgibbs if ((ifp->if_flags & IFF_UP) == 0 && 163526996Sgibbs (ifp->if_flags & IFF_RUNNING)) { 163626996Sgibbs ifp->if_flags &= ~IFF_RUNNING; 1637112734Smdodd ie_stop(sc); 163826996Sgibbs } else if ((ifp->if_flags & IFF_UP) && 163926996Sgibbs (ifp->if_flags & IFF_RUNNING) == 0) { 1640112734Smdodd sc->promisc = 164126996Sgibbs ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI); 1642112734Smdodd ieinit(sc); 1643112734Smdodd } else if (sc->promisc ^ 164426996Sgibbs (ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI))) { 1645112734Smdodd sc->promisc = 164626996Sgibbs ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI); 1647112734Smdodd ieinit(sc); 164826996Sgibbs } 164926996Sgibbs break; 1650581Srgrimes 165126996Sgibbs case SIOCADDMULTI: 165226996Sgibbs case SIOCDELMULTI: 165326996Sgibbs /* 165426996Sgibbs * Update multicast listeners 165526996Sgibbs */ 165626996Sgibbs /* reset multicast filtering */ 1657112734Smdodd ie_mc_reset(sc); 165826996Sgibbs error = 0; 165926996Sgibbs break; 1660581Srgrimes 166126996Sgibbs default: 1662106937Ssam error = ether_ioctl(ifp, command, data); 1663106937Ssam break; 166426996Sgibbs } 1665581Srgrimes 166626996Sgibbs splx(s); 166726996Sgibbs return (error); 1668581Srgrimes} 1669581Srgrimes 167026996Sgibbsstatic void 1671112734Smdoddie_mc_reset(struct ie_softc *sc) 167226996Sgibbs{ 167326996Sgibbs struct ifmultiaddr *ifma; 1674581Srgrimes 167526996Sgibbs /* 167626996Sgibbs * Step through the list of addresses. 167726996Sgibbs */ 1678112734Smdodd sc->mcast_count = 0; 1679147256Sbrooks TAILQ_FOREACH(ifma, &sc->ifp->if_multiaddrs, ifma_link) { 168026996Sgibbs if (ifma->ifma_addr->sa_family != AF_LINK) 168126996Sgibbs continue; 1682581Srgrimes 168326996Sgibbs /* XXX - this is broken... */ 1684112734Smdodd if (sc->mcast_count >= MAXMCAST) { 1685147256Sbrooks sc->ifp->if_flags |= IFF_ALLMULTI; 1686147256Sbrooks ieioctl(sc->ifp, SIOCSIFFLAGS, (void *) 0); 168726996Sgibbs goto setflag; 168826996Sgibbs } 168926996Sgibbs bcopy(LLADDR((struct sockaddr_dl *) ifma->ifma_addr), 1690112734Smdodd &(sc->mcast_addrs[sc->mcast_count]), 6); 1691112734Smdodd sc->mcast_count++; 169226996Sgibbs } 169321666Swollman 1694581Srgrimessetflag: 1695112734Smdodd sc->want_mcsetup = 1; 1696581Srgrimes} 1697581Srgrimes 1698581Srgrimes 1699581Srgrimes#ifdef DEBUG 170033181Seivindstatic void 170126996Sgibbsprint_rbd(volatile struct ie_recv_buf_desc * rbd) 170226996Sgibbs{ 170338224Sbde printf("RBD at %p:\n" 170438224Sbde "actual %04x, next %04x, buffer %p\n" 170526996Sgibbs "length %04x, mbz %04x\n", 170643314Sdillon (volatile void *) rbd, 170737618Sbde rbd->ie_rbd_actual, rbd->ie_rbd_next, 170837618Sbde (void *) rbd->ie_rbd_buffer, 170926996Sgibbs rbd->ie_rbd_length, rbd->mbz); 1710581Srgrimes} 1711581Srgrimes 171226996Sgibbs#endif /* DEBUG */ 1713112790Smdodd 1714112790Smdoddint 1715112790Smdoddie_alloc_resources (device_t dev) 1716112790Smdodd{ 1717112790Smdodd struct ie_softc * sc; 1718112790Smdodd int error; 1719112790Smdodd 1720112790Smdodd error = 0; 1721112790Smdodd sc = device_get_softc(dev); 1722112790Smdodd 1723127135Snjl sc->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &sc->io_rid, 1724127135Snjl RF_ACTIVE); 1725112790Smdodd if (!sc->io_res) { 1726112790Smdodd device_printf(dev, "No I/O space?!\n"); 1727112790Smdodd error = ENOMEM; 1728112790Smdodd goto bad; 1729112790Smdodd } 1730112790Smdodd sc->io_bt = rman_get_bustag(sc->io_res); 1731112790Smdodd sc->io_bh = rman_get_bushandle(sc->io_res); 1732112790Smdodd 1733127135Snjl sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, 1734127135Snjl RF_ACTIVE); 1735112790Smdodd if (!sc->mem_res) { 1736112790Smdodd device_printf(dev, "No Memory!\n"); 1737112790Smdodd error = ENOMEM; 1738112790Smdodd goto bad; 1739112790Smdodd } 1740112790Smdodd sc->mem_bt = rman_get_bustag(sc->mem_res); 1741112790Smdodd sc->mem_bh = rman_get_bushandle(sc->mem_res); 1742112790Smdodd 1743127135Snjl sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid, 1744127135Snjl RF_ACTIVE); 1745112790Smdodd if (!sc->irq_res) { 1746112790Smdodd device_printf(dev, "No IRQ!\n"); 1747112790Smdodd error = ENOMEM; 1748112790Smdodd goto bad; 1749112790Smdodd } 1750112790Smdodd 1751112790Smdodd sc->port = rman_get_start(sc->io_res); /* XXX hack */ 1752112790Smdodd sc->iomembot = rman_get_virtual(sc->mem_res); 1753112790Smdodd sc->iosize = rman_get_size(sc->mem_res); 1754112790Smdodd 1755112790Smdodd return (0); 1756112790Smdoddbad: 1757112790Smdodd return (error); 1758112790Smdodd} 1759112790Smdodd 1760112790Smdoddvoid 1761112790Smdoddie_release_resources (device_t dev) 1762112790Smdodd{ 1763112790Smdodd struct ie_softc * sc; 1764112790Smdodd 1765112790Smdodd sc = device_get_softc(dev); 1766112790Smdodd 1767112790Smdodd if (sc->irq_ih) 1768112790Smdodd bus_teardown_intr(dev, sc->irq_res, sc->irq_ih); 1769112790Smdodd if (sc->io_res) 1770112790Smdodd bus_release_resource(dev, SYS_RES_IOPORT, 1771112790Smdodd sc->io_rid, sc->io_res); 1772112790Smdodd if (sc->irq_res) 1773112790Smdodd bus_release_resource(dev, SYS_RES_IRQ, 1774112790Smdodd sc->irq_rid, sc->irq_res); 1775112790Smdodd if (sc->mem_res) 1776112790Smdodd bus_release_resource(dev, SYS_RES_MEMORY, 1777112790Smdodd sc->mem_rid, sc->mem_res); 1778112790Smdodd 1779112790Smdodd return; 1780112790Smdodd} 1781112790Smdodd 1782112790Smdoddint 1783112790Smdoddie_detach (device_t dev) 1784112790Smdodd{ 1785112790Smdodd struct ie_softc * sc; 1786112790Smdodd struct ifnet * ifp; 1787112790Smdodd 1788112790Smdodd sc = device_get_softc(dev); 1789147256Sbrooks ifp = sc->ifp; 1790112790Smdodd 1791112790Smdodd if (sc->hard_type == IE_EE16) 1792112790Smdodd ee16_shutdown(sc, 0); 1793112790Smdodd 1794112790Smdodd ie_stop(sc); 1795112790Smdodd ifp->if_flags &= ~IFF_RUNNING; 1796112790Smdodd ether_ifdetach(ifp); 1797147256Sbrooks if_free(ifp); 1798112790Smdodd ie_release_resources(dev); 1799112790Smdodd 1800112790Smdodd return (0); 1801112790Smdodd} 1802