--- /dev/null
+/*-
+ * 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;
+}
--- /dev/null
+/*-
+ * 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
+};
+