diff options
Diffstat (limited to 'sys/dev/ex')
-rw-r--r-- | sys/dev/ex/if_ex.c | 1078 | ||||
-rw-r--r-- | sys/dev/ex/if_ex_isa.c | 311 | ||||
-rw-r--r-- | sys/dev/ex/if_ex_pccard.c | 146 | ||||
-rw-r--r-- | sys/dev/ex/if_exreg.h | 209 | ||||
-rw-r--r-- | sys/dev/ex/if_exvar.h | 90 |
5 files changed, 1834 insertions, 0 deletions
diff --git a/sys/dev/ex/if_ex.c b/sys/dev/ex/if_ex.c new file mode 100644 index 0000000..7988d0f --- /dev/null +++ b/sys/dev/ex/if_ex.c @@ -0,0 +1,1078 @@ +/* + * Copyright (c) 1996, Javier Martín Rueda (jmrueda@diatel.upm.es) + * 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 unmodified, this list of conditions, and the following + * disclaimer. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 THE AUTHOR OR CONTRIBUTORS 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. + * + * $FreeBSD$ + * + * MAINTAINER: Matthew N. Dodd <winter@jurai.net> + * <mdodd@FreeBSD.org> + */ + +/* + * Intel EtherExpress Pro/10, Pro/10+ Ethernet driver + * + * Revision history: + * + * dd-mmm-yyyy: Multicast support ported from NetBSD's if_iy driver. + * 30-Oct-1996: first beta version. Inet and BPF supported, but no multicast. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/sockio.h> +#include <sys/mbuf.h> +#include <sys/socket.h> + +#include <sys/module.h> +#include <sys/bus.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/ethernet.h> +#include <net/bpf.h> + +#include <netinet/in.h> +#include <netinet/if_ether.h> + + +#include <isa/isavar.h> +#include <isa/pnpvar.h> + +#include <dev/ex/if_exreg.h> +#include <dev/ex/if_exvar.h> + +#ifdef EXDEBUG +# define Start_End 1 +# define Rcvd_Pkts 2 +# define Sent_Pkts 4 +# define Status 8 +static int debug_mask = 0; +static int exintr_count = 0; +# define DODEBUG(level, action) if (level & debug_mask) action +#else +# define DODEBUG(level, action) +#endif + +devclass_t ex_devclass; + +char irq2eemap[] = + { -1, -1, 0, 1, -1, 2, -1, -1, -1, 0, 3, 4, -1, -1, -1, -1 }; +u_char ee2irqmap[] = + { 9, 3, 5, 10, 11, 0, 0, 0 }; + +char plus_irq2eemap[] = + { -1, -1, -1, 0, 1, 2, -1, 3, -1, 4, 5, 6, 7, -1, -1, -1 }; +u_char plus_ee2irqmap[] = + { 3, 4, 5, 7, 9, 10, 11, 12 }; + +/* Network Interface Functions */ +static void ex_init (void *); +static void ex_start (struct ifnet *); +static int ex_ioctl (struct ifnet *, u_long, caddr_t); +static void ex_watchdog (struct ifnet *); + +/* ifmedia Functions */ +static int ex_ifmedia_upd (struct ifnet *); +static void ex_ifmedia_sts (struct ifnet *, struct ifmediareq *); + +static int ex_get_media (u_int32_t iobase); + +static void ex_reset (struct ex_softc *); +static void ex_setmulti (struct ex_softc *); + +static void ex_tx_intr (struct ex_softc *); +static void ex_rx_intr (struct ex_softc *); + +int +look_for_card (u_int32_t iobase) +{ + int count1, count2; + + /* + * Check for the i82595 signature, and check that the round robin + * counter actually advances. + */ + if (((count1 = inb(iobase + ID_REG)) & Id_Mask) != Id_Sig) + return(0); + count2 = inb(iobase + ID_REG); + count2 = inb(iobase + ID_REG); + count2 = inb(iobase + ID_REG); + + return((count2 & Counter_bits) == ((count1 + 0xc0) & Counter_bits)); +} + +void +ex_get_address (u_int32_t iobase, u_char *enaddr) +{ + u_int16_t eaddr_tmp; + + eaddr_tmp = eeprom_read(iobase, EE_Eth_Addr_Lo); + enaddr[5] = eaddr_tmp & 0xff; + enaddr[4] = eaddr_tmp >> 8; + eaddr_tmp = eeprom_read(iobase, EE_Eth_Addr_Mid); + enaddr[3] = eaddr_tmp & 0xff; + enaddr[2] = eaddr_tmp >> 8; + eaddr_tmp = eeprom_read(iobase, EE_Eth_Addr_Hi); + enaddr[1] = eaddr_tmp & 0xff; + enaddr[0] = eaddr_tmp >> 8; + + return; +} + +int +ex_card_type (u_char *enaddr) +{ + if ((enaddr[0] == 0x00) && (enaddr[1] == 0xA0) && (enaddr[2] == 0xC9)) + return (CARD_TYPE_EX_10_PLUS); + + return (CARD_TYPE_EX_10); +} + +/* + * Caller is responsible for eventually calling + * ex_release_resources() on failure. + */ +int +ex_alloc_resources (device_t dev) +{ + struct ex_softc * sc = device_get_softc(dev); + int error = 0; + + sc->ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->ioport_rid, + 0, ~0, 1, RF_ACTIVE); + if (!sc->ioport) { + device_printf(dev, "No I/O space?!\n"); + error = ENOMEM; + goto bad; + } + + sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irq_rid, + 0, ~0, 1, RF_ACTIVE); + + if (!sc->irq) { + device_printf(dev, "No IRQ?!\n"); + error = ENOMEM; + goto bad; + } + +bad: + return (error); +} + +void +ex_release_resources (device_t dev) +{ + struct ex_softc * sc = device_get_softc(dev); + + if (sc->ih) { + bus_teardown_intr(dev, sc->irq, sc->ih); + sc->ih = NULL; + } + + if (sc->ioport) { + bus_release_resource(dev, SYS_RES_IOPORT, + sc->ioport_rid, sc->ioport); + sc->ioport = NULL; + } + + if (sc->irq) { + bus_release_resource(dev, SYS_RES_IRQ, + sc->irq_rid, sc->irq); + sc->irq = NULL; + } + + return; +} + +int +ex_attach(device_t dev) +{ + struct ex_softc * sc = device_get_softc(dev); + struct ifnet * ifp = &sc->arpcom.ac_if; + struct ifmedia * ifm; + int unit = device_get_unit(dev); + u_int16_t temp; + + /* work out which set of irq <-> internal tables to use */ + if (ex_card_type(sc->arpcom.ac_enaddr) == CARD_TYPE_EX_10_PLUS) { + sc->irq2ee = plus_irq2eemap; + sc->ee2irq = plus_ee2irqmap; + } else { + sc->irq2ee = irq2eemap; + sc->ee2irq = ee2irqmap; + } + + sc->mem_size = CARD_RAM_SIZE; /* XXX This should be read from the card itself. */ + + /* + * Initialize the ifnet structure. + */ + ifp->if_softc = sc; + ifp->if_unit = unit; + ifp->if_name = "ex"; + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; + ifp->if_output = ether_output; + ifp->if_start = ex_start; + ifp->if_ioctl = ex_ioctl; + ifp->if_watchdog = ex_watchdog; + ifp->if_init = ex_init; + ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; + + ifmedia_init(&sc->ifmedia, 0, ex_ifmedia_upd, ex_ifmedia_sts); + + temp = eeprom_read(sc->iobase, EE_W5); + if (temp & EE_W5_PORT_TPE) + ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); + if (temp & EE_W5_PORT_BNC) + ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_2, 0, NULL); + if (temp & EE_W5_PORT_AUI) + ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_5, 0, NULL); + + ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL); + ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_NONE, 0, NULL); + ifmedia_set(&sc->ifmedia, ex_get_media(sc->iobase)); + + ifm = &sc->ifmedia; + ifm->ifm_media = ifm->ifm_cur->ifm_media; + ex_ifmedia_upd(ifp); + + /* + * Attach the interface. + */ + ether_ifattach(ifp, sc->arpcom.ac_enaddr); + + device_printf(sc->dev, "Ethernet address %6D\n", + sc->arpcom.ac_enaddr, ":"); + + return(0); +} + +int +ex_detach (device_t dev) +{ + struct ex_softc *sc; + struct ifnet *ifp; + + sc = device_get_softc(dev); + ifp = &sc->arpcom.ac_if; + + ex_stop(sc); + + ifp->if_flags &= ~IFF_RUNNING; + ether_ifdetach(ifp); + + ex_release_resources(dev); + + return (0); +} + +static void +ex_init(void *xsc) +{ + struct ex_softc * sc = (struct ex_softc *) xsc; + struct ifnet * ifp = &sc->arpcom.ac_if; + int s; + int i; + register int iobase = sc->iobase; + unsigned short temp_reg; + + DODEBUG(Start_End, printf("ex_init%d: start\n", ifp->if_unit);); + + if (TAILQ_FIRST(&ifp->if_addrhead) == NULL) { + return; + } + s = splimp(); + ifp->if_timer = 0; + + /* + * Load the ethernet address into the card. + */ + outb(iobase + CMD_REG, Bank2_Sel); + temp_reg = inb(iobase + EEPROM_REG); + if (temp_reg & Trnoff_Enable) { + outb(iobase + EEPROM_REG, temp_reg & ~Trnoff_Enable); + } + for (i = 0; i < ETHER_ADDR_LEN; i++) { + outb(iobase + I_ADDR_REG0 + i, sc->arpcom.ac_enaddr[i]); + } + /* + * - Setup transmit chaining and discard bad received frames. + * - Match broadcast. + * - Clear test mode. + * - Set receiving mode. + * - Set IRQ number. + */ + outb(iobase + REG1, inb(iobase + REG1) | Tx_Chn_Int_Md | Tx_Chn_ErStp | Disc_Bad_Fr); + outb(iobase + REG2, inb(iobase + REG2) | No_SA_Ins | RX_CRC_InMem); + outb(iobase + REG3, inb(iobase + REG3) & 0x3f /* XXX constants. */ ); + outb(iobase + CMD_REG, Bank1_Sel); + outb(iobase + INT_NO_REG, (inb(iobase + INT_NO_REG) & 0xf8) | sc->irq2ee[sc->irq_no]); + + /* + * Divide the available memory in the card into rcv and xmt buffers. + * By default, I use the first 3/4 of the memory for the rcv buffer, + * and the remaining 1/4 of the memory for the xmt buffer. + */ + sc->rx_mem_size = sc->mem_size * 3 / 4; + sc->tx_mem_size = sc->mem_size - sc->rx_mem_size; + sc->rx_lower_limit = 0x0000; + sc->rx_upper_limit = sc->rx_mem_size - 2; + sc->tx_lower_limit = sc->rx_mem_size; + sc->tx_upper_limit = sc->mem_size - 2; + outb(iobase + RCV_LOWER_LIMIT_REG, sc->rx_lower_limit >> 8); + outb(iobase + RCV_UPPER_LIMIT_REG, sc->rx_upper_limit >> 8); + outb(iobase + XMT_LOWER_LIMIT_REG, sc->tx_lower_limit >> 8); + outb(iobase + XMT_UPPER_LIMIT_REG, sc->tx_upper_limit >> 8); + + /* + * Enable receive and transmit interrupts, and clear any pending int. + */ + outb(iobase + REG1, inb(iobase + REG1) | TriST_INT); + outb(iobase + CMD_REG, Bank0_Sel); + outb(iobase + MASK_REG, All_Int & ~(Rx_Int | Tx_Int)); + outb(iobase + STATUS_REG, All_Int); + + /* + * Initialize receive and transmit ring buffers. + */ + outw(iobase + RCV_BAR, sc->rx_lower_limit); + sc->rx_head = sc->rx_lower_limit; + outw(iobase + RCV_STOP_REG, sc->rx_upper_limit | 0xfe); + outw(iobase + XMT_BAR, sc->tx_lower_limit); + sc->tx_head = sc->tx_tail = sc->tx_lower_limit; + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + DODEBUG(Status, printf("OIDLE init\n");); + + ex_setmulti(sc); + + /* + * Final reset of the board, and enable operation. + */ + outb(iobase + CMD_REG, Sel_Reset_CMD); + DELAY(2); + outb(iobase + CMD_REG, Rcv_Enable_CMD); + + ex_start(ifp); + splx(s); + + DODEBUG(Start_End, printf("ex_init%d: finish\n", ifp->if_unit);); +} + + +static void +ex_start(struct ifnet *ifp) +{ + struct ex_softc * sc = ifp->if_softc; + int iobase = sc->iobase; + int i, s, len, data_len, avail, dest, next; + unsigned char tmp16[2]; + struct mbuf * opkt; + struct mbuf * m; + + DODEBUG(Start_End, printf("ex_start%d: start\n", unit);); + + s = splimp(); + + /* + * Main loop: send outgoing packets to network card until there are no + * more packets left, or the card cannot accept any more yet. + */ + while (((opkt = ifp->if_snd.ifq_head) != NULL) && + !(ifp->if_flags & IFF_OACTIVE)) { + + /* + * Ensure there is enough free transmit buffer space for + * this packet, including its header. Note: the header + * cannot wrap around the end of the transmit buffer and + * must be kept together, so we allow space for twice the + * length of the header, just in case. + */ + + for (len = 0, m = opkt; m != NULL; m = m->m_next) { + len += m->m_len; + } + + data_len = len; + + DODEBUG(Sent_Pkts, printf("1. Sending packet with %d data bytes. ", data_len);); + + if (len & 1) { + len += XMT_HEADER_LEN + 1; + } else { + len += XMT_HEADER_LEN; + } + + if ((i = sc->tx_tail - sc->tx_head) >= 0) { + avail = sc->tx_mem_size - i; + } else { + avail = -i; + } + + DODEBUG(Sent_Pkts, printf("i=%d, avail=%d\n", i, avail);); + + if (avail >= len + XMT_HEADER_LEN) { + IF_DEQUEUE(&ifp->if_snd, opkt); + +#ifdef EX_PSA_INTR + /* + * Disable rx and tx interrupts, to avoid corruption + * of the host address register by interrupt service + * routines. + * XXX Is this necessary with splimp() enabled? + */ + outb(iobase + MASK_REG, All_Int); +#endif + + /* + * Compute the start and end addresses of this + * frame in the tx buffer. + */ + dest = sc->tx_tail; + next = dest + len; + + if (next > sc->tx_upper_limit) { + if ((sc->tx_upper_limit + 2 - sc->tx_tail) <= + XMT_HEADER_LEN) { + dest = sc->tx_lower_limit; + next = dest + len; + } else { + next = sc->tx_lower_limit + + next - sc->tx_upper_limit - 2; + } + } + + /* + * Build the packet frame in the card's ring buffer. + */ + DODEBUG(Sent_Pkts, printf("2. dest=%d, next=%d. ", dest, next);); + + outw(iobase + HOST_ADDR_REG, dest); + outw(iobase + IO_PORT_REG, Transmit_CMD); + outw(iobase + IO_PORT_REG, 0); + outw(iobase + IO_PORT_REG, next); + outw(iobase + IO_PORT_REG, data_len); + + /* + * Output the packet data to the card. Ensure all + * transfers are 16-bit wide, even if individual + * mbufs have odd length. + */ + + for (m = opkt, i = 0; m != NULL; m = m->m_next) { + DODEBUG(Sent_Pkts, printf("[%d]", m->m_len);); + if (i) { + tmp16[1] = *(mtod(m, caddr_t)); + outsw(iobase + IO_PORT_REG, tmp16, 1); + } + outsw(iobase + IO_PORT_REG, + mtod(m, caddr_t) + i, (m->m_len - i) / 2); + + if ((i = (m->m_len - i) & 1) != 0) { + tmp16[0] = *(mtod(m, caddr_t) + + m->m_len - 1); + } + } + if (i) { + outsw(iobase + IO_PORT_REG, tmp16, 1); + } + + /* + * If there were other frames chained, update the + * chain in the last one. + */ + if (sc->tx_head != sc->tx_tail) { + if (sc->tx_tail != dest) { + outw(iobase + HOST_ADDR_REG, + sc->tx_last + XMT_Chain_Point); + outw(iobase + IO_PORT_REG, dest); + } + outw(iobase + HOST_ADDR_REG, + sc->tx_last + XMT_Byte_Count); + i = inw(iobase + IO_PORT_REG); + outw(iobase + HOST_ADDR_REG, + sc->tx_last + XMT_Byte_Count); + outw(iobase + IO_PORT_REG, i | Ch_bit); + } + + /* + * Resume normal operation of the card: + * - Make a dummy read to flush the DRAM write + * pipeline. + * - Enable receive and transmit interrupts. + * - Send Transmit or Resume_XMT command, as + * appropriate. + */ + inw(iobase + IO_PORT_REG); +#ifdef EX_PSA_INTR + outb(iobase + MASK_REG, All_Int & ~(Rx_Int | Tx_Int)); +#endif + if (sc->tx_head == sc->tx_tail) { + outw(iobase + XMT_BAR, dest); + outb(iobase + CMD_REG, Transmit_CMD); + sc->tx_head = dest; + DODEBUG(Sent_Pkts, printf("Transmit\n");); + } else { + outb(iobase + CMD_REG, Resume_XMT_List_CMD); + DODEBUG(Sent_Pkts, printf("Resume\n");); + } + + sc->tx_last = dest; + sc->tx_tail = next; + + BPF_MTAP(ifp, opkt); + + ifp->if_timer = 2; + ifp->if_opackets++; + m_freem(opkt); + } else { + ifp->if_flags |= IFF_OACTIVE; + DODEBUG(Status, printf("OACTIVE start\n");); + } + } + + splx(s); + + DODEBUG(Start_End, printf("ex_start%d: finish\n", unit);); +} + +void +ex_stop(struct ex_softc *sc) +{ + int iobase = sc->iobase; + + DODEBUG(Start_End, printf("ex_stop%d: start\n", unit);); + + /* + * Disable card operation: + * - Disable the interrupt line. + * - Flush transmission and disable reception. + * - Mask and clear all interrupts. + * - Reset the 82595. + */ + outb(iobase + CMD_REG, Bank1_Sel); + outb(iobase + REG1, inb(iobase + REG1) & ~TriST_INT); + outb(iobase + CMD_REG, Bank0_Sel); + outb(iobase + CMD_REG, Rcv_Stop); + sc->tx_head = sc->tx_tail = sc->tx_lower_limit; + 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. */ + outb(iobase + MASK_REG, All_Int); + outb(iobase + STATUS_REG, All_Int); + outb(iobase + CMD_REG, Reset_CMD); + DELAY(200); + + DODEBUG(Start_End, printf("ex_stop%d: finish\n", unit);); + + return; +} + +void +ex_intr(void *arg) +{ + struct ex_softc * sc = (struct ex_softc *)arg; + struct ifnet * ifp = &sc->arpcom.ac_if; + int iobase = sc->iobase; + int int_status, send_pkts; + + DODEBUG(Start_End, printf("ex_intr%d: start\n", unit);); + +#ifdef EXDEBUG + if (++exintr_count != 1) + printf("WARNING: nested interrupt (%d). Mail the author.\n", exintr_count); +#endif + + send_pkts = 0; + while ((int_status = inb(iobase + STATUS_REG)) & (Tx_Int | Rx_Int)) { + if (int_status & Rx_Int) { + outb(iobase + STATUS_REG, Rx_Int); + + ex_rx_intr(sc); + } else if (int_status & Tx_Int) { + outb(iobase + STATUS_REG, Tx_Int); + + ex_tx_intr(sc); + send_pkts = 1; + } + } + + /* + * If any packet has been transmitted, and there are queued packets to + * be sent, attempt to send more packets to the network card. + */ + + if (send_pkts && (ifp->if_snd.ifq_head != NULL)) { + ex_start(ifp); + } + +#ifdef EXDEBUG + exintr_count--; +#endif + + DODEBUG(Start_End, printf("ex_intr%d: finish\n", unit);); + + return; +} + +static void +ex_tx_intr(struct ex_softc *sc) +{ + struct ifnet * ifp = &sc->arpcom.ac_if; + int iobase = sc->iobase; + int tx_status; + + DODEBUG(Start_End, printf("ex_tx_intr%d: start\n", unit);); + + /* + * - Cancel the watchdog. + * For all packets transmitted since last transmit interrupt: + * - Advance chain pointer to next queued packet. + * - Update statistics. + */ + + ifp->if_timer = 0; + + while (sc->tx_head != sc->tx_tail) { + outw(iobase + HOST_ADDR_REG, sc->tx_head); + + if (! inw(iobase + IO_PORT_REG) & Done_bit) + break; + + tx_status = inw(iobase + IO_PORT_REG); + sc->tx_head = inw(iobase + IO_PORT_REG); + + if (tx_status & TX_OK_bit) { + ifp->if_opackets++; + } else { + ifp->if_oerrors++; + } + + ifp->if_collisions += tx_status & No_Collisions_bits; + } + + /* + * The card should be ready to accept more packets now. + */ + + ifp->if_flags &= ~IFF_OACTIVE; + + DODEBUG(Status, printf("OIDLE tx_intr\n");); + DODEBUG(Start_End, printf("ex_tx_intr%d: finish\n", unit);); + + return; +} + +static void +ex_rx_intr(struct ex_softc *sc) +{ + struct ifnet * ifp = &sc->arpcom.ac_if; + int iobase = sc->iobase; + int rx_status; + int pkt_len; + int QQQ; + struct mbuf * m; + struct mbuf * ipkt; + struct ether_header * eh; + + DODEBUG(Start_End, printf("ex_rx_intr%d: start\n", unit);); + + /* + * For all packets received since last receive interrupt: + * - If packet ok, read it into a new mbuf and queue it to interface, + * updating statistics. + * - If packet bad, just discard it, and update statistics. + * Finally, advance receive stop limit in card's memory to new location. + */ + + outw(iobase + HOST_ADDR_REG, sc->rx_head); + + while (inw(iobase + IO_PORT_REG) == RCV_Done) { + + rx_status = inw(iobase + IO_PORT_REG); + sc->rx_head = inw(iobase + IO_PORT_REG); + QQQ = pkt_len = inw(iobase + IO_PORT_REG); + + if (rx_status & RCV_OK_bit) { + MGETHDR(m, M_DONTWAIT, MT_DATA); + ipkt = m; + if (ipkt == NULL) { + ifp->if_iqdrops++; + } else { + ipkt->m_pkthdr.rcvif = ifp; + ipkt->m_pkthdr.len = pkt_len; + ipkt->m_len = MHLEN; + + while (pkt_len > 0) { + if (pkt_len > MINCLSIZE) { + MCLGET(m, M_DONTWAIT); + if (m->m_flags & M_EXT) { + m->m_len = MCLBYTES; + } else { + m_freem(ipkt); + ifp->if_iqdrops++; + goto rx_another; + } + } + m->m_len = min(m->m_len, pkt_len); + + /* + * NOTE: I'm assuming that all mbufs allocated are of even length, + * except for the last one in an odd-length packet. + */ + + insw(iobase + IO_PORT_REG, + mtod(m, caddr_t), m->m_len / 2); + + if (m->m_len & 1) { + *(mtod(m, caddr_t) + m->m_len - 1) = inb(iobase + IO_PORT_REG); + } + pkt_len -= m->m_len; + + if (pkt_len > 0) { + MGET(m->m_next, M_DONTWAIT, MT_DATA); + if (m->m_next == NULL) { + m_freem(ipkt); + ifp->if_iqdrops++; + goto rx_another; + } + m = m->m_next; + m->m_len = MLEN; + } + } + eh = mtod(ipkt, struct ether_header *); +#ifdef EXDEBUG + if (debug_mask & Rcvd_Pkts) { + if ((eh->ether_dhost[5] != 0xff) || (eh->ether_dhost[0] != 0xff)) { + printf("Receive packet with %d data bytes: %6D -> ", QQQ, eh->ether_shost, ":"); + printf("%6D\n", eh->ether_dhost, ":"); + } /* QQQ */ + } +#endif + (*ifp->if_input)(ifp, ipkt); + ifp->if_ipackets++; + } + } else { + ifp->if_ierrors++; + } + outw(iobase + HOST_ADDR_REG, sc->rx_head); +rx_another: ; + } + + if (sc->rx_head < sc->rx_lower_limit + 2) + outw(iobase + RCV_STOP_REG, sc->rx_upper_limit); + else + outw(iobase + RCV_STOP_REG, sc->rx_head - 2); + + DODEBUG(Start_End, printf("ex_rx_intr%d: finish\n", unit);); + + return; +} + + +static int +ex_ioctl(register struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct ex_softc * sc = ifp->if_softc; + struct ifreq * ifr = (struct ifreq *)data; + int s; + int error = 0; + + DODEBUG(Start_End, printf("ex_ioctl%d: start ", ifp->if_unit);); + + s = splimp(); + + switch(cmd) { + case SIOCSIFADDR: + case SIOCGIFADDR: + case SIOCSIFMTU: + error = ether_ioctl(ifp, cmd, data); + break; + + case SIOCSIFFLAGS: + DODEBUG(Start_End, printf("SIOCSIFFLAGS");); + if ((ifp->if_flags & IFF_UP) == 0 && + (ifp->if_flags & IFF_RUNNING)) { + + ifp->if_flags &= ~IFF_RUNNING; + ex_stop(sc); + } else { + ex_init(sc); + } + break; +#ifdef NODEF + case SIOCGHWADDR: + DODEBUG(Start_End, printf("SIOCGHWADDR");); + bcopy((caddr_t)sc->sc_addr, (caddr_t)&ifr->ifr_data, + sizeof(sc->sc_addr)); + break; +#endif + case SIOCADDMULTI: + case SIOCDELMULTI: + ex_init(sc); + error = 0; + break; + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, cmd); + break; + default: + DODEBUG(Start_End, printf("unknown");); + error = EINVAL; + } + + splx(s); + + DODEBUG(Start_End, printf("\nex_ioctl%d: finish\n", ifp->if_unit);); + + return(error); +} + +static void +ex_setmulti(struct ex_softc *sc) +{ + struct ifnet *ifp; + struct ifmultiaddr *maddr; + u_int16_t *addr; + int iobase = sc->iobase; + int count; + int timeout, status; + + ifp = &sc->arpcom.ac_if; + + count = 0; + TAILQ_FOREACH(maddr, &ifp->if_multiaddrs, ifma_link) { + if (maddr->ifma_addr->sa_family != AF_LINK) + continue; + count++; + } + + if ((ifp->if_flags & IFF_PROMISC) || (ifp->if_flags & IFF_ALLMULTI) + || count > 63) { + /* Interface is in promiscuous mode or there are too many + * multicast addresses for the card to handle */ + outb(iobase + CMD_REG, Bank2_Sel); + outb(iobase + REG2, inb(iobase + REG2) | Promisc_Mode); + outb(iobase + REG3, inb(iobase + REG3)); + outb(iobase + CMD_REG, Bank0_Sel); + } + else if ((ifp->if_flags & IFF_MULTICAST) && (count > 0)) { + /* Program multicast addresses plus our MAC address + * into the filter */ + outb(iobase + CMD_REG, Bank2_Sel); + outb(iobase + REG2, inb(iobase + REG2) | Multi_IA); + outb(iobase + REG3, inb(iobase + REG3)); + outb(iobase + CMD_REG, Bank0_Sel); + + /* Borrow space from TX buffer; this should be safe + * as this is only called from ex_init */ + + outw(iobase + HOST_ADDR_REG, sc->tx_lower_limit); + outw(iobase + IO_PORT_REG, MC_Setup_CMD); + outw(iobase + IO_PORT_REG, 0); + outw(iobase + IO_PORT_REG, 0); + outw(iobase + IO_PORT_REG, (count + 1) * 6); + + TAILQ_FOREACH(maddr, &ifp->if_multiaddrs, ifma_link) { + if (maddr->ifma_addr->sa_family != AF_LINK) + continue; + + addr = (u_int16_t*)LLADDR((struct sockaddr_dl *) + maddr->ifma_addr); + outw(iobase + IO_PORT_REG, *addr++); + outw(iobase + IO_PORT_REG, *addr++); + outw(iobase + IO_PORT_REG, *addr++); + } + + /* Program our MAC address as well */ + /* XXX: Is this necessary? The Linux driver does this + * but the NetBSD driver does not */ + addr = (u_int16_t*)(&sc->arpcom.ac_enaddr); + outw(iobase + IO_PORT_REG, *addr++); + outw(iobase + IO_PORT_REG, *addr++); + outw(iobase + IO_PORT_REG, *addr++); + + inw(iobase + IO_PORT_REG); + outw(iobase + XMT_BAR, sc->tx_lower_limit); + outb(iobase + CMD_REG, MC_Setup_CMD); + + sc->tx_head = sc->tx_lower_limit; + sc->tx_tail = sc->tx_head + XMT_HEADER_LEN + (count + 1) * 6; + + for (timeout=0; timeout<100; timeout++) { + DELAY(2); + if ((inb(iobase + STATUS_REG) & Exec_Int) == 0) + continue; + + status = inb(iobase + CMD_REG); + outb(iobase + STATUS_REG, Exec_Int); + break; + } + + sc->tx_head = sc->tx_tail; + } + else + { + /* No multicast or promiscuous mode */ + outb(iobase + CMD_REG, Bank2_Sel); + outb(iobase + REG2, inb(iobase + REG2) & 0xDE); + /* ~(Multi_IA | Promisc_Mode) */ + outb(iobase + REG3, inb(iobase + REG3)); + outb(iobase + CMD_REG, Bank0_Sel); + } +} + +static void +ex_reset(struct ex_softc *sc) +{ + int s; + + DODEBUG(Start_End, printf("ex_reset%d: start\n", unit);); + + s = splimp(); + + ex_stop(sc); + ex_init(sc); + + splx(s); + + DODEBUG(Start_End, printf("ex_reset%d: finish\n", unit);); + + return; +} + +static void +ex_watchdog(struct ifnet *ifp) +{ + struct ex_softc * sc = ifp->if_softc; + + DODEBUG(Start_End, printf("ex_watchdog%d: start\n", ifp->if_unit);); + + ifp->if_flags &= ~IFF_OACTIVE; + + DODEBUG(Status, printf("OIDLE watchdog\n");); + + ifp->if_oerrors++; + ex_reset(sc); + ex_start(ifp); + + DODEBUG(Start_End, printf("ex_watchdog%d: finish\n", ifp->if_unit);); + + return; +} + +static int +ex_get_media (u_int32_t iobase) +{ + int current; + int media; + + media = eeprom_read(iobase, EE_W5); + + outb(iobase + CMD_REG, Bank2_Sel); + current = inb(iobase + REG3); + outb(iobase + CMD_REG, Bank0_Sel); + + if ((current & TPE_bit) && (media & EE_W5_PORT_TPE)) + return(IFM_ETHER|IFM_10_T); + if ((current & BNC_bit) && (media & EE_W5_PORT_BNC)) + return(IFM_ETHER|IFM_10_2); + + if (media & EE_W5_PORT_AUI) + return (IFM_ETHER|IFM_10_5); + + return (IFM_ETHER|IFM_AUTO); +} + +static int +ex_ifmedia_upd (ifp) + struct ifnet * ifp; +{ + struct ex_softc * sc = ifp->if_softc; + + if (IFM_TYPE(sc->ifmedia.ifm_media) != IFM_ETHER) + return EINVAL; + + return (0); +} + +static void +ex_ifmedia_sts(ifp, ifmr) + struct ifnet * ifp; + struct ifmediareq * ifmr; +{ + struct ex_softc * sc = ifp->if_softc; + + ifmr->ifm_active = ex_get_media(sc->iobase); + ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE; + + return; +} + +u_short +eeprom_read(u_int32_t iobase, int location) +{ + int i; + u_short data = 0; + int ee_addr; + int read_cmd = location | EE_READ_CMD; + short ctrl_val = EECS; + + ee_addr = iobase + EEPROM_REG; + outb(iobase + CMD_REG, Bank2_Sel); + outb(ee_addr, EECS); + for (i = 8; i >= 0; i--) { + short outval = (read_cmd & (1 << i)) ? ctrl_val | EEDI : ctrl_val; + outb(ee_addr, outval); + outb(ee_addr, outval | EESK); + DELAY(3); + outb(ee_addr, outval); + DELAY(2); + } + outb(ee_addr, ctrl_val); + + for (i = 16; i > 0; i--) { + outb(ee_addr, ctrl_val | EESK); + DELAY(3); + data = (data << 1) | ((inb(ee_addr) & EEDO) ? 1 : 0); + outb(ee_addr, ctrl_val); + DELAY(2); + } + + ctrl_val &= ~EECS; + outb(ee_addr, ctrl_val | EESK); + DELAY(3); + outb(ee_addr, ctrl_val); + DELAY(2); + outb(iobase + CMD_REG, Bank0_Sel); + return(data); +} diff --git a/sys/dev/ex/if_ex_isa.c b/sys/dev/ex/if_ex_isa.c new file mode 100644 index 0000000..b5d9681 --- /dev/null +++ b/sys/dev/ex/if_ex_isa.c @@ -0,0 +1,311 @@ +/*- + * Copyright (c) 2000 Matthew N. Dodd + * 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. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 THE AUTHOR OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/socket.h> + +#include <sys/module.h> +#include <sys/bus.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/if_media.h> + + +#include <isa/isavar.h> +#include <isa/pnpvar.h> + +#include <dev/ex/if_exreg.h> +#include <dev/ex/if_exvar.h> + +/* Bus Front End Functions */ +static void ex_isa_identify (driver_t *, device_t); +static int ex_isa_probe (device_t); +static int ex_isa_attach (device_t); + +#if 0 +static void ex_pnp_wakeup (void *); + +SYSINIT(ex_pnpwakeup, SI_SUB_CPU, SI_ORDER_ANY, ex_pnp_wakeup, NULL); +#endif + +static device_method_t ex_isa_methods[] = { + /* Device interface */ + DEVMETHOD(device_identify, ex_isa_identify), + DEVMETHOD(device_probe, ex_isa_probe), + DEVMETHOD(device_attach, ex_isa_attach), + DEVMETHOD(device_detach, ex_detach), + + { 0, 0 } +}; + +static driver_t ex_isa_driver = { + "ex", + ex_isa_methods, + sizeof(struct ex_softc), +}; + +DRIVER_MODULE(ex, isa, ex_isa_driver, ex_devclass, 0, 0); + +static struct isa_pnp_id ex_ids[] = { + { 0x3110d425, NULL }, /* INT1031 */ + { 0x3010d425, NULL }, /* INT1030 */ + { 0, NULL }, +}; + +#if 0 +#define EX_PNP_WAKE 0x279 + +static u_int8_t ex_pnp_wake_seq[] = + { 0x6A, 0xB5, 0xDA, 0xED, 0xF6, 0xFB, 0x7D, 0xBE, + 0xDF, 0x6F, 0x37, 0x1B, 0x0D, 0x86, 0xC3, 0x61, + 0xB0, 0x58, 0x2C, 0x16, 0x8B, 0x45, 0xA2, 0xD1, + 0xE8, 0x74, 0x3A, 0x9D, 0xCE, 0xE7, 0x73, 0x43 }; + +static void +ex_pnp_wakeup (void * dummy) +{ + int tmp; + + if (bootverbose) + printf("ex_pnp_wakeup()\n"); + + outb(EX_PNP_WAKE, 0); + outb(EX_PNP_WAKE, 0); + for (tmp = 0; tmp < 32; tmp++) { + outb(EX_PNP_WAKE, ex_pnp_wake_seq[tmp]); + } +} +#endif + +/* + * Non-destructive identify. + */ +static void +ex_isa_identify (driver_t *driver, device_t parent) +{ + device_t child; + u_int32_t ioport; + u_char enaddr[6]; + u_int irq; + int tmp; + const char * desc; + + if (bootverbose) + printf("ex_isa_identify()\n"); + + for (ioport = 0x200; ioport < 0x3a0; ioport += 0x10) { + + /* No board found at address */ + if (!look_for_card(ioport)) { + continue; + } + + if (bootverbose) + printf("ex: Found card at 0x%03x!\n", ioport); + + /* Board in PnP mode */ + if (eeprom_read(ioport, EE_W0) & EE_W0_PNP) { + /* Reset the card. */ + outb(ioport + CMD_REG, Reset_CMD); + DELAY(500); + if (bootverbose) + printf("ex: card at 0x%03x in PnP mode!\n", ioport); + continue; + } + + bzero(enaddr, sizeof(enaddr)); + + /* Reset the card. */ + outb(ioport + CMD_REG, Reset_CMD); + DELAY(400); + + ex_get_address(ioport, enaddr); + tmp = eeprom_read(ioport, EE_W1) & EE_W1_INT_SEL; + + /* work out which set of irq <-> internal tables to use */ + if (ex_card_type(enaddr) == CARD_TYPE_EX_10_PLUS) { + irq = plus_ee2irqmap[tmp]; + desc = "Intel Pro/10+"; + } else { + irq = ee2irqmap[tmp]; + desc = "Intel Pro/10"; + } + + child = BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, "ex", -1); + device_set_desc_copy(child, desc); + device_set_driver(child, driver); + bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1); + bus_set_resource(child, SYS_RES_IOPORT, 0, ioport, EX_IOSIZE); + + if (bootverbose) + printf("ex: Adding board at 0x%03x, irq %d\n", ioport, irq); + } + + return; +} + +static int +ex_isa_probe(device_t dev) +{ + u_int iobase; + u_int irq; + char * irq2ee; + u_char * ee2irq; + u_char enaddr[6]; + int tmp; + int error; + + /* Check isapnp ids */ + error = ISA_PNP_PROBE(device_get_parent(dev), dev, ex_ids); + + /* If the card had a PnP ID that didn't match any we know about */ + if (error == ENXIO) { + return(error); + } + + /* If we had some other problem. */ + if (!(error == 0 || error == ENOENT)) { + return(error); + } + + iobase = bus_get_resource_start(dev, SYS_RES_IOPORT, 0); + if (!iobase) { + printf("ex: no iobase?\n"); + return(ENXIO); + } + + if (!look_for_card(iobase)) { + printf("ex: no card found at 0x%03x\n", iobase); + return(ENXIO); + } + + if (bootverbose) + printf("ex: ex_isa_probe() found card at 0x%03x\n", iobase); + + /* + * Reset the card. + */ + outb(iobase + CMD_REG, Reset_CMD); + DELAY(800); + + ex_get_address(iobase, enaddr); + + /* work out which set of irq <-> internal tables to use */ + if (ex_card_type(enaddr) == CARD_TYPE_EX_10_PLUS) { + irq2ee = plus_irq2eemap; + ee2irq = plus_ee2irqmap; + } else { + irq2ee = irq2eemap; + ee2irq = ee2irqmap; + } + + tmp = eeprom_read(iobase, EE_W1) & EE_W1_INT_SEL; + irq = bus_get_resource_start(dev, SYS_RES_IRQ, 0); + + if (irq > 0) { + /* This will happen if board is in PnP mode. */ + if (ee2irq[tmp] != irq) { + printf("ex: WARNING: board's EEPROM is configured" + " for IRQ %d, using %d\n", + ee2irq[tmp], irq); + } + } else { + irq = ee2irq[tmp]; + bus_set_resource(dev, SYS_RES_IRQ, 0, irq, 1); + } + + if (irq == 0) { + printf("ex: invalid IRQ.\n"); + return(ENXIO); + } + + return(0); +} + +static int +ex_isa_attach(device_t dev) +{ + struct ex_softc * sc = device_get_softc(dev); + int error = 0; + u_int16_t temp; + + sc->dev = dev; + sc->ioport_rid = 0; + sc->irq_rid = 0; + + if ((error = ex_alloc_resources(dev)) != 0) { + device_printf(dev, "ex_alloc_resources() failed!\n"); + goto bad; + } + + /* + * Fill in several fields of the softc structure: + * - I/O base address. + * - Hardware Ethernet address. + * - IRQ number (if not supplied in config file, read it from EEPROM). + * - Connector type. + */ + sc->iobase = rman_get_start(sc->ioport); + sc->irq_no = rman_get_start(sc->irq); + + ex_get_address(sc->iobase, sc->arpcom.ac_enaddr); + + temp = eeprom_read(sc->iobase, EE_W0); + device_printf(sc->dev, "%s config, %s bus, ", + (temp & EE_W0_PNP) ? "PnP" : "Manual", + (temp & EE_W0_BUS16) ? "16-bit" : "8-bit"); + + temp = eeprom_read(sc->iobase, EE_W6); + printf("board id 0x%03x, stepping 0x%01x\n", + (temp & EE_W6_BOARD_MASK) >> EE_W6_BOARD_SHIFT, + temp & EE_W6_STEP_MASK); + + if ((error = ex_attach(dev)) != 0) { + device_printf(dev, "ex_attach() failed!\n"); + goto bad; + } + + error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET, + ex_intr, (void *)sc, &sc->ih); + if (error) { + device_printf(dev, "bus_setup_intr() failed!\n"); + goto bad; + } + + return(0); +bad: + ex_release_resources(dev); + return (error); +} diff --git a/sys/dev/ex/if_ex_pccard.c b/sys/dev/ex/if_ex_pccard.c new file mode 100644 index 0000000..ca876e6 --- /dev/null +++ b/sys/dev/ex/if_ex_pccard.c @@ -0,0 +1,146 @@ +/*- + * Copyright (c) 2000 Mitsuru IWASAKI + * 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. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 THE AUTHOR OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/socket.h> + +#include <sys/module.h> +#include <sys/bus.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/if_media.h> + + +#include <dev/ex/if_exreg.h> +#include <dev/ex/if_exvar.h> + +#include <dev/pccard/pccardvar.h> + +/* Bus Front End Functions */ +static int ex_pccard_probe (device_t); +static int ex_pccard_attach (device_t); + +static device_method_t ex_pccard_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ex_pccard_probe), + DEVMETHOD(device_attach, ex_pccard_attach), + DEVMETHOD(device_detach, ex_detach), + + { 0, 0 } +}; + +static driver_t ex_pccard_driver = { + "ex", + ex_pccard_methods, + sizeof(struct ex_softc), +}; + +DRIVER_MODULE(ex, pccard, ex_pccard_driver, ex_devclass, 0, 0); + +static int +ex_pccard_probe(device_t dev) +{ + u_int iobase; + u_int irq; + + iobase = bus_get_resource_start(dev, SYS_RES_IOPORT, 0); + if (!iobase) { + printf("ex: no iobase?\n"); + return(ENXIO); + } + + if (bootverbose) + printf("ex: ex_pccard_probe() found card at 0x%03x\n", iobase); + + irq = bus_get_resource_start(dev, SYS_RES_IRQ, 0); + + if (irq == 0) { + printf("ex: invalid IRQ.\n"); + return(ENXIO); + } + + return(0); +} + +static int +ex_pccard_attach(device_t dev) +{ + struct ex_softc * sc = device_get_softc(dev); + int error = 0; + int i; + u_char sum; + u_char ether_addr[ETHER_ADDR_LEN]; + + sc->dev = dev; + sc->ioport_rid = 0; + sc->irq_rid = 0; + + if ((error = ex_alloc_resources(dev)) != 0) { + device_printf(dev, "ex_alloc_resources() failed!\n"); + goto bad; + } + + /* + * Fill in several fields of the softc structure: + * - I/O base address. + * - Hardware Ethernet address. + * - IRQ number. + */ + sc->iobase = rman_get_start(sc->ioport); + sc->irq_no = rman_get_start(sc->irq); + + pccard_get_ether(dev, ether_addr); + for (i = 0, sum = 0; i < ETHER_ADDR_LEN; i++) + sum |= ether_addr[i]; + if (sum) + bcopy(ether_addr, sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); + + if ((error = ex_attach(dev)) != 0) { + device_printf(dev, "ex_attach() failed!\n"); + goto bad; + } + + error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET, + ex_intr, (void *)sc, &sc->ih); + if (error) { + device_printf(dev, "bus_setup_intr() failed!\n"); + goto bad; + } + + return(0); +bad: + ex_release_resources(dev); + return (error); +} diff --git a/sys/dev/ex/if_exreg.h b/sys/dev/ex/if_exreg.h new file mode 100644 index 0000000..a924c28 --- /dev/null +++ b/sys/dev/ex/if_exreg.h @@ -0,0 +1,209 @@ +/* + * Copyright (c) 1996, Javier Martín Rueda (jmrueda@diatel.upm.es) + * 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 unmodified, this list of conditions, and the following + * disclaimer. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 THE AUTHOR OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +/* + * Intel EtherExpress Pro/10 Ethernet driver + */ + +/* + * Several constants. + */ + +#define CARD_TYPE_EX_10 1 +#define CARD_TYPE_EX_10_PLUS 2 + +/* Length of an ethernet address. */ +#define ETHER_ADDR_LEN 6 +/* Default RAM size in board. */ +#define CARD_RAM_SIZE 0x8000 +/* Number of I/O ports used. */ +#define EX_IOSIZE 16 + +/* + * Intel EtherExpress Pro (i82595 based) registers + */ + +/* Common registers to all banks. */ + +#define CMD_REG 0 +#define REG1 1 +#define REG2 2 +#define REG3 3 +#define REG4 4 +#define REG5 5 +#define REG6 6 +#define REG7 7 +#define REG8 8 +#define REG9 9 +#define REG10 10 +#define REG11 11 +#define REG12 12 +#define REG13 13 +#define REG14 14 +#define REG15 15 + +/* Definitions for command register (CMD_REG). */ + +#define Switch_Bank_CMD 0 +#define MC_Setup_CMD 3 +#define Transmit_CMD 4 +#define Diagnose_CMD 7 +#define Rcv_Enable_CMD 8 +#define Rcv_Stop 11 +#define Reset_CMD 14 +#define Resume_XMT_List_CMD 28 +#define Sel_Reset_CMD 30 +#define Abort 0x20 +#define Bank0_Sel 0x00 +#define Bank1_Sel 0x40 +#define Bank2_Sel 0x80 + +/* Bank 0 specific registers. */ + +#define STATUS_REG 1 +#define ID_REG 2 +#define Id_Mask 0x2c +#define Id_Sig 0x24 +#define Counter_bits 0xc0 +#define MASK_REG 3 +#define Exec_Int 0x08 +#define Tx_Int 0x04 +#define Rx_Int 0x02 +#define Rx_Stp_Int 0x01 +#define All_Int 0x0f +#define RCV_BAR 4 +#define RCV_BAR_Lo 4 +#define RCV_BAR_Hi 5 +#define RCV_STOP_REG 6 +#define XMT_BAR 10 +#define HOST_ADDR_REG 12 /* 16-bit register */ +#define IO_PORT_REG 14 /* 16-bit register */ + +/* Bank 1 specific registers. */ + +#define TriST_INT 0x80 +#define INT_NO_REG 2 +#define RCV_LOWER_LIMIT_REG 8 +#define RCV_UPPER_LIMIT_REG 9 +#define XMT_LOWER_LIMIT_REG 10 +#define XMT_UPPER_LIMIT_REG 11 + +/* Bank 2 specific registers. */ + +#define Disc_Bad_Fr 0x80 +#define Tx_Chn_ErStp 0x40 +#define Tx_Chn_Int_Md 0x20 +#define Multi_IA 0x20 +#define No_SA_Ins 0x10 +#define RX_CRC_InMem 0x04 +#define Promisc_Mode 0x01 +#define BNC_bit 0x20 +#define TPE_bit 0x04 +#define I_ADDR_REG0 4 +#define EEPROM_REG 10 +#define Trnoff_Enable 0x10 + +/* EEPROM memory positions (16-bit wide). */ + +#define EE_W0 0x00 +# define EE_W0_PNP 0x0001 +# define EE_W0_BUS16 0x0004 +# define EE_W0_FLASH_ADDR_MASK 0x0038 +# define EE_W0_FLASH_ADDR_SHIFT 3 +# define EE_W0_AUTO_IO 0x0040 +# define EE_W0_FLASH 0x0100 +# define EE_W0_AUTO_NEG 0x0200 +# define EE_W0_IO_MASK 0xFC00 +# define EE_W0_IO_SHIFT 10 + +#define EE_IRQ_No 1 +#define IRQ_No_Mask 0x07 + +#define EE_W1 0x01 +# define EE_W1_INT_SEL 0x0007 +# define EE_W1_NO_LINK_INT 0x0008 /* Link Integrity Off */ +# define EE_W1_NO_POLARITY 0x0010 /* Polarity Correction Off */ +# define EE_W1_TPE_AUI 0x0020 /* 1 = TPE, 0 = AUI */ +# define EE_W1_NO_JABBER_PREV 0x0040 /* Jabber prevention Off */ +# define EE_W1_NO_AUTO_SELECT 0x0080 /* Auto Port Selection Off */ +# define EE_W1_SMOUT 0x0100 /* SMout Pin Control 0= Input */ +# define EE_W1_PROM 0x0200 /* Flash = 0, PROM = 1 */ +# define EE_W1_ALT_READY 0x2000 /* Alternate Ready, 0=normal */ +# define EE_W1_FULL_DUPLEX 0x8000 + +#define EE_W2 0x02 +#define EE_W3 0x03 +#define EE_W4 0x04 + +#define EE_Eth_Addr_Lo 2 +#define EE_Eth_Addr_Mid 3 +#define EE_Eth_Addr_Hi 4 + +#define EE_W5 0x05 +# define EE_W5_BNC_TPE 0x0001 /* 0 = TPE, 1 = BNC */ +# define EE_W5_BOOT_IPX 0x0002 +# define EE_W5_BOOT_ODI 0x0004 +# define EE_W5_BOOT_NDIS (EE_W5_BOOT_IPX|EE_W5_BOOT_ODI) +# define EE_W5_NUM_CONN 0x0008 /* 0 = 2, 1 = 3 */ +# define EE_W5_NOFLASH 0x0010 /* No flash socket present */ +# define EE_W5_PORT_TPE 0x0020 /* TPE present */ +# define EE_W5_PORT_BNC 0x0040 /* BNC present */ +# define EE_W5_PORT_AUI 0x0080 /* AUI present */ +# define EE_W5_PWR_MGT 0x0100 /* Power Management */ +# define EE_W5_CP 0x0200 /* COncurrent Processing */ + +#define EE_W6 0x05 +# define EE_W6_STEP_MASK 0x000F +# define EE_W6_BOARD_MASK 0xFFF0 +# define EE_W6_BOARD_SHIFT 4 + +/* EEPROM serial interface. */ + +#define EESK 0x01 +#define EECS 0x02 +#define EEDI 0x04 +#define EEDO 0x08 +#define EE_READ_CMD (6 << 6) + +/* Frame chain constants. */ + +/* Transmit header length (in board's ring buffer). */ +#define XMT_HEADER_LEN 8 +#define XMT_Chain_Point 4 +#define XMT_Byte_Count 6 +#define Done_bit 0x0080 +#define Ch_bit 0x8000 + +/* Transmit result bits. */ +#define No_Collisions_bits 0x000f +#define TX_OK_bit 0x2000 + +/* Receive result bits. */ +#define RCV_Done 8 +#define RCV_OK_bit 0x2000 diff --git a/sys/dev/ex/if_exvar.h b/sys/dev/ex/if_exvar.h new file mode 100644 index 0000000..2f94c41 --- /dev/null +++ b/sys/dev/ex/if_exvar.h @@ -0,0 +1,90 @@ +/*- + * Copyright (c) 1996, Javier Mart^mn Rueda (jmrueda@diatel.upm.es) + * All rights reserved. + * + * Copyright (c) 2000 Matthew N. Dodd + * 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. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 THE AUTHOR OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +struct ex_softc { + struct arpcom arpcom; /* Ethernet common data */ + struct ifmedia ifmedia; + + device_t dev; + struct resource *ioport; + int ioport_rid; + struct resource *irq; + int irq_rid; + void * ih; + + u_int iobase; /* I/O base address. */ + u_short irq_no; /* IRQ number. */ + + char * irq2ee; /* irq <-> internal */ + u_char * ee2irq; /* representation conversion */ + + u_int mem_size; /* Total memory size, in bytes. */ + u_int rx_mem_size; /* Rx memory size (by default, */ + /* first 3/4 of total memory). */ + + u_int rx_lower_limit; /* Lower and upper limits of */ + u_int rx_upper_limit; /* receive buffer. */ + + u_int rx_head; /* Head of receive ring buffer. */ + u_int tx_mem_size; /* Tx memory size (by default, */ + /* last quarter of total memory).*/ + + u_int tx_lower_limit; /* Lower and upper limits of */ + u_int tx_upper_limit; /* transmit buffer. */ + + u_int tx_head; /* Head and tail of */ + u_int tx_tail; /* transmit ring buffer. */ + + u_int tx_last; /* Pointer to beginning of last */ + /* frame in the chain. */ +}; + +extern devclass_t ex_devclass; + +extern char irq2eemap[]; +extern u_char ee2irqmap[]; +extern char plus_irq2eemap[]; +extern u_char plus_ee2irqmap[]; + +int ex_alloc_resources (device_t); +void ex_release_resources (device_t); +int ex_attach (device_t); +int ex_detach (device_t); + +driver_intr_t ex_intr; + +u_int16_t eeprom_read (u_int32_t, int); + +int look_for_card (u_int32_t); +void ex_get_address (u_int32_t, u_char *); +int ex_card_type (u_char *); + +void ex_stop (struct ex_softc *); |