summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorakiyama <akiyama@FreeBSD.org>2003-05-03 10:16:56 +0000
committerakiyama <akiyama@FreeBSD.org>2003-05-03 10:16:56 +0000
commit62fd773f5d0c66fe90a5cbd5aa6c1fc7bac9f71a (patch)
tree9932384b6e75f30181063a38b122b7893171ed43
parent8fbb9826c9227a9d32013339d1e0d9d267121ce9 (diff)
downloadFreeBSD-src-62fd773f5d0c66fe90a5cbd5aa6c1fc7bac9f71a.zip
FreeBSD-src-62fd773f5d0c66fe90a5cbd5aa6c1fc7bac9f71a.tar.gz
Add RealTek RTL8150 USB to fast Ethernet controller driver.
This driver now supports the Melco LUA-KTX and the GREEN HOUSE GH-USB100B. Reviewed by: imp MFC after: 2 weeks
-rw-r--r--etc/devd.conf2
-rw-r--r--etc/usbd.conf2
-rw-r--r--share/man/man4/Makefile1
-rw-r--r--share/man/man4/rue.4128
-rw-r--r--sys/conf/NOTES4
-rw-r--r--sys/conf/files2
-rw-r--r--sys/dev/mii/ruephy.c297
-rw-r--r--sys/dev/mii/ruephyreg.h38
-rw-r--r--sys/dev/usb/if_rue.c1477
-rw-r--r--sys/dev/usb/if_ruereg.h247
-rw-r--r--sys/dev/usb/usbdevs5
-rw-r--r--sys/modules/Makefile1
-rw-r--r--sys/modules/mii/Makefile1
-rw-r--r--sys/modules/rue/Makefile10
14 files changed, 2213 insertions, 2 deletions
diff --git a/etc/devd.conf b/etc/devd.conf
index 017330e..6e7ba86 100644
--- a/etc/devd.conf
+++ b/etc/devd.conf
@@ -19,7 +19,7 @@ options {
# Setup some shorthand for regex that we use later in the file.
set ethernet-nic-regex
"(an|ar|ath|aue|awi|bge|cm|cnw|cs|cue|dc|de|ed|el|em|ep|ex|\
- fe|fxp|gem|gx|hme|ie|kue|lge|lnc|my|nge|pcn|ray|rl|\
+ fe|fxp|gem|gx|hme|ie|kue|lge|lnc|my|nge|pcn|ray|rl|rue|\
sf|sis|sk|sn|snc|ste|ti|tl|tx|txp|vr|vx|wb|wi|xe|xl)[0-9]+";
set scsi-controller-regex
"(adv|advw|aic|aha|ahb|ahc|ahd|bt|ct|iir|isp|mly|mpt|ncv|nsp|\
diff --git a/etc/usbd.conf b/etc/usbd.conf
index 550bd26..03c3f4f 100644
--- a/etc/usbd.conf
+++ b/etc/usbd.conf
@@ -27,7 +27,7 @@ device "Entrega Serial with UART"
# it shouldn't be too big a deal :-)
#
device "USB ethernet"
- devname "[ack]ue[0-9]+"
+ devname "[ackr]ue[0-9]+"
attach "/etc/pccard_ether ${DEVNAME} start"
detach "/etc/pccard_ether ${DEVNAME} stop"
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index cab4b58..346d198 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -185,6 +185,7 @@ MAN= aac.4 \
rndtest.4 \
route.4 \
rp.4 \
+ rue.4 \
sa.4 \
sbc.4 \
sbp.4 \
diff --git a/share/man/man4/rue.4 b/share/man/man4/rue.4
new file mode 100644
index 0000000..3b69068
--- /dev/null
+++ b/share/man/man4/rue.4
@@ -0,0 +1,128 @@
+.\"
+.\" Copyright (c) 2001-2003, Shunsuke Akiyama <akiyama@FreeBSD.org>.
+.\" 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$
+.\"
+.Dd May 3, 2003
+.Dt RUE 4
+.Os FreeBSD
+.Sh NAME
+.Nm rue
+.Nd
+RealTek RTL8150 USB To Fast Ethernet controller driver
+.Sh SYNOPSIS
+.Cd "device uhci"
+.Cd "device ohci"
+.Cd "device usb"
+.Cd "device miibus"
+.Cd "device rue"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for USB Ethernet adapters based on the RealTek
+RTL8150 USB to Fast Ethernet controller chip.
+This includes the Melco Inc. LUA-KTX and the GREEN HOUSE GH-USB100B.
+.Pp
+The RTL8150 contains a integrated Fast Ethernet MAC, which supports
+both 10 and 100Mbps speeds in either full or half duplex.
+Although designed to interface with
+100Mbps peripheral, the existing USB standard specifies a maximum
+transfer speed of 12Mbps.
+Users should therefore not expect to actually
+achieve 100Mbps speeds with this device.
+.Pp
+The
+.Nm
+driver supports the following media types:
+.Pp
+.Bl -tag -width xxxxxxxxxxxxxxxx
+.It autoselect
+Enable auto selection of the media type and options.
+The user can manually override
+the auto selected mode by adding media options to the
+.Pa /etc/rc.conf
+file.
+.It 10baseT/UTP
+Set 10Mbps operation.
+The
+.Ar mediaopt
+option can also be used to select either
+.Ar full-duplex
+or
+.Ar half-duplex
+modes.
+.It 100baseTX
+Set 100Mbps (Fast Ethernet) operation.
+The
+.Ar mediaopt
+option can also be used to select either
+.Ar full-duplex
+or
+.Ar half-duplex
+modes.
+.El
+.Pp
+The
+.Nm
+driver supports the following media options:
+.Pp
+.Bl -tag -width xxxxxxxxxxxxxxxx
+.It full-duplex
+Force full duplex operation.
+.It half-duplex
+Force half duplex operation.
+.El
+.Pp
+For more information on configuring this device, see
+.Xr ifconfig 8 .
+.Sh DIAGNOSTICS
+.Bl -diag
+.It "rue%d: watchdog timeout"
+A packet was queued for transmission and a transmit command was
+issued, however the device failed to acknowledge the transmission
+before a timeout expired.
+.It "rue%d: rx list init failed"
+The driver failed to allocate an mbuf for the transmitter ring.
+.It "rue%d: no memory for rx list"
+The driver failed to allocate an mbuf for the receiver ring.
+.Sh SEE ALSO
+.Xr arp 4 ,
+.Xr netintro 4 ,
+.Xr ng_ether 4 ,
+.Xr ifconfig 8
+.Rs
+.%T ReakTek RTL8150 data sheet
+.%O ftp://ftp.realtek.com.tw/lancard/data_sheet/8150/
+.Re
+.Sh HISTORY
+The
+.Nm
+device driver first appeared in
+.Fx 5.1 .
+.Sh AUTHORS
+The
+.Nm
+driver was written by
+.An Shunsuke Akiyama Aq akiyama@FreeBSD.org .
diff --git a/sys/conf/NOTES b/sys/conf/NOTES
index 26be71e..a3c57f0 100644
--- a/sys/conf/NOTES
+++ b/sys/conf/NOTES
@@ -2131,6 +2131,10 @@ device cue
# the Netgear EA101, the D-Link DSB-650, the SMC 2102USB
# and 2104USB, and the Corega USB-T.
device kue
+#
+# RealTek RTL8150 USB to fast ethernet. Supports the Melco LUA-KTX
+# and the GREEN HOUSE GH-USB100B.
+device rue
# debugging options for the USB subsystem
#
diff --git a/sys/conf/files b/sys/conf/files
index 90c4664..bb3c4bf 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -498,6 +498,7 @@ dev/mii/nsgphy.c optional miibus
dev/mii/pnphy.c optional miibus
dev/mii/pnaphy.c optional miibus
dev/mii/rlphy.c optional miibus
+dev/mii/ruephy.c optional miibus
dev/mii/tdkphy.c optional miibus
dev/mii/tlphy.c optional miibus
dev/mii/ukphy.c optional miibus
@@ -760,6 +761,7 @@ dev/usb/if_aue.c optional aue
dev/usb/if_axe.c optional axe
dev/usb/if_cue.c optional cue
dev/usb/if_kue.c optional kue
+dev/usb/if_rue.c optional rue
dev/usb/ehci.c optional ehci
dev/usb/ehci_pci.c optional ehci pci
dev/usb/ohci.c optional ohci
diff --git a/sys/dev/mii/ruephy.c b/sys/dev/mii/ruephy.c
new file mode 100644
index 0000000..5e876af
--- /dev/null
+++ b/sys/dev/mii/ruephy.c
@@ -0,0 +1,297 @@
+/*-
+ * Copyright (c) 2001-2003, Shunsuke Akiyama <akiyama@FreeBSD.org>.
+ * 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$
+ */
+
+/*
+ * driver for RealTek RTL8150 internal PHY
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/socket.h>
+#include <sys/bus.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_media.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+#include "miidevs.h"
+
+#include <machine/bus.h>
+#include <dev/mii/ruephyreg.h>
+
+#include "miibus_if.h"
+
+static int ruephy_probe(device_t);
+static int ruephy_attach(device_t);
+
+static device_method_t ruephy_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, ruephy_probe),
+ DEVMETHOD(device_attach, ruephy_attach),
+ DEVMETHOD(device_detach, mii_phy_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ { 0, 0 }
+};
+
+static devclass_t ruephy_devclass;
+
+static driver_t ruephy_driver = {
+ "ruephy",
+ ruephy_methods,
+ sizeof(struct mii_softc)
+};
+
+DRIVER_MODULE(ruephy, miibus, ruephy_driver, ruephy_devclass, 0, 0);
+
+static int ruephy_service(struct mii_softc *, struct mii_data *, int);
+static void ruephy_reset(struct mii_softc *);
+static void ruephy_status(struct mii_softc *);
+
+static int
+ruephy_probe(device_t dev)
+{
+ struct mii_attach_args *ma;
+ device_t parent;
+
+ ma = device_get_ivars(dev);
+ parent = device_get_parent(device_get_parent(dev));
+
+ /*
+ * RealTek RTL8150 PHY doesn't have vendor/device ID registers:
+ * the rue driver fakes up a return value of all zeros.
+ */
+ if (MII_OUI(ma->mii_id1, ma->mii_id2) != 0 ||
+ MII_MODEL(ma->mii_id2) != 0)
+ return (ENXIO);
+
+ /*
+ * Make sure the parent is an 'rue'.
+ */
+ if (strcmp(device_get_name(parent), "rue") != 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "RealTek RTL8150 internal media interface");
+
+ return (0);
+}
+
+static int
+ruephy_attach(device_t dev)
+{
+ struct mii_softc *sc;
+ struct mii_attach_args *ma;
+ struct mii_data *mii;
+
+ sc = device_get_softc(dev);
+ ma = device_get_ivars(dev);
+ sc->mii_dev = device_get_parent(dev);
+ mii = device_get_softc(sc->mii_dev);
+
+ /*
+ * The RealTek PHY can never be isolated, so never allow non-zero
+ * instances!
+ */
+ if (mii->mii_instance != 0) {
+ device_printf(dev, "ignoring this PHY, non-zero instance\n");
+ return (ENXIO);
+ }
+
+ LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list);
+
+ sc->mii_inst = mii->mii_instance;
+ sc->mii_phy = ma->mii_phyno;
+ sc->mii_service = ruephy_service;
+ sc->mii_pdata = mii;
+ mii->mii_instance++;
+
+ sc->mii_flags |= MIIF_NOISOLATE;
+
+#define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL)
+
+ ruephy_reset(sc);
+
+ sc->mii_capabilities =
+ PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
+ device_printf(dev, " ");
+ mii_phy_add_media(sc);
+ printf("\n");
+#undef ADD
+
+ MIIBUS_MEDIAINIT(sc->mii_dev);
+ return (0);
+}
+
+static int
+ruephy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
+{
+ struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
+ int reg;
+
+ /*
+ * We can't isolate the RealTek RTL8150 PHY,
+ * so it has to be the only one!
+ */
+ if (IFM_INST(ife->ifm_media) != sc->mii_inst)
+ panic("ruephy_service: can't isolate RealTek RTL8150 PHY");
+
+ switch (cmd) {
+ case MII_POLLSTAT:
+ break;
+
+ case MII_MEDIACHG:
+ /*
+ * If the interface is not up, don't do anything.
+ */
+ if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
+ break;
+
+ switch (IFM_SUBTYPE(ife->ifm_media)) {
+ case IFM_AUTO:
+ /*
+ * If we're already in auto mode, just return.
+ */
+ if (PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN)
+ return (0);
+ (void) mii_phy_auto(sc);
+ break;
+ case IFM_100_T4:
+ /*
+ * XXX Not supported as a manual setting right now.
+ */
+ return (EINVAL);
+ default:
+ /*
+ * BMCR data is stored in the ifmedia entry.
+ */
+ PHY_WRITE(sc, MII_ANAR,
+ mii_anar(ife->ifm_media));
+ PHY_WRITE(sc, MII_BMCR, ife->ifm_data);
+ }
+ break;
+
+ case MII_TICK:
+ /*
+ * Is the interface even up?
+ */
+ if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
+ return (0);
+
+ /*
+ * Only used for autonegotiation.
+ */
+ if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
+ break;
+
+ /*
+ * Check to see if we have link. If we do, we don't
+ * need to restart the autonegotiation process. Read
+ * the MSR twice in case it's latched.
+ */
+ reg = PHY_READ(sc, RUEPHY_MII_MSR) |
+ PHY_READ(sc, RUEPHY_MII_MSR);
+ if (reg & RUEPHY_MSR_LINK)
+ break;
+
+ /*
+ * Only retry autonegotiation every 5 seconds.
+ */
+ if (++sc->mii_ticks != 5)
+ return (0);
+
+ sc->mii_ticks = 0;
+ ruephy_reset(sc);
+ if (mii_phy_auto(sc) == EJUSTRETURN)
+ return (0);
+ break;
+ }
+
+ /* Update the media status. */
+ ruephy_status(sc);
+
+ /* Callback if something changed. */
+ mii_phy_update(sc, cmd);
+
+ return (0);
+}
+
+static void
+ruephy_reset(struct mii_softc *sc)
+{
+
+ mii_phy_reset(sc);
+
+ /*
+ * XXX RealTek RTL8150 PHY doesn't set the BMCR properly after
+ * XXX reset, which breaks autonegotiation.
+ */
+ PHY_WRITE(sc, MII_BMCR, (BMCR_S100 | BMCR_AUTOEN | BMCR_FDX));
+}
+
+static void
+ruephy_status(struct mii_softc *phy)
+{
+ struct mii_data *mii = phy->mii_pdata;
+ int bmsr, bmcr, msr;
+
+ mii->mii_media_status = IFM_AVALID;
+ mii->mii_media_active = IFM_ETHER;
+
+ msr = PHY_READ(phy, RUEPHY_MII_MSR) | PHY_READ(phy, RUEPHY_MII_MSR);
+ if (msr & RUEPHY_MSR_LINK)
+ mii->mii_media_status |= IFM_ACTIVE;
+
+ bmcr = PHY_READ(phy, MII_BMCR);
+ if (bmcr & BMCR_ISO) {
+ mii->mii_media_active |= IFM_NONE;
+ mii->mii_media_status = 0;
+ return;
+ }
+
+ bmsr = PHY_READ(phy, MII_BMSR) | PHY_READ(phy, MII_BMSR);
+
+ if (bmcr & BMCR_AUTOEN) {
+ if ((bmsr & BMSR_ACOMP) == 0) {
+ /* Erg, still trying, I guess... */
+ mii->mii_media_active |= IFM_NONE;
+ return;
+ }
+
+ if (msr & RUEPHY_MSR_SPEED100)
+ mii->mii_media_active |= IFM_100_TX;
+ else
+ mii->mii_media_active |= IFM_10_T;
+
+ if (msr & RUEPHY_MSR_DUPLEX)
+ mii->mii_media_active |= IFM_FDX;
+ } else
+ mii->mii_media_active = mii_media_from_bmcr(bmcr);
+}
diff --git a/sys/dev/mii/ruephyreg.h b/sys/dev/mii/ruephyreg.h
new file mode 100644
index 0000000..5f3911b
--- /dev/null
+++ b/sys/dev/mii/ruephyreg.h
@@ -0,0 +1,38 @@
+/*-
+ * Copyright (c) 2001-2003, Shunsuke Akiyama <akiyama@FreeBSD.org>.
+ * 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$
+ */
+
+#ifndef _RUEPHYREG_H_
+#define _RUEPHYREG_H_
+
+#define RUEPHY_MII_MSR 0x0137 /* B, R/W */
+#define RUEPHY_MSR_RXFCE 0x40
+#define RUEPHY_MSR_DUPLEX 0x10
+#define RUEPHY_MSR_SPEED100 0x08
+#define RUEPHY_MSR_LINK 0x04
+
+#endif /* _RUEPHYREG_H_ */
diff --git a/sys/dev/usb/if_rue.c b/sys/dev/usb/if_rue.c
new file mode 100644
index 0000000..9a6c15b
--- /dev/null
+++ b/sys/dev/usb/if_rue.c
@@ -0,0 +1,1477 @@
+/*-
+ * Copyright (c) 2001-2003, Shunsuke Akiyama <akiyama@FreeBSD.org>.
+ * 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$
+ */
+
+/*
+ * RealTek RTL8150 USB to fast ethernet controller driver.
+ * Datasheet is available from
+ * ftp://ftp.realtek.com.tw/lancard/data_sheet/8150/.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+
+#include <net/bpf.h>
+
+#include <sys/bus.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/usb_ethersubr.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+#include <dev/usb/if_ruereg.h>
+
+/* "controller miibus0" required. See GENERIC if you get errors here. */
+#include "miibus_if.h"
+
+#ifdef USB_DEBUG
+static int ruedebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, rue, CTLFLAG_RW, 0, "USB rue");
+SYSCTL_INT(_hw_usb_rue, OID_AUTO, debug, CTLFLAG_RW,
+ &ruedebug, 0, "rue debug level");
+
+#define DPRINTFN(n, x) do { \
+ if (ruedebug > (n)) \
+ logprintf x; \
+ } while (0);
+#else
+#define DPRINTFN(n, x)
+#endif
+#define DPRINTF(x) DPRINTFN(0, x)
+
+/*
+ * Various supported device vendors/products.
+ */
+
+Static struct rue_type rue_devs[] = {
+ { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAKTX },
+ { USB_VENDOR_REALTEK, USB_PRODUCT_REALTEK_USBKR100 },
+ { 0, 0 }
+};
+
+Static struct usb_qdat rue_qdat;
+
+Static int rue_match(device_ptr_t);
+Static int rue_attach(device_ptr_t);
+Static int rue_detach(device_ptr_t);
+
+Static int rue_tx_list_init(struct rue_softc *);
+Static int rue_rx_list_init(struct rue_softc *);
+Static int rue_newbuf(struct rue_softc *, struct rue_chain *, struct mbuf *);
+Static int rue_encap(struct rue_softc *, struct mbuf *, int);
+#ifdef RUE_INTR_PIPE
+Static void rue_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
+#endif
+Static void rue_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+Static void rue_txeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+Static void rue_tick(void *);
+Static void rue_rxstart(struct ifnet *);
+Static void rue_start(struct ifnet *);
+Static int rue_ioctl(struct ifnet *, u_long, caddr_t);
+Static void rue_init(void *);
+Static void rue_stop(struct rue_softc *);
+Static void rue_watchdog(struct ifnet *);
+Static void rue_shutdown(device_ptr_t);
+Static int rue_ifmedia_upd(struct ifnet *);
+Static void rue_ifmedia_sts(struct ifnet *, struct ifmediareq *);
+
+Static int rue_miibus_readreg(device_ptr_t, int, int);
+Static int rue_miibus_writereg(device_ptr_t, int, int, int);
+Static void rue_miibus_statchg(device_ptr_t);
+
+static u_int8_t rue_calchash(caddr_t);
+Static void rue_setmulti(struct rue_softc *);
+Static void rue_reset(struct rue_softc *);
+
+Static int rue_read_mem(struct rue_softc *, u_int16_t, void *, u_int16_t);
+Static int rue_write_mem(struct rue_softc *, u_int16_t, void *, u_int16_t);
+Static int rue_csr_read_1(struct rue_softc *, int);
+Static int rue_csr_write_1(struct rue_softc *, int, u_int8_t);
+Static int rue_csr_read_2(struct rue_softc *, int);
+Static int rue_csr_write_2(struct rue_softc *, int, u_int16_t);
+Static int rue_csr_write_4(struct rue_softc *, int, u_int32_t);
+
+Static device_method_t rue_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, rue_match),
+ DEVMETHOD(device_attach, rue_attach),
+ DEVMETHOD(device_detach, rue_detach),
+ DEVMETHOD(device_shutdown, rue_shutdown),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, rue_miibus_readreg),
+ DEVMETHOD(miibus_writereg, rue_miibus_writereg),
+ DEVMETHOD(miibus_statchg, rue_miibus_statchg),
+
+ { 0, 0 }
+};
+
+Static driver_t rue_driver = {
+ "rue",
+ rue_methods,
+ sizeof(struct rue_softc)
+};
+
+Static devclass_t rue_devclass;
+
+DRIVER_MODULE(rue, uhub, rue_driver, rue_devclass, usbd_driver_load, 0);
+DRIVER_MODULE(miibus, rue, miibus_driver, miibus_devclass, 0, 0);
+MODULE_DEPEND(rue, usb, 1, 1, 1);
+MODULE_DEPEND(rue, ether, 1, 1, 1);
+MODULE_DEPEND(rue, miibus, 1, 1, 1);
+
+#define RUE_SETBIT(sc, reg, x) \
+ rue_csr_write_1(sc, reg, rue_csr_read_1(sc, reg) | (x))
+
+#define RUE_CLRBIT(sc, reg, x) \
+ rue_csr_write_1(sc, reg, rue_csr_read_1(sc, reg) & ~(x))
+
+#define RUE_SETBIT_2(sc, reg, x) \
+ rue_csr_write_2(sc, reg, rue_csr_read_2(sc, reg) | (x))
+
+#define RUE_CLRBIT_2(sc, reg, x) \
+ rue_csr_write_2(sc, reg, rue_csr_read_2(sc, reg) & ~(x))
+
+Static int
+rue_read_mem(struct rue_softc *sc, u_int16_t addr, void *buf, u_int16_t len)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc->rue_dying)
+ return (0);
+
+ RUE_LOCK(sc);
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = UR_SET_ADDRESS;
+ USETW(req.wValue, addr);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, len);
+
+ err = usbd_do_request(sc->rue_udev, &req, buf);
+
+ RUE_UNLOCK(sc);
+
+ if (err) {
+ printf("rue%d: control pipe read failed: %s\n",
+ sc->rue_unit, usbd_errstr(err));
+ return (-1);
+ }
+
+ return (0);
+}
+
+Static int
+rue_write_mem(struct rue_softc *sc, u_int16_t addr, void *buf, u_int16_t len)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc->rue_dying)
+ return (0);
+
+ RUE_LOCK(sc);
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UR_SET_ADDRESS;
+ USETW(req.wValue, addr);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, len);
+
+ err = usbd_do_request(sc->rue_udev, &req, buf);
+
+ RUE_UNLOCK(sc);
+
+ if (err) {
+ printf("rue%d: control pipe write failed: %s\n",
+ sc->rue_unit, usbd_errstr(err));
+ return (-1);
+ }
+
+ return (0);
+}
+
+Static int
+rue_csr_read_1(struct rue_softc *sc, int reg)
+{
+ int err;
+ u_int8_t val = 0;
+
+ err = rue_read_mem(sc, reg, &val, 1);
+
+ if (err)
+ return (0);
+
+ return (val);
+}
+
+Static int
+rue_csr_read_2(struct rue_softc *sc, int reg)
+{
+ int err;
+ u_int16_t val = 0;
+ uWord w;
+
+ USETW(w, val);
+ err = rue_read_mem(sc, reg, &w, 2);
+ val = UGETW(w);
+
+ if (err)
+ return (0);
+
+ return (val);
+}
+
+Static int
+rue_csr_write_1(struct rue_softc *sc, int reg, u_int8_t val)
+{
+ int err;
+
+ err = rue_write_mem(sc, reg, &val, 1);
+
+ if (err)
+ return (-1);
+
+ return (0);
+}
+
+Static int
+rue_csr_write_2(struct rue_softc *sc, int reg, u_int16_t val)
+{
+ int err;
+ uWord w;
+
+ USETW(w, val);
+ err = rue_write_mem(sc, reg, &w, 2);
+
+ if (err)
+ return (-1);
+
+ return (0);
+}
+
+Static int
+rue_csr_write_4(struct rue_softc *sc, int reg, u_int32_t val)
+{
+ int err;
+ uDWord dw;
+
+ USETDW(dw, val);
+ err = rue_write_mem(sc, reg, &dw, 4);
+
+ if (err)
+ return (-1);
+
+ return (0);
+}
+
+Static int
+rue_miibus_readreg(device_ptr_t dev, int phy, int reg)
+{
+ struct rue_softc *sc = USBGETSOFTC(dev);
+ int rval;
+ int ruereg;
+
+ if (phy != 0) /* RTL8150 supports PHY == 0, only */
+ return (0);
+
+ switch (reg) {
+ case MII_BMCR:
+ ruereg = RUE_BMCR;
+ break;
+ case MII_BMSR:
+ ruereg = RUE_BMSR;
+ break;
+ case MII_ANAR:
+ ruereg = RUE_ANAR;
+ break;
+ case MII_ANER:
+ ruereg = RUE_AER;
+ break;
+ case MII_ANLPAR:
+ ruereg = RUE_ANLP;
+ break;
+ case MII_PHYIDR1:
+ case MII_PHYIDR2:
+ return (0);
+ break;
+ default:
+ if (RUE_REG_MIN <= reg && reg <= RUE_REG_MAX) {
+ rval = rue_csr_read_1(sc, reg);
+ return (rval);
+ }
+ printf("rue%d: bad phy register\n", sc->rue_unit);
+ return (0);
+ }
+
+ rval = rue_csr_read_2(sc, ruereg);
+
+ return (rval);
+}
+
+Static int
+rue_miibus_writereg(device_ptr_t dev, int phy, int reg, int data)
+{
+ struct rue_softc *sc = USBGETSOFTC(dev);
+ int ruereg;
+
+ if (phy != 0) /* RTL8150 supports PHY == 0, only */
+ return (0);
+
+ switch (reg) {
+ case MII_BMCR:
+ ruereg = RUE_BMCR;
+ break;
+ case MII_BMSR:
+ ruereg = RUE_BMSR;
+ break;
+ case MII_ANAR:
+ ruereg = RUE_ANAR;
+ break;
+ case MII_ANER:
+ ruereg = RUE_AER;
+ break;
+ case MII_ANLPAR:
+ ruereg = RUE_ANLP;
+ break;
+ case MII_PHYIDR1:
+ case MII_PHYIDR2:
+ return (0);
+ break;
+ default:
+ if (RUE_REG_MIN <= reg && reg <= RUE_REG_MAX) {
+ rue_csr_write_1(sc, reg, data);
+ return (0);
+ }
+ printf("rue%d: bad phy register\n", sc->rue_unit);
+ return (0);
+ }
+ rue_csr_write_2(sc, ruereg, data);
+
+ return (0);
+}
+
+Static void
+rue_miibus_statchg(device_ptr_t dev)
+{
+ struct rue_softc *sc = USBGETSOFTC(dev);
+ struct mii_data *mii = GET_MII(sc);
+ int bmcr;
+
+ RUE_CLRBIT(sc, RUE_CR, (RUE_CR_RE | RUE_CR_TE));
+
+ bmcr = rue_csr_read_2(sc, RUE_BMCR);
+
+ if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX)
+ bmcr |= RUE_BMCR_SPD_SET;
+ else
+ bmcr &= ~RUE_BMCR_SPD_SET;
+
+ if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX)
+ bmcr |= RUE_BMCR_DUPLEX;
+ else
+ bmcr &= ~RUE_BMCR_DUPLEX;
+
+ rue_csr_write_2(sc, RUE_BMCR, bmcr);
+
+ RUE_SETBIT(sc, RUE_CR, (RUE_CR_RE | RUE_CR_TE));
+}
+
+/*
+ * Calculate CRC of a multicast group address, return the upper 6 bits.
+ */
+
+static u_int8_t
+rue_calchash(caddr_t addr)
+{
+ u_int32_t crc, carry;
+ int i, j;
+ u_int8_t c;
+
+ /* Compute CRC for the address value. */
+ crc = 0xFFFFFFFF; /* initial value */
+
+ for (i = 0; i < 6; i++) {
+ c = *(addr + i);
+ for (j = 0; j < 8; j++) {
+ carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01);
+ crc <<= 1;
+ c >>= 1;
+ if (carry)
+ crc = (crc ^ 0x04c11db6) | carry;
+ }
+ }
+
+ /* return the filter bit position */
+ return (crc >> 26);
+}
+
+/*
+ * Program the 64-bit multicast hash filter.
+ */
+
+Static void
+rue_setmulti(struct rue_softc *sc)
+{
+ struct ifnet *ifp;
+ int h = 0;
+ u_int32_t hashes[2] = { 0, 0 };
+ struct ifmultiaddr *ifma;
+ u_int32_t rxcfg;
+ int mcnt = 0;
+
+ ifp = &sc->arpcom.ac_if;
+
+ rxcfg = rue_csr_read_2(sc, RUE_RCR);
+
+ if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
+ rxcfg |= (RUE_RCR_AAM | RUE_RCR_AAP);
+ rxcfg &= ~RUE_RCR_AM;
+ rue_csr_write_2(sc, RUE_RCR, rxcfg);
+ rue_csr_write_4(sc, RUE_MAR0, 0xFFFFFFFF);
+ rue_csr_write_4(sc, RUE_MAR4, 0xFFFFFFFF);
+ return;
+ }
+
+ /* first, zot all the existing hash bits */
+ rue_csr_write_4(sc, RUE_MAR0, 0);
+ rue_csr_write_4(sc, RUE_MAR4, 0);
+
+ /* now program new ones */
+ TAILQ_FOREACH (ifma, &ifp->if_multiaddrs, ifma_link) {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ h = rue_calchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr));
+ if (h < 32)
+ hashes[0] |= (1 << h);
+ else
+ hashes[1] |= (1 << (h - 32));
+ mcnt++;
+ }
+
+ if (mcnt)
+ rxcfg |= RUE_RCR_AM;
+ else
+ rxcfg &= ~RUE_RCR_AM;
+
+ rxcfg &= ~(RUE_RCR_AAM | RUE_RCR_AAP);
+
+ rue_csr_write_2(sc, RUE_RCR, rxcfg);
+ rue_csr_write_4(sc, RUE_MAR0, hashes[0]);
+ rue_csr_write_4(sc, RUE_MAR4, hashes[1]);
+}
+
+Static void
+rue_reset(struct rue_softc *sc)
+{
+ int i;
+
+ rue_csr_write_1(sc, RUE_CR, RUE_CR_SOFT_RST);
+
+ for (i = 0; i < RUE_TIMEOUT; i++) {
+ DELAY(500);
+ if (!(rue_csr_read_1(sc, RUE_CR) & RUE_CR_SOFT_RST))
+ break;
+ }
+ if (i == RUE_TIMEOUT)
+ printf("rue%d: reset never completed!\n", sc->rue_unit);
+
+ DELAY(10000);
+}
+
+/*
+ * Probe for a RTL8150 chip.
+ */
+
+USB_MATCH(rue)
+{
+ USB_MATCH_START(rue, uaa);
+ struct rue_type *t;
+
+ if (uaa->iface == NULL)
+ return (UMATCH_NONE);
+
+ t = rue_devs;
+ while (t->rue_vid) {
+ if (uaa->vendor == t->rue_vid &&
+ uaa->product == t->rue_did) {
+ return (UMATCH_VENDOR_PRODUCT);
+ }
+ t++;
+ }
+
+ return (UMATCH_NONE);
+}
+
+/*
+ * Attach the interface. Allocate softc structures, do ifmedia
+ * setup and ethernet/BPF attach.
+ */
+
+USB_ATTACH(rue)
+{
+ USB_ATTACH_START(rue, sc, uaa);
+ char *devinfo;
+ u_char eaddr[ETHER_ADDR_LEN];
+ struct ifnet *ifp;
+ usbd_interface_handle iface;
+ usbd_status err;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ int i;
+ struct rue_type *t;
+
+ devinfo = malloc(1024, M_USBDEV, M_WAITOK);
+
+ bzero(sc, sizeof (struct rue_softc));
+ usbd_devinfo(uaa->device, 0, devinfo);
+
+ sc->rue_udev = uaa->device;
+ sc->rue_unit = device_get_unit(self);
+
+ if (usbd_set_config_no(sc->rue_udev, RUE_CONFIG_NO, 0)) {
+ printf("rue%d: getting interface handle failed\n",
+ sc->rue_unit);
+ goto error;
+ }
+
+ err = usbd_device2interface_handle(uaa->device, RUE_IFACE_IDX, &iface);
+ if (err) {
+ printf("rue%d: getting interface handle failed\n",
+ sc->rue_unit);
+ goto error;
+ }
+
+ sc->rue_iface = iface;
+
+ t = rue_devs;
+ while (t->rue_vid) {
+ if (uaa->vendor == t->rue_vid &&
+ uaa->product == t->rue_did) {
+ sc->rue_info = t;
+ break;
+ }
+ t++;
+ }
+
+ id = usbd_get_interface_descriptor(sc->rue_iface);
+
+ usbd_devinfo(uaa->device, 0, devinfo);
+ device_set_desc_copy(self, devinfo);
+ printf("%s: %s\n", USBDEVNAME(self), devinfo);
+
+ /* Find endpoints */
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(iface, i);
+ if (ed == NULL) {
+ printf("rue%d: couldn't get ep %d\n", sc->rue_unit, i);
+ goto error;
+ }
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->rue_ed[RUE_ENDPT_RX] = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->rue_ed[RUE_ENDPT_TX] = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
+ sc->rue_ed[RUE_ENDPT_INTR] = ed->bEndpointAddress;
+ }
+ }
+
+ mtx_init(&sc->rue_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK,
+ MTX_DEF | MTX_RECURSE);
+ RUE_LOCK(sc);
+
+ /* Reset the adapter */
+ rue_reset(sc);
+
+ /* Get station address from the EEPROM */
+ err = rue_read_mem(sc, RUE_EEPROM_IDR0,
+ (caddr_t)&eaddr, ETHER_ADDR_LEN);
+ if (err) {
+ printf("rue%d: couldn't get station address\n", sc->rue_unit);
+ goto error1;
+ }
+
+ /* RealTek RTL8150 was detected */
+ printf("rue%d: Ethernet address: %6D\n", sc->rue_unit, eaddr, ":");
+
+ bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
+
+ ifp = &sc->arpcom.ac_if;
+ ifp->if_softc = sc;
+ ifp->if_unit = sc->rue_unit;
+ ifp->if_name = "rue";
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_ioctl = rue_ioctl;
+ ifp->if_output = ether_output;
+ ifp->if_start = rue_start;
+ ifp->if_watchdog = rue_watchdog;
+ ifp->if_init = rue_init;
+ ifp->if_baudrate = 10000000;
+ ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
+
+ /* MII setup */
+ if (mii_phy_probe(self, &sc->rue_miibus,
+ rue_ifmedia_upd, rue_ifmedia_sts)) {
+ printf("rue%d: MII without any PHY!\n", sc->rue_unit);
+ goto error1;
+ }
+
+ rue_qdat.ifp = ifp;
+ rue_qdat.if_rxstart = rue_rxstart;
+
+ /* Call MI attach routine */
+ ether_ifattach(ifp, eaddr);
+ callout_handle_init(&sc->rue_stat_ch);
+ usb_register_netisr();
+ sc->rue_dying = 0;
+
+ RUE_UNLOCK(sc);
+ free(devinfo, M_USBDEV);
+ USB_ATTACH_SUCCESS_RETURN;
+
+ error1:
+ RUE_UNLOCK(sc);
+ mtx_destroy(&sc->rue_mtx);
+ error:
+ free(devinfo, M_USBDEV);
+ USB_ATTACH_ERROR_RETURN;
+}
+
+Static int
+rue_detach(device_ptr_t dev)
+{
+ struct rue_softc *sc;
+ struct ifnet *ifp;
+
+ sc = device_get_softc(dev);
+ RUE_LOCK(sc);
+ ifp = &sc->arpcom.ac_if;
+
+ sc->rue_dying = 1;
+ untimeout(rue_tick, sc, sc->rue_stat_ch);
+ ether_ifdetach(ifp);
+
+ if (sc->rue_ep[RUE_ENDPT_TX] != NULL)
+ usbd_abort_pipe(sc->rue_ep[RUE_ENDPT_TX]);
+ if (sc->rue_ep[RUE_ENDPT_RX] != NULL)
+ usbd_abort_pipe(sc->rue_ep[RUE_ENDPT_RX]);
+#ifdef RUE_INTR_PIPE
+ if (sc->rue_ep[RUE_ENDPT_INTR] != NULL)
+ usbd_abort_pipe(sc->rue_ep[RUE_ENDPT_INTR]);
+#endif
+
+ RUE_UNLOCK(sc);
+ mtx_destroy(&sc->rue_mtx);
+
+ return (0);
+}
+
+/*
+ * Initialize an RX descriptor and attach an MBUF cluster.
+ */
+
+Static int
+rue_newbuf(struct rue_softc *sc, struct rue_chain *c, struct mbuf *m)
+{
+ struct mbuf *m_new = NULL;
+
+ if (m == NULL) {
+ MGETHDR(m_new, M_NOWAIT, MT_DATA);
+ if (m_new == NULL) {
+ printf("rue%d: no memory for rx list "
+ "-- packet dropped!\n", sc->rue_unit);
+ return (ENOBUFS);
+ }
+
+ MCLGET(m_new, M_NOWAIT);
+ if (!(m_new->m_flags & M_EXT)) {
+ printf("rue%d: no memory for rx list "
+ "-- packet dropped!\n", sc->rue_unit);
+ m_freem(m_new);
+ return (ENOBUFS);
+ }
+ m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
+ } else {
+ m_new = m;
+ m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
+ m_new->m_data = m_new->m_ext.ext_buf;
+ }
+
+ m_adj(m_new, ETHER_ALIGN);
+ c->rue_mbuf = m_new;
+
+ return (0);
+}
+
+Static int
+rue_rx_list_init(struct rue_softc *sc)
+{
+ struct rue_cdata *cd;
+ struct rue_chain *c;
+ int i;
+
+ cd = &sc->rue_cdata;
+ for (i = 0; i < RUE_RX_LIST_CNT; i++) {
+ c = &cd->rue_rx_chain[i];
+ c->rue_sc = sc;
+ c->rue_idx = i;
+ if (rue_newbuf(sc, c, NULL) == ENOBUFS)
+ return (ENOBUFS);
+ if (c->rue_xfer == NULL) {
+ c->rue_xfer = usbd_alloc_xfer(sc->rue_udev);
+ if (c->rue_xfer == NULL)
+ return (ENOBUFS);
+ }
+ }
+
+ return (0);
+}
+
+Static int
+rue_tx_list_init(struct rue_softc *sc)
+{
+ struct rue_cdata *cd;
+ struct rue_chain *c;
+ int i;
+
+ cd = &sc->rue_cdata;
+ for (i = 0; i < RUE_TX_LIST_CNT; i++) {
+ c = &cd->rue_tx_chain[i];
+ c->rue_sc = sc;
+ c->rue_idx = i;
+ c->rue_mbuf = NULL;
+ if (c->rue_xfer == NULL) {
+ c->rue_xfer = usbd_alloc_xfer(sc->rue_udev);
+ if (c->rue_xfer == NULL)
+ return (ENOBUFS);
+ }
+ c->rue_buf = malloc(RUE_BUFSZ, M_USBDEV, M_NOWAIT);
+ if (c->rue_buf == NULL)
+ return (ENOBUFS);
+ }
+
+ return (0);
+}
+
+#ifdef RUE_INTR_PIPE
+Static void
+rue_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct rue_softc *sc = priv;
+ struct ifnet *ifp;
+ struct rue_intrpkt *p;
+
+ RUE_LOCK(sc);
+ ifp = &sc->arpcom.ac_if;
+
+ if (!(ifp->if_flags & IFF_RUNNING)) {
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
+ RUE_UNLOCK(sc);
+ return;
+ }
+ printf("rue%d: usb error on intr: %s\n", sc->rue_unit,
+ usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall(sc->rue_ep[RUE_ENDPT_INTR]);
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ usbd_get_xfer_status(xfer, NULL, (void **)&p, NULL, NULL);
+
+ ifp->if_ierrors += p->rue_rxlost_cnt;
+ ifp->if_ierrors += p->rue_crcerr_cnt;
+ ifp->if_collisions += p->rue_col_cnt;
+
+ RUE_UNLOCK(sc);
+}
+#endif
+
+Static void
+rue_rxstart(struct ifnet *ifp)
+{
+ struct rue_softc *sc;
+ struct rue_chain *c;
+
+ sc = ifp->if_softc;
+ RUE_LOCK(sc);
+ c = &sc->rue_cdata.rue_rx_chain[sc->rue_cdata.rue_rx_prod];
+
+ if (rue_newbuf(sc, c, NULL) == ENOBUFS) {
+ ifp->if_ierrors++;
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ /* Setup new transfer. */
+ usbd_setup_xfer(c->rue_xfer, sc->rue_ep[RUE_ENDPT_RX],
+ c, mtod(c->rue_mbuf, char *), RUE_BUFSZ, USBD_SHORT_XFER_OK,
+ USBD_NO_TIMEOUT, rue_rxeof);
+ usbd_transfer(c->rue_xfer);
+
+ RUE_UNLOCK(sc);
+}
+
+/*
+ * A frame has been uploaded: pass the resulting mbuf chain up to
+ * the higher level protocols.
+ */
+
+Static void
+rue_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct rue_chain *c = priv;
+ struct rue_softc *sc = c->rue_sc;
+ struct mbuf *m;
+ struct ifnet *ifp;
+ int total_len = 0;
+ struct rue_rxpkt r;
+
+ if (sc->rue_dying)
+ return;
+ RUE_LOCK(sc);
+ ifp = &sc->arpcom.ac_if;
+
+ if (!(ifp->if_flags & IFF_RUNNING)) {
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
+ RUE_UNLOCK(sc);
+ return;
+ }
+ if (usbd_ratecheck(&sc->rue_rx_notice))
+ printf("rue%d: usb error on rx: %s\n", sc->rue_unit,
+ usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall(sc->rue_ep[RUE_ENDPT_RX]);
+ goto done;
+ }
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL);
+
+ if (total_len <= ETHER_CRC_LEN) {
+ ifp->if_ierrors++;
+ goto done;
+ }
+
+ m = c->rue_mbuf;
+ bcopy(mtod(m, char *) + total_len - 4, (char *)&r, sizeof (r));
+
+ /* Check recieve packet was valid or not */
+ if ((r.rue_rxstat & RUE_RXSTAT_VALID) == 0) {
+ ifp->if_ierrors++;
+ goto done;
+ }
+
+ /* No errors; receive the packet. */
+ total_len -= ETHER_CRC_LEN;
+
+ ifp->if_ipackets++;
+ m->m_pkthdr.rcvif = (struct ifnet *)&rue_qdat;
+ m->m_pkthdr.len = m->m_len = total_len;
+
+ /* Put the packet on the special USB input queue. */
+ usb_ether_input(m);
+
+ RUE_UNLOCK(sc);
+ return;
+
+ done:
+ /* Setup new transfer. */
+ usbd_setup_xfer(xfer, sc->rue_ep[RUE_ENDPT_RX],
+ c, mtod(c->rue_mbuf, char *), RUE_BUFSZ,
+ USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, rue_rxeof);
+ usbd_transfer(xfer);
+ RUE_UNLOCK(sc);
+}
+
+/*
+ * A frame was downloaded to the chip. It's safe for us to clean up
+ * the list buffers.
+ */
+
+Static void
+rue_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct rue_chain *c = priv;
+ struct rue_softc *sc = c->rue_sc;
+ struct ifnet *ifp;
+ usbd_status err;
+
+ RUE_LOCK(sc);
+
+ ifp = &sc->arpcom.ac_if;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
+ RUE_UNLOCK(sc);
+ return;
+ }
+ printf("rue%d: usb error on tx: %s\n", sc->rue_unit,
+ usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall(sc->rue_ep[RUE_ENDPT_TX]);
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ ifp->if_timer = 0;
+ ifp->if_flags &= ~IFF_OACTIVE;
+ usbd_get_xfer_status(c->rue_xfer, NULL, NULL, NULL, &err);
+
+ if (c->rue_mbuf != NULL) {
+ c->rue_mbuf->m_pkthdr.rcvif = ifp;
+ usb_tx_done(c->rue_mbuf);
+ c->rue_mbuf = NULL;
+ }
+
+ if (err)
+ ifp->if_oerrors++;
+ else
+ ifp->if_opackets++;
+
+ RUE_UNLOCK(sc);
+}
+
+Static void
+rue_tick(void *xsc)
+{
+ struct rue_softc *sc = xsc;
+ struct ifnet *ifp;
+ struct mii_data *mii;
+
+ if (sc == NULL)
+ return;
+
+ RUE_LOCK(sc);
+
+ ifp = &sc->arpcom.ac_if;
+ mii = GET_MII(sc);
+ if (mii == NULL) {
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ mii_tick(mii);
+ if (!sc->rue_link && mii->mii_media_status & IFM_ACTIVE &&
+ IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
+ sc->rue_link++;
+ if (ifp->if_snd.ifq_head != NULL)
+ rue_start(ifp);
+ }
+
+ sc->rue_stat_ch = timeout(rue_tick, sc, hz);
+
+ RUE_UNLOCK(sc);
+}
+
+Static int
+rue_encap(struct rue_softc *sc, struct mbuf *m, int idx)
+{
+ int total_len;
+ struct rue_chain *c;
+ usbd_status err;
+
+ c = &sc->rue_cdata.rue_tx_chain[idx];
+
+ /*
+ * Copy the mbuf data into a contiguous buffer
+ */
+ m_copydata(m, 0, m->m_pkthdr.len, c->rue_buf);
+ c->rue_mbuf = m;
+
+ total_len = m->m_pkthdr.len;
+
+ /*
+ * This is an undocumented behavior.
+ * RTL8150 chip doesn't send frame length smaller than
+ * RUE_MIN_FRAMELEN (60) byte packet.
+ */
+ if (total_len < RUE_MIN_FRAMELEN)
+ total_len = RUE_MIN_FRAMELEN;
+
+ usbd_setup_xfer(c->rue_xfer, sc->rue_ep[RUE_ENDPT_TX],
+ c, c->rue_buf, total_len, USBD_FORCE_SHORT_XFER,
+ 10000, rue_txeof);
+
+ /* Transmit */
+ err = usbd_transfer(c->rue_xfer);
+ if (err != USBD_IN_PROGRESS) {
+ rue_stop(sc);
+ return (EIO);
+ }
+
+ sc->rue_cdata.rue_tx_cnt++;
+
+ return (0);
+}
+
+Static void
+rue_start(struct ifnet *ifp)
+{
+ struct rue_softc *sc = ifp->if_softc;
+ struct mbuf *m_head = NULL;
+
+ RUE_LOCK(sc);
+
+ if (!sc->rue_link) {
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ if (ifp->if_flags & IFF_OACTIVE) {
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ IF_DEQUEUE(&ifp->if_snd, m_head);
+ if (m_head == NULL) {
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ if (rue_encap(sc, m_head, 0)) {
+ IF_PREPEND(&ifp->if_snd, m_head);
+ ifp->if_flags |= IFF_OACTIVE;
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ /*
+ * If there's a BPF listener, bounce a copy of this frame
+ * to him.
+ */
+ BPF_MTAP(ifp, m_head);
+
+ ifp->if_flags |= IFF_OACTIVE;
+
+ /*
+ * Set a timeout in case the chip goes out to lunch.
+ */
+ ifp->if_timer = 5;
+
+ RUE_UNLOCK(sc);
+}
+
+Static void
+rue_init(void *xsc)
+{
+ struct rue_softc *sc = xsc;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct mii_data *mii = GET_MII(sc);
+ struct rue_chain *c;
+ usbd_status err;
+ int i;
+ int rxcfg;
+
+ RUE_LOCK(sc);
+
+ if (ifp->if_flags & IFF_RUNNING) {
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ /*
+ * Cancel pending I/O and free all RX/TX buffers.
+ */
+ rue_reset(sc);
+
+ /* Set MAC address */
+ rue_write_mem(sc, RUE_IDR0, sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
+
+ /* Init TX ring. */
+ if (rue_tx_list_init(sc) == ENOBUFS) {
+ printf("rue%d: tx list init failed\n", sc->rue_unit);
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ /* Init RX ring. */
+ if (rue_rx_list_init(sc) == ENOBUFS) {
+ printf("rue%d: rx list init failed\n", sc->rue_unit);
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+#ifdef RUE_INTR_PIPE
+ sc->rue_cdata.rue_ibuf = malloc(RUE_INTR_PKTLEN, M_USBDEV, M_NOWAIT);
+#endif
+
+ /*
+ * Set the initial TX and RX configuration.
+ */
+ rue_csr_write_1(sc, RUE_TCR, RUE_TCR_CONFIG);
+
+ rxcfg = RUE_RCR_CONFIG;
+
+ /* Set capture broadcast bit to capture broadcast frames. */
+ if (ifp->if_flags & IFF_BROADCAST)
+ rxcfg |= RUE_RCR_AB;
+ else
+ rxcfg &= ~RUE_RCR_AB;
+
+ /* If we want promiscuous mode, set the allframes bit. */
+ if (ifp->if_flags & IFF_PROMISC)
+ rxcfg |= RUE_RCR_AAP;
+ else
+ rxcfg &= ~RUE_RCR_AAP;
+
+ rue_csr_write_2(sc, RUE_RCR, rxcfg);
+
+ /* Load the multicast filter. */
+ rue_setmulti(sc);
+
+ /* Enable RX and TX */
+ rue_csr_write_1(sc, RUE_CR, (RUE_CR_TE | RUE_CR_RE | RUE_CR_EP3CLREN));
+
+ mii_mediachg(mii);
+
+ /* Open RX and TX pipes. */
+ err = usbd_open_pipe(sc->rue_iface, sc->rue_ed[RUE_ENDPT_RX],
+ USBD_EXCLUSIVE_USE, &sc->rue_ep[RUE_ENDPT_RX]);
+ if (err) {
+ printf("rue%d: open rx pipe failed: %s\n",
+ sc->rue_unit, usbd_errstr(err));
+ RUE_UNLOCK(sc);
+ return;
+ }
+ err = usbd_open_pipe(sc->rue_iface, sc->rue_ed[RUE_ENDPT_TX],
+ USBD_EXCLUSIVE_USE, &sc->rue_ep[RUE_ENDPT_TX]);
+ if (err) {
+ printf("rue%d: open tx pipe failed: %s\n",
+ sc->rue_unit, usbd_errstr(err));
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+#ifdef RUE_INTR_PIPE
+ err = usbd_open_pipe_intr(sc->rue_iface, sc->rue_ed[RUE_ENDPT_INTR],
+ USBD_SHORT_XFER_OK,
+ &sc->rue_ep[RUE_ENDPT_INTR], sc,
+ sc->rue_cdata.rue_ibuf, RUE_INTR_PKTLEN,
+ rue_intr, RUE_INTR_INTERVAL);
+ if (err) {
+ printf("rue%d: open intr pipe failed: %s\n",
+ sc->rue_unit, usbd_errstr(err));
+ RUE_UNLOCK(sc);
+ return;
+ }
+#endif
+
+ /* Start up the receive pipe. */
+ for (i = 0; i < RUE_RX_LIST_CNT; i++) {
+ c = &sc->rue_cdata.rue_rx_chain[i];
+ usbd_setup_xfer(c->rue_xfer, sc->rue_ep[RUE_ENDPT_RX],
+ c, mtod(c->rue_mbuf, char *), RUE_BUFSZ,
+ USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, rue_rxeof);
+ usbd_transfer(c->rue_xfer);
+ }
+
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ sc->rue_stat_ch = timeout(rue_tick, sc, hz);
+
+ RUE_UNLOCK(sc);
+}
+
+/*
+ * Set media options.
+ */
+
+Static int
+rue_ifmedia_upd(struct ifnet *ifp)
+{
+ struct rue_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+
+ sc->rue_link = 0;
+ if (mii->mii_instance) {
+ struct mii_softc *miisc;
+ LIST_FOREACH (miisc, &mii->mii_phys, mii_list)
+ mii_phy_reset(miisc);
+ }
+ mii_mediachg(mii);
+
+ return (0);
+}
+
+/*
+ * Report current media status.
+ */
+
+Static void
+rue_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct rue_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+
+ mii_pollstat(mii);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+}
+
+Static int
+rue_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
+{
+ struct rue_softc *sc = ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *)data;
+ struct mii_data *mii;
+ int error = 0;
+
+ RUE_LOCK(sc);
+
+ switch (command) {
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_UP) {
+ if (ifp->if_flags & IFF_RUNNING &&
+ ifp->if_flags & IFF_PROMISC &&
+ !(sc->rue_if_flags & IFF_PROMISC)) {
+ RUE_SETBIT_2(sc, RUE_RCR,
+ (RUE_RCR_AAM | RUE_RCR_AAP));
+ rue_setmulti(sc);
+ } else if (ifp->if_flags & IFF_RUNNING &&
+ !(ifp->if_flags & IFF_PROMISC) &&
+ sc->rue_if_flags & IFF_PROMISC) {
+ RUE_CLRBIT_2(sc, RUE_RCR,
+ (RUE_RCR_AAM | RUE_RCR_AAP));
+ rue_setmulti(sc);
+ } else if (!(ifp->if_flags & IFF_RUNNING))
+ rue_init(sc);
+ } else {
+ if (ifp->if_flags & IFF_RUNNING)
+ rue_stop(sc);
+ }
+ sc->rue_if_flags = ifp->if_flags;
+ error = 0;
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ rue_setmulti(sc);
+ error = 0;
+ break;
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ mii = GET_MII(sc);
+ error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
+ break;
+ default:
+ error = ether_ioctl(ifp, command, data);
+ break;
+ }
+
+ RUE_UNLOCK(sc);
+
+ return (error);
+}
+
+Static void
+rue_watchdog(struct ifnet *ifp)
+{
+ struct rue_softc *sc = ifp->if_softc;
+ struct rue_chain *c;
+ usbd_status stat;
+
+ RUE_LOCK(sc);
+
+ ifp->if_oerrors++;
+ printf("rue%d: watchdog timeout\n", sc->rue_unit);
+
+ c = &sc->rue_cdata.rue_tx_chain[0];
+ usbd_get_xfer_status(c->rue_xfer, NULL, NULL, NULL, &stat);
+ rue_txeof(c->rue_xfer, c, stat);
+
+ if (ifp->if_snd.ifq_head != NULL)
+ rue_start(ifp);
+
+ RUE_UNLOCK(sc);
+}
+
+/*
+ * Stop the adapter and free any mbufs allocated to the
+ * RX and TX lists.
+ */
+
+Static void
+rue_stop(struct rue_softc *sc)
+{
+ usbd_status err;
+ struct ifnet *ifp;
+ int i;
+
+ RUE_LOCK(sc);
+
+ ifp = &sc->arpcom.ac_if;
+ ifp->if_timer = 0;
+
+ rue_csr_write_1(sc, RUE_CR, 0x00);
+ rue_reset(sc);
+
+ untimeout(rue_tick, sc, sc->rue_stat_ch);
+
+ /* Stop transfers. */
+ if (sc->rue_ep[RUE_ENDPT_RX] != NULL) {
+ err = usbd_abort_pipe(sc->rue_ep[RUE_ENDPT_RX]);
+ if (err) {
+ printf("rue%d: abort rx pipe failed: %s\n",
+ sc->rue_unit, usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->rue_ep[RUE_ENDPT_RX]);
+ if (err) {
+ printf("rue%d: close rx pipe failed: %s\n",
+ sc->rue_unit, usbd_errstr(err));
+ }
+ sc->rue_ep[RUE_ENDPT_RX] = NULL;
+ }
+
+ if (sc->rue_ep[RUE_ENDPT_TX] != NULL) {
+ err = usbd_abort_pipe(sc->rue_ep[RUE_ENDPT_TX]);
+ if (err) {
+ printf("rue%d: abort tx pipe failed: %s\n",
+ sc->rue_unit, usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->rue_ep[RUE_ENDPT_TX]);
+ if (err) {
+ printf("rue%d: close tx pipe failed: %s\n",
+ sc->rue_unit, usbd_errstr(err));
+ }
+ sc->rue_ep[RUE_ENDPT_TX] = NULL;
+ }
+
+#ifdef RUE_INTR_PIPE
+ if (sc->rue_ep[RUE_ENDPT_INTR] != NULL) {
+ err = usbd_abort_pipe(sc->rue_ep[RUE_ENDPT_INTR]);
+ if (err) {
+ printf("rue%d: abort intr pipe failed: %s\n",
+ sc->rue_unit, usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->rue_ep[RUE_ENDPT_INTR]);
+ if (err) {
+ printf("rue%d: close intr pipe failed: %s\n",
+ sc->rue_unit, usbd_errstr(err));
+ }
+ sc->rue_ep[RUE_ENDPT_INTR] = NULL;
+ }
+#endif
+
+ /* Free RX resources. */
+ for (i = 0; i < RUE_RX_LIST_CNT; i++) {
+ if (sc->rue_cdata.rue_rx_chain[i].rue_buf != NULL) {
+ free(sc->rue_cdata.rue_rx_chain[i].rue_buf, M_USBDEV);
+ sc->rue_cdata.rue_rx_chain[i].rue_buf = NULL;
+ }
+ if (sc->rue_cdata.rue_rx_chain[i].rue_mbuf != NULL) {
+ m_freem(sc->rue_cdata.rue_rx_chain[i].rue_mbuf);
+ sc->rue_cdata.rue_rx_chain[i].rue_mbuf = NULL;
+ }
+ if (sc->rue_cdata.rue_rx_chain[i].rue_xfer != NULL) {
+ usbd_free_xfer(sc->rue_cdata.rue_rx_chain[i].rue_xfer);
+ sc->rue_cdata.rue_rx_chain[i].rue_xfer = NULL;
+ }
+ }
+
+ /* Free TX resources. */
+ for (i = 0; i < RUE_TX_LIST_CNT; i++) {
+ if (sc->rue_cdata.rue_tx_chain[i].rue_buf != NULL) {
+ free(sc->rue_cdata.rue_tx_chain[i].rue_buf, M_USBDEV);
+ sc->rue_cdata.rue_tx_chain[i].rue_buf = NULL;
+ }
+ if (sc->rue_cdata.rue_tx_chain[i].rue_mbuf != NULL) {
+ m_freem(sc->rue_cdata.rue_tx_chain[i].rue_mbuf);
+ sc->rue_cdata.rue_tx_chain[i].rue_mbuf = NULL;
+ }
+ if (sc->rue_cdata.rue_tx_chain[i].rue_xfer != NULL) {
+ usbd_free_xfer(sc->rue_cdata.rue_tx_chain[i].rue_xfer);
+ sc->rue_cdata.rue_tx_chain[i].rue_xfer = NULL;
+ }
+ }
+
+#ifdef RUE_INTR_PIPE
+ free(sc->rue_cdata.rue_ibuf, M_USBDEV);
+ sc->rue_cdata.rue_ibuf = NULL;
+#endif
+
+ sc->rue_link = 0;
+
+ ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+
+ RUE_UNLOCK(sc);
+}
+
+/*
+ * Stop all chip I/O so that the kernel's probe routines don't
+ * get confused by errant DMAs when rebooting.
+ */
+
+Static void
+rue_shutdown(device_ptr_t dev)
+{
+ struct rue_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ sc->rue_dying++;
+ RUE_LOCK(sc);
+ rue_reset(sc);
+ rue_stop(sc);
+ RUE_UNLOCK(sc);
+}
diff --git a/sys/dev/usb/if_ruereg.h b/sys/dev/usb/if_ruereg.h
new file mode 100644
index 0000000..ec8b0e6
--- /dev/null
+++ b/sys/dev/usb/if_ruereg.h
@@ -0,0 +1,247 @@
+/*-
+ * Copyright (c) 2001-2003, Shunsuke Akiyama <akiyama@FreeBSD.org>.
+ * 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$
+ */
+
+#ifndef _IF_RUEREG_H_
+#define _IF_RUEREG_H_
+
+#define RUE_INTR_PIPE 1 /* Use INTR PIPE */
+
+#define RUE_CONFIG_NO 1
+#define RUE_IFACE_IDX 0
+
+#define RUE_ENDPT_RX 0x0
+#define RUE_ENDPT_TX 0x1
+#define RUE_ENDPT_INTR 0x2
+#define RUE_ENDPT_MAX 0x3
+
+#define RUE_INTR_PKTLEN 0x8
+
+#define RUE_TIMEOUT 1000
+#define ETHER_ALIGN 2
+#define RUE_BUFSZ 1536
+#define RUE_MIN_FRAMELEN 60
+#define RUE_INTR_INTERVAL 100 /* ms */
+
+/*
+ * Registers
+ */
+
+#define RUE_IDR0 0x0120
+#define RUE_IDR1 0x0121
+#define RUE_IDR2 0x0122
+#define RUE_IDR3 0x0123
+#define RUE_IDR4 0x0124
+#define RUE_IDR5 0x0125
+
+#define RUE_MAR0 0x0126
+#define RUE_MAR1 0x0127
+#define RUE_MAR2 0x0128
+#define RUE_MAR3 0x0129
+#define RUE_MAR4 0x012A
+#define RUE_MAR5 0x012B
+#define RUE_MAR6 0x012C
+#define RUE_MAR7 0x012D
+
+#define RUE_CR 0x012E /* B, R/W */
+#define RUE_CR_SOFT_RST 0x10
+#define RUE_CR_RE 0x08
+#define RUE_CR_TE 0x04
+#define RUE_CR_EP3CLREN 0x02
+
+#define RUE_TCR 0x012F /* B, R/W */
+#define RUE_TCR_TXRR1 0x80
+#define RUE_TCR_TXRR0 0x40
+#define RUE_TCR_IFG1 0x10
+#define RUE_TCR_IFG0 0x08
+#define RUE_TCR_NOCRC 0x01
+#define RUE_TCR_CONFIG (RUE_TCR_TXRR1|RUE_TCR_TXRR0|RUE_TCR_IFG1|RUE_TCR_IFG0)
+
+#define RUE_RCR 0x0130 /* W, R/W */
+#define RUE_RCR_TAIL 0x80
+#define RUE_RCR_AER 0x40
+#define RUE_RCR_AR 0x20
+#define RUE_RCR_AM 0x10
+#define RUE_RCR_AB 0x08
+#define RUE_RCR_AD 0x04
+#define RUE_RCR_AAM 0x02
+#define RUE_RCR_AAP 0x01
+#define RUE_RCR_CONFIG (RUE_RCR_TAIL|RUE_RCR_AD)
+
+#define RUE_TSR 0x0132
+#define RUE_RSR 0x0133
+#define RUE_CON0 0x0135
+#define RUE_CON1 0x0136
+#define RUE_MSR 0x0137
+#define RUE_PHYADD 0x0138
+#define RUE_PHYDAT 0x0139
+
+#define RUE_PHYCNT 0x013B /* B, R/W */
+#define RUE_PHYCNT_PHYOWN 0x40
+#define RUE_PHYCNT_RWCR 0x20
+
+#define RUE_GPPC 0x013D
+#define RUE_WAKECNT 0x013E
+
+#define RUE_BMCR 0x0140
+#define RUE_BMCR_SPD_SET 0x2000
+#define RUE_BMCR_DUPLEX 0x0100
+
+#define RUE_BMSR 0x0142
+
+#define RUE_ANAR 0x0144 /* W, R/W */
+#define RUE_ANAR_PAUSE 0x0400
+
+#define RUE_ANLP 0x0146 /* W, R/O */
+#define RUE_ANLP_PAUSE 0x0400
+
+#define RUE_AER 0x0148
+
+#define RUE_NWAYT 0x014A
+#define RUE_CSCR 0x014C
+
+#define RUE_CRC0 0x014E
+#define RUE_CRC1 0x0150
+#define RUE_CRC2 0x0152
+#define RUE_CRC3 0x0154
+#define RUE_CRC4 0x0156
+
+#define RUE_BYTEMASK0 0x0158
+#define RUE_BYTEMASK1 0x0160
+#define RUE_BYTEMASK2 0x0168
+#define RUE_BYTEMASK3 0x0170
+#define RUE_BYTEMASK4 0x0178
+
+#define RUE_PHY1 0x0180
+#define RUE_PHY2 0x0184
+
+#define RUE_TW1 0x0186
+
+#define RUE_REG_MIN 0x0120
+#define RUE_REG_MAX 0x0189
+
+/*
+ * EEPROM address declarations
+ */
+
+#define RUE_EEPROM_BASE 0x1200
+
+#define RUE_EEPROM_IDR0 (RUE_EEPROM_BASE + 0x02)
+#define RUE_EEPROM_IDR1 (RUE_EEPROM_BASE + 0x03)
+#define RUE_EEPROM_IDR2 (RUE_EEPROM_BASE + 0x03)
+#define RUE_EEPROM_IDR3 (RUE_EEPROM_BASE + 0x03)
+#define RUE_EEPROM_IDR4 (RUE_EEPROM_BASE + 0x03)
+#define RUE_EEPROM_IDR5 (RUE_EEPROM_BASE + 0x03)
+
+#define RUE_EEPROM_INTERVAL (RUE_EEPROM_BASE + 0x17)
+
+struct rue_intrpkt {
+ u_int8_t rue_tsr;
+ u_int8_t rue_rsr;
+ u_int8_t rue_gep_msr;
+ u_int8_t rue_waksr;
+ u_int8_t rue_txok_cnt;
+ u_int8_t rue_rxlost_cnt;
+ u_int8_t rue_crcerr_cnt;
+ u_int8_t rue_col_cnt;
+};
+
+struct rue_rxpkt {
+ u_int16_t rue_pktlen : 12;
+ u_int16_t rue_rxstat : 4;
+};
+
+#define RUE_RXSTAT_VALID 0x01
+#define RUE_RXSTAT_RUNT 0x02
+#define RUE_RXSTAT_PMATCH 0x04
+#define RUE_RXSTAT_MCAST 0x08
+
+#define RUE_RXSTAT_MASK RUE_RXSTAT_VALID
+
+struct rue_type {
+ u_int16_t rue_vid;
+ u_int16_t rue_did;
+};
+
+#define RUE_TX_LIST_CNT 1
+#define RUE_RX_LIST_CNT 1
+
+struct rue_softc;
+
+struct rue_chain {
+ struct rue_softc *rue_sc;
+ usbd_xfer_handle rue_xfer;
+ char *rue_buf;
+ struct mbuf *rue_mbuf;
+ int rue_idx;
+};
+
+struct rue_cdata {
+ struct rue_chain rue_tx_chain[RUE_TX_LIST_CNT];
+ struct rue_chain rue_rx_chain[RUE_RX_LIST_CNT];
+ struct rue_intrpkt *rue_ibuf;
+ int rue_tx_prod;
+ int rue_tx_cons;
+ int rue_tx_cnt;
+ int rue_rx_prod;
+};
+
+struct rue_softc {
+ struct arpcom arpcom;
+ device_t rue_miibus;
+ usbd_device_handle rue_udev;
+ usbd_interface_handle rue_iface;
+ struct rue_type *rue_info;
+ int rue_ed[RUE_ENDPT_MAX];
+ usbd_pipe_handle rue_ep[RUE_ENDPT_MAX];
+ int rue_unit;
+ u_int8_t rue_link;
+ int rue_if_flags;
+ struct rue_cdata rue_cdata;
+ struct callout_handle rue_stat_ch;
+ struct mtx rue_mtx;
+ char rue_dying;
+ struct timeval rue_rx_notice;
+};
+
+#if defined(__FreeBSD__)
+#define GET_MII(sc) (device_get_softc((sc)->rue_miibus))
+#elif defined(__NetBSD__)
+#define GET_MII(sc) (&(sc)->rue_mii)
+#elif defined(__OpenBSD__)
+#define GET_MII(sc) (&(sc)->rue_mii)
+#endif
+
+#if 0
+#define RUE_LOCK(_sc) mtx_lock(&(_sc)->rue_mtx)
+#define RUE_UNLOCK(_sc) mtx_unlock(&(_sc)->rue_mtx)
+#else
+#define RUE_LOCK(_sc)
+#define RUE_UNLOCK(_sc)
+#endif
+
+#endif /* _IF_RUEREG_H_ */
diff --git a/sys/dev/usb/usbdevs b/sys/dev/usb/usbdevs
index 162c99f..92fc93c 100644
--- a/sys/dev/usb/usbdevs
+++ b/sys/dev/usb/usbdevs
@@ -335,6 +335,7 @@ vendor TODOS 0x0b0c Todos Data System
vendor NEC2 0x0b62 NEC
vendor ATI2 0x0b6f ATI
vendor ASIX 0x0b95 ASIX Electronics
+vendor REALTEK 0x0bda RealTek
vendor AGATE 0x0c08 Agate Technologies
vendor DMI 0x0c0b DMI
vendor LUWEN 0x0c76 Luwen
@@ -823,6 +824,7 @@ product MCT SITECOM_USB232 0x0230 Sitecom USB-232 Products
product MELCO LUATX1 0x0001 LUA-TX Ethernet
product MELCO LUATX5 0x0005 LUA-TX Ethernet
product MELCO LUA2TX5 0x0009 LUA2-TX Ethernet
+product MELCO LUAKTX 0x0012 LUA-KTX Ethernet
product MELCO DUBPXXG 0x001c USB-IDE Bridge: DUB-PxxG
/* Metricom products */
@@ -985,6 +987,9 @@ product QUICKSHOT STRIKEPAD 0x6238 USB StrikePad
/* Rainbow Technologies products */
product RAINBOW IKEY2000 0x1200 i-Key 2000
+/* ReakTek products */
+product REALTEK USBKR100 0x8150 USBKR100 USB Ethernet (GREEN HOUSE)
+
/* Roland products */
product ROLAND UM1 0x0009 UM-1 MIDI I/F
product ROLAND UM880N 0x0014 EDIROL UM-880 MIDI I/F (native)
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
index 59a0cdf..746d740 100644
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -96,6 +96,7 @@ SUBDIR= accf_data \
rc4 \
rl \
rp \
+ rue \
sbsh \
sf \
sis \
diff --git a/sys/modules/mii/Makefile b/sys/modules/mii/Makefile
index 67da740..dfebd60 100644
--- a/sys/modules/mii/Makefile
+++ b/sys/modules/mii/Makefile
@@ -7,6 +7,7 @@ SRCS= mii.c mii_physubr.c ukphy.c ukphy_subr.c bus_if.h pci_if.h
SRCS+= miibus_if.h miidevs.h device_if.h miibus_if.c e1000phy.c exphy.c nsphy.c
SRCS+= mlphy.c tlphy.c rlphy.c amphy.c dcphy.c pnphy.c inphy.c
SRCS+= bmtphy.c brgphy.c xmphy.c pnaphy.c lxtphy.c qsphy.c acphy.c nsgphy.c
+SRCS+= ruephy.c
EXPORT_SYMS= mii_mediachg \
mii_tick \
diff --git a/sys/modules/rue/Makefile b/sys/modules/rue/Makefile
new file mode 100644
index 0000000..92456e5
--- /dev/null
+++ b/sys/modules/rue/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+S= ${.CURDIR}/../..
+.PATH: $S/dev/usb
+
+KMOD= if_rue
+SRCS= if_rue.c opt_bdg.h opt_usb.h device_if.h bus_if.h
+SRCS+= miibus_if.h
+
+.include <bsd.kmod.mk>
OpenPOWER on IntegriCloud