diff options
Diffstat (limited to 'sys/i386/isa/if_rdp.c')
-rw-r--r-- | sys/i386/isa/if_rdp.c | 1456 |
1 files changed, 0 insertions, 1456 deletions
diff --git a/sys/i386/isa/if_rdp.c b/sys/i386/isa/if_rdp.c deleted file mode 100644 index 0d29b0aba602..000000000000 --- a/sys/i386/isa/if_rdp.c +++ /dev/null @@ -1,1456 +0,0 @@ -/* - * Copyright 1998, Joerg Wunsch - * 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. - * - * $Id: if_rdp.c,v 1.2 1998/12/21 18:11:10 joerg Exp $ - */ - -/* - * Device driver for RealTek RTL 8002 (`REDP') based pocket-ethernet - * adapters, hooked up to a printer port. `rdp' is a shorthand for - * REDP since some tools like netstat work best if the interface name - * has no more than three letters. - * - * Driver configuration flags so far: - * flags 0x1 -- assume 74S288 EEPROM (default 94C46) - * flags 0x2 -- use `slow' mode (mode 3 of the packet driver, default 0) - * - * Maybe this driver will some day also work with the successor, RTL - * 8012 (`AREDP'), which is unfortunately not fully register- - * compatible with the 8002. The 8012 offers support for faster - * transfer modi like bidirectional SPP and EPP, 64 K x 4 buffer - * memory as opposed to 16 K x 4 for the 8002, a multicast filter, and - * a builtin multiplexer that allows chaining a printer behind the - * ethernet adapter. - * - * About the only documentation i've been able to find about the RTL - * 8002 was the packet driver source code at ftp.realtek.com.tw, so - * this driver is somewhat based on the way the packet driver handles - * the chip. The exact author of the packet driver is unknown, the - * only name that i could find in the source was someone called Chiu, - * supposedly an employee of RealTek. So credits to them for that - * piece of code which has proven valuable to me. - * - * Later on, Leo kuo <leo@realtek.com.tw> has been very helpful to me - * by sending me a readable (PDF) file documenting the RTL 8012, which - * helped me to also understand the 8002, as well as by providing me - * with the source code of the 8012 packet driver that i haven't been - * able to find on the FTP site. A big Thanks! goes here to RealTek - * for this kind of service. - */ - -#include "rdp.h" -#include "bpfilter.h" - -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/conf.h> -#include <sys/sockio.h> -#include <sys/malloc.h> -#include <sys/mbuf.h> -#include <sys/socket.h> -#include <sys/syslog.h> - -#include <net/ethernet.h> -#include <net/if.h> -#include <net/if_arp.h> -#include <net/if_dl.h> -#include <net/if_mib.h> - -#ifdef INET -#include <netinet/in.h> -#include <netinet/if_ether.h> -#endif - -#ifdef NS -#include <netns/ns.h> -#include <netns/ns_if.h> -#endif - -#if NBPFILTER > 0 -#include <net/bpf.h> -#endif - -#include <machine/clock.h> -#include <machine/md_var.h> - -#include <i386/isa/isa_device.h> -#include <i386/isa/icu.h> -#include <i386/isa/if_rdpreg.h> -#include <i386/isa/intr_machdep.h> - -#define IOCTL_CMD_T u_long - -/* - * Debug levels (ORed together): - * != 0 - general (bad packets etc.) - * 2 - debug EEPROM IO - * 4 - debug interrupt status - */ -#undef DEBUG -#define DEBUG 0 - -/* - * rdp_softc: per interface info and status - */ -struct rdp_softc { - struct arpcom arpcom; /* - * Ethernet common, always goes first so - * a rdp_softc * can be cast into an - * arpcom * or into an ifnet *. - */ - - /* - * local stuff, somewhat sorted by memory alignment class - */ - u_short baseaddr; /* IO port address */ - u_short txsize; /* tx size for next (buffered) packet, - * there's only one additional packet - * we can buffer, thus a single variable - * ought to be enough */ - int txbusy; /* tx is transmitting */ - int txbuffered; /* # of packets in tx buffer */ - int slow; /* use lpt_control to send data */ - u_char irqenbit; /* mirror of current Ctrl_IRQEN */ - /* - * type of parameter EEPROM; device flags 0x1 selects 74S288 - */ - enum { - EEPROM_93C46, EEPROM_74S288 /* or 82S123 */ - } eeprom; -}; - -static struct rdp_softc rdp_softc[NRDP]; - -/* - * Since there's no fixed location in the EEPROM about where to find - * the ethernet hardware address, we drop a table of valid OUIs here, - * and search through the EEPROM until we find a possible valid - * Ethernet address. Only the first 16 bits of all possible OUIs are - * recorded in the table (as obtained from - * http://standards.ieee.org/regauth/oui/oui.txt). - */ - -static u_short allowed_ouis[] = { - 0x0000, 0x0001, 0x0002, 0x0004, 0x0005, 0x0006, 0x0007, - 0x0008, 0x0010, 0x001C, 0x0020, 0x0040, 0x0050, 0x0060, - 0x0070, 0x0080, 0x0090, 0x009D, 0x00A0, 0x00AA, 0x00BB, - 0x00C0, 0x00CF, 0x00DD, 0x00E0, 0x00E6, 0x0207, 0x021C, - 0x0260, 0x0270, 0x029D, 0x02AA, 0x02BB, 0x02C0, 0x02CF, - 0x02E6, 0x040A, 0x04E0, 0x0800, 0x08BB, 0x1000, 0x1100, - 0x8000, 0xAA00 -}; - -/* - * ISA bus support. - */ -static int rdp_probe __P((struct isa_device *)); -static int rdp_attach __P((struct isa_device *)); - -/* - * Required entry points. - */ -static void rdp_init(void *); -static int rdp_ioctl(struct ifnet *, IOCTL_CMD_T, caddr_t); -static void rdp_start(struct ifnet *); -static void rdp_reset(struct ifnet *); -static void rdp_watchdog(struct ifnet *); -static void rdpintr(int); - -/* - * REDP private functions. - */ - -static void rdp_stop(struct rdp_softc *); -static void rdp_rint(struct rdp_softc *); -static void rdp_get_packet(struct rdp_softc *, unsigned); -static u_short rdp_write_mbufs(struct rdp_softc *, struct mbuf *); -static int rdp_gethwaddr_93c46(struct rdp_softc *, u_char *); -static void rdp_gethwaddr_74s288(struct rdp_softc *, u_char *); -static void rdp_93c46_cmd(struct rdp_softc *, u_short, unsigned); -static u_short rdp_93c46_read(struct rdp_softc *); - -struct isa_driver rdpdriver = { - rdp_probe, - rdp_attach, - "rdp", - 1 /* we wanna get a chance before lptN */ -}; - -/* - * REDP-specific functions. - * - * They are inlined, thus go first in this file. Together with gcc's - * usual optimization, these functions probably come close to the - * packet driver's hand-optimized code. ;-) - * - * Comments are partially obtained from the packet driver as well. - * Some of the function names contain register names which don't make - * much sense for us, but i've kept them for easier reference in - * comparision to the packet driver. - * - * Some of the functions are currently not used by the driver; it's - * not quite clear whether we ever need them at all. They are - * supposedly even slower than what is currently implemented as `slow' - * mode. Right now, `fast' (default) mode is what the packet driver - * calls mode 0, slow mode is mode 3 (writing through lpt_control, - * reading twice). - * - * We should autoprobe the modi, as opposed to making them dependent - * on a kernel configuration flag. - */ - -/* - * read a nibble from rreg; end-of-data cmd is not issued; - * used for general register read. - * - * Unlike the packet driver's version, i'm shifting the result - * by 3 here (as opposed to within the caller's code) for clarity. - * -- Joerg - */ -static __inline u_char -RdNib(struct rdp_softc *sc, u_char rreg) -{ - - outb(sc->baseaddr + lpt_data, EOC + rreg); - outb(sc->baseaddr + lpt_data, RdAddr + rreg); /* write addr */ - (void)inb(sc->baseaddr + lpt_status); - return (inb(sc->baseaddr + lpt_status) >> 3) & 0x0f; -} - -#if 0 -/* - * read a byte from MAR register through lpt_data; the low nibble is - * read prior to the high one; end-of-read command is not issued; used - * for remote DMA in mode 4 + 5 - */ -static __inline u_char -RdByte(struct rdp_softc *sc) -{ - u_char hinib, lonib; - - outb(sc->baseaddr + lpt_data, RdAddr + MAR); /* cmd for low nibble */ - lonib = (inb(sc->baseaddr + lpt_status) >> 3) & 0x0f; - outb(sc->baseaddr + lpt_data, RdAddr + MAR + HNib); - hinib = (inb(sc->baseaddr + lpt_status) << 1) & 0xf0; - return hinib + lonib; -} - - -/* - * read a byte from MAR register through lpt_data; the low nibble is - * read prior to the high one; end-of-read command is not issued; used - * for remote DMA in mode 6 + 7 - */ -static __inline u_char -RdByte1(struct rdp_softc *sc) -{ - u_char hinib, lonib; - - outb(sc->baseaddr + lpt_data, RdAddr + MAR); /* cmd for low nibble */ - (void)inb(sc->baseaddr + lpt_status); - lonib = (inb(sc->baseaddr + lpt_status) >> 3) & 0x0f; - outb(sc->baseaddr + lpt_data, RdAddr + MAR + HNib); - (void)inb(sc->baseaddr + lpt_status); - hinib = (inb(sc->baseaddr + lpt_status) << 1) & 0xf0; - return hinib + lonib; -} -#endif - - -/* - * read a byte from MAR register through lpt_control; the low nibble is - * read prior to the high one; end-of-read command is not issued; used - * for remote DMA in mode 0 + 1 - */ -static __inline u_char -RdByteA1(struct rdp_softc *sc) -{ - u_char hinib, lonib; - - outb(sc->baseaddr + lpt_control, Ctrl_LNibRead); - lonib = (inb(sc->baseaddr + lpt_status) >> 3) & 0x0f; - outb(sc->baseaddr + lpt_control, Ctrl_HNibRead); - hinib = (inb(sc->baseaddr + lpt_status) << 1) & 0xf0; - return hinib + lonib; -} - - -/* - * read a byte from MAR register through lpt_control; the low nibble is - * read prior to the high one; end-of-read command is not issued; used - * for remote DMA in mode 2 + 3 - */ -static __inline u_char -RdByteA2(struct rdp_softc *sc) -{ - u_char hinib, lonib; - - outb(sc->baseaddr + lpt_control, Ctrl_LNibRead); - (void)inb(sc->baseaddr + lpt_status); - lonib = (inb(sc->baseaddr + lpt_status) >> 3) & 0x0f; - outb(sc->baseaddr + lpt_control, Ctrl_HNibRead); - (void)inb(sc->baseaddr + lpt_status); - hinib = (inb(sc->baseaddr + lpt_status) << 1) & 0xf0; - return hinib + lonib; -} - -/* - * End-of-read cmd - */ -static __inline void -RdEnd(struct rdp_softc *sc, u_char rreg) -{ - - outb(sc->baseaddr + lpt_data, EOC + rreg); -} - -/* - * Write a nibble to a register; end-of-write is issued. - * Used for general register write. - */ -static __inline void -WrNib(struct rdp_softc *sc, u_char wreg, u_char wdata) -{ - - /* prepare and write address */ - outb(sc->baseaddr + lpt_data, EOC + wreg); - outb(sc->baseaddr + lpt_data, WrAddr + wreg); - outb(sc->baseaddr + lpt_data, WrAddr + wreg); - /* prepare and write data */ - outb(sc->baseaddr + lpt_data, WrAddr + wdata); - outb(sc->baseaddr + lpt_data, wdata); - outb(sc->baseaddr + lpt_data, wdata); - /* end-of-write */ - outb(sc->baseaddr + lpt_data, EOC + wdata); -} - -/* - * Write a byte to a register; end-of-write is issued. - * Used for general register write. - */ -static __inline void -WrByte(struct rdp_softc *sc, u_char wreg, u_char wdata) -{ - - /* prepare and write address */ - outb(sc->baseaddr + lpt_data, EOC + wreg); - outb(sc->baseaddr + lpt_data, WrAddr + wreg); - outb(sc->baseaddr + lpt_data, WrAddr + wreg); - /* prepare and write low nibble */ - outb(sc->baseaddr + lpt_data, WrAddr + (wdata & 0x0F)); - outb(sc->baseaddr + lpt_data, (wdata & 0x0F)); - outb(sc->baseaddr + lpt_data, (wdata & 0x0F)); - /* prepare and write high nibble */ - wdata >>= 4; - outb(sc->baseaddr + lpt_data, wdata); - outb(sc->baseaddr + lpt_data, wdata + HNib); - outb(sc->baseaddr + lpt_data, wdata + HNib); - /* end-of-write */ - outb(sc->baseaddr + lpt_data, EOC + wdata + HNib); -} - -/* - * Write the byte to DRAM via lpt_data; - * used for remote DMA write in mode 0 / 2 / 4 - */ -static __inline void -WrByteALToDRAM(struct rdp_softc *sc, u_char val) -{ - - outb(sc->baseaddr + lpt_data, val & 0x0F); - outb(sc->baseaddr + lpt_data, MkHi(val)); -} - -/* - * Write the byte to DRAM via lpt_control; - * used for remote DMA write in mode 1 / 3 / 5 - */ -static __inline void -WrByteALToDRAMA(struct rdp_softc *sc, u_char val) -{ - - outb(sc->baseaddr + lpt_data, val & 0x0F); - outb(sc->baseaddr + lpt_control, Ctrl_LNibRead | sc->irqenbit); - outb(sc->baseaddr + lpt_data, val >> 4); - outb(sc->baseaddr + lpt_control, Ctrl_HNibRead | sc->irqenbit); -} - -#if 0 /* they could be used for the RAM test */ -/* - * Write the u_short to DRAM via lpt_data; - * used for remote DMA write in mode 0 / 2 / 4 - */ -static __inline void -WrWordbxToDRAM(struct rdp_softc *sc, u_short val) -{ - - outb(sc->baseaddr + lpt_data, val & 0x0F); - val >>= 4; - outb(sc->baseaddr + lpt_data, (val & 0x0F) + HNib); - val >>= 4; - outb(sc->baseaddr + lpt_data, val & 0x0F); - val >>= 4; - outb(sc->baseaddr + lpt_data, val + HNib); -} - - -/* - * Write the u_short to DRAM via lpt_control; - * used for remote DMA write in mode 1 / 3 / 5 - */ -static __inline void -WrWordbxToDRAMA(struct rdp_softc *sc, u_short val) -{ - - outb(sc->baseaddr + lpt_data, val & 0x0F); - outb(sc->baseaddr + lpt_control, Ctrl_LNibRead | sc->irqenbit); - val >>= 4; - outb(sc->baseaddr + lpt_data, (val & 0x0F) + HNib); - outb(sc->baseaddr + lpt_control, Ctrl_HNibRead | sc->irqenbit); - val >>= 4; - outb(sc->baseaddr + lpt_data, val & 0x0F); - outb(sc->baseaddr + lpt_control, Ctrl_LNibRead | sc->irqenbit); - val >>= 4; - outb(sc->baseaddr + lpt_data, val + HNib); - outb(sc->baseaddr + lpt_control, Ctrl_HNibRead | sc->irqenbit); -} -#endif - - -/* - * Determine if the device is present - * - * on entry: - * a pointer to an isa_device struct - * on exit: - * 0 if device not found - * or # of i/o addresses used (if found) - */ -static int -rdp_probe(struct isa_device *isa_dev) -{ - int unit = isa_dev->id_unit; - struct rdp_softc *sc = &rdp_softc[unit]; - u_char b1, b2; - intrmask_t irqmap[3]; - u_char sval[3]; - - if (unit < 0 || unit >= NRDP) - return 0; - - sc->baseaddr = isa_dev->id_iobase; - if (isa_dev->id_flags & 1) - sc->eeprom = EEPROM_74S288; - /* else defaults to 93C46 */ - if (isa_dev->id_flags & 2) - sc->slow = 1; - - /* let R/WB = A/DB = CSB = high to be ready for next r/w cycle */ - outb(sc->baseaddr + lpt_data, 0xFF); - /* DIR = 0 for write mode, IRQEN=0, SLCT=INIT=AUTOFEED=STB=high */ - outb(sc->baseaddr + lpt_control, Ctrl_SelData); - /* software reset */ - WrNib(sc, CMR1 + HNib, MkHi(CMR1_RST)); - DELAY(2000); - /* is EPLC alive? */ - b1 = RdNib(sc, CMR1); - RdEnd(sc, CMR1); - b2 = RdNib(sc, CMR2) & 0x0f; - b2 |= RdNib(sc, CMR2 + HNib) << 4; - RdEnd(sc, CMR2 + HNib); - /* - * After the reset, we expect CMR1 & 7 to be 1 (rx buffer empty), - * and CMR2 & 0xf7 to be 0x20 (receive mode set to physical and - * broadcasts). - */ - if (bootverbose) - printf("rdp%d: CMR1 = %#x, CMR2 = %#x\n", unit, b1, b2); - - if ((b1 & (CMR1_BUFE | CMR1_IRQ | CMR1_TRA)) != CMR1_BUFE - || (b2 & ~CMR2_IRQINV) != CMR2_AM_PB) - return 0; - - /* - * We have found something that could be a RTL 80[01]2, now - * see whether we can generate an interrupt. - */ - disable_intr(); - - /* - * Test whether our configured IRQ is working. - * - * Set to no acception mode + IRQout, then enable RxE + TxE, - * then cause RBER (by advancing the read pointer although - * the read buffer is empty) to generate an interrupt. - */ - WrByte(sc, CMR2, CMR2_IRQOUT); - WrNib(sc, CMR1 + HNib, MkHi(CMR1_TE | CMR1_RE)); - WrNib(sc, CMR1, CMR1_RDPAC); - DELAY(1000); - - irqmap[0] = isa_irq_pending(); - sval[0] = inb(sc->baseaddr + lpt_status); - - /* allow IRQs to pass the parallel interface */ - outb(sc->baseaddr + lpt_control, Ctrl_IRQEN + Ctrl_SelData); - DELAY(1000); - /* generate interrupt */ - WrNib(sc, IMR + HNib, MkHi(ISR_RBER)); - DELAY(1000); - - irqmap[1] = isa_irq_pending(); - sval[1] = inb(sc->baseaddr + lpt_status); - - /* de-assert and disable IRQ */ - WrNib(sc, IMR + HNib, MkHi(0)); - (void)inb(sc->baseaddr + lpt_status); /* might be necessary to - clear IRQ */ - DELAY(1000); - irqmap[2] = isa_irq_pending(); - sval[2] = inb(sc->baseaddr + lpt_status); - - WrNib(sc, CMR1 + HNib, MkHi(0)); - outb(sc->baseaddr + lpt_control, Ctrl_SelData); - WrNib(sc, CMR2, CMR2_IRQINV); - - enable_intr(); - - if (bootverbose) - printf("rdp%d: irq maps / lpt status " - "%#x/%#x - %#x/%#x - %#x/%#x (id_irq %#x)\n", - unit, irqmap[0], sval[0], irqmap[1], sval[1], - irqmap[2], sval[2], isa_dev->id_irq); - - if ((irqmap[1] & isa_dev->id_irq) == 0) { - printf("rdp%d: configured IRQ (%d) cannot be asserted " - "by device", - unit, ffs(isa_dev->id_irq) - 1); - if (irqmap[1]) - printf(" (probable IRQ: %d)", ffs(irqmap[1]) - 1); - printf("\n"); - return 0; - } - - /* - * XXX should do RAMtest here - */ - - switch (sc->eeprom) { - case EEPROM_93C46: - if (rdp_gethwaddr_93c46(sc, sc->arpcom.ac_enaddr) == 0) { - printf("rdp%d: failed to find a valid hardware " - "address in EEPROM\n", - unit); - return 0; - } - break; - - case EEPROM_74S288: - rdp_gethwaddr_74s288(sc, sc->arpcom.ac_enaddr); - break; - } - - return lpt_control + 1; -} - -/* - * Install interface into kernel networking data structures - */ -static int -rdp_attach(struct isa_device *isa_dev) -{ - int unit = isa_dev->id_unit; - struct rdp_softc *sc = &rdp_softc[unit]; - struct ifnet *ifp = &sc->arpcom.ac_if; - - isa_dev->id_ointr = rdpintr; - - /* - * Reset interface - */ - rdp_stop(sc); - - if (!ifp->if_name) { - /* - * Initialize ifnet structure - */ - ifp->if_softc = sc; - ifp->if_unit = unit; - ifp->if_name = "rdp"; - ifp->if_output = ether_output; - ifp->if_start = rdp_start; - ifp->if_ioctl = rdp_ioctl; - ifp->if_watchdog = rdp_watchdog; - ifp->if_init = rdp_init; - ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; - ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; - - /* - * Attach the interface - */ - if_attach(ifp); - ether_ifattach(ifp); - } - - /* - * Print additional info when attached - */ - printf("%s%d: RealTek RTL%s pocket ethernet, EEPROM %s, %s mode\n", - ifp->if_name, ifp->if_unit, - "8002", /* hook for 8012 */ - sc->eeprom == EEPROM_93C46? "93C46": "74S288", - sc->slow? "slow": "fast"); - printf("%s%d: address %6D\n", ifp->if_name, ifp->if_unit, - sc->arpcom.ac_enaddr, ":"); - - /* - * If BPF is in the kernel, call the attach for it - */ -#if NBPFILTER > 0 - bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); -#endif - return 1; -} - -/* - * Reset interface. - */ -static void -rdp_reset(struct ifnet *ifp) -{ - struct rdp_softc *sc = ifp->if_softc; - int s; - - s = splimp(); - - /* - * Stop interface and re-initialize. - */ - rdp_stop(sc); - rdp_init(sc); - - (void) splx(s); -} - -/* - * Take interface offline. - */ -static void -rdp_stop(struct rdp_softc *sc) -{ - - sc->txbusy = sc->txbusy = 0; - - /* disable printer interface interrupts */ - sc->irqenbit = 0; - outb(sc->baseaddr + lpt_control, Ctrl_SelData); - outb(sc->baseaddr + lpt_data, 0xff); - - /* reset the RTL 8002 */ - WrNib(sc, CMR1 + HNib, MkHi(CMR1_RST)); - DELAY(100); -} - -/* - * Device timeout/watchdog routine. Entered if the device neglects to - * generate an interrupt after a transmit has been started on it. - */ -static void -rdp_watchdog(struct ifnet *ifp) -{ - - log(LOG_ERR, "rdp%d: device timeout\n", ifp->if_unit); - ifp->if_oerrors++; - - rdp_reset(ifp); -} - -/* - * Initialize device. - */ -static void -rdp_init(void *xsc) -{ - struct rdp_softc *sc = xsc; - struct ifnet *ifp = &sc->arpcom.ac_if; - int i, s; - u_char reg; - - /* address not known */ - if (TAILQ_EMPTY(&ifp->if_addrhead)) - return; - - s = splimp(); - - ifp->if_timer = 0; - - /* program ethernet ID into the chip */ - for (i = 0, reg = IDR0; i < 6; i++, reg++) - WrByte(sc, reg, sc->arpcom.ac_enaddr[i]); - - /* set accept mode */ - WrNib(sc, CMR2 + HNib, - MkHi((ifp->if_flags & IFF_PROMISC)? CMR2_AM_ALL: CMR2_AM_PB)); - - /* enable tx and rx */ - WrNib(sc, CMR1 + HNib, MkHi(CMR1_TE | CMR1_RE)); - - /* allow interrupts to happen */ - WrNib(sc, CMR2, CMR2_IRQOUT | CMR2_IRQINV); - WrNib(sc, IMR, ISR_TOK | ISR_TER | ISR_ROK | ISR_RER); - WrNib(sc, IMR + HNib, MkHi(ISR_RBER)); - - /* allow IRQs to pass the parallel interface */ - sc->irqenbit = Ctrl_IRQEN; - outb(sc->baseaddr + lpt_control, sc->irqenbit + Ctrl_SelData); - - /* clear all flags */ - sc->txbusy = sc->txbuffered = 0; - - /* - * Set 'running' flag, and clear output active flag. - */ - ifp->if_flags |= IFF_RUNNING; - ifp->if_flags &= ~IFF_OACTIVE; - - /* - * ...and attempt to start output - */ - rdp_start(ifp); - - (void) splx(s); -} - -/* - * Start output on interface. - * We make two assumptions here: - * 1) that the current priority is set to splimp _before_ this code - * is called *and* is returned to the appropriate priority after - * return - * 2) that the IFF_OACTIVE flag is checked before this code is called - * (i.e. that the output part of the interface is idle) - */ -static void -rdp_start(struct ifnet *ifp) -{ - struct rdp_softc *sc = ifp->if_softc; - struct mbuf *m; - int len; - -outloop: - - /* - * See if there is room to put another packet in the buffer. - */ - if (sc->txbuffered) { - /* - * No room. Indicate this to the outside world and exit. - */ - ifp->if_flags |= IFF_OACTIVE; - return; - } - IF_DEQUEUE(&ifp->if_snd, m); - if (m == 0) { - /* - * We are using the !OACTIVE flag to indicate to the outside - * world that we can accept an additional packet rather than - * that the transmitter is _actually_ active. Indeed, the - * transmitter may be active, but if we haven't filled all the - * buffers with data then we still want to accept more. - */ - ifp->if_flags &= ~IFF_OACTIVE; - return; - } - - /* - * Copy the mbuf chain into the transmit buffer - */ - - len = rdp_write_mbufs(sc, m); - if (len == 0) - goto outloop; - - /* ensure minimal valid ethernet length */ - len = max(len, (ETHER_MIN_LEN-ETHER_CRC_LEN)); - - /* - * Actually start the transceiver. Set a timeout in case the - * Tx interrupt never arrives. - */ - if (!sc->txbusy) { - WrNib(sc, TBCR1, len >> 8); - WrByte(sc, TBCR0, len & 0xff); - WrNib(sc, CMR1, CMR1_TRA); - sc->txbusy = 1; - ifp->if_timer = 2; - } else { - sc->txbuffered = 1; - sc->txsize = len; - } - - /* - * Tap off here if there is a bpf listener. - */ -#if NBPFILTER > 0 - if (ifp->if_bpf) { - bpf_mtap(ifp, m); - } -#endif - - m_freem(m); - - /* - * Loop back to the top to possibly buffer more packets - */ - goto outloop; -} - -/* - * Process an ioctl request. - */ -static int -rdp_ioctl(struct ifnet *ifp, IOCTL_CMD_T command, caddr_t data) -{ - struct rdp_softc *sc = ifp->if_softc; - int s, error = 0; - - s = splimp(); - - switch (command) { - - case SIOCSIFADDR: - case SIOCGIFADDR: - case SIOCSIFMTU: - error = ether_ioctl(ifp, command, data); - break; - - case SIOCSIFFLAGS: - /* - * If the interface is marked up and stopped, then start it. - * If it is marked down and running, then stop it. - */ - if (ifp->if_flags & IFF_UP) { - if ((ifp->if_flags & IFF_RUNNING) == 0) - rdp_init(sc); - } else { - if (ifp->if_flags & IFF_RUNNING) { - rdp_stop(sc); - ifp->if_flags &= ~IFF_RUNNING; - } - } - -#if NBPFILTER > 0 - /* - * Promiscuous flag may have changed, propagage this - * to the NIC. - */ - if (ifp->if_flags & IFF_UP) - WrNib(sc, CMR2 + HNib, - MkHi((ifp->if_flags & IFF_PROMISC)? - CMR2_AM_ALL: CMR2_AM_PB)); - -#endif - break; - - case SIOCADDMULTI: - case SIOCDELMULTI: - /* - * Multicast list has changed; we don't support it. - */ - error = ENOTTY; - break; - - default: - error = EINVAL; - } - (void) splx(s); - return (error); -} - -/* - * External interrupt service routine. - */ -void -rdpintr(int unit) -{ - struct rdp_softc *sc = rdp_softc + unit; - struct ifnet *ifp = (struct ifnet *)sc; - u_char isr, tsr, rsr, colls; - - /* disable interrupts, so SD3 can be routed to the pin */ - sc->irqenbit = 0; - outb(sc->baseaddr + lpt_control, Ctrl_SelData); - WrNib(sc, CMR2, CMR2_IRQINV); - /* - * loop until there are no more new interrupts - */ - for (;;) { - isr = RdNib(sc, ISR); - isr |= RdNib(sc, ISR + HNib) << 4; - RdEnd(sc, ISR + HNib); - - if (isr == 0) - break; -#if DEBUG & 4 - printf("rdp%d: ISR = %#x\n", unit, isr); -#endif - - /* - * Clear the pending interrupt bits. - */ - WrNib(sc, ISR, isr & 0x0f); - if (isr & 0xf0) - WrNib(sc, ISR + HNib, MkHi(isr)); - - /* - * Handle transmitter interrupts. - */ - if (isr & (ISR_TOK | ISR_TER)) { - tsr = RdNib(sc, TSR); - RdEnd(sc, TSR); -#if DEBUG & 4 - if (isr & ISR_TER) - printf("rdp%d: tsr %#x\n", unit, tsr); -#endif - if (tsr & TSR_TABT) - ifp->if_oerrors++; - else - /* - * Update total number of successfully - * transmitted packets. - */ - ifp->if_opackets++; - - if (tsr & TSR_COL) { - colls = RdNib(sc, COLR); - RdEnd(sc, COLR); - ifp->if_collisions += colls; - } - - /* - * reset tx busy and output active flags - */ - sc->txbusy = 0; - ifp->if_flags &= ~IFF_OACTIVE; - - /* - * If we had already queued up another packet, - * start sending it now. - */ - if (sc->txbuffered) { - WrNib(sc, TBCR1, sc->txsize >> 8); - WrByte(sc, TBCR0, sc->txsize & 0xff); - WrNib(sc, CMR1, CMR1_TRA); - sc->txbusy = 1; - sc->txbuffered = 0; - ifp->if_timer = 2; - } else { - /* - * clear watchdog timer - */ - ifp->if_timer = 0; - } - - } - - /* - * Handle receiver interrupts - */ - if (isr & (ISR_ROK | ISR_RER | ISR_RBER)) { - rsr = RdNib(sc, RSR); - rsr |= RdNib(sc, RSR + HNib) << 4; - RdEnd(sc, RSR + HNib); -#if DEBUG & 4 - if (isr & (ISR_RER | ISR_RBER)) - printf("rdp%d: rsr %#x\n", unit, rsr); -#endif - - if (rsr & (RSR_PUN | RSR_POV)) { - printf("rdp%d: rsr %#x, resetting\n", - unit, rsr); - rdp_reset(ifp); - break; - } - - if (rsr & RSR_BUFO) - /* - * CRC and FA errors are recorded in - * rdp_rint() on a per-packet basis - */ - ifp->if_ierrors++; - if (isr & (ISR_ROK | ISR_RER)) - rdp_rint(sc); - } - - /* - * If it looks like the transmitter can take more data, - * attempt to start output on the interface. This is done - * after handling the receiver to give the receiver priority. - */ - if ((ifp->if_flags & IFF_OACTIVE) == 0) - rdp_start(ifp); - - } - /* re-enable interrupts */ - WrNib(sc, CMR2, CMR2_IRQOUT | CMR2_IRQINV); - sc->irqenbit = Ctrl_IRQEN; - outb(sc->baseaddr + lpt_control, Ctrl_SelData + sc->irqenbit); -} - -/* - * Ethernet interface receiver interrupt. - */ -static void -rdp_rint(struct rdp_softc *sc) -{ - struct ifnet *ifp = &sc->arpcom.ac_if; - struct rdphdr rh; - u_short len; - size_t i; - u_char *packet_ptr, b, status; - int excessive_bad_pkts = 0; - - /* - * Fetch the packets from the NIC's buffer. - */ - for (;;) { - b = RdNib(sc, CMR1); - RdEnd(sc, CMR1); - - if (b & CMR1_BUFE) - /* no more packets */ - break; - - /* first, obtain the buffer header */ - - outb(sc->baseaddr + lpt_data, MAR + EOC); /* prepare addr */ - outb(sc->baseaddr + lpt_control, Ctrl_LNibRead); - outb(sc->baseaddr + lpt_data, MAR + RdAddr + HNib); - - packet_ptr = (u_char *)&rh; - if (sc->slow) - for (i = 0; i < sizeof rh; i++, packet_ptr++) - *packet_ptr = RdByteA2(sc); - else - for (i = 0; i < sizeof rh; i++, packet_ptr++) - *packet_ptr = RdByteA1(sc); - - RdEnd(sc, MAR + HNib); - outb(sc->baseaddr + lpt_control, Ctrl_SelData); - - len = rh.pktlen - ETHER_CRC_LEN; - status = rh.status; - - if ((status & (RSR_ROK | RSR_CRC | RSR_FA)) != RSR_ROK || - len > (ETHER_MAX_LEN - ETHER_CRC_LEN) || - len < (ETHER_MIN_LEN - ETHER_CRC_LEN) || - len > MCLBYTES) { -#if DEBUG - printf("rdp%d: bad packet in buffer, " - "len %d, status %#x\n", - ifp->if_unit, (int)len, (int)status); -#endif - ifp->if_ierrors++; - /* rx jump packet */ - WrNib(sc, CMR1, CMR1_RDPAC); - if (++excessive_bad_pkts > 5) { - /* - * the chip seems to be stuck, we are - * probably seeing the same bad packet - * over and over again - */ -#if DEBUG - printf("rdp%d: resetting due to an " - "excessive number of bad packets\n", - ifp->if_unit); -#endif - rdp_reset(ifp); - return; - } - continue; - } - - /* - * Go get packet. - */ - excessive_bad_pkts = 0; - rdp_get_packet(sc, len); - ifp->if_ipackets++; - } -} - -/* - * Retreive packet from NIC memory and send to the next level up via - * ether_input(). If there is a BPF listener, give a copy to BPF, - * too. - */ -static void -rdp_get_packet(struct rdp_softc *sc, unsigned len) -{ - struct ether_header *eh; - struct mbuf *m; - u_char *packet_ptr; - size_t s; - - /* Allocate a header mbuf */ - MGETHDR(m, M_DONTWAIT, MT_DATA); - if (m == NULL) - return; - m->m_pkthdr.rcvif = &sc->arpcom.ac_if; - m->m_pkthdr.len = m->m_len = len; - - /* - * We always put the received packet in a single buffer - - * either with just an mbuf header or in a cluster attached - * to the header. The +2 is to compensate for the alignment - * fixup below. - */ - if ((len + 2) > MHLEN) { - /* Attach an mbuf cluster */ - MCLGET(m, M_DONTWAIT); - - /* Insist on getting a cluster */ - if ((m->m_flags & M_EXT) == 0) { - m_freem(m); - return; - } - } - - /* - * The +2 is to longword align the start of the real packet. - * This is important for NFS. - */ - m->m_data += 2; - eh = mtod(m, struct ether_header *); - - /* - * Get packet, including link layer address, from interface. - */ - outb(sc->baseaddr + lpt_control, Ctrl_LNibRead); - outb(sc->baseaddr + lpt_data, RdAddr + MAR); - - packet_ptr = (u_char *)eh; - if (sc->slow) - for (s = 0; s < len; s++, packet_ptr++) - *packet_ptr = RdByteA2(sc); - else - for (s = 0; s < len; s++, packet_ptr++) - *packet_ptr = RdByteA1(sc); - - RdEnd(sc, MAR + HNib); - outb(sc->baseaddr + lpt_control, Ctrl_SelData); - WrNib(sc, CMR1, CMR1_RDPAC); - -#if NBPFILTER > 0 - - /* - * Check if there's a BPF listener on this interface. If so, hand off - * the raw packet to bpf. - */ - if (sc->arpcom.ac_if.if_bpf) { - bpf_mtap(&sc->arpcom.ac_if, m); - - /* - * Note that the interface cannot be in promiscuous mode if - * there are no BPF listeners. And if we are in promiscuous - * mode, we have to check if this packet is really ours. - */ - if ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) && - bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, - sizeof(eh->ether_dhost)) != 0) { - m_freem(m); - return; - } - } -#endif - - /* - * Remove link layer address. - */ - m->m_pkthdr.len = m->m_len = len - sizeof(struct ether_header); - m->m_data += sizeof(struct ether_header); - - ether_input(&sc->arpcom.ac_if, eh, m); - return; -} - -/* - * Write an mbuf chain to the NIC's tx buffer. - */ -static u_short -rdp_write_mbufs(struct rdp_softc *sc, struct mbuf *m) -{ - u_short total_len; - struct mbuf *mp; - u_char *dp, b; - int i; - - /* First, count up the total number of bytes to copy */ - for (total_len = 0, mp = m; mp; mp = mp->m_next) - total_len += mp->m_len; - - if (total_len == 0) - return 0; - - outb(sc->baseaddr + lpt_data, MAR | EOC); - - /* - * Transfer the mbuf chain to the NIC memory. - */ - if (sc->slow) { - /* writing the first byte is complicated */ - outb(sc->baseaddr + lpt_control, - Ctrl_LNibRead | sc->irqenbit); - outb(sc->baseaddr + lpt_data, MAR | WrAddr); - b = *(u_char *)m->m_data; - outb(sc->baseaddr + lpt_data, (b & 0x0f) | 0x40); - outb(sc->baseaddr + lpt_data, b & 0x0f); - outb(sc->baseaddr + lpt_data, b >> 4); - outb(sc->baseaddr + lpt_control, - Ctrl_HNibRead | sc->irqenbit); - /* advance the mbuf pointer */ - mp = m; - m->m_len--; - m->m_data++; - /* write the remaining bytes */ - while (m) { - for (i = 0, dp = (u_char *)m->m_data; - i < m->m_len; - i++, dp++) - WrByteALToDRAMA(sc, *dp); - m = m->m_next; - } - /* - * restore old mbuf in case we have to hand it off to - * BPF again - */ - m = mp; - m->m_len++; - m->m_data--; - - /* the RTL 8002 requires an even byte-count remote DMA */ - if (total_len & 1) - WrByteALToDRAMA(sc, 0); - } else { - outb(sc->baseaddr + lpt_data, MAR | WrAddr); - while (m) { - for (i = 0, dp = (u_char *)m->m_data; - i < m->m_len; - i++, dp++) - WrByteALToDRAM(sc, *dp); - m = m->m_next; - } - - /* the RTL 8002 requires an even byte-count remote DMA */ - if (total_len & 1) - WrByteALToDRAM(sc, 0); - } - - outb(sc->baseaddr + lpt_data, 0xff); - outb(sc->baseaddr + lpt_control, - Ctrl_HNibRead | Ctrl_SelData | sc->irqenbit); - - return total_len; -} - -/* - * Read the designated ethernet hardware address out of a 93C46 - * (serial) EEPROM. - * Note that the 93C46 uses 16-bit words in big-endian notation. - */ -static int -rdp_gethwaddr_93c46(struct rdp_softc *sc, u_char *etheraddr) -{ - int i, magic; - size_t j = 0; - u_short w; - - WrNib(sc, CMR2, CMR2_PAGE | CMR2_IRQINV); /* select page 1 */ - - /* - * The original RealTek packet driver had the ethernet address - * starting at EEPROM address 0. Other vendors seem to have - * gone `creative' here -- while they didn't do anything else - * than changing a few strings in the entire driver, compared - * to the RealTek version, they also moved out the ethernet - * address to a different location in the EEPROM, so the - * original RealTek driver won't work correctly with them, and - * vice versa. Sounds pretty cool, eh? $@%&! - * - * Anyway, we walk through the EEPROM, until we find some - * allowable value based upon our table of IEEE OUI assignments. - */ - for (i = magic = 0; magic < 3 && i < 32; i++) { - /* read cmd (+ 6 bit address) */ - rdp_93c46_cmd(sc, 0x180 + i, 10); - w = rdp_93c46_read(sc); - switch (magic) { - case 0: - for (j = 0; - j < sizeof allowed_ouis / sizeof(u_short); - j++) - if (w == allowed_ouis[j]) { - etheraddr[0] = (w >> 8) & 0xff; - etheraddr[1] = w & 0xff; - magic++; - break; - } - break; - - case 1: - /* - * If the first two bytes have been 00:00, we - * discard the match iff the next two bytes - * are also 00:00, so we won't get fooled by - * an EEPROM that has been filled with zeros. - * This in theory would disallow 64 K of legal - * addresses assigned to Xerox, but it's - * almost certain that those addresses haven't - * been used for RTL80[01]2 chips anyway. - */ - if ((etheraddr[0] | etheraddr[1]) == 0 && w == 0) { - magic--; - break; - } - - etheraddr[2] = (w >> 8) & 0xff; - etheraddr[3] = w & 0xff; - magic++; - break; - - case 2: - etheraddr[4] = (w >> 8) & 0xff; - etheraddr[5] = w & 0xff; - magic++; - break; - } - } - - WrNib(sc, CMR2, CMR2_IRQINV); /* back to page 0 */ - - return magic == 3; -} - -/* - * Read the designated ethernet hardware address out of a 74S288 - * EEPROM. - * - * This is untested, since i haven't seen any adapter actually using - * a 74S288. In the RTL 8012, only the serial EEPROM (94C46) is - * supported anymore. - */ -static void -rdp_gethwaddr_74s288(struct rdp_softc *sc, u_char *etheraddr) -{ - int i; - u_char b; - - WrNib(sc, CMR2, CMR2_PAGE | CMR2_IRQINV); /* select page 1 */ - - for (i = 0; i < 6; i++) { - WrNib(sc, PCMR, i & 0x0f); /* lower 4 bit of addr */ - WrNib(sc, PCMR + HNib, HNib + 4); /* upper 2 bit addr + /CS */ - WrNib(sc, PCMR + HNib, HNib); /* latch data now */ - b = RdNib(sc, PDR) & 0x0f; - b |= (RdNib(sc, PDR + HNib) & 0x0f) << 4; - etheraddr[i] = b; - } - - RdEnd(sc, PDR + HNib); - WrNib(sc, CMR2, CMR2_IRQINV); /* reselect page 0 */ -} - -/* - * Send nbits of data (starting with MSB) out to the 93c46 as a - * command. Assumes register page 1 has already been selected. - */ -static void -rdp_93c46_cmd(struct rdp_softc *sc, u_short data, unsigned nbits) -{ - u_short mask = 1 << (nbits - 1); - unsigned i; - u_char b; - -#if DEBUG & 2 - printf("rdp_93c46_cmd(): "); -#endif - for (i = 0; i < nbits; i++, mask >>= 1) { - b = HNib + PCMR_SK + PCMR_CS; - if (data & mask) - b += PCMR_DO; -#if DEBUG & 2 - printf("%d", b & 1); -#endif - WrNib(sc, PCMR + HNib, b); - DELAY(1); - WrNib(sc, PCMR + HNib, b & ~PCMR_SK); - DELAY(1); - } -#if DEBUG & 2 - printf("\n"); -#endif -} - -/* - * Read one word of data from the 93c46. Actually, we have to read - * 17 bits, and discard the very first bit. Assumes register page 1 - * to be selected as well. - */ -static u_short -rdp_93c46_read(struct rdp_softc *sc) -{ - u_short data = 0; - u_char b; - int i; - -#if DEBUG & 2 - printf("rdp_93c46_read(): "); -#endif - for (i = 0; i < 17; i++) { - WrNib(sc, PCMR + HNib, PCMR_SK + PCMR_CS + HNib); - DELAY(1); - WrNib(sc, PCMR + HNib, PCMR_CS + HNib); - DELAY(1); - b = RdNib(sc, PDR); - data <<= 1; - if (b & 1) - data |= 1; -#if DEBUG & 2 - printf("%d", b & 1); -#endif - RdEnd(sc, PDR); - DELAY(1); - } - -#if DEBUG & 2 - printf("\n"); -#endif - /* end of cycle */ - WrNib(sc, PCMR + HNib, PCMR_SK + HNib); - DELAY(1); - - return data; -} |