add ure(4), a driver for Realtek RTL8152 10/100 USB Ethernet adapters,
authorjmatthew <jmatthew@openbsd.org>
Mon, 27 Jun 2016 11:42:47 +0000 (11:42 +0000)
committerjmatthew <jmatthew@openbsd.org>
Mon, 27 Jun 2016 11:42:47 +0000 (11:42 +0000)
ported from FreeBSD.

ok dereaadt@

sys/dev/usb/files.usb
sys/dev/usb/if_ure.c [new file with mode: 0644]
sys/dev/usb/if_urereg.h [new file with mode: 0644]

index 748ee95..4448282 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: files.usb,v 1.128 2016/06/15 19:39:34 gerhard Exp $
+#      $OpenBSD: files.usb,v 1.129 2016/06/27 11:42:47 jmatthew Exp $
 #      $NetBSD: files.usb,v 1.16 2000/02/14 20:29:54 augustss Exp $
 #
 # Config file and device description for machine-independent USB code.
@@ -270,6 +270,11 @@ device     url: ether, ifnet, mii
 attach url at uhub
 file   dev/usb/if_url.c                url
 
+# Realtek RTL8152
+device ure: ether, ifnet, mii
+attach ure at uhub
+file   dev/usb/if_ure.c                ure
+
 
 # Serial drivers
 # Modems
diff --git a/sys/dev/usb/if_ure.c b/sys/dev/usb/if_ure.c
new file mode 100644 (file)
index 0000000..1997f04
--- /dev/null
@@ -0,0 +1,1277 @@
+/*-
+ * Copyright (c) 2015 Kevin Lo <kevlo@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.
+ */
+
+#include "bpfilter.h"
+
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sockio.h>
+#include <sys/rwlock.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/device.h>
+
+#include <machine/bus.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <dev/mii/miivar.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 "if_urereg.h"
+
+#ifdef URE_DEBUG
+#define DPRINTF(x)     do { if (uredebug) printf x; } while (0)
+#define DPRINTFN(n,x)  do { if (uredebug >= (n)) printf x; } while (0)
+int    uredebug = 0;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+
+const struct usb_devno ure_devs[] = {
+       { USB_VENDOR_REALTEK, USB_PRODUCT_REALTEK_RTL8152 }
+};
+
+int    ure_match(struct device *, void *, void *);
+void   ure_attach(struct device *, struct device *, void *);
+int    ure_detach(struct device *, int);
+
+struct cfdriver ure_cd = {
+       NULL, "ure", DV_IFNET
+};
+
+const struct cfattach ure_ca = {
+       sizeof(struct ure_softc), ure_match, ure_attach, ure_detach
+};
+
+int            ure_ctl(struct ure_softc *, uint8_t, uint16_t, uint16_t,
+                   void *, int);
+int            ure_read_mem(struct ure_softc *, uint16_t, uint16_t, void *,
+                   int);
+int            ure_write_mem(struct ure_softc *, uint16_t, uint16_t, void *,
+                   int);
+uint8_t                ure_read_1(struct ure_softc *, uint16_t, uint16_t);
+uint16_t       ure_read_2(struct ure_softc *, uint16_t, uint16_t);
+uint32_t       ure_read_4(struct ure_softc *, uint16_t, uint16_t);
+int            ure_write_1(struct ure_softc *, uint16_t, uint16_t, uint32_t);
+int            ure_write_2(struct ure_softc *, uint16_t, uint16_t, uint32_t);
+int            ure_write_4(struct ure_softc *, uint16_t, uint16_t, uint32_t);
+uint16_t       ure_ocp_reg_read(struct ure_softc *, uint16_t);
+void           ure_ocp_reg_write(struct ure_softc *, uint16_t, uint16_t);
+
+void           ure_init(void *);
+void           ure_stop(struct ure_softc *);
+void           ure_start(struct ifnet *);
+void           ure_reset(struct ure_softc *);
+
+void           ure_miibus_statchg(struct device *);
+int            ure_miibus_readreg(struct device *, int, int);
+void           ure_miibus_writereg(struct device *, int, int, int);
+void           ure_lock_mii(struct ure_softc *);
+void           ure_unlock_mii(struct ure_softc *);
+
+int            ure_encap(struct ure_softc *, struct mbuf *, int);
+void           ure_rxeof(struct usbd_xfer *, void *, usbd_status);
+void           ure_txeof(struct usbd_xfer *, void *, usbd_status);
+int            ure_rx_list_init(struct ure_softc *);
+int            ure_tx_list_init(struct ure_softc *);
+struct mbuf *  ure_newbuf(void);
+
+void           ure_tick_task(void *);
+void           ure_tick(void *);
+
+int            ure_ifmedia_upd(struct ifnet *);
+void           ure_ifmedia_sts(struct ifnet *, struct ifmediareq *);
+int            ure_ioctl(struct ifnet *, u_long, caddr_t);
+void           ure_rtl8152_init(struct ure_softc *);
+void           ure_disable_teredo(struct ure_softc *);
+void           ure_init_fifo(struct ure_softc *);
+
+
+
+int
+ure_ctl(struct ure_softc *sc, uint8_t rw, uint16_t val, uint16_t index,
+    void *buf, int len)
+{
+       usb_device_request_t    req;
+       usbd_status             err;
+
+       if (usbd_is_dying(sc->ure_udev))
+               return 0;
+
+       if (rw == URE_CTL_WRITE)
+               req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+       else
+               req.bmRequestType = UT_READ_VENDOR_DEVICE;
+       req.bRequest = UR_SET_ADDRESS;
+       USETW(req.wValue, val);
+       USETW(req.wIndex, index);
+       USETW(req.wLength, len);
+
+       DPRINTFN(5, ("ure_ctl: rw %d, val 0x%04hu, index 0x%04hu, len %d\n",
+           rw, val, index, len));
+       err = usbd_do_request(sc->ure_udev, &req, buf);
+       if (err) {
+               DPRINTF(("ure_ctl: error %d\n", err));
+               return -1;
+       }
+
+       return 0;
+}
+
+int
+ure_read_mem(struct ure_softc *sc, uint16_t addr, uint16_t index,
+    void *buf, int len)
+{
+
+       return (ure_ctl(sc, URE_CTL_READ, addr, index, buf, len));
+}
+
+int
+ure_write_mem(struct ure_softc *sc, uint16_t addr, uint16_t index,
+    void *buf, int len)
+{
+
+       return (ure_ctl(sc, URE_CTL_WRITE, addr, index, buf, len));
+}
+
+uint8_t
+ure_read_1(struct ure_softc *sc, uint16_t reg, uint16_t index)
+{
+       uint32_t val;
+       uint8_t temp[4];
+       uint8_t shift;
+
+       shift = (reg & 3) << 3;
+       reg &= ~3;
+       
+       ure_read_mem(sc, reg, index, &temp, 4);
+       val = UGETDW(temp);
+       val >>= shift;
+
+       return (val & 0xff);
+}
+
+uint16_t
+ure_read_2(struct ure_softc *sc, uint16_t reg, uint16_t index)
+{
+       uint32_t val;
+       uint8_t temp[4];
+       uint8_t shift;
+
+       shift = (reg & 2) << 3;
+       reg &= ~3;
+
+       ure_read_mem(sc, reg, index, &temp, 4);
+       val = UGETDW(temp);
+       val >>= shift;
+
+       return (val & 0xffff);
+}
+
+uint32_t
+ure_read_4(struct ure_softc *sc, uint16_t reg, uint16_t index)
+{
+       uint8_t temp[4];
+
+       ure_read_mem(sc, reg, index, &temp, 4);
+       return (UGETDW(temp));
+}
+
+int
+ure_write_1(struct ure_softc *sc, uint16_t reg, uint16_t index, uint32_t val)
+{
+       uint16_t byen;
+       uint8_t temp[4];
+       uint8_t shift;
+
+       byen = URE_BYTE_EN_BYTE;
+       shift = reg & 3;
+       val &= 0xff;
+
+       if (reg & 3) {
+               byen <<= shift;
+               val <<= (shift << 3);
+               reg &= ~3;
+       }
+
+       USETDW(temp, val);
+       return (ure_write_mem(sc, reg, index | byen, &temp, 4));
+}
+
+int
+ure_write_2(struct ure_softc *sc, uint16_t reg, uint16_t index, uint32_t val)
+{
+       uint16_t byen;
+       uint8_t temp[4];
+       uint8_t shift;
+
+       byen = URE_BYTE_EN_WORD;
+       shift = reg & 2;
+       val &= 0xffff;
+
+       if (reg & 2) {
+               byen <<= shift;
+               val <<= (shift << 3);
+               reg &= ~3;
+       }
+
+       USETDW(temp, val);
+       return (ure_write_mem(sc, reg, index | byen, &temp, 4));
+}
+
+int
+ure_write_4(struct ure_softc *sc, uint16_t reg, uint16_t index, uint32_t val)
+{
+       uint8_t temp[4];
+
+       USETDW(temp, val);
+       return (ure_write_mem(sc, reg, index | URE_BYTE_EN_DWORD, &temp, 4));
+}
+
+uint16_t
+ure_ocp_reg_read(struct ure_softc *sc, uint16_t addr)
+{
+       uint16_t reg;
+
+       ure_write_2(sc, URE_PLA_OCP_GPHY_BASE, URE_MCU_TYPE_PLA, addr & 0xf000);
+       reg = (addr & 0x0fff) | 0xb000;
+
+       return (ure_read_2(sc, reg, URE_MCU_TYPE_PLA));
+}
+
+void
+ure_ocp_reg_write(struct ure_softc *sc, uint16_t addr, uint16_t data)
+{
+       uint16_t reg;
+
+       ure_write_2(sc, URE_PLA_OCP_GPHY_BASE, URE_MCU_TYPE_PLA, addr & 0xf000);
+       reg = (addr & 0x0fff) | 0xb000;
+
+       ure_write_2(sc, reg, URE_MCU_TYPE_PLA, data);
+}
+
+int
+ure_miibus_readreg(struct device *dev, int phy, int reg)
+{
+       struct ure_softc        *sc = (void *)dev;
+       uint16_t                val;
+
+       if (usbd_is_dying(sc->ure_udev))
+               return 0;
+
+       ure_lock_mii(sc);
+       val = ure_ocp_reg_read(sc, URE_OCP_BASE_MII + reg * 2);
+       ure_unlock_mii(sc);
+
+       return val;     /* letoh16? */
+}
+
+void
+ure_miibus_writereg(struct device *dev, int phy, int reg, int val)
+{
+       struct ure_softc        *sc = (void *)dev;
+
+       ure_lock_mii(sc);
+       ure_ocp_reg_write(sc, URE_OCP_BASE_MII + reg * 2, val); /* htole16? */
+       ure_unlock_mii(sc);
+}
+
+void
+ure_miibus_statchg(struct device *dev)
+{
+       struct ure_softc        *sc = (void *)dev;
+       struct mii_data         *mii = &sc->ure_mii;
+       struct ifnet            *ifp = &sc->ure_ac.ac_if;
+
+       if ((ifp->if_flags & IFF_RUNNING) == 0)
+               return;
+
+       sc->ure_flags &= ~URE_FLAG_LINK;
+       if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) ==
+           (IFM_ACTIVE | IFM_AVALID)) {
+               switch (IFM_SUBTYPE(mii->mii_media_active)) {
+               case IFM_10_T:
+               case IFM_100_TX:
+                       sc->ure_flags |= URE_FLAG_LINK;
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
+int
+ure_ifmedia_upd(struct ifnet *ifp)
+{
+       struct ure_softc *sc = ifp->if_softc;
+       struct mii_data *mii = &sc->ure_mii;
+       int err;
+
+       sc->ure_flags &= ~URE_FLAG_LINK;
+       if (mii->mii_instance) {
+               struct mii_softc *miisc;
+               LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
+                       PHY_RESET(miisc);
+       }
+
+       err = mii_mediachg(mii);
+       if (err == ENXIO)
+               return 0;
+       else
+               return err;
+}
+
+void
+ure_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+       struct ure_softc *sc = ifp->if_softc;
+       struct mii_data *mii = &sc->ure_mii;
+
+       mii_pollstat(mii);
+       ifmr->ifm_active = mii->mii_media_active;
+       ifmr->ifm_status = mii->mii_media_status;
+}
+
+void
+ure_iff(struct ure_softc *sc)
+{
+       struct ifnet            *ifp = &sc->ure_ac.ac_if;
+       struct ether_multi      *enm;
+       struct ether_multistep  step;
+       uint32_t                hashes[2] = { 0, 0 };
+       uint32_t                hash;
+       uint32_t                rxmode;
+
+       if (usbd_is_dying(sc->ure_udev))
+               return;
+
+       ifp->if_flags &= ~IFF_ALLMULTI;
+       rxmode = ure_read_4(sc, URE_PLA_RCR, URE_MCU_TYPE_PLA);
+       if (ifp->if_flags & IFF_PROMISC || sc->ure_ac.ac_multirangecnt > 0) {
+               ifp->if_flags |= IFF_ALLMULTI;
+               if (ifp->if_flags & IFF_PROMISC)
+                       rxmode |= URE_RCR_AAP;
+               rxmode |= URE_RCR_AM;
+               hashes[0] = hashes[1] = 0xffffffff;
+       } else {
+               ETHER_FIRST_MULTI(step, &sc->ure_ac, enm);
+               while (enm != NULL) {
+                       hash = ether_crc32_be(enm->enm_addrlo, ETHER_ADDR_LEN)
+                           >> 26;
+                       if (hash < 32)
+                               hashes[0] |= (1 << hash);
+                       else
+                               hashes[1] |= (1 << (hash - 32));
+
+                       ETHER_NEXT_MULTI(step, enm);
+               }
+
+               hash = swap32(hashes[0]);
+               hashes[0] = swap32(hashes[1]);
+               hashes[1] = hash;
+               rxmode |= URE_RCR_AM;
+       }
+
+       ure_write_4(sc, URE_PLA_MAR0, URE_MCU_TYPE_PLA, hashes[0]);
+       ure_write_4(sc, URE_PLA_MAR4, URE_MCU_TYPE_PLA, hashes[1]);
+       ure_write_4(sc, URE_PLA_RCR, URE_MCU_TYPE_PLA, rxmode);
+}
+
+void
+ure_reset(struct ure_softc *sc)
+{
+       int i;
+
+       ure_write_1(sc, URE_PLA_CR, URE_MCU_TYPE_PLA, URE_CR_RST);
+
+       for (i = 0; i < URE_TIMEOUT; i++) {
+               if (!(ure_read_1(sc, URE_PLA_CR, URE_MCU_TYPE_PLA) &
+                   URE_CR_RST))
+                       break;
+               usbd_delay_ms(sc->ure_udev, 10);
+       }
+       if (i == URE_TIMEOUT)
+               printf("%s: reset never completed\n", sc->ure_dev.dv_xname);
+}
+
+void
+ure_init(void *xsc)
+{
+       struct ure_softc        *sc = xsc;
+       struct ure_chain        *c;
+       struct ifnet            *ifp = &sc->ure_ac.ac_if;
+       uint32_t                rxmode;
+       usbd_status             err;
+       int                     s, i;
+
+       s = splnet();
+
+       /* Cancel pending I/O. */
+       ure_stop(sc);
+
+       ure_reset(sc);
+
+       if (ure_rx_list_init(sc) == ENOBUFS) {
+               printf("%s: rx list init failed\n", sc->ure_dev.dv_xname);
+               splx(s);
+               return;
+       }
+
+       if (ure_tx_list_init(sc) == ENOBUFS) {
+               printf("%s: tx list init failed\n", sc->ure_dev.dv_xname);
+               splx(s);
+               return;
+       }
+
+       /* Set MAC address. */
+       ure_write_mem(sc, URE_PLA_IDR, URE_MCU_TYPE_PLA | URE_BYTE_EN_SIX_BYTES,
+           sc->ure_ac.ac_enaddr, 8);
+
+       /* Reset the packet filter. */
+       ure_write_2(sc, URE_PLA_FMC, URE_MCU_TYPE_PLA,
+           ure_read_2(sc, URE_PLA_FMC, URE_MCU_TYPE_PLA) &
+           ~URE_FMC_FCR_MCU_EN);
+       ure_write_2(sc, URE_PLA_FMC, URE_MCU_TYPE_PLA,
+           ure_read_2(sc, URE_PLA_FMC, URE_MCU_TYPE_PLA) |
+           URE_FMC_FCR_MCU_EN);
+           
+       /* Enable transmit and receive. */
+       ure_write_1(sc, URE_PLA_CR, URE_MCU_TYPE_PLA,
+           ure_read_1(sc, URE_PLA_CR, URE_MCU_TYPE_PLA) | URE_CR_RE |
+           URE_CR_TE);
+
+       ure_write_2(sc, URE_PLA_MISC_1, URE_MCU_TYPE_PLA,
+           ure_read_2(sc, URE_PLA_MISC_1, URE_MCU_TYPE_PLA) &
+           ~URE_RXDY_GATED_EN);
+
+       /* Set Rx mode. */
+       rxmode = URE_RCR_APM;
+
+       /* If we want promiscuous mode, set the allframes bit. */
+       if (ifp->if_flags & IFF_PROMISC)
+               rxmode |= URE_RCR_AAP;
+
+       if (ifp->if_flags & IFF_BROADCAST)
+               rxmode |= URE_RCR_AB;
+
+       ure_write_4(sc, URE_PLA_RCR, URE_MCU_TYPE_PLA, rxmode);
+
+       /* Load the multicast filter. */
+       ure_iff(sc);
+
+       /* Open RX and TX pipes. */
+       err = usbd_open_pipe(sc->ure_iface, sc->ure_ed[URE_ENDPT_RX],
+           USBD_EXCLUSIVE_USE, &sc->ure_ep[URE_ENDPT_RX]);
+       if (err) {
+               printf("%s: open rx pipe failed: %s\n",
+                   sc->ure_dev.dv_xname, usbd_errstr(err));
+               splx(s);
+               return;
+       }
+
+       err = usbd_open_pipe(sc->ure_iface, sc->ure_ed[URE_ENDPT_TX],
+           USBD_EXCLUSIVE_USE, &sc->ure_ep[URE_ENDPT_TX]);
+       if (err) {
+               printf("%s: open tx pipe failed: %s\n",
+                   sc->ure_dev.dv_xname, usbd_errstr(err));
+               splx(s);
+               return;
+       }
+
+       /* Start up the receive pipe. */
+       for (i = 0; i < URE_RX_LIST_CNT; i++) {
+               c = &sc->ure_cdata.rx_chain[i];
+               usbd_setup_xfer(c->uc_xfer, sc->ure_ep[URE_ENDPT_RX],
+                   c, c->uc_buf, sc->ure_bufsz,
+                   USBD_SHORT_XFER_OK | USBD_NO_COPY,
+                   USBD_NO_TIMEOUT, ure_rxeof);
+               usbd_transfer(c->uc_xfer);
+       }
+
+       /* Indicate we are up and running. */
+       ifp->if_flags |= IFF_RUNNING;
+       ifq_clr_oactive(&ifp->if_snd);
+
+       timeout_add_sec(&sc->ure_stat_ch, 1);
+
+       splx(s);
+}
+
+void
+ure_start(struct ifnet *ifp)
+{
+       struct ure_softc        *sc = ifp->if_softc;
+       struct mbuf             *m_head = NULL;
+
+       if ((sc->ure_flags & URE_FLAG_LINK) == 0 ||
+           ifq_is_oactive(&ifp->if_snd)) {
+               return;
+       }
+
+       m_head = ifq_deq_begin(&ifp->if_snd);
+       if (m_head == NULL) {
+               return;
+       }
+
+       if (ure_encap(sc, m_head, 0)) {
+               ifq_deq_rollback(&ifp->if_snd, m_head);
+               ifq_set_oactive(&ifp->if_snd);
+               return;
+       }
+       ifq_deq_commit(&ifp->if_snd, m_head);
+
+#if NBPFILTER > 0
+       if (ifp->if_bpf)
+               bpf_mtap(ifp->if_bpf, m_head, BPF_DIRECTION_OUT);
+#endif
+       ifq_set_oactive(&ifp->if_snd);
+}
+
+void
+ure_tick(void *xsc)
+{
+       struct ure_softc *sc = xsc;
+
+       if (sc == NULL)
+               return;
+
+       if (usbd_is_dying(sc->ure_udev))
+               return;
+
+       usb_add_task(sc->ure_udev, &sc->ure_tick_task);
+}
+
+void
+ure_stop(struct ure_softc *sc)
+{
+       usbd_status             err;
+       struct ifnet            *ifp;
+       int                     i;
+
+       ure_reset(sc);
+
+       ifp = &sc->ure_ac.ac_if;
+       ifp->if_timer = 0;
+       ifp->if_flags &= ~IFF_RUNNING;
+       ifq_clr_oactive(&ifp->if_snd);
+
+       timeout_del(&sc->ure_stat_ch);
+
+       if (sc->ure_ep[URE_ENDPT_RX] != NULL) {
+               usbd_abort_pipe(sc->ure_ep[URE_ENDPT_RX]);
+               err = usbd_close_pipe(sc->ure_ep[URE_ENDPT_RX]);
+               if (err) {
+                       printf("%s: close rx pipe failed: %s\n",
+                           sc->ure_dev.dv_xname, usbd_errstr(err));
+               }
+               sc->ure_ep[URE_ENDPT_RX] = NULL;
+       }
+
+       if (sc->ure_ep[URE_ENDPT_TX] != NULL) {
+               usbd_abort_pipe(sc->ure_ep[URE_ENDPT_TX]);
+               err = usbd_close_pipe(sc->ure_ep[URE_ENDPT_TX]);
+               if (err) {
+                       printf("%s: close tx pipe failed: %s\n",
+                           sc->ure_dev.dv_xname, usbd_errstr(err));
+               }
+               sc->ure_ep[URE_ENDPT_TX] = NULL;
+       }
+
+       for (i = 0; i < URE_RX_LIST_CNT; i++) {
+               if (sc->ure_cdata.rx_chain[i].uc_mbuf != NULL) {
+                       m_freem(sc->ure_cdata.rx_chain[i].uc_mbuf);
+                       sc->ure_cdata.rx_chain[i].uc_mbuf = NULL;
+               }
+               if (sc->ure_cdata.rx_chain[i].uc_xfer != NULL) {
+                       usbd_free_xfer(sc->ure_cdata.rx_chain[i].uc_xfer);
+                       sc->ure_cdata.rx_chain[i].uc_xfer = NULL;
+               }
+       }
+
+       for (i = 0; i < URE_TX_LIST_CNT; i++) {
+               if (sc->ure_cdata.tx_chain[i].uc_mbuf != NULL) {
+                       m_freem(sc->ure_cdata.tx_chain[i].uc_mbuf);
+                       sc->ure_cdata.tx_chain[i].uc_mbuf = NULL;
+               }
+               if (sc->ure_cdata.tx_chain[i].uc_xfer != NULL) {
+                       usbd_free_xfer(sc->ure_cdata.tx_chain[i].uc_xfer);
+                       sc->ure_cdata.tx_chain[i].uc_xfer = NULL;
+               }
+       }
+}
+
+void
+ure_rtl8152_init(struct ure_softc *sc)
+{
+       uint32_t pwrctrl;
+
+       /* Disable ALDPS. */
+       ure_ocp_reg_write(sc, URE_OCP_ALDPS_CONFIG, URE_ENPDNPS | URE_LINKENA |
+           URE_DIS_SDSAVE);
+       usbd_delay_ms(sc->ure_udev, 20);
+
+       if (sc->ure_chip & URE_CHIP_VER_4C00) {
+               ure_write_2(sc, URE_PLA_LED_FEATURE, URE_MCU_TYPE_PLA,
+                   ure_read_2(sc, URE_PLA_LED_FEATURE, URE_MCU_TYPE_PLA) &
+                   ~URE_LED_MODE_MASK);
+       }
+
+       ure_write_2(sc, URE_USB_UPS_CTRL, URE_MCU_TYPE_USB,
+           ure_read_2(sc, URE_USB_UPS_CTRL, URE_MCU_TYPE_USB) &
+           ~URE_POWER_CUT);
+       ure_write_2(sc, URE_USB_PM_CTRL_STATUS, URE_MCU_TYPE_USB,
+           ure_read_2(sc, URE_USB_PM_CTRL_STATUS, URE_MCU_TYPE_USB) &
+           ~URE_RESUME_INDICATE);
+
+       ure_write_2(sc, URE_PLA_PHY_PWR, URE_MCU_TYPE_PLA,
+           ure_read_2(sc, URE_PLA_PHY_PWR, URE_MCU_TYPE_PLA) |
+           URE_TX_10M_IDLE_EN | URE_PFM_PWM_SWITCH);
+       pwrctrl = ure_read_4(sc, URE_PLA_MAC_PWR_CTRL, URE_MCU_TYPE_PLA);
+       pwrctrl &= ~URE_MCU_CLK_RATIO_MASK;
+       pwrctrl |= URE_MCU_CLK_RATIO | URE_D3_CLK_GATED_EN;
+       ure_write_4(sc, URE_PLA_MAC_PWR_CTRL, URE_MCU_TYPE_PLA, pwrctrl);
+       ure_write_2(sc, URE_PLA_GPHY_INTR_IMR, URE_MCU_TYPE_PLA,
+           URE_GPHY_STS_MSK | URE_SPEED_DOWN_MSK | URE_SPDWN_RXDV_MSK |
+           URE_SPDWN_LINKCHG_MSK);
+
+       /* Disable Rx aggregation. */
+       ure_write_2(sc, URE_USB_USB_CTRL, URE_MCU_TYPE_USB,
+           ure_read_2(sc, URE_USB_USB_CTRL, URE_MCU_TYPE_USB) |
+           URE_RX_AGG_DISABLE);
+
+       /* Disable ALDPS. */
+       ure_ocp_reg_write(sc, URE_OCP_ALDPS_CONFIG, URE_ENPDNPS | URE_LINKENA |
+           URE_DIS_SDSAVE);
+       usbd_delay_ms(sc->ure_udev, 20);
+
+       ure_init_fifo(sc);
+
+       ure_write_1(sc, URE_USB_TX_AGG, URE_MCU_TYPE_USB,
+           URE_TX_AGG_MAX_THRESHOLD);
+       ure_write_4(sc, URE_USB_RX_BUF_TH, URE_MCU_TYPE_USB, URE_RX_THR_HIGH);
+       ure_write_4(sc, URE_USB_TX_DMA, URE_MCU_TYPE_USB,
+           URE_TEST_MODE_DISABLE | URE_TX_SIZE_ADJUST1);
+}
+
+void
+ure_disable_teredo(struct ure_softc *sc)
+{
+       ure_write_4(sc, URE_PLA_TEREDO_CFG, URE_MCU_TYPE_PLA,
+           ure_read_4(sc, URE_PLA_TEREDO_CFG, URE_MCU_TYPE_PLA) & 
+           ~(URE_TEREDO_SEL | URE_TEREDO_RS_EVENT_MASK | URE_OOB_TEREDO_EN));
+       ure_write_2(sc, URE_PLA_WDT6_CTRL, URE_MCU_TYPE_PLA,
+           URE_WDT6_SET_MODE);
+       ure_write_2(sc, URE_PLA_REALWOW_TIMER, URE_MCU_TYPE_PLA, 0);
+       ure_write_4(sc, URE_PLA_TEREDO_TIMER, URE_MCU_TYPE_PLA, 0);
+}
+
+void
+ure_init_fifo(struct ure_softc *sc)
+{
+       uint32_t rx_fifo1, rx_fifo2;
+       int i;
+
+       ure_write_2(sc, URE_PLA_MISC_1, URE_MCU_TYPE_PLA,
+           ure_read_2(sc, URE_PLA_MISC_1, URE_MCU_TYPE_PLA) |
+           URE_RXDY_GATED_EN);
+
+       ure_disable_teredo(sc);
+
+       ure_write_4(sc, URE_PLA_RCR, URE_MCU_TYPE_PLA,
+           ure_read_4(sc, URE_PLA_RCR, URE_MCU_TYPE_PLA) &
+           ~URE_RCR_ACPT_ALL);
+
+       ure_reset(sc);
+
+       ure_write_1(sc, URE_PLA_CR, URE_MCU_TYPE_PLA, 0);
+
+       ure_write_1(sc, URE_PLA_OOB_CTRL, URE_MCU_TYPE_PLA,
+           ure_read_1(sc, URE_PLA_OOB_CTRL, URE_MCU_TYPE_PLA) &
+           ~URE_NOW_IS_OOB);
+
+       ure_write_2(sc, URE_PLA_SFF_STS_7, URE_MCU_TYPE_PLA,
+           ure_read_2(sc, URE_PLA_SFF_STS_7, URE_MCU_TYPE_PLA) &
+           ~URE_MCU_BORW_EN);
+       for (i = 0; i < URE_TIMEOUT; i++) {
+               if (ure_read_1(sc, URE_PLA_OOB_CTRL, URE_MCU_TYPE_PLA) &
+                   URE_LINK_LIST_READY)
+                       break;
+               usbd_delay_ms(sc->ure_udev, 10);
+       }
+       if (i == URE_TIMEOUT)
+               printf("%s timeout waiting for OOB control\n",
+                   sc->ure_dev.dv_xname);
+       ure_write_2(sc, URE_PLA_SFF_STS_7, URE_MCU_TYPE_PLA,
+           ure_read_2(sc, URE_PLA_SFF_STS_7, URE_MCU_TYPE_PLA) |
+           URE_RE_INIT_LL);
+       for (i = 0; i < URE_TIMEOUT; i++) {
+               if (ure_read_1(sc, URE_PLA_OOB_CTRL, URE_MCU_TYPE_PLA) &
+                   URE_LINK_LIST_READY)
+                       break;
+               usbd_delay_ms(sc->ure_udev, 10);
+       }
+       if (i == URE_TIMEOUT)
+               printf("%s: timeout waiting for OOB control\n",
+                   sc->ure_dev.dv_xname);
+
+       ure_write_2(sc, URE_PLA_CPCR, URE_MCU_TYPE_PLA,
+           ure_read_2(sc, URE_PLA_CPCR, URE_MCU_TYPE_PLA) &
+           ~URE_CPCR_RX_VLAN);
+       ure_write_2(sc, URE_PLA_TCR0, URE_MCU_TYPE_PLA,
+           ure_read_2(sc, URE_PLA_TCR0, URE_MCU_TYPE_PLA) |
+           URE_TCR0_AUTO_FIFO);
+
+       /* Configure Rx FIFO threshold and coalescing. */
+       ure_write_4(sc, URE_PLA_RXFIFO_CTRL0, URE_MCU_TYPE_PLA,
+           URE_RXFIFO_THR1_NORMAL);
+       if (sc->ure_udev->speed == USB_SPEED_FULL) {
+               rx_fifo1 = URE_RXFIFO_THR2_FULL;
+               rx_fifo2 = URE_RXFIFO_THR3_FULL;
+       } else {
+               rx_fifo1 = URE_RXFIFO_THR2_HIGH;
+               rx_fifo2 = URE_RXFIFO_THR3_HIGH;
+       }
+       ure_write_4(sc, URE_PLA_RXFIFO_CTRL1, URE_MCU_TYPE_PLA, rx_fifo1);
+       ure_write_4(sc, URE_PLA_RXFIFO_CTRL2, URE_MCU_TYPE_PLA, rx_fifo2);
+
+       /* Configure Tx FIFO threshold. */
+       ure_write_4(sc, URE_PLA_TXFIFO_CTRL, URE_MCU_TYPE_PLA,
+           URE_TXFIFO_THR_NORMAL);
+}
+
+int
+ure_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+       struct ure_softc        *sc = ifp->if_softc;;
+       struct ifreq            *ifr = (struct ifreq *)data;
+       int                     s, error = 0;
+
+       s = splnet();
+
+       switch (cmd) {
+       case SIOCSIFADDR:
+               ifp->if_flags |= IFF_UP;
+               if (!(ifp->if_flags & IFF_RUNNING))
+                       ure_init(sc);
+               break;
+
+       case SIOCSIFFLAGS:
+               if (ifp->if_flags & IFF_UP) {
+                       if (ifp->if_flags & IFF_RUNNING)
+                               error = ENETRESET;
+                       else
+                               ure_init(sc);
+               } else {
+                       if (ifp->if_flags & IFF_RUNNING)
+                               ure_stop(sc);
+               }
+               break;
+
+       case SIOCGIFMEDIA:
+       case SIOCSIFMEDIA:
+               error = ifmedia_ioctl(ifp, ifr, &sc->ure_mii.mii_media, cmd);
+               break;
+
+       default:
+               error = ether_ioctl(ifp, &sc->ure_ac, cmd, data);
+       }
+
+       if (error == ENETRESET) {
+               if (ifp->if_flags & IFF_RUNNING)
+                       ure_iff(sc);
+               error = 0;
+       }
+
+       splx(s);
+
+       return (error);
+}
+
+int
+ure_match(struct device *parent, void *match, void *aux)
+{
+       struct usb_attach_arg *uaa = aux;
+
+       /*
+       if (uaa->configno != URE_CONFIG_IDX) 
+               return UMATCH_NONE;
+       if (uaa->ifaceno != URE_IFACE_IDX)
+               return UMATCH_NONE;
+       */
+       if (uaa->iface == NULL || uaa->configno != 1)
+               return UMATCH_NONE;
+       
+       return (usb_lookup(ure_devs, uaa->vendor, uaa->product) != NULL ?
+           UMATCH_VENDOR_PRODUCT_CONF_IFACE : UMATCH_NONE);
+}
+
+void
+ure_attach(struct device *parent, struct device *self, void *aux)
+{
+       struct ure_softc                *sc = (struct ure_softc *)self;
+       struct usb_attach_arg           *uaa = aux;
+       usb_interface_descriptor_t      *id;
+       usb_endpoint_descriptor_t       *ed;
+       struct mii_data                 *mii;
+       u_char                          eaddr[8]; /* 4byte padded */
+       struct ifnet                    *ifp;
+       int                             i, s;
+       uint16_t                        ver;
+
+       sc->ure_udev = uaa->device;
+       sc->ure_iface = uaa->iface;
+
+       usb_init_task(&sc->ure_tick_task, ure_tick_task, sc,
+           USB_TASK_TYPE_GENERIC);
+       rw_init(&sc->ure_mii_lock, "uremii");
+       usb_init_task(&sc->ure_stop_task, (void (*)(void *))ure_stop, sc,
+           USB_TASK_TYPE_GENERIC);
+
+       id = usbd_get_interface_descriptor(sc->ure_iface);
+
+       sc->ure_bufsz = 16 * 1024;
+
+       for (i = 0; i < id->bNumEndpoints; i++) {
+               ed = usbd_interface2endpoint_descriptor(sc->ure_iface, i);
+               if (!ed) {
+                       printf("%s: couldn't get ep %d\n",
+                           sc->ure_dev.dv_xname, i);
+                       return;
+               }
+               if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+                   UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+                       sc->ure_ed[URE_ENDPT_RX] = ed->bEndpointAddress;
+               } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+                   UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+                       sc->ure_ed[URE_ENDPT_TX] = ed->bEndpointAddress;
+               }
+       }
+
+       s = splnet();
+
+       sc->ure_phyno = 0;
+       printf("%s: ", sc->ure_dev.dv_xname);
+
+       ver = ure_read_2(sc, URE_PLA_TCR1, URE_MCU_TYPE_PLA) & URE_VERSION_MASK;
+       switch (ver) {
+       case 0x4c00:
+               sc->ure_chip |= URE_CHIP_VER_4C00;
+               ure_read_mem(sc, URE_PLA_IDR, URE_MCU_TYPE_PLA, eaddr,
+                   sizeof(eaddr));
+               printf("ver 4c00");
+               break;
+       case 0x4c10:
+               sc->ure_chip |= URE_CHIP_VER_4C10;
+               ure_read_mem(sc, URE_PLA_BACKUP, URE_MCU_TYPE_PLA, eaddr,
+                   sizeof(eaddr));
+               printf("ver 4c01");
+               break;
+       default:
+               printf(", unknown ver %02x", ver);
+               /* fake addr?  or just fail? */
+               break;
+       }
+
+       printf(", address %s\n", ether_sprintf(eaddr));
+
+       bcopy(eaddr, (char *)&sc->ure_ac.ac_enaddr, ETHER_ADDR_LEN);
+
+       ure_rtl8152_init(sc);
+
+       ifp = &sc->ure_ac.ac_if;
+       ifp->if_softc = sc;
+       strlcpy(ifp->if_xname, sc->ure_dev.dv_xname, IFNAMSIZ);
+       ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+       ifp->if_ioctl = ure_ioctl;
+       ifp->if_start = ure_start;
+       ifp->if_capabilities = 0;
+
+       mii = &sc->ure_mii;
+       mii->mii_ifp = ifp;
+       mii->mii_readreg = ure_miibus_readreg;
+       mii->mii_writereg = ure_miibus_writereg;
+       mii->mii_statchg = ure_miibus_statchg;
+       mii->mii_flags = MIIF_AUTOTSLEEP;
+
+       ifmedia_init(&mii->mii_media, 0, ure_ifmedia_upd, ure_ifmedia_sts);
+       mii_attach(self, mii, 0xffffffff, sc->ure_phyno, MII_OFFSET_ANY, 0);
+
+       if (LIST_FIRST(&mii->mii_phys) == NULL) {
+               ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_NONE, 0, NULL);
+               ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_NONE);
+       } else
+               ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_AUTO);
+
+       if_attach(ifp);
+       ether_ifattach(ifp);
+
+       timeout_set(&sc->ure_stat_ch, ure_tick, sc);
+
+       splx(s);
+}
+
+int
+ure_detach(struct device *self, int flags)
+{
+       struct ure_softc        *sc = (struct ure_softc *)self;
+       struct ifnet            *ifp = &sc->ure_ac.ac_if;
+       int                     s;
+
+       if (timeout_initialized(&sc->ure_stat_ch))
+               timeout_del(&sc->ure_stat_ch);
+
+       if (sc->ure_ep[URE_ENDPT_TX] != NULL)
+               usbd_abort_pipe(sc->ure_ep[URE_ENDPT_TX]);
+       if (sc->ure_ep[URE_ENDPT_RX] != NULL)
+               usbd_abort_pipe(sc->ure_ep[URE_ENDPT_RX]);
+
+       usb_rem_task(sc->ure_udev, &sc->ure_tick_task);
+       usb_rem_task(sc->ure_udev, &sc->ure_stop_task);
+
+       s = splusb();
+
+       if (--sc->ure_refcnt >= 0) {
+               usb_detach_wait(&sc->ure_dev);
+       }
+
+       if (ifp->if_flags & IFF_RUNNING)
+               ure_stop(sc);
+
+       mii_detach(&sc->ure_mii, MII_PHY_ANY, MII_OFFSET_ANY);
+       ifmedia_delete_instance(&sc->ure_mii.mii_media, IFM_INST_ANY);
+       if (ifp->if_softc != NULL) {
+               ether_ifdetach(ifp);
+               if_detach(ifp);
+       }
+
+       splx(s);
+
+       return 0;
+}
+
+void
+ure_tick_task(void *xsc)
+{
+       int                     s;
+       struct ure_softc        *sc = xsc;
+       struct mii_data         *mii;
+
+       if (sc == NULL)
+               return;
+
+       if (usbd_is_dying(sc->ure_udev))
+               return;
+       mii = &sc->ure_mii;
+
+       s = splnet();
+       mii_tick(mii);
+       if ((sc->ure_flags & URE_FLAG_LINK) == 0)
+               ure_miibus_statchg(&sc->ure_dev);
+       timeout_add_sec(&sc->ure_stat_ch, 1);
+       splx(s);
+}
+
+void
+ure_lock_mii(struct ure_softc *sc)
+{
+       sc->ure_refcnt++;
+       rw_enter_write(&sc->ure_mii_lock);
+}
+
+void
+ure_unlock_mii(struct ure_softc *sc)
+{
+       rw_exit_write(&sc->ure_mii_lock);
+       if (--sc->ure_refcnt < 0)
+               usb_detach_wakeup(&sc->ure_dev);
+}
+
+void
+ure_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
+{
+       struct ure_chain        *c = (struct ure_chain *)priv;
+       struct ure_softc        *sc = c->uc_sc;
+       struct ifnet            *ifp = &sc->ure_ac.ac_if;
+       u_char                  *buf = c->uc_buf;
+       uint32_t                total_len;
+       uint16_t                pktlen = 0;
+       struct mbuf_list        ml = MBUF_LIST_INITIALIZER();
+       struct mbuf             *m;
+       int                     s;
+       struct ure_rxpkt        rxhdr;
+       
+       if (usbd_is_dying(sc->ure_udev))
+               return;
+
+       if (!(ifp->if_flags & IFF_RUNNING))
+               return;
+
+       if (status != USBD_NORMAL_COMPLETION) {
+               if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
+                       return;
+               if (usbd_ratecheck(&sc->ure_rx_notice)) {
+                       printf("%s: usb errors on rx: %s\n",
+                               sc->ure_dev.dv_xname, usbd_errstr(status));
+               }
+               if (status == USBD_STALLED)
+                       usbd_clear_endpoint_stall_async(sc->ure_ep[URE_ENDPT_RX]);
+               goto done;
+       }
+
+       usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL);
+       DPRINTFN(3, ("received %d bytes\n", total_len));
+
+       do {
+               if (total_len < sizeof(rxhdr)) {
+                       DPRINTF(("too few bytes left for a packet header\n"));
+                       ifp->if_ierrors++;
+                       goto done;
+               }
+
+               buf += pktlen;
+
+               memcpy(&rxhdr, buf, sizeof(rxhdr));
+               total_len -= sizeof(rxhdr);
+
+               pktlen = lemtoh32(&rxhdr.ure_pktlen) & URE_RXPKT_LEN_MASK;
+               DPRINTFN(4, ("next packet is %d bytes\n", pktlen));
+               if (pktlen > total_len) {
+                       DPRINTF(("not enough bytes left for next packet\n"));
+                       ifp->if_ierrors++;
+                       goto done;
+               }
+
+               total_len -= pktlen;
+               buf += sizeof(rxhdr);
+
+               m = ure_newbuf();
+               if (m == NULL) {
+                       DPRINTF(("unable to allocate mbuf for next packet\n"));
+                       ifp->if_ierrors++;
+                       goto done;
+               }
+
+               m->m_pkthdr.len = m->m_len = pktlen;
+               m_adj(m, ETHER_ALIGN);
+
+               memcpy(mtod(m, char *), buf, pktlen);
+               ml_enqueue(&ml, m);
+       } while (total_len > 0);
+
+done:
+       s = splnet();
+       if_input(ifp, &ml);
+       splx(s);
+       memset(c->uc_buf, 0, sc->ure_bufsz);
+
+       usbd_setup_xfer(xfer, sc->ure_ep[URE_ENDPT_RX], c, c->uc_buf,
+           sc->ure_bufsz, USBD_SHORT_XFER_OK | USBD_NO_COPY,
+           USBD_NO_TIMEOUT, ure_rxeof);
+       usbd_transfer(xfer);
+}
+
+
+void
+ure_txeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
+{
+       struct ure_softc        *sc;
+       struct ure_chain        *c;
+       struct ifnet            *ifp;
+       int                     s;
+
+       c = priv;
+       sc = c->uc_sc;
+       ifp = &sc->ure_ac.ac_if;
+
+       if (usbd_is_dying(sc->ure_udev))
+               return;
+
+       DPRINTFN(2, ("tx completion\n"));
+
+       s = splnet();
+
+       if (status != USBD_NORMAL_COMPLETION) {
+               if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
+                       splx(s);
+                       return;
+               }
+               ifp->if_oerrors++;
+               printf("%s: usb error on tx: %s\n", sc->ure_dev.dv_xname,
+                   usbd_errstr(status));
+               if (status == USBD_STALLED)
+                       usbd_clear_endpoint_stall_async(sc->ure_ep[URE_ENDPT_TX]);
+               splx(s);
+               return;
+       }
+
+       ifp->if_timer = 0;
+       ifq_clr_oactive(&ifp->if_snd);
+
+       m_freem(c->uc_mbuf);
+       c->uc_mbuf = NULL;
+
+       if (IFQ_IS_EMPTY(&ifp->if_snd) == 0)
+               ure_start(ifp);
+
+       ifp->if_opackets++;
+       splx(s);
+       
+}
+
+int
+ure_tx_list_init(struct ure_softc *sc)
+{
+       struct ure_cdata *cd;
+       struct ure_chain *c;
+       int i;
+
+       cd = &sc->ure_cdata;
+       for (i = 0; i < URE_TX_LIST_CNT; i++) {
+               c = &cd->tx_chain[i];
+               c->uc_sc = sc;
+               c->uc_idx = i;
+               c->uc_mbuf = NULL;
+               if (c->uc_xfer == NULL) {
+                       c->uc_xfer = usbd_alloc_xfer(sc->ure_udev);
+                       if (c->uc_xfer == NULL)
+                               return ENOBUFS;
+                       c->uc_buf = usbd_alloc_buffer(c->uc_xfer,
+                           sc->ure_bufsz);
+                       if (c->uc_buf == NULL) {
+                               usbd_free_xfer(c->uc_xfer);
+                               return ENOBUFS;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+int
+ure_rx_list_init(struct ure_softc *sc)
+{
+       struct ure_cdata *cd;
+       struct ure_chain *c;
+       int i;
+
+       cd = &sc->ure_cdata;
+       for (i = 0; i < URE_RX_LIST_CNT; i++) {
+               c = &cd->rx_chain[i];
+               c->uc_sc = sc;
+               c->uc_idx = i;
+               c->uc_mbuf = NULL;
+               if (c->uc_xfer == NULL) {
+                       c->uc_xfer = usbd_alloc_xfer(sc->ure_udev);
+                       if (c->uc_xfer == NULL)
+                               return ENOBUFS;
+                       c->uc_buf = usbd_alloc_buffer(c->uc_xfer,
+                           sc->ure_bufsz);
+                       if (c->uc_buf == NULL) {
+                               usbd_free_xfer(c->uc_xfer);
+                               return ENOBUFS;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+struct mbuf *
+ure_newbuf(void)
+{
+       struct mbuf *m;
+
+       MGETHDR(m, M_DONTWAIT, MT_DATA);
+       if (m == NULL)
+               return NULL;
+
+       MCLGET(m, M_DONTWAIT);
+       if (!(m->m_flags & M_EXT)) {
+               m_freem(m);
+               return NULL;
+       }
+
+       return m;
+}
+
+int
+ure_encap(struct ure_softc *sc, struct mbuf *m, int idx)
+{
+       struct ure_chain        *c;
+       usbd_status             err;
+       struct ure_txpkt        txhdr;
+       uint32_t                frm_len = 0;
+       u_char                  *buf;
+
+       c = &sc->ure_cdata.tx_chain[idx];
+       buf = c->uc_buf;
+
+       /* header */
+       htolem32(&txhdr.ure_pktlen, m->m_pkthdr.len | URE_TXPKT_TX_FS |
+           URE_TXPKT_TX_LS);
+       txhdr.ure_rsvd0 = 0;
+       memcpy(buf, &txhdr, sizeof(txhdr));
+       buf += sizeof(txhdr);
+       frm_len = sizeof(txhdr);
+
+       /* packet */
+       m_copydata(m, 0, m->m_pkthdr.len, buf);
+       frm_len += m->m_pkthdr.len;
+
+       c->uc_mbuf = m;
+
+       DPRINTFN(2, ("tx %d bytes\n", frm_len));
+       usbd_setup_xfer(c->uc_xfer, sc->ure_ep[URE_ENDPT_TX], c, c->uc_buf,
+           frm_len, USBD_FORCE_SHORT_XFER | USBD_NO_COPY, 10000, ure_txeof);
+
+       err = usbd_transfer(c->uc_xfer);
+       if (err != USBD_IN_PROGRESS) {
+               ure_stop(sc);
+               return EIO;
+       }
+
+       sc->ure_cdata.tx_cnt++;
+       return 0;
+}
diff --git a/sys/dev/usb/if_urereg.h b/sys/dev/usb/if_urereg.h
new file mode 100644 (file)
index 0000000..aa99bc8
--- /dev/null
@@ -0,0 +1,469 @@
+/*-
+ * Copyright (c) 2015 Kevin Lo <kevlo@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$
+ */
+
+#define        URE_CONFIG_IDX          0       /* config number 1 */
+#define        URE_IFACE_IDX           0
+
+#define        URE_CTL_READ            0x01
+#define        URE_CTL_WRITE           0x02
+
+#define        URE_TIMEOUT             1000
+#define        URE_PHY_TIMEOUT         2000
+
+#define        URE_BYTE_EN_DWORD       0xff
+#define        URE_BYTE_EN_WORD        0x33
+#define        URE_BYTE_EN_BYTE        0x11
+#define        URE_BYTE_EN_SIX_BYTES   0x3f
+
+#define        URE_MAX_FRAMELEN        (ETHER_MAX_LEN + ETHER_VLAN_ENCAP_LEN)
+
+#define        URE_PLA_IDR             0xc000
+#define        URE_PLA_RCR             0xc010
+#define        URE_PLA_RMS             0xc016
+#define        URE_PLA_RXFIFO_CTRL0    0xc0a0
+#define        URE_PLA_RXFIFO_CTRL1    0xc0a4
+#define        URE_PLA_RXFIFO_CTRL2    0xc0a8
+#define        URE_PLA_DMY_REG0        0xc0b0
+#define        URE_PLA_FMC             0xc0b4
+#define        URE_PLA_CFG_WOL         0xc0b6
+#define        URE_PLA_TEREDO_CFG      0xc0bc
+#define        URE_PLA_MAR0            0xcd00
+#define        URE_PLA_MAR4            0xcd04
+#define        URE_PLA_BACKUP          0xd000
+#define        URE_PLA_BDC_CR          0xd1a0
+#define        URE_PLA_TEREDO_TIMER    0xd2cc
+#define        URE_PLA_REALWOW_TIMER   0xd2e8
+#define        URE_PLA_LEDSEL          0xdd90
+#define        URE_PLA_LED_FEATURE     0xdd92
+#define        URE_PLA_PHYAR           0xde00
+#define        URE_PLA_BOOT_CTRL       0xe004
+#define        URE_PLA_GPHY_INTR_IMR   0xe022
+#define        URE_PLA_EEE_CR          0xe040
+#define        URE_PLA_EEEP_CR         0xe080
+#define        URE_PLA_MAC_PWR_CTRL    0xe0c0
+#define        URE_PLA_MAC_PWR_CTRL2   0xe0ca
+#define        URE_PLA_MAC_PWR_CTRL3   0xe0cc
+#define        URE_PLA_MAC_PWR_CTRL4   0xe0ce
+#define        URE_PLA_WDT6_CTRL       0xe428
+#define        URE_PLA_TCR0            0xe610
+#define        URE_PLA_TCR1            0xe612
+#define        URE_PLA_MTPS            0xe615
+#define        URE_PLA_TXFIFO_CTRL     0xe618
+#define        URE_PLA_RSTTELLY        0xe800
+#define        URE_PLA_CR              0xe813
+#define        URE_PLA_CRWECR          0xe81c
+#define        URE_PLA_CONFIG5         0xe822
+#define        URE_PLA_PHY_PWR         0xe84c
+#define        URE_PLA_OOB_CTRL        0xe84f
+#define        URE_PLA_CPCR            0xe854
+#define        URE_PLA_MISC_0          0xe858
+#define        URE_PLA_MISC_1          0xe85a
+#define        URE_PLA_OCP_GPHY_BASE   0xe86c
+#define        URE_PLA_TELLYCNT        0xe890
+#define        URE_PLA_SFF_STS_7       0xe8de
+#define        URE_PLA_PHYSTATUS       0xe908
+
+#define        URE_USB_USB2PHY         0xb41e
+#define        URE_USB_SSPHYLINK2      0xb428
+#define        URE_USB_U2P3_CTRL       0xb460
+#define        URE_USB_CSR_DUMMY1      0xb464
+#define        URE_USB_CSR_DUMMY2      0xb466
+#define        URE_USB_DEV_STAT        0xb808
+#define        URE_USB_CONNECT_TIMER   0xcbf8
+#define        URE_USB_BURST_SIZE      0xcfc0
+#define        URE_USB_USB_CTRL        0xd406
+#define        URE_USB_PHY_CTRL        0xd408
+#define        URE_USB_TX_AGG          0xd40a
+#define        URE_USB_RX_BUF_TH       0xd40c
+#define        URE_USB_USB_TIMER       0xd428
+#define        URE_USB_RX_EARLY_AGG    0xd42c
+#define        URE_USB_PM_CTRL_STATUS  0xd432
+#define        URE_USB_TX_DMA          0xd434
+#define        URE_USB_TOLERANCE       0xd490
+#define        URE_USB_LPM_CTRL        0xd41a
+#define        URE_USB_UPS_CTRL        0xd800
+#define        URE_USB_MISC_0          0xd81a
+#define        URE_USB_POWER_CUT       0xd80a
+#define        URE_USB_AFE_CTRL2       0xd824
+#define        URE_USB_WDT11_CTRL      0xe43c
+
+/* OCP Registers. */
+#define        URE_OCP_ALDPS_CONFIG    0x2010
+#define        URE_OCP_EEE_CONFIG1     0x2080
+#define        URE_OCP_EEE_CONFIG2     0x2092
+#define        URE_OCP_EEE_CONFIG3     0x2094
+#define        URE_OCP_BASE_MII        0xa400
+#define        URE_OCP_EEE_AR          0xa41a
+#define        URE_OCP_EEE_DATA        0xa41c
+#define        URE_OCP_PHY_STATUS      0xa420
+#define        URE_OCP_POWER_CFG       0xa430
+#define        URE_OCP_EEE_CFG         0xa432
+#define        URE_OCP_SRAM_ADDR       0xa436
+#define        URE_OCP_SRAM_DATA       0xa438
+#define        URE_OCP_DOWN_SPEED      0xa442
+#define        URE_OCP_EEE_ABLE        0xa5c4
+#define        URE_OCP_EEE_ADV         0xa5d0
+#define        URE_OCP_EEE_LPABLE      0xa5d2
+#define        URE_OCP_PHY_STATE       0xa708
+#define        URE_OCP_ADC_CFG         0xbc06
+
+/* SRAM Register. */
+#define        URE_SRAM_LPF_CFG        0x8012
+#define        URE_SRAM_10M_AMP1       0x8080
+#define        URE_SRAM_10M_AMP2       0x8082
+#define        URE_SRAM_IMPEDANCE      0x8084
+
+/* PLA_RCR */
+#define        URE_RCR_AAP             0x00000001
+#define        URE_RCR_APM             0x00000002
+#define        URE_RCR_AM              0x00000004
+#define        URE_RCR_AB              0x00000008
+#define        URE_RCR_ACPT_ALL        \
+       (URE_RCR_AAP | URE_RCR_APM | URE_RCR_AM | URE_RCR_AB)
+
+/* PLA_RXFIFO_CTRL0 */
+#define        URE_RXFIFO_THR1_NORMAL  0x00080002
+#define        URE_RXFIFO_THR1_OOB     0x01800003
+
+/* PLA_RXFIFO_CTRL1 */
+#define        URE_RXFIFO_THR2_FULL    0x00000060
+#define        URE_RXFIFO_THR2_HIGH    0x00000038
+#define        URE_RXFIFO_THR2_OOB     0x0000004a
+#define        URE_RXFIFO_THR2_NORMAL  0x00a0
+
+/* PLA_RXFIFO_CTRL2 */
+#define        URE_RXFIFO_THR3_FULL    0x00000078
+#define        URE_RXFIFO_THR3_HIGH    0x00000048
+#define        URE_RXFIFO_THR3_OOB     0x0000005a
+#define        URE_RXFIFO_THR3_NORMAL  0x0110
+
+/* PLA_TXFIFO_CTRL */
+#define        URE_TXFIFO_THR_NORMAL   0x00400008
+#define        URE_TXFIFO_THR_NORMAL2  0x01000008
+
+/* PLA_DMY_REG0 */
+#define        URE_ECM_ALDPS           0x0002
+
+/* PLA_FMC */
+#define        URE_FMC_FCR_MCU_EN      0x0001
+
+/* PLA_EEEP_CR */
+#define        URE_EEEP_CR_EEEP_TX     0x0002
+
+/* PLA_WDT6_CTRL */
+#define        URE_WDT6_SET_MODE       0x001
+
+/* PLA_TCR0 */
+#define        URE_TCR0_TX_EMPTY       0x0800
+#define        URE_TCR0_AUTO_FIFO      0x0080
+
+/* PLA_TCR1 */
+#define        URE_VERSION_MASK        0x7cf0
+
+/* PLA_CR */
+#define        URE_CR_RST              0x10
+#define        URE_CR_RE               0x08
+#define        URE_CR_TE               0x04
+
+/* PLA_CRWECR */
+#define        URE_CRWECR_NORAML       0x00
+#define        URE_CRWECR_CONFIG       0xc0
+
+/* PLA_OOB_CTRL */
+#define        URE_NOW_IS_OOB          0x80    
+#define        URE_TXFIFO_EMPTY        0x20    
+#define        URE_RXFIFO_EMPTY        0x10    
+#define        URE_LINK_LIST_READY     0x02    
+#define        URE_DIS_MCU_CLROOB      0x01    
+#define        URE_FIFO_EMPTY          (URE_TXFIFO_EMPTY | URE_RXFIFO_EMPTY)
+
+/* PLA_MISC_1 */
+#define        URE_RXDY_GATED_EN       0x0008  
+
+/* PLA_SFF_STS_7 */
+#define        URE_RE_INIT_LL          0x8000  
+#define        URE_MCU_BORW_EN         0x4000  
+
+/* PLA_CPCR */
+#define        URE_CPCR_RX_VLAN        0x0040
+
+/* PLA_TEREDO_CFG */
+#define        URE_TEREDO_SEL                  0x8000
+#define        URE_TEREDO_WAKE_MASK            0x7f00
+#define        URE_TEREDO_RS_EVENT_MASK        0x00fe
+#define        URE_OOB_TEREDO_EN               0x0001
+
+/* PAL_BDC_CR */
+#define        URE_ALDPS_PROXY_MODE    0x0001
+
+/* PLA_CONFIG5 */
+#define        URE_LAN_WAKE_EN         0x0002
+
+/* PLA_LED_FEATURE */
+#define        URE_LED_MODE_MASK       0x0700
+
+/* PLA_PHY_PWR */
+#define        URE_TX_10M_IDLE_EN      0x0080
+#define        URE_PFM_PWM_SWITCH      0x0040
+
+/* PLA_MAC_PWR_CTRL */
+#define        URE_D3_CLK_GATED_EN     0x00004000
+#define        URE_MCU_CLK_RATIO       0x07010f07
+#define        URE_MCU_CLK_RATIO_MASK  0x0f0f0f0f
+#define        URE_ALDPS_SPDWN_RATIO   0x0f87
+
+/* PLA_MAC_PWR_CTRL2 */
+#define        URE_EEE_SPDWN_RATIO     0x8007
+
+/* PLA_MAC_PWR_CTRL3 */
+#define        URE_PKT_AVAIL_SPDWN_EN  0x0100
+#define        URE_SUSPEND_SPDWN_EN    0x0004
+#define        URE_U1U2_SPDWN_EN       0x0002
+#define        URE_L1_SPDWN_EN         0x0001
+
+/* PLA_MAC_PWR_CTRL4 */
+#define        URE_PWRSAVE_SPDWN_EN    0x1000
+#define        URE_RXDV_SPDWN_EN       0x0800
+#define        URE_TX10MIDLE_EN        0x0100
+#define        URE_TP100_SPDWN_EN      0x0020
+#define        URE_TP500_SPDWN_EN      0x0010
+#define        URE_TP1000_SPDWN_EN     0x0008
+#define        URE_EEE_SPDWN_EN        0x0001
+
+/* PLA_GPHY_INTR_IMR */
+#define        URE_GPHY_STS_MSK        0x0001
+#define        URE_SPEED_DOWN_MSK      0x0002
+#define        URE_SPDWN_RXDV_MSK      0x0004
+#define        URE_SPDWN_LINKCHG_MSK   0x0008
+
+/* PLA_PHYAR */
+#define        URE_PHYAR_PHYDATA       0x0000ffff
+#define        URE_PHYAR_BUSY          0x80000000
+
+/* PLA_EEE_CR */
+#define        URE_EEE_RX_EN           0x0001
+#define        URE_EEE_TX_EN           0x0002
+
+/* PLA_BOOT_CTRL */
+#define        URE_AUTOLOAD_DONE       0x0002
+
+/* USB_USB2PHY */
+#define        URE_USB2PHY_SUSPEND     0x0001
+#define        URE_USB2PHY_L1          0x0002
+
+/* USB_SSPHYLINK2 */
+#define        URE_PWD_DN_SCALE_MASK   0x3ffe
+#define        URE_PWD_DN_SCALE(x)     ((x) << 1)
+
+/* USB_CSR_DUMMY1 */
+#define        URE_DYNAMIC_BURST       0x0001
+
+/* USB_CSR_DUMMY2 */
+#define        URE_EP4_FULL_FC         0x0001
+
+/* USB_DEV_STAT */
+#define        URE_STAT_SPEED_MASK     0x0006
+#define        URE_STAT_SPEED_HIGH     0x0000
+#define        URE_STAT_SPEED_FULL     0x0001
+
+/* USB_TX_AGG */
+#define        URE_TX_AGG_MAX_THRESHOLD        0x03
+
+/* USB_RX_BUF_TH */
+#define        URE_RX_THR_SUPER        0x0c350180
+#define        URE_RX_THR_HIGH         0x7a120180
+#define        URE_RX_THR_SLOW         0xffff0180
+
+/* USB_TX_DMA */
+#define        URE_TEST_MODE_DISABLE   0x00000001
+#define        URE_TX_SIZE_ADJUST1     0x00000100
+
+/* USB_UPS_CTRL */
+#define        URE_POWER_CUT           0x0100
+
+/* USB_PM_CTRL_STATUS */
+#define        URE_RESUME_INDICATE     0x0001
+
+/* USB_USB_CTRL */
+#define        URE_RX_AGG_DISABLE      0x0010
+#define        URE_RX_ZERO_EN          0x0080
+
+/* USB_U2P3_CTRL */
+#define        URE_U2P3_ENABLE         0x0001
+
+/* USB_POWER_CUT */
+#define        URE_PWR_EN              0x0001
+#define        URE_PHASE2_EN           0x0008
+
+/* USB_MISC_0 */
+#define        URE_PCUT_STATUS         0x0001
+
+/* USB_RX_EARLY_TIMEOUT */
+#define        URE_COALESCE_SUPER      85000U
+#define        URE_COALESCE_HIGH       250000U
+#define        URE_COALESCE_SLOW       524280U
+
+/* USB_WDT11_CTRL */
+#define        URE_TIMER11_EN          0x0001
+
+/* USB_LPM_CTRL */
+#define        URE_FIFO_EMPTY_1FB      0x30
+#define        URE_LPM_TIMER_MASK      0x0c
+#define        URE_LPM_TIMER_500MS     0x04
+#define        URE_LPM_TIMER_500US     0x0c
+#define        URE_ROK_EXIT_LPM        0x02
+
+/* USB_AFE_CTRL2 */
+#define        URE_SEN_VAL_MASK        0xf800
+#define        URE_SEN_VAL_NORMAL      0xa000
+#define        URE_SEL_RXIDLE          0x0100
+
+/* OCP_ALDPS_CONFIG */
+#define        URE_ENPWRSAVE           0x8000  
+#define        URE_ENPDNPS             0x0200  
+#define        URE_LINKENA             0x0100  
+#define        URE_DIS_SDSAVE          0x0010
+
+/* OCP_PHY_STATUS */
+#define        URE_PHY_STAT_MASK       0x0007
+#define        URE_PHY_STAT_LAN_ON     3       
+#define        URE_PHY_STAT_PWRDN      5
+
+/* OCP_POWER_CFG */
+#define        URE_EEE_CLKDIV_EN       0x8000
+#define        URE_EN_ALDPS            0x0004
+#define        URE_EN_10M_PLLOFF       0x0001
+
+/* OCP_EEE_CFG */
+#define        URE_CTAP_SHORT_EN       0x0040
+#define        URE_EEE10_EN            0x0010
+
+/* OCP_DOWN_SPEED */
+#define        URE_EN_10M_BGOFF        0x0080
+
+/* OCP_PHY_STATE */
+#define        URE_TXDIS_STATE         0x01
+#define        URE_ABD_STATE           0x02
+
+/* OCP_ADC_CFG */
+#define        URE_CKADSEL_L           0x0100
+#define        URE_ADC_EN              0x0080
+#define        URE_EN_EMI_L            0x0040
+
+#define        URE_MCU_TYPE_PLA        0x0100
+#define        URE_MCU_TYPE_USB        0x0000
+
+#define        GET_MII(sc)             uether_getmii(&(sc)->sc_ue)
+
+struct ure_intrpkt {
+       uint8_t ure_tsr;
+       uint8_t ure_rsr;
+       uint8_t ure_gep_msr;
+       uint8_t ure_waksr;
+       uint8_t ure_txok_cnt;
+       uint8_t ure_rxlost_cnt;
+       uint8_t ure_crcerr_cnt;
+       uint8_t ure_col_cnt;
+} __packed;
+
+struct ure_rxpkt {
+       uint32_t ure_pktlen;
+#define        URE_RXPKT_LEN_MASK      0x7fff
+       uint32_t ure_rsvd0;
+       uint32_t ure_rsvd1;
+       uint32_t ure_rsvd2;
+       uint32_t ure_rsvd3;
+       uint32_t ure_rsvd4;
+} __packed;
+
+struct ure_txpkt {
+       uint32_t ure_pktlen;
+#define        URE_TXPKT_TX_FS         (1 << 31)
+#define        URE_TXPKT_TX_LS         (1 << 30)
+#define        URE_TXPKT_LEN_MASK      0xffff
+       uint32_t ure_rsvd0;
+} __packed;
+
+#define URE_ENDPT_RX           0
+#define URE_ENDPT_TX           1
+#define URE_ENDPT_MAX          2
+
+#define URE_TX_LIST_CNT                1
+#define URE_RX_LIST_CNT                1
+
+struct ure_chain {
+       struct ure_softc        *uc_sc;
+       struct usbd_xfer        *uc_xfer;
+       char                    *uc_buf;
+       struct mbuf             *uc_mbuf;
+       int                     uc_accum;
+       int                     uc_idx;
+};
+
+struct ure_cdata {
+       struct ure_chain        tx_chain[URE_TX_LIST_CNT];
+       struct ure_chain        rx_chain[URE_RX_LIST_CNT];
+       int                     tx_prod;
+       int                     tx_const;
+       int                     tx_cnt;
+       int                     rx_prod;
+};
+
+struct ure_softc {
+       struct device           ure_dev;
+       struct usbd_device      *ure_udev;
+
+       /* usb */
+       struct usbd_interface   *ure_iface;
+       struct usb_task         ure_tick_task;
+       struct usb_task         ure_stop_task;
+       int                     ure_ed[URE_ENDPT_MAX];
+       struct usbd_pipe        *ure_ep[URE_ENDPT_MAX];
+
+       /* ethernet */
+       struct arpcom           ure_ac;
+       struct mii_data         ure_mii;
+       struct rwlock           ure_mii_lock;
+       int                     ure_refcnt;
+
+       struct ure_cdata        ure_cdata;
+       struct timeout          ure_stat_ch;
+
+       struct timeval          ure_rx_notice;
+       int                     ure_bufsz;
+
+       int                     ure_phyno;
+
+       u_int                   ure_flags;
+#define        URE_FLAG_LINK           0x0001
+
+       u_int                   ure_chip;
+#define        URE_CHIP_VER_4C00       0x01
+#define        URE_CHIP_VER_4C10       0x02
+};
+