summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjoerg <joerg@FreeBSD.org>1998-12-21 18:01:15 +0000
committerjoerg <joerg@FreeBSD.org>1998-12-21 18:01:15 +0000
commit26fd01e252723f7a23a9d6ae717cd09882ebb65c (patch)
tree44bcf91ab8fec32df644149df754ba415be28575
parentbd891ad121eef3252c5a13b189de0e717b2d7691 (diff)
parent749703c29e5c7ebcabd70e977747e4d94ffe849b (diff)
downloadFreeBSD-src-26fd01e252723f7a23a9d6ae717cd09882ebb65c.zip
FreeBSD-src-26fd01e252723f7a23a9d6ae717cd09882ebb65c.tar.gz
This commit was generated by cvs2svn to compensate for changes in r41980,
which included commits to RCS files with non-trunk default branches.
-rw-r--r--share/man/man4/man4.i386/rdp.4181
-rw-r--r--sys/i386/isa/if_rdp.c1493
-rw-r--r--sys/i386/isa/if_rdpreg.h187
3 files changed, 1861 insertions, 0 deletions
diff --git a/share/man/man4/man4.i386/rdp.4 b/share/man/man4/man4.i386/rdp.4
new file mode 100644
index 0000000..fb70f40
--- /dev/null
+++ b/share/man/man4/man4.i386/rdp.4
@@ -0,0 +1,181 @@
+.\"
+.\"
+.\" Copyright (c) 1997 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, 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 DEVELOPERS ``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 DEVELOPERS 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: rdp.4,v 1.1.1.1 1998/12/21 12:43:35 j Exp $
+.\"
+.\"
+.\" " (emacs disconfusion)
+.Dd December 21, 1998
+.Dt RDP 4 i386
+.Os
+.Sh NAME
+.Nm rdp
+.Nd Ethernet driver for RealTek RTL 8002 pocket ethernet
+.Sh SYNOPSIS
+.Cd "device rdp0 at isa? port 0x378 net irq 7"
+.Cd "device rdp0 at isa? port 0x378 net irq 7 flags 0x2"
+.Sh DESCRIPTION
+The
+.Nm
+device driver supports RealTek RTL 8002-based pocket ethernet adapters,
+connected to a standard parallel port.
+.Pp
+These adapters seem to belong to the cheaper choices among pocket
+ethernet adapters. The RTL 8002 is the central part, containing an
+interface to BNC and UTP (10 Mbit/s) media, as well as a host
+interface that is designed to talk to standard parallel printer
+adapters. For the full ethernet adapter to work, it is completed by
+an external RAM used as the Tx and Rx packet buffer (16 K x 4 for the
+RTL 8002), and an EEPROM to hold the assigned ethernet hardware
+address. For the RTL 8002, the EEPROM can be either a standard 93C46
+serial EEPROM (which seems to be a common choice), or a 74S288
+parallel one. The latter variant needs the device configuration flag
+0x1 in order to work.
+.Pp
+Since standard printer adapters seem to vary wildly among their timing
+requirements, there are currently two possible choices for the way
+data are being exchanged between the pocket ethernet adapter and the
+printer interface. The default is the fastest mode the RTL 8002
+supports. If the printer adapter to use is particularly slow (which
+can be noticed by watching the ethernet wire for crippled packets, or
+by not seeing correclty received packets), the configuration flag 0x2
+can be set in order to throttle down the
+.Nm
+driver. Note that in fast mode, the data rate is assymetric, sending
+is a little faster (up to two times) than receiving. Rates like 150
+KB/s for sending and 80 KB/s for receiving are common. For slow mode,
+both rates are about the same, and in the range of 50 KB/s through 70
+KB/s. As always, your mileage may vary.
+.Pp
+In case the adapter isn't recognized at boot-time, setting the
+.Em bootverbose
+flag
+.Pq Ql Fl v
+might help in diagnosing the reason. Since the RTL 8002 requires
+the availability of a working interrupt for the printer adapter (unlike
+the
+.Xr lpt 4
+driver), the
+.Nm
+driver fails to attach if the ethernet adapter cannot assert an
+interrupt at probe time.
+.Pp
+The RTL 8002 doesn't support (hardware) multicast.
+.Pp
+The
+.Nm
+driver internally sets a flag so it gets probed very early. This way,
+it is possible to configure both, an
+.Nm
+driver as well as an
+.Xr lpt 4
+driver into the same kernel. If no RTL 8002 hardware is present, probing
+will eventually detect the printer driver.
+.Sh DIAGNOSTICS
+.Pp
+.Dl "rdp0: configured IRQ (7) cannot be asserted by device"
+.Pp
+The probe routine was unable to get the RTL 8002 asserting an interrupt
+request through the printer adapter.
+.Pp
+.Dl "rdp0: failed to find a valid hardware address in EEPROM"
+.Pp
+Since there doesn't seem to be a standard place for storing the hardware
+ethernet address within the EEPROM, the
+.Nm
+driver walks the entire (serial) EEPROM contents until it finds something
+that looks like a valid ethernet hardware address, based on the IEEE's
+OUI assignments. This diagnostic tells the driver was unable to find
+one. Note: it might as well be the current adapter is one of the rare
+examples with a 74S288 EEPROM, so
+.Ql flags 0x1
+should be tried.
+.Pp
+.Dl "rdp0: Device timeout"
+.Pp
+After initiating a packet transmission, the ethernet adapter didn't
+return a notification of the (successful or failed) transmission. The
+hardware is likely to be wedged, and is being reset.
+.Pp
+.Sh SEE ALSO
+.Xr lpt 4 ,
+.Xr ifconfig 8
+.Sh AUTHORS
+This driver was written by
+.ie t J\(:org Wunsch,
+.el Joerg Wunsch,
+based on RealTek's packet driver for the RTL 8002, as well as on some
+description of the successor chip, RTL 8012, RealTek was gratefully
+providing.
+.Sh BUGS
+There are certainly many of them.
+.Pp
+Since the
+.Nm
+driver wants to probe its hardware at boot-time, the adapter needs
+to be present then in order to be detected.
+.Pp
+Only two out of the eight different speed modes RealTek's packet
+driver could handle are implemented. Thus there might be hardware
+where even the current slow mode is too fast.
+.Pp
+There should be a DMA transfer test in the probe routine that figures
+out the usable mode automatically.
+.Pp
+Abusing a standard printer interface for data exchange is error-prone.
+Occasional stuck hardware shouldn't surprise too much, hopefully the
+timeout routine will catch these cases. Flood-pinging is a good
+example of triggering this problem. Likewise, albeit BPF is of course
+supported, it's certainly a bad idea attempting to watch a crowded
+ethernet wire using promiscuous mode.
+.Pp
+Since the RTL 8002 has only 4 KB of Rx buffer space (2 x 2 KB are used
+as Tx buffers), the usual NFS deadlock with large packets arriving too
+quickly could happen if a machine using the
+.Nm
+driver NFS-mounts some fast server with the standard NFS blocksize of
+8 KB. (Since NFS can only retransmit entire NFS packets, the same
+packet will be retransmitted over and over again.)
+.Pp
+The heuristic to find out the ethernet hardware address from the
+EEPROM sucks, but seems to be the only sensible generic way that
+doesn't depend on the actual location in EEPROM. RealTek's sample
+driver placed it directly at address 0, other vendors picked something
+like 15, with other junk in front of it that must not be confused with
+a valid ethernet address.
+.Pp
+The driver should support the successor chip RTL 8012, which seems to
+be available and used these days. (The RTL 8002 is already somewhat
+aged, around 1992/93.) The RTL 8012 offers support for advanced
+printer adapter hardware, like bidirectional SPP, or EPP, which could
+speed up the transfers substantially. The RTL 8012 also supports
+hardware multicast, and has the ability to address 64 K x 4 packet
+buffer RAM.
+.Pp
+The driver should be layered upon the ppc driver, instead of working
+standalone, and should be available as a loadable module, so the
+device probing can be deferred until the pocket ethernet adapter has
+actually been attached.
diff --git a/sys/i386/isa/if_rdp.c b/sys/i386/isa/if_rdp.c
new file mode 100644
index 0000000..26773c0
--- /dev/null
+++ b/sys/i386/isa/if_rdp.c
@@ -0,0 +1,1493 @@
+/*
+ * 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.1.1.1 1998/12/21 12:43:35 j 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>
+#if __FreeBSD_version >= 300000
+#include <i386/isa/intr_machdep.h>
+#endif
+
+#if __FreeBSD_version >= 300003
+#define IOCTL_CMD_T u_long
+#else
+#define IOCTL_CMD_T int
+#endif
+
+/*
+ * Debug levels (ORed together):
+ * != 0 - general (bad packets etc.)
+ * 2 - debug EEPROM IO
+ * 4 - debug interrupt status
+ */
+#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 *);
+#if __FreeBSD_version >= 300004
+static void rdpintr(int);
+#endif
+
+/*
+ * 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;
+#if __FreeBSD_version < 300000
+ int irqmap[3];
+# define IRQPEND_ARGS isa_dev
+#else
+ intrmask_t irqmap[3];
+# define IRQPEND_ARGS /* */
+#endif
+ 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(IRQPEND_ARGS);
+ 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(IRQPEND_ARGS);
+ 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(IRQPEND_ARGS);
+ 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;
+
+#if __FreeBSD_version >= 300004
+ isa_dev->id_ointr = rdpintr;
+#endif
+
+ /*
+ * 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 defined __FreeBSD_version && __FreeBSD_version >= 300000
+ if (TAILQ_EMPTY(&ifp->if_addrhead))
+ return;
+#else
+ if (ifp->if_addrlist == (struct ifaddr *) 0)
+ return;
+#endif
+
+ 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:
+#if __FreeBSD_version < 225000
+ ether_ioctl(ifp, command, data);
+#else
+ case SIOCSIFMTU:
+ error = ether_ioctl(ifp, command, data);
+#endif
+ break;
+
+#if __FreeBSD_version < 225000
+ case SIOCSIFMTU:
+ {
+ struct ifreq *ifr = (struct ifreq *) data;
+
+ if (ifr->ifr_mtu > ETHERMTU)
+ error = EINVAL;
+ else
+ ifp->if_mtu = ifr->ifr_mtu;
+ break;
+ }
+#endif
+
+ 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;
+}
diff --git a/sys/i386/isa/if_rdpreg.h b/sys/i386/isa/if_rdpreg.h
new file mode 100644
index 0000000..cab679a
--- /dev/null
+++ b/sys/i386/isa/if_rdpreg.h
@@ -0,0 +1,187 @@
+#ifndef IF_RDPREG_H
+#define IF_RDPREG_H 1
+/*
+ * Copyright (c) 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, 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 DEVELOPERS ``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 DEVELOPERS 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_rdpreg.h,v 1.1.1.1 1998/12/21 12:43:35 j Exp $
+ */
+
+/*
+ * Part of the definitions here has been copied over from the REDP
+ * packet driver's REDPPD.INC file. This provides us with the same
+ * set of acronyms as the packet driver is using.
+ *
+ * The packet driver had no copyright, and is believed to be in the
+ * public domain. The author seems to be someone who calls himself
+ * "Chiu", so that's the only acknowledgment i can give here.
+ * Supposedly the author was someone from RealTek.
+ */
+
+/*
+ * We're hanging upon an LPT port, thus suck in the lpt defs as well.
+ */
+#include <i386/isa/lptreg.h>
+
+struct rdphdr {
+ /* RTL8002 header that is prepended to the actual packet */
+ u_char unused2[2];
+ u_short pktlen;
+ u_char status; /* copy of RSR for this packet */
+ u_char unused3[3];
+};
+
+/*
+ *
+ * 8 Data Modes are provided:
+ *
+ * +--------+---------------+-------------+
+ * | Mode | Read | Write |
+ * +--------+---------------+-------------+
+ * | 0 | LptCtrl | LptData |
+ * +--------+---------------+-------------+
+ * | 1 | LptCtrl | LptCtrl |
+ * +--------+---------------+-------------+
+ * | 2 | LptCtrl*2 | LptData |
+ * +--------+---------------+-------------+
+ * | 3 | LptCtrl*2 | LptCtrl |
+ * +--------+---------------+-------------+
+ * | 4 | LptData | LptData |
+ * +--------+---------------+-------------+
+ * | 5 | LptData | LptCtrl |
+ * +--------+---------------+-------------+
+ * | 6 | LptData*2 | LptData |
+ * +--------+---------------+-------------+
+ * | 7 | LptData*2 | LptCtrl |
+ * +--------+---------------+-------------+
+ *
+ * Right now, this driver only implements mode 0 (which ought to work
+ * on any standard parallel interface).
+ *
+ */
+
+/*
+ * Page 0 of EPLC registers
+ */
+#define IDR0 0x00 /* Ethernet ID register (R/W) */
+#define IDR1 0x01
+#define IDR2 0x02
+#define IDR3 0x03
+#define IDR4 0x04
+#define IDR5 0x05
+#define TBCR0 0x06 /* transmit byte count (W), 11 bits valid */
+#define TBCR1 0x07
+#define TSR 0x08 /* transmit status (R), cleared upon next tx */
+# define TSR_TOK 1 /* transmit OK */
+# define TSR_TABT 2 /* transmit aborted (excessive collisions) */
+# define TSR_COL 4 /* collision detected */
+# define TSR_CDH 8 /* CD heartbeat detected */
+#define RSR 0x09 /*
+ * receiver status (R), cleared upon next
+ * received packet (but stored in rx buffer
+ * header anyway)
+ */
+# define RSR_ROK 1 /* receive OK */
+# define RSR_CRC 2 /* CRC error */
+# define RSR_FA 4 /* frame alignment error (not multiple of 8) */
+# define RSR_BUFO 0x10 /* rx buffer overflow, packet discarded */
+# define RSR_PUN 0x20 /* packet count underflow (jump command issued
+ * but rx buffer was empty) */
+# define RSR_POV 0x40 /* packet count overflow (more than 254 (?)
+ * packets still in buffer) */
+#define ISR 0x0A /* interrupt status register (R), writing
+ * clears the written bits */
+# define ISR_TOK 1 /* transmission OK (~ TSR_TOK) */
+# define ISR_TER 2 /* transmitter error (~ TSR_TABT) */
+# define ISR_ROK 4 /* receive OK (~ RSR_ROK) */
+# define ISR_RER 8 /* receiver error (~ RSR_CRC|RSR_FA) */
+# define ISR_RBER 0x10 /* rx buffer overflow (POV|PUN|BUFO) */
+#define IMR 0x0B /* interrupt mask register (R/W), bit as ISR */
+#define CMR1 0x0C /* command register 1 (R/W) */
+# define CMR1_BUFE 1 /* (R) rx buffer empty */
+# define CMR1_IRQ 2 /* (R) interrupt request */
+# define CMR1_TRA 4 /* (R) transmission in progress */
+ /* (W) transmit start */
+# define CMR1_TE 0x10 /* (R/W) transmitter enable */
+# define CMR1_RE 0x20 /* (R/W) receiver enable */
+# define CMR1_RST 0x40 /* (R/W) reset; sticks until reset completed */
+# define CMR1_RDPAC 1 /* (W) `rx jump packet', prepare for reading
+ * next packet from ring buffer */
+# define CMR1_WRPAC 2 /* (W) `tx jump packet', packet in tx buffer
+ * is complete and can be sent */
+# define CMR1_RETX 8 /* (W) retransmit (must be accomp'ed by TRA) */
+# define CMR1_MUX 0x80 /* (W) RTL8012: tell the printer MUX to
+ * connect the output pins to the host */
+#define CMR2 0x0D /* command register 2 (R/W) */
+# define CMR2_IRQOUT 1 /* interrupt signal output enabled */
+# define CMR2_RAMTST 2 /* enable RAM test */
+# define CMR2_PAGE 4 /* select register page #1 */
+# define CMR2_IRQINV 8 /* make active IRQ `low' */
+# define CMR2_AMbits 0x30 /* address mode bits: */
+# define CMR2_AM_NONE 0x00 /* 0: accept nothing */
+# define CMR2_AM_PHYS 0x10 /* 1: only physical addr */
+# define CMR2_AM_PB 0x20 /* 2: phys + broadcast */
+# define CMR2_AM_ALL 0x30 /* 3: promiscuous */
+# define CMR2_LBK 0x40 /* enable loopback */
+# define CMR2_SER 0x80 /* save error packet */
+#define MAR 0x0E /* memory access register (?), used for
+ * remote DMA to the 8002's buffer */
+#define PNR TBCR0 /* received packet number (R) */
+#define COLR TBCR1 /* collision count (R) (4 bit valid) */
+
+/*
+ * Page 1 of EPLC registers -- EEPROM control
+ */
+#define PCMR TBCR0 /* port command register */
+/* bits for 93C46 control -- add HNib */
+#define PCMR_SK 0x04 /* serial clock for EEPROM */
+#define PCMR_CS 0x02 /* chip select for EEPROM */
+#define PCMR_DO 0x01 /* DI to EEPROM */
+
+/* EEPROM data, nibbles for 74S288, bits for 93C46 */
+#define PDR TBCR1 /* DO from EEPROM, only bit 0 valid for
+ * serial EEPROM */
+
+/*
+ * The following definitionss define remote DMA command through LptCtrl
+ */
+#define ATFD 3 /* ATFD bit in Lpt's Control register */
+ /* -> ATFD bit is added for Xircom's MUX */
+#define Ctrl_LNibRead (0x08+ATFD) /* specify low nibble */
+#define Ctrl_HNibRead (0+ATFD) /* specify high nibble */
+#define Ctrl_SelData (0x04+ATFD) /* not through LptCtrl but through */
+ /* LptData */
+#define Ctrl_IRQEN 0x10 /* set IRQEN of lpt control register */
+
+/* Here define constants to construct the required read/write commands */
+#define WrAddr 0x40 /* set address of EPLC write register */
+#define RdAddr 0x0C0 /* set address of EPLC read register */
+#define EOR 0x20 /* ORed to make 'end of read',set CSB=1 */
+#define EOW 0x0E0 /* end of write, R/WB=A/DB=CSB=1 */
+#define EOC 0x0E0 /* End Of r/w Command, R/WB=A/DB=CSB=1 */
+#define HNib 0x10
+
+#define MkHi(value) (((value) >> 4) | HNib)
+
+#endif /* IF_RDPREG_H */
OpenPOWER on IntegriCloud