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