/* * Copyright (c) 1993, 1994, 1995 * Rodney W. Grimes, Milwaukie, Oregon 97222. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer as * the first lines of this file unmodified. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Rodney W. Grimes. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY RODNEY W. GRIMES ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL RODNEY W. GRIMES BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $Id: if_ix.c,v 1.16 1995/12/10 13:38:47 phk Exp $ */ #include "ix.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #include #include #include #endif /* INET */ #ifdef IPX /*ZZZ no work done on this, this is just here to remind me*/ #include #include #endif /* IPX */ #ifdef NS /*ZZZ no work done on this, this is just here to remind me*/ #include #include #endif /* NS */ #ifdef ISO /*ZZZ no work done on this, this is just here to remind me*/ #include #include extern char all_es_snpa[], all_is_snpa[], all_l1is_snpa[], all_l2is_snpa[]; #endif /* ISO */ /*ZZZ no work done on this, this is just here to remind me*/ #include "bpfilter.h" #if NBPFILTER > 0 #include #include #endif /* NBPFILTER > 0 */ #include #include #include static ix_softc_t ix_softc[NIX]; #define DEBUGNONE 0x0000 #define DEBUGPROBE 0x0001 #define DEBUGATTACH (DEBUGPROBE << 1) #define DEBUGINIT (DEBUGATTACH << 1) #define DEBUGINIT_RFA (DEBUGINIT << 1) #define DEBUGINIT_TFA (DEBUGINIT_RFA << 1) #define DEBUGINTR (DEBUGINIT_TFA << 1) #define DEBUGINTR_FR (DEBUGINTR << 1) #define DEBUGINTR_CX (DEBUGINTR_FR << 1) #define DEBUGSTART (DEBUGINTR_CX << 1) #define DEBUGSTOP (DEBUGSTART << 1) #define DEBUGRESET (DEBUGSTOP << 1) #define DEBUGDONE (DEBUGRESET << 1) #define DEBUGIOCTL (DEBUGDONE << 1) #define DEBUGACK (DEBUGIOCTL << 1) #define DEBUGCA (DEBUGACK << 1) #define DEBUGCB_WAIT (DEBUGCA << 1) #define DEBUGALL 0xFFFFFFFF /* #define IXDEBUG (DEBUGPROBE | DEBUGINIT | DEBUGSTOP | DEBUGCB_WAIT) */ #ifdef IXDEBUG int ixdebug=IXDEBUG; #define DEBUGBEGIN(flag) \ {\ if (ixdebug & flag)\ { #define DEBUGEND \ }\ } #define DEBUGDO(x) x #else /* IXDEBUG */ #define DEBUGBEGIN(flag) #define DEBUGEND #define DEBUGDO(x) #endif /* IXDEBUG */ /* * Enable the exteneded ixcounters by using #define IXCOUNTERS, note that * this requires a modification to the ifnet structure to add the counters * in. Some day a standard extended ifnet structure will be done so that * additional statistics can be gathered for any board that supports these * counters. */ #ifdef IXCOUNTERS #define IXCOUNTER(x) x #else /* IXCOUNTERS */ #define IXCOUNTER(x) #endif /* IXCOUNTERS */ /* * Function Prototypes */ static inline void ixinterrupt_enable(int); static inline void ixinterrupt_disable(int); static inline void ixchannel_attention(int); static u_short ixacknowledge(int); static int ix_cb_wait(cb_t *, char *); static int ix_scb_wait(scb_t *, u_short, char *); static int ixprobe(struct isa_device *); static int ixattach(struct isa_device *); static void ixinit(int); static void ixinit_rfa(int); static void ixinit_tfa(int); static inline void ixintr_cx(int); static inline void ixintr_cx_free(int, cb_t *); static inline void ixintr_fr(int); static inline void ixintr_fr_copy(int, rfd_t *); static inline void ixintr_fr_free(int, rfd_t *); static void ixstart(struct ifnet *); static int ixstop(struct ifnet *); static int ixdone(struct ifnet *); static int ixioctl(struct ifnet *, int, caddr_t); static void ixreset(int); static void ixwatchdog(struct ifnet *); static u_short ixeeprom_read(int, int); static void ixeeprom_outbits(int, int, int); static int ixeeprom_inbits(int); static void ixeeprom_clock(int, int); /* RRR */ struct isa_driver ixdriver = {ixprobe, ixattach, "ix"}; static struct kern_devconf kdc_ix_template = { 0, 0, 0, /* filled in by dev_attach */ "ix", 0, { MDDT_ISA, 0, "net" }, isa_generic_externalize, 0, 0, ISA_EXTERNALLEN, &kdc_isa0, /* parent */ 0, /* parentdata */ DC_UNCONFIGURED, /* state */ "", /* description */ DC_CLS_NETIF /* class */ }; static inline void ix_registerdev(struct isa_device *id, const char *descr) { struct kern_devconf *kdc = &ix_softc[id->id_unit].kdc; *kdc = kdc_ix_template; kdc->kdc_unit = id->id_unit; kdc->kdc_parentdata = id; kdc->kdc_description = descr; dev_attach(kdc); } /* * Enable the interrupt signal on the board so that it may interrupt * the host. */ static inline void ixinterrupt_enable(int unit) { ix_softc_t *sc = &ix_softc[unit]; outb(sc->iobase + sel_irq, sc->irq_encoded | IRQ_ENABLE); } /* * Disable the interrupt signal on the board so that it will not interrupt * the host. */ static inline void ixinterrupt_disable(int unit) { ix_softc_t *sc = &ix_softc[unit]; outb(sc->iobase + sel_irq, sc->irq_encoded); } /* * Send a channel attention to the 82586 chip */ static inline void ixchannel_attention(int unit) { DEBUGBEGIN(DEBUGCA) DEBUGDO(printf("ca");) DEBUGEND outb(ix_softc[unit].iobase + ca_ctrl, 0); } u_short ixacknowledge(int unit) { ix_softc_t *sc = &ix_softc[unit]; scb_t *scb = (scb_t *)(sc->maddr + SCB_ADDR); u_short status; int i; DEBUGBEGIN(DEBUGACK) DEBUGDO(printf("ack:");) DEBUGEND status = scb->status; scb->command = status & SCB_ACK_MASK; if ((status & SCB_ACK_MASK) != 0) { ixchannel_attention(unit); for (i = 1000000; scb->command && (i > 0); i--); /*ZZZ timeout*/ if (i == 0) { printf(".TO=%x:", scb->command); printf("\nshutting down\n"); ixinterrupt_disable(unit); sc->flags = IXF_NONE; status = 0; } else { DEBUGBEGIN(DEBUGACK) DEBUGDO(printf(".ok:");) DEBUGEND } } else { /* nothing to acknowledge */ DEBUGBEGIN(DEBUGACK) DEBUGDO(printf("NONE:");) DEBUGEND } DEBUGBEGIN(DEBUGACK) DEBUGDO(printf("%x ", status);) DEBUGEND return(status); } int ix_cb_wait(cb_t *cb, char *message) { int i; int status; for (i=1000000; i>0; i--) { if (cb->status & CB_COMPLETE) break; /* Wait for done */ } if (i == 0) { printf("%s timeout cb->status = %x\n", message, cb->status); status = 1; } else { DEBUGBEGIN(DEBUGCB_WAIT) DEBUGDO(printf("%s cb ok count = %d\n", message, i);) DEBUGEND status = 0; } return (status); } int ix_scb_wait(scb_t *scb, u_short expect, char *message) { int i; int status; for (i=1000000; i>0; i--) { if (scb->status == expect) break; /* Wait for done */ } if (i == 0) { printf("%s timeout scb->status = %x\n", message, scb->status); status = 1; } else { DEBUGBEGIN(DEBUGINIT) DEBUGDO(printf("%s scb ok count = %d\n", message, i);) DEBUGEND status = 0; } return (status); } int ixprobe(struct isa_device *dvp) { int unit = dvp->id_unit; ix_softc_t *sc = &ix_softc[unit]; char tempid, idstate; int i; int status = 0; u_short boardid, checksum, connector, eaddrtemp, irq; /* ZZZ irq_translate should really be unsigned, but until * isa_device.h and all uses are fixed we have to live with it */ short irq_translate[] = {0, IRQ9, IRQ3, IRQ4, IRQ5, IRQ10, IRQ11, 0}; char irq_encode[] = { 0, 0, 0, 2, 3, 4, 0, 0, 0, 1, 5, 6, 0, 0, 0, 0 }; DEBUGBEGIN(DEBUGPROBE) DEBUGDO(printf ("ixprobe:");) DEBUGEND /* * Since Intel gives us such a nice way to ID this board lets * see if we really have one at this I/O address */ idstate = inb(dvp->id_iobase + autoid) & 0x03; for (i=0, boardid=0; i < 4; i++) { tempid = inb(dvp->id_iobase + autoid); boardid |= ((tempid & 0xF0) >> 4) << ((tempid & 0x03) << 2); if ((tempid & 0x03) != (++idstate & 0x03)) { /* out of sequence, destroy boardid and bail out */ boardid = 0; break; } } DEBUGBEGIN(DEBUGPROBE) DEBUGDO(printf("boardid = %x\n", boardid);) DEBUGEND if (boardid != BOARDID) { goto ixprobe_exit; } /* * We now know that we have a board, so save the I/O base * address in the softc and use the softc from here on out */ sc->iobase = dvp->id_iobase; /* * Reset the Bart ASIC by pulsing the reset bit and waiting * the required 240 uSecounds. Also place the 82856 in the reset * mode so that we can access the EEPROM */ outb(sc->iobase + ee_ctrl, GA_RESET); outb(sc->iobase + ee_ctrl, 0); DELAY(240); outb(sc->iobase + ee_ctrl, I586_RESET); /* * Checksum the EEPROM, should be equal to BOARDID */ for (i=0, checksum=0; i<64; i++) { checksum += ixeeprom_read(unit, i); } DEBUGBEGIN(DEBUGPROBE) DEBUGDO(printf ("checksum = %x\n", checksum);) DEBUGEND if (checksum != BOARDID) { goto ixprobe_exit; } /* * Do the I/O channel ready test */ { u_char lock_bit; lock_bit = ixeeprom_read(unit, eeprom_lock_address); if (lock_bit & EEPROM_LOCKED) { DEBUGBEGIN(DEBUGPROBE) DEBUGDO(printf ("lockbit set, no doing io channel ready test\n");) DEBUGEND } else { u_char bart_config, junk; bart_config = inb(sc->iobase + config); bart_config |= BART_IOCHRDY_LATE | BART_IO_TEST_EN; outb(sc->iobase + config, bart_config); junk = inb(sc->iobase + 0x4000); /*XXX read junk */ bart_config = inb(sc->iobase + config); outb(sc->iobase + config, bart_config & ~(BART_IO_TEST_EN)); if (bart_config & BART_IO_RESULT) { printf ("iochannel ready test failed!!\n"); } else { DEBUGBEGIN(DEBUGPROBE) DEBUGDO(printf ("iochannel ready test passed\n");) DEBUGEND } } } /* * Size and test the memory on the board. The size of the memory * can be one of 16k, 32k, 48k or 64k. It can be located in the * address range 0xC0000 to 0xEFFFF on 16k boundaries. Although * the board can be configured for 0xEC0000 to 0xEEFFFF, * or 0xFC0000 to 0xFFFFFF these ranges are not supported by 386bsd. * * If the size does not match the passed in memory allocation size * issue a warning, but continue with the minimum of the two sizes. */ { u_short memory_page; u_short memory_adjust; u_short memory_decode; u_short memory_edecode; switch (dvp->id_msize) { case 65536: case 32768: /* XXX Only support 32k and 64k right now */ { break; } case 16384: case 49512: default: { printf("ixprobe mapped memory size out of range\n"); goto ixprobe_exit; } } if ((kvtop(dvp->id_maddr) < 0xC0000) || (kvtop(dvp->id_maddr) + dvp->id_msize > 0xF0000)) { printf("ixprobe mapped memory address out of range\n"); goto ixprobe_exit; } memory_page = (kvtop(dvp->id_maddr) & 0x3C000) >> 14; memory_adjust = MEMCTRL_FMCS16 | (memory_page & 0x3) << 2; memory_decode = ((1 << (dvp->id_msize / 16384)) - 1) << memory_page; memory_edecode = ((~memory_decode >> 4) & 0xF0) | (memory_decode >> 8); /* ZZZ This should be checked against eeprom location 6, low byte */ outb(sc->iobase + memdec, memory_decode & 0xFF); /* ZZZ This should be checked against eeprom location 1, low byte */ outb(sc->iobase + memctrl, memory_adjust); /* ZZZ Now if I could find this one I would have it made */ outb(sc->iobase + mempc, (~memory_decode & 0xFF)); /* ZZZ I think this is location 6, high byte */ outb(sc->iobase + memectrl, memory_edecode); /*XXX disable Exxx */ sc->maddr = dvp->id_maddr; sc->msize = dvp->id_msize; DEBUGBEGIN(DEBUGPROBE) DEBUGDO(printf("Physical address = %lx\n", kvtop(sc->maddr));) DEBUGEND } /* * first prime the stupid bart DRAM controller so that it * works, then zero out all or memory. */ bzero(sc->maddr, 32); bzero(sc->maddr, sc->msize); /* * Get the type of connector used, either AUI, BNC or TPE. */ connector = ixeeprom_read(unit, eeprom_config1); if (connector & CONNECT_BNCTPE) { connector = ixeeprom_read(unit, eeprom_config2); if (connector & CONNECT_TPE) { sc->connector = TPE; DEBUGBEGIN(DEBUGPROBE) DEBUGDO(printf ("Using TPE connector\n");) DEBUGEND } else { sc->connector = BNC; DEBUGBEGIN(DEBUGPROBE) DEBUGDO(printf ("Using BNC connector\n");) DEBUGEND } } else { sc->connector = AUI; DEBUGBEGIN(DEBUGPROBE) DEBUGDO(printf ("Using AUI connector\n");) DEBUGEND } /* * Get the encoded interrupt number from the EEPROM, check it * against the passed in IRQ. Issue a warning if they do not * match. Always use the passed in IRQ, not the one in the EEPROM. */ irq = ixeeprom_read(unit, eeprom_config1); irq = (irq & IRQ) >> IRQ_SHIFT; irq = irq_translate[irq]; if (dvp->id_irq > 0) { if (irq != dvp->id_irq) { printf("ix%d: WARNING: board is configured for IRQ %d, using %d\n", unit, ffs(irq) - 1, ffs(dvp->id_irq) - 1); irq = dvp->id_irq; } } else { dvp->id_irq = irq; } sc->irq_encoded = irq_encode[ffs(irq) - 1]; if (sc->irq_encoded == 0) { printf("ix%d: invalid irq (%d)\n", unit, ffs(irq) - 1); goto ixprobe_exit; } /* * Get the slot width, either 8 bit or 16 bit. */ if (inb(sc->iobase + config) & SLOT_WIDTH) { sc->width = WIDTH_16; DEBUGBEGIN(DEBUGPROBE) DEBUGDO(printf("Using 16-bit slot\n");) DEBUGEND } else { sc->width = WIDTH_8; DEBUGBEGIN(DEBUGPROBE) DEBUGDO(printf("Using 8-bit slot\n");) DEBUGEND } /* * Get the hardware ethernet address from the EEPROM and * save it in the softc for use by the 586 setup code. */ eaddrtemp = ixeeprom_read(unit, eeprom_enetaddr_high); sc->arpcom.ac_enaddr[1] = eaddrtemp & 0xFF; sc->arpcom.ac_enaddr[0] = eaddrtemp >> 8; eaddrtemp = ixeeprom_read(unit, eeprom_enetaddr_mid); sc->arpcom.ac_enaddr[3] = eaddrtemp & 0xFF; sc->arpcom.ac_enaddr[2] = eaddrtemp >> 8; eaddrtemp = ixeeprom_read(unit, eeprom_enetaddr_low); sc->arpcom.ac_enaddr[5] = eaddrtemp & 0xFF; sc->arpcom.ac_enaddr[4] = eaddrtemp >> 8; sc->flags = IXF_NONE; /* make sure the flag word is NONE */ status = IX_IO_PORTS; #ifndef DEV_LKM ix_registerdev(dvp, "Ethernet adapter: Intel EtherExpress16"); #endif /* not DEV_LKM */ ixprobe_exit: DEBUGBEGIN(DEBUGPROBE) DEBUGDO(printf ("ixprobe exited\n");) DEBUGEND return(status); } int ixattach(struct isa_device *dvp) { int unit = dvp->id_unit; ix_softc_t *sc = &ix_softc[unit]; struct ifnet *ifp = &sc->arpcom.ac_if; struct ifaddr *ifa; struct sockaddr_dl *sdl; DEBUGBEGIN(DEBUGATTACH) DEBUGDO(printf("ixattach:");) DEBUGEND /* * Fill in the interface parameters for if_attach * Note: We could save some code here by first using a * bzero(ifp, sizeof(ifp)); and then not doing all * the = 0;'s * Infact we should bzero this just to make sure * that something does not get missed. * Further note by GW: * Actually, it's a complete waste of time to zero any of * this stuff because the C language guarantees that it's * already zeroed. If this code is changed to do dynamic * allocation, this will have to get revisited. */ bzero(ifp, sizeof(ifp)); ifp->if_name = ixdriver.name; ifp->if_unit = unit; ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST; ifp->if_output = ether_output; ifp->if_start = ixstart; ifp->if_done = ixdone; ifp->if_ioctl = ixioctl; ifp->if_watchdog = ixwatchdog; ifp->if_type = IFT_ETHER; ifp->if_addrlen = ETHER_ADDRESS_LENGTH; ifp->if_hdrlen = ETHER_HEADER_LENGTH; #ifdef IXCOUNTERS /* * ZZZ more counters added, but bzero gets them */ #endif /* IXCOUNTERS */ if_attach(ifp); sc->kdc.kdc_state = DC_IDLE; /* Search down the ifa address list looking for the AF_LINK type entry */ ifa = ifp->if_addrlist; while ((ifa != 0) && (ifa->ifa_addr != 0) && (ifa->ifa_addr->sa_family != AF_LINK)) { ifa = ifa->ifa_next; } /* If we find an AF_LINK type entry, we well fill in the hardware addr */ if ((ifa != 0) && (ifa->ifa_addr != 0)) { /* Fill in the link level address for this interface */ sdl = (struct sockaddr_dl *)ifa->ifa_addr; sdl->sdl_type = IFT_ETHER; sdl->sdl_alen = ETHER_ADDRESS_LENGTH; sdl->sdl_slen = 0; bcopy(sc->arpcom.ac_enaddr, LLADDR(sdl), ETHER_ADDRESS_LENGTH); } printf("ix%d: address %s\n", unit, ether_sprintf(sc->arpcom.ac_enaddr)); return(0); } void ixinit(int unit) { ix_softc_t *sc = &ix_softc[unit]; struct ifnet *ifp = &sc->arpcom.ac_if; scp_t *scp = (scp_t *)(sc->maddr + SCP_ADDR); iscp_t *iscp = (iscp_t *)(sc->maddr + ISCP_ADDR); scb_t *scb = (scb_t *)(sc->maddr + SCB_ADDR); cb_t *cb; tbd_t *tbd; int i; u_char bart_config; /* bart config byte */ int status = 0; DEBUGBEGIN(DEBUGINIT) DEBUGDO(printf("ixinit:");) DEBUGEND sc->kdc.kdc_state = DC_BUSY; /* Put bart into loopback until we are done intializing to * make sure that packets don't hit the wire */ bart_config = inb(sc->iobase + config); bart_config |= BART_LOOPBACK; bart_config |= BART_MCS16_TEST; /* inb does not get this bit! */ outb(sc->iobase + config, bart_config); bart_config = inb(sc->iobase + config); scp->unused1 = 0; /* Intel says to put zeros in it */ scp->sysbus = sc->width; /* ZZZ need to fix for 596 */ scp->unused2 = 0; /* Intel says to put zeros in it */ scp->unused3 = 0; /* Intel says to put zeros in it */ scp->iscp = ISCP_ADDR; iscp->busy = ISCP_BUSY; iscp->scb_offset = SCB_ADDR; iscp->scb_base = TFA_START; scb->status = SCB_STAT_NULL; scb->command = SCB_RESET; scb->cbl_offset = TFA_START; scb->rfa_offset = RFA_START; scb->crc_errors = 0; scb->aln_errors = 0; scb->rsc_errors = 0; scb->ovr_errors = 0; ixinit_tfa(unit); ixinit_rfa(unit); cb = sc->cb_head; tbd = sc->tbd_head; /* * remove the reset signal and start the 586 up, the 586 well read * the SCP, ISCP and the reset CB. This should put it into a * known state: RESET! */ outb(sc->iobase + ee_ctrl, EENORMAL); ixchannel_attention(unit); for (i=100000; iscp->busy && (i>0); i--); /* Wait for done */ if (i == 0) { printf("iscp->busy time out\n"); status = 1; } else { DEBUGBEGIN(DEBUGINIT) DEBUGDO(printf ("iscp->busy did not timeout = %d\n", i);) DEBUGEND } status |= ix_scb_wait(scb, (u_short)(SCB_STAT_CX | SCB_STAT_CNA), "Reset"); ixacknowledge(unit); /* XXX this belongs some place else, run diagnostics on the 586 */ { cb_diagnose_t *cb_diag = (cb_diagnose_t *)(cb); cb_diag->common.status = 0; cb_diag->common.command = CB_CMD_EL | CB_CMD_DIAGNOSE; scb->command = SCB_CUC_START; ixchannel_attention(unit); status |= ix_cb_wait((cb_t *)cb_diag, "Diagnose"); status |= ix_scb_wait(scb, (u_short)SCB_STAT_CNA, "Diagnose"); ixacknowledge(unit); } /* XXX end this belongs some place else, run diagnostics on the 586 */ /* XXX this belongs some place else, run configure on the 586 */ { cb_configure_t *cb_conf = (cb_configure_t *)(cb); cb_conf->common.status = 0; cb_conf->common.command = CB_CMD_EL | CB_CMD_CONF; cb_conf->byte[0] = 12; /* 12 byte configure block */ cb_conf->byte[1] = 8; /* fifo limit at 8 bytes */ cb_conf->byte[2] = 0x40; /* don't save bad frames, srdy/ardy is srdy */ cb_conf->byte[3] = 0x2E; /* address length is 6 bytes, address and length are in tb, preamble length is 8 bytes, internal loopback off, external loopback off */ cb_conf->byte[4] = 0; /* linear priority is 0, ACR (Exponential priorty) is 0, exponential backoff is 802.3 */ cb_conf->byte[5] = 96; /* interframe spacing in TxC clocks */ cb_conf->byte[6] = 0; /* lower 8 bits of slot time */ cb_conf->byte[7] = 0xf2; /* upper slot time (512 bits), 15 transmision retries */ cb_conf->byte[8] = 0; /* promiscuous mode off, broadcast enabled, nrz encodeing, cease transmission if ^CRS, insert crc, end of carrier mode bit stuffing, no padding */ cb_conf->byte[9] = 0; /* carrier sense filter = 0 bits, carrier sense source external, collision detect filter = 0 bits, collision detect source external */ cb_conf->byte[10] = 60; /* minimum number of bytes is a frame */ cb_conf->byte[11] = 0; /* unused */ scb->command = SCB_CUC_START; ixchannel_attention(unit); status |= ix_cb_wait((cb_t *)cb_conf, "Configure"); status |= ix_scb_wait(scb, (u_short)SCB_STAT_CNA, "Configure"); ixacknowledge(unit); } /* XXX end this belongs some place else, run configure on the 586 */ /* XXX this belongs some place else, run ias on the 586 */ { cb_ias_t *cb_ias = (cb_ias_t *)(cb); cb_ias->common.status = 0; cb_ias->common.command = CB_CMD_EL | CB_CMD_IAS; bcopy(sc->arpcom.ac_enaddr, cb_ias->source, ETHER_ADDRESS_LENGTH); scb->command = SCB_CUC_START; ixchannel_attention(unit); status |= ix_cb_wait((cb_t *)cb_ias, "IAS"); status |= ix_scb_wait(scb, (u_short)SCB_STAT_CNA, "IAS"); ixacknowledge(unit); } /* XXX end this belongs some place else, run ias on the 586 */ if (status == 0) { /* Take bart out of loopback as we are done intializing */ bart_config = inb(sc->iobase + config); bart_config &= ~BART_LOOPBACK; bart_config |= BART_MCS16_TEST; /* inb does not get this bit! */ outb(sc->iobase + config, bart_config); /* The above code screwed with the tfa, reinit it! */ ixinit_tfa(unit); scb->command = SCB_RUC_START; /* start up the receive unit */ ixchannel_attention(unit); sc->flags |= IXF_INITED; /* we have been initialized */ ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; ixinterrupt_enable(unit); /* Let err fly!!! */ } DEBUGBEGIN(DEBUGINIT) DEBUGDO(printf("ixinit exited\n");) DEBUGEND return; } /* * ixinit_rfa(int unit) * * This routine initializes the Receive Frame Area for the 82586 * * input the unit number to build the RFA for * access the softc for memory address * output an initialized RFA, ready for packet receiption * the following queue pointers in the softc structure are * also initialize * sc->rfd_head sc->rfd_tail * sc->rbd_head sc->rbd_tail * defines RFA_START the starting offset of the RFA * RFA_SIZE size of the RFA area * RB_SIZE size of the receive buffer, this must * be even and should be greater than the * minumum packet size and less than the * maximum packet size */ void ixinit_rfa(int unit) { ix_softc_t *sc = &ix_softc[unit]; rfd_t *rfd; rbd_t *rbd; caddr_t rb; int i, complete_frame_size, how_many_frames; DEBUGBEGIN(DEBUGINIT_RFA) DEBUGDO(printf("\nix%d: ixinit_rfa\n", unit);) DEBUGEND complete_frame_size = sizeof(rfd_t) + sizeof(rbd_t) + RB_SIZE; how_many_frames = RFA_SIZE / complete_frame_size; /* build the list of rfd's, rbd's and rb's */ rfd = (rfd_t *)(sc->maddr + RFA_START); rbd = (rbd_t *)(sc->maddr + RFA_START + (how_many_frames * sizeof(rfd_t))); rb = sc->maddr + RFA_START + (how_many_frames * (sizeof(rfd_t) + sizeof(rbd_t))); sc->rfd_head = rfd; sc->rbd_head = rbd; for (i = 0; i < how_many_frames; i++, rfd++, rbd++, rb += RB_SIZE) { rfd->status = 0; rfd->command = 0; rfd->next = KVTOBOARD(rfd) + sizeof(rfd_t); rfd->rbd_offset = INTEL586NULL; /* ZZZ could bzero this, but just leave a note for now */ /* ZZZ bzero(rfd->destination); */ /* ZZZ bzero(rfd->source); */ rfd->length = 0; rbd->act_count = 0; rbd->next = KVTOBOARD(rbd) + sizeof(rbd_t); rbd->buffer = KVTOBOARD(rb); rbd->size = RB_SIZE; /* * handle the boundary conditions here. for the zeroth * rfd we must set the rbd_offset to point at the zeroth * rbd. for the last rfd and rbd we need to close the * list into a ring and set the end of list bits. */ if (i == 0) { rfd->rbd_offset = KVTOBOARD(rbd); } if (i == how_many_frames - 1) { rfd->command = RFD_CMD_EL | RFD_CMD_SUSP; rfd->next = KVTOBOARD(sc->rfd_head); rbd->next = KVTOBOARD(sc->rbd_head); rbd->size = RBD_SIZE_EL | RB_SIZE; } } sc->rfd_tail = (--rfd); sc->rbd_tail = (--rbd); #ifdef IXDEBUG DEBUGBEGIN(DEBUGINIT_RFA) rfd = (rfd_t *)(sc->maddr + RFA_START); rbd = (rbd_t *)(sc->maddr + RFA_START + (how_many_frames * sizeof(rfd_t))); rb = sc->maddr + RFA_START + (how_many_frames * (sizeof(rfd_t) + sizeof(rbd_t))); printf(" complete_frame_size = %d\n", complete_frame_size); printf(" how_many_frames = %d\n", how_many_frames); printf(" rfd_head = %lx\t\trfd_tail = %lx\n", kvtop(sc->rfd_head), kvtop(sc->rfd_tail)); printf(" rbd_head = %lx\t\trbd_tail = %lx\n", kvtop(sc->rbd_head), kvtop(sc->rbd_tail)); for (i = 0; i < how_many_frames; i++, rfd++, rbd++, rb += RB_SIZE) { printf(" %d:\trfd = %lx\t\trbd = %lx\t\trb = %lx\n", i, kvtop(rfd), kvtop(rbd), kvtop(rb)); printf("\trfd->command = %x\n", rfd->command); printf("\trfd->next = %x\trfd->rbd_offset = %x\n", rfd->next, rfd->rbd_offset); printf("\trbd->next = %x\trbd->size = %x", rbd->next, rbd->size); printf("\trbd->buffer = %lx\n\n", rbd->buffer); } DEBUGEND #endif /* IXDEBUG */ /* * ZZZ need to add sanity check to see if last rb runs into * the stuff after it in memory, this should not be possible * but if someone screws up with the defines it can happen */ DEBUGBEGIN(DEBUGINIT_RFA) DEBUGDO(printf (" next rb would be at %lx\n", kvtop(rb));) DEBUGDO(printf("ix%d: ixinit_rfa exit\n", unit);) DEBUGEND } /* * ixinit_tfa(int unit) * * This routine initializes the Transmit Frame Area for the 82586 * * input the unit number to build the TFA for * access the softc for memory address * output an initialized TFA, ready for packet transmission * the following queue pointers in the softc structure are * also initialize * sc->cb_head sc->cb_tail * sc->tbd_head sc->tbd_tail * defines TB_COUNT home many transmit buffers to create * TB_SIZE size of the tranmit buffer, this must * be even and should be greater than the * minumum packet size and less than the * maximum packet size * TFA_START the starting offset of the TFA */ void ixinit_tfa(int unit) { ix_softc_t *sc = &ix_softc[unit]; cb_transmit_t *cb; tbd_t *tbd; caddr_t tb; int i; DEBUGBEGIN(DEBUGINIT_TFA) DEBUGDO(printf("\nix%d: ixinit_tfa\n", unit);) DEBUGEND /* build the list of cb's, tbd's and tb's */ cb = (cb_transmit_t *)(sc->maddr + TFA_START); tbd = (tbd_t *)(sc->maddr + TFA_START + (TB_COUNT * sizeof(cb_transmit_t))); tb = sc->maddr + TFA_START + (TB_COUNT * (sizeof(cb_transmit_t) + sizeof(tbd_t))); sc->cb_head = (cb_t *)cb; sc->tbd_head = tbd; for (i = 0; i < TB_COUNT; i++, cb++, tbd++, tb += TB_SIZE) { cb->common.status = 0; cb->common.command = CB_CMD_NOP; cb->common.next = KVTOBOARD(cb) + sizeof(cb_transmit_t); cb->tbd_offset = KVTOBOARD(tbd); /* ZZZ could bzero this, but just leave a note for now */ /* ZZZ bzero(cb->destination); */ cb->length = 0; tbd->act_count = 0; tbd->act_count = TBD_STAT_EOF; tbd->next = KVTOBOARD(tbd) + sizeof(tbd_t); tbd->buffer = KVTOBOARD(tb); /* * handle the boundary conditions here. */ if (i == TB_COUNT - 1) { cb->common.command = CB_CMD_EL | CB_CMD_NOP; cb->common.next = INTEL586NULL; /*RRR KVTOBOARD(sc->cb_head);*/ tbd->next = INTEL586NULL; /*RRR KVTOBOARD(sc->tbd_head);*/ } } sc->cb_tail = (cb_t *)(--cb); sc->tbd_tail = (--tbd); #ifdef IXDEBUG DEBUGBEGIN(DEBUGINIT_TFA) cb = (cb_transmit_t *)(sc->maddr + TFA_START); tbd = (tbd_t *)(sc->maddr + TFA_START + (TB_COUNT * sizeof(cb_transmit_t))); tb = sc->maddr + TFA_START + (TB_COUNT * (sizeof(cb_transmit_t) + sizeof(tbd_t))); printf(" TB_COUNT = %d\n", TB_COUNT); printf(" cb_head = %lx\t\tcb_tail = %lx\n", kvtop(sc->cb_head), kvtop(sc->cb_tail)); printf(" tbd_head = %lx\t\ttbd_tail = %lx\n", kvtop(sc->tbd_head), kvtop(sc->tbd_tail)); for (i = 0; i < TB_COUNT; i++, cb++, tbd++, tb += TB_SIZE) { printf(" %d:\tcb = %lx\t\ttbd = %lx\t\ttb = %lx\n", i, kvtop(cb), kvtop(tbd), kvtop(tb)); printf("\tcb->common.command = %x\n", cb->common.command); printf("\tcb->common.next = %x\tcb->tbd_offset = %x\n", cb->common.next, cb->tbd_offset); printf("\ttbd->act_count = %x", tbd->act_count); printf("\ttbd->next = %x", tbd->next); printf("\ttbd->buffer = %lx\n\n", tbd->buffer); } DEBUGEND #endif /* IXDEBUG */ /* * ZZZ need to add sanity check to see if last tb runs into * the stuff after it in memory, this should not be possible * but if someone screws up with the defines it can happen */ DEBUGBEGIN(DEBUGINIT_TFA) DEBUGDO(printf (" next tb would be at %lx\n", kvtop(tb));) DEBUGDO(printf("ix%d: ixinit_tfa exit\n", unit);) DEBUGEND } void ixintr(int unit) { ix_softc_t *sc = &ix_softc[unit]; struct ifnet *ifp = &sc->arpcom.ac_if; scb_t *scb = (scb_t *)(sc->maddr + SCB_ADDR); int check_queue; /* flag to tell us to check the queue */ u_short status; DEBUGBEGIN(DEBUGINTR) DEBUGDO(printf("ixintr: ");) DEBUGEND if ((sc->flags & IXF_INITED) == 0) { printf ("\n ixintr without being inited!!\n"); /* ZZZ */ ixinterrupt_disable(unit); goto ixintr_exit; } if (ifp->if_flags & IFF_RUNNING == 0) { printf("\n ixintr when device not running!!\n"); /* ZZZ */ ixinterrupt_disable(unit); goto ixintr_exit; } /* The sequence, disable ints, status=ack must be done * as quick as possible to avoid missing things */ ixinterrupt_disable(unit); status = ixacknowledge(unit); check_queue = 0; while ((status & SCB_STAT_MASK) != 0) { if (status & SCB_STAT_FR) { ixintr_fr(unit); } if (status & SCB_STAT_CX) { ixintr_cx(unit); check_queue++; } if (status & SCB_STAT_CNA) { DEBUGBEGIN(DEBUGINTR) DEBUGDO(printf("cna:");) DEBUGEND } if ((status & SCB_STAT_RNR) || ((status & SCB_RUS_MASK) == SCB_RUS_NRSC)) { DEBUGBEGIN(DEBUGINTR) printf("RNR:"); /* ZZZ this means trouble */ DEBUGEND IXCOUNTER(ifp->if_rnr++;) ixinit_rfa(unit); scb->status = SCB_STAT_NULL; scb->command = SCB_RUC_START; scb->rfa_offset = RFA_START; ixchannel_attention(unit); } if (scb->status & SCB_STAT_MASK) { status = ixacknowledge(unit); } else { status = 0; } } ixinterrupt_enable(unit); if (check_queue && ifp->if_snd.ifq_head != 0) { ixstart(ifp); /* we have stuff on the queue */ } ixintr_exit: DEBUGBEGIN(DEBUGINTR) DEBUGDO(printf(" ixintr exited\n");) DEBUGEND } static inline void ixintr_cx(int unit) { ix_softc_t *sc = &ix_softc[unit]; struct ifnet *ifp = &sc->arpcom.ac_if; cb_t *cb; DEBUGBEGIN(DEBUGINTR_CX) DEBUGDO(printf("cx:");) DEBUGEND cb = sc->cb_head; do { if (cb->status & CB_BUSY) { IXCOUNTER(ifp->if_busy++;) printf("ix.cx.busy"); /* This should never occur */ } if (cb->status & CB_COMPLETE) { IXCOUNTER(ifp->if_complete++;) switch(cb->command & CB_CMD_MASK) { case CB_CMD_NOP: { break; } case CB_CMD_IAS: { break; } case CB_CMD_CONF: { break; } case CB_CMD_MCAS: { break; } case CB_CMD_TRANSMIT: { if (cb->status & CB_OK) { ifp->if_opackets++; IXCOUNTER(ifp->if_ok++;) } else { if (cb->status & CB_ABORT) { IXCOUNTER(ifp->if_abort++;) printf("ix.cx.abort"); } if (cb->status & CB_LATECOLL) { IXCOUNTER(ifp->if_latecoll++;) printf("ix.cx.latecoll"); } if (cb->status & CB_NOCS) { IXCOUNTER(ifp->if_nocs++;) printf("ix.cx.nocs"); } if (cb->status & CB_NOCTS) { IXCOUNTER(ifp->if_nocts++;) printf("ix.cx.nocts"); } if (cb->status & CB_DMAUNDER) { IXCOUNTER(ifp->if_dmaunder++;) printf("ix.cx.dmaunder"); } if (cb->status & CB_DEFER) { IXCOUNTER(ifp->if_defer++;) printf("ix.cx.defer"); } if (cb->status & CB_HEARTBEAT) { IXCOUNTER(ifp->if_heartbeat++;) printf("ix.cx.heartbeat"); } if (cb->status & CB_EXCESSCOLL) { IXCOUNTER(ifp->if_excesscoll++;) printf("ix.cx.excesscoll"); } ifp->if_oerrors++; } ifp->if_collisions += cb->status & CB_COLLISIONS; ifp->if_timer = 0; /* clear watchdog timeout */ break; } case CB_CMD_TDR: { break; } case CB_CMD_DUMP: { break; } case CB_CMD_DIAGNOSE: { break; } default: { break; } } ixintr_cx_free(unit, cb); } else { } if (cb->next == INTEL586NULL) { break; } else { cb = (cb_t *)BOARDTOKV(cb->next); } } while (1); /* * clear the IFF_OACTIVE flag because the CU should now be * idle, this only holds true as long as the last CB is the * only one with the CB_CMD_INT bit set. If the start routine * violates this rule this code well have to change. */ ifp->if_flags &= ~IFF_OACTIVE; } static inline void ixintr_cx_free(int unit, cb_t *cb) { DEBUGBEGIN(DEBUGINTR_CX) DEBUGDO(ix_softc_t *sc = &ix_softc[unit];) DEBUGDO(printf("cb=%x:cb->status=%x:", KVTOBOARD(cb), cb->status);) DEBUGEND /*1*/ cb->command = CB_CMD_EL | CB_CMD_NOP; /*2*/ cb->status = 0; } static inline void ixintr_fr(int unit) { ix_softc_t *sc = &ix_softc[unit]; DEBUGBEGIN(DEBUGINTR_FR) DEBUGDO(printf("fr:");) DEBUGEND /* find each frame in the rfa and copy it up, then free it */ while ((sc->rfd_head->status & (RFD_COMPLETE | RFD_BUSY)) == RFD_COMPLETE) { ixintr_fr_copy(unit, sc->rfd_head); ixintr_fr_free(unit, sc->rfd_head); } } static inline void ixintr_fr_copy(int unit, rfd_t *rfd) { ix_softc_t *sc = &ix_softc[unit]; struct ifnet *ifp = &sc->arpcom.ac_if; rbd_t *rbd; caddr_t rb; struct mbuf *m0, *m; struct ether_header *eh; int length, bytesleft; rbd = (rbd_t *)(sc->maddr + rfd->rbd_offset); rb = (caddr_t)(sc->maddr + rbd->buffer); DEBUGBEGIN(DEBUGINTR_FR) DEBUGDO(int i;) DEBUGDO(printf("rfd=%x:", KVTOBOARD(rfd));) DEBUGDO(printf("rfd->status=%x:", rfd->status);) DEBUGDO(printf("rbd->act_count=%x:", rbd->act_count);) DEBUGDO(printf("data=");) DEBUGDO(for (i = 0; i < 16; i ++) printf ("%02x", rb[i] & 0xFF);) DEBUGDO(printf(":");) DEBUGEND /* trickery here, eh points right at memory on * the board. eh is only used by ether_input, * it is not passed to the upper layer */ eh = (struct ether_header *)rb; /* here we go, lets build an mbuf chain up to hold all this */ MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == 0) { printf ("MGETHDR:"); /* ZZZ need to add drop counters */ return; } m0 = m; length = rbd->act_count & RBD_STAT_SIZE; bytesleft = length - sizeof(struct ether_header); rb += sizeof(struct ether_header); m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = bytesleft; m->m_len = MHLEN; while (bytesleft > 0) { if (bytesleft > MINCLSIZE) { MCLGET(m, M_DONTWAIT); if (m->m_flags & M_EXT) { m->m_len = MCLBYTES; } else { printf ("MCLGET:"); m_freem(m0); /* ZZZ need to add drop counters */ return; } } m->m_len = min(m->m_len, bytesleft); bcopy(rb, mtod(m, caddr_t), m->m_len); rb += m->m_len; bytesleft -= m->m_len; if (bytesleft > 0) { MGET(m->m_next, M_DONTWAIT, MT_DATA); if (m->m_next == 0) { printf ("MGET"); m_freem(m0); /* ZZZ need to add drop counters */ return; } m = m->m_next; m->m_len = MLEN; } } ether_input(ifp, eh, m0); ifp->if_ipackets++; return; } static inline void ixintr_fr_free(int unit, rfd_t *rfd) { ix_softc_t *sc = &ix_softc[unit]; rbd_t *rbd; rbd = (rbd_t *)(sc->maddr + rfd->rbd_offset); /* XXX this still needs work, does not handle chained rbd's */ /*1*/ rbd->act_count = 0; /*2*/ rbd->size = RBD_SIZE_EL | RB_SIZE; /*3*/ sc->rbd_tail->size = RB_SIZE; /*4*/ sc->rbd_tail = rbd; /* Free the rfd buy putting it back on the rfd queue */ /*1*/ rfd->command = RFD_CMD_EL | RFD_CMD_SUSP; /*2*/ rfd->status = 0; /*3*/ rfd->rbd_offset = INTEL586NULL; /*4*/ sc->rfd_head = (rfd_t *)BOARDTOKV(rfd->next); /*5*/ sc->rfd_tail->command &= ~(RFD_CMD_EL | RFD_CMD_SUSP); /*6*/ sc->rfd_tail = rfd; } /* Psuedo code: * Do consistency check: * IFF_UP should be set. * IFF_RUNNING should be set. * IFF_OACTIVE should be clear. * ifp->snd.ifq_head should point to an MBUF * I82586 CU should be in the idle state. * All cb's should have CUC = NOP. * The real work: * while there are packets to send & free cb's do: * build a cb, tbd, and tb * copy the MBUF chain to a tb * setup the scb for a start CU * start the CU * set IFF_OACTIVE * set ifp->if_timer for watchdog timeout * Exit: */ void ixstart(struct ifnet *ifp) { int unit = ifp->if_unit; ix_softc_t *sc = &ix_softc[unit]; scb_t *scb = (scb_t *)BOARDTOKV(SCB_ADDR); cb_t *cb = sc->cb_head; tbd_t *tbd; caddr_t tb; struct mbuf *m, *m_temp; u_short length; IXCOUNTER(int queued;) DEBUGBEGIN(DEBUGSTART) DEBUGDO(printf("ixstart:");) DEBUGEND /* check that if is up and running */ if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING)) { goto ixstart_exit; } /* check to see that we are not already active */ if ((ifp->if_flags & IFF_OACTIVE) == IFF_OACTIVE) { goto ixstart_exit; } /* check that there are packets to send */ if (ifp->if_snd.ifq_head == 0) { goto ixstart_exit; } /* check that the command unit is idle */ if ((scb->status & SCB_CUS_MASK) != SCB_CUS_IDLE) { goto ixstart_exit; } /* check that all cb's on the list are free */ #ifdef THISDONTDONOTHING cb = sc->cb_head; IXCOUNTER(ifp->if_could_queue = 0;) do { /* XXX this does nothing right now! */ DEBUGBEGIN(DEBUGSTART) DEBUGDO(printf("chk_cb=%x:", KVTOBOARD(cb));) DEBUGEND IXCOUNTER(ifp->if_could_queue++;) if (cb->next == INTEL586NULL) { break; } else { cb = (cb_t *)BOARDTOKV(cb->next); } } while (1); #endif /* THISDONTDONOTHING */ /* build as many cb's as we can */ IXCOUNTER(queued = 0;) cb = sc->cb_head; do { cb->status = 0; cb->command = CB_CMD_TRANSMIT; tbd = (tbd_t *)BOARDTOKV(((cb_transmit_t *)cb)->tbd_offset); tb = (caddr_t)BOARDTOKV(tbd->buffer); DEBUGBEGIN(DEBUGSTART) DEBUGDO(printf("cb=%x:", KVTOBOARD(cb));) DEBUGDO(printf("tbd=%x:", KVTOBOARD(tbd));) DEBUGDO(printf("tb=%x:", KVTOBOARD(tb));) DEBUGEND IF_DEQUEUE(&ifp->if_snd, m); length = 0; for (m_temp = m; m_temp != 0; m_temp = m_temp->m_next) { bcopy(mtod(m_temp, caddr_t), tb, m_temp->m_len); tb += m_temp->m_len; length += m_temp->m_len; } m_freem(m); if (length < ETHER_MIN_LENGTH) length = ETHER_MIN_LENGTH; #ifdef DIAGNOSTIC if (length > ETHER_MAX_LENGTH) { /* XXX * This should never ever happen, if it does * we probable screwed up all sorts of board data * in the above bcopy's and should probably shut * down, but for now just issue a warning that * something is real wrong */ printf("ix%d: ixstart: Packet length=%d > MTU=%d\n", unit, length, ETHER_MAX_LENGTH); } #endif /* DIAGNOSTIC */ tbd->act_count = TBD_STAT_EOF | length; IXCOUNTER(queued++;) /* check to see if we have used the last cb */ if (cb->next == INTEL586NULL) { IXCOUNTER(ifp->if_filled_queue++;) break; } else { cb = (cb_t *)BOARDTOKV(cb->next); } } while (ifp->if_snd.ifq_head != 0); IXCOUNTER(ifp->if_high_queue = max(ifp->if_high_queue, queued);) /* set the end of list and interrupt bits in the last cb */ cb->command |= (CB_CMD_EL | CB_CMD_INT); /* build the scb */ scb->status = SCB_STAT_NULL; scb->command = SCB_CUC_START; scb->cbl_offset = KVTOBOARD(sc->cb_head);/* This should not be needed */ /* start the cu */ ixchannel_attention(unit); /* mark the interface as having output active */ ifp->if_flags |= IFF_OACTIVE; /* * set the watchdog timer so that if the board fails to interrupt * we will go clean up */ ifp->if_timer = 2; ixstart_exit: DEBUGBEGIN(DEBUGSTART) DEBUGDO(printf("ixstart exited\n");) DEBUGEND return; } int ixstop(struct ifnet *ifp) { int unit = ifp->if_unit; ix_softc_t *sc = &ix_softc[unit]; DEBUGBEGIN(DEBUGSTOP) DEBUGDO(printf("ixstop:");) DEBUGEND /* XXX Need to find out what spl we are at, and maybe add splx */ ifp->if_flags &= ~IFF_RUNNING; ixinterrupt_disable(unit); /* force the 82586 reset pin high */ outb(sc->iobase + ee_ctrl, I586_RESET); sc->kdc.kdc_state = DC_IDLE; DEBUGBEGIN(DEBUGSTOP) DEBUGDO(printf("ixstop exiting\n");) DEBUGEND return(0); } /* * I can't find any calls to if_done, it may be deprecated, but I left * it here until I find out. rwgrimes 1993/01/15 */ int ixdone(struct ifnet *ifp) { DEBUGBEGIN(DEBUGDONE) DEBUGDO(printf("ixdone:");) DEBUGEND DEBUGBEGIN(DEBUGDONE) DEBUGDO(printf("ixdone exited\n");) DEBUGEND return(0); } int ixioctl(struct ifnet *ifp, int cmd, caddr_t data) { int unit = ifp->if_unit; int status = 0; int s; DEBUGBEGIN(DEBUGIOCTL) DEBUGDO(printf("ixioctl:");) DEBUGEND s = splimp(); switch(cmd) { case SIOCSIFADDR: { struct ifaddr *ifa = (struct ifaddr *)data; if (ifp->if_flags & IFF_RUNNING) ixstop(ifp); ifp->if_flags |= IFF_UP; ixinit(unit); switch(ifa->ifa_addr->sa_family) { #ifdef INET case AF_INET: { arp_ifinit((struct arpcom *)ifp, ifa); break; } #endif /* INET */ #ifdef IPX case AF_IPX: { /*ZZZ*/printf("Address family IPX not supported by ixioctl\n"); break; } #endif /* IPX */ #ifdef NS case AF_NS: { /*ZZZ*/printf("Address family NS not supported by ixioctl\n"); break; } #endif /* NS */ default: { DEBUGBEGIN(DEBUGIOCTL) DEBUGDO(printf("Unknow Address Family in ixioctl\n");) DEBUGEND status = EINVAL; break; } } break; } case SIOCSIFFLAGS: { if (((ifp->if_flags & IFF_UP) == 0) && (ifp->if_flags & IFF_RUNNING)) { ixstop(ifp); } else if ((ifp->if_flags & IFF_UP) && ((ifp->if_flags & IFF_RUNNING) == 0)) { ixinit(unit); } break; } default: { DEBUGBEGIN(DEBUGIOCTL) DEBUGDO(printf("Unknown cmd in ixioctl\n");) DEBUGEND status = EINVAL; break; } } splx(s); DEBUGBEGIN(DEBUGIOCTL) DEBUGDO(printf("ixioctl exit\n");) DEBUGEND return(status); } void ixreset(int unit) { ix_softc_t *sc = &ix_softc[unit]; struct ifnet *ifp = &sc->arpcom.ac_if; int s; s = splimp(); DEBUGBEGIN(DEBUGRESET) DEBUGDO(printf("ixreset:");) DEBUGEND ixstop(ifp); ixinit(unit); DEBUGBEGIN(DEBUGRESET) DEBUGDO(printf("ixreset exit\n");) DEBUGEND (void) splx(s); return; } /* * The ixwatchdog routine gets called if the transmitter failed to interrupt * within ifp->if_timer XXXseconds. The interrupt service routine must reset * ifp->if_timer to 0 after an transmitter interrupt occurs to stop the * watchdog from happening. */ void ixwatchdog(struct ifnet *ifp) { log(LOG_ERR, "ix%d: device timeout\n", ifp->if_unit); ifp->if_oerrors++; ixreset(ifp->if_unit); return; } u_short ixeeprom_read(int unit, int location) { int eeprom_control, data; eeprom_control = inb(ix_softc[unit].iobase + ee_ctrl); eeprom_control &= 0xB2; /* XXX fix 0xB2 */ eeprom_control |= EECS; outb(ix_softc[unit].iobase + ee_ctrl, eeprom_control); ixeeprom_outbits(unit, eeprom_read_op, eeprom_opsize1); ixeeprom_outbits(unit, location, eeprom_addr_size); data = ixeeprom_inbits(unit); eeprom_control = inb(ix_softc[unit].iobase + ee_ctrl); eeprom_control &= ~(GA_RESET | EEDI | EECS); outb(ix_softc[unit].iobase + ee_ctrl, eeprom_control); ixeeprom_clock(unit, 1); ixeeprom_clock(unit, 0); return(data); } void ixeeprom_outbits(int unit, int data, int count) { int eeprom_control, i; eeprom_control = inb(ix_softc[unit].iobase + ee_ctrl); eeprom_control &= ~GA_RESET; for(i=count-1; i>=0; i--) { eeprom_control &= ~EEDI; if (data & (1 << i)) { eeprom_control |= EEDI; } outb(ix_softc[unit].iobase + ee_ctrl, eeprom_control); DELAY(1); /* eeprom data must be setup for 0.4 uSec */ ixeeprom_clock(unit, 1); ixeeprom_clock(unit, 0); } eeprom_control &= ~EEDI; outb(ix_softc[unit].iobase + ee_ctrl, eeprom_control); DELAY(1); /* eeprom data must be held for 0.4 uSec */ } int ixeeprom_inbits(int unit) { int eeprom_control, data, i; eeprom_control = inb(ix_softc[unit].iobase + ee_ctrl); eeprom_control &= ~GA_RESET; for(data=0, i=0; i<16; i++) { data = data << 1; ixeeprom_clock(unit, 1); eeprom_control = inb(ix_softc[unit].iobase + ee_ctrl); if (eeprom_control & EEDO) { data |= 1; } ixeeprom_clock(unit, 0); } return(data); } void ixeeprom_clock(int unit, int state) { int eeprom_control; eeprom_control = inb(ix_softc[unit].iobase + ee_ctrl); eeprom_control &= ~(GA_RESET | EESK); if (state) { eeprom_control = eeprom_control | EESK; } outb(ix_softc[unit].iobase + ee_ctrl, eeprom_control); DELAY(9); /* EESK must be stable for 8.38 uSec */ }