summaryrefslogtreecommitdiffstats
path: root/sys/dev/usb/net
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/usb/net')
-rw-r--r--sys/dev/usb/net/if_aue.c1054
-rw-r--r--sys/dev/usb/net/if_auereg.h220
-rw-r--r--sys/dev/usb/net/if_axe.c1076
-rw-r--r--sys/dev/usb/net/if_axereg.h196
-rw-r--r--sys/dev/usb/net/if_cdce.c771
-rw-r--r--sys/dev/usb/net/if_cdcereg.h68
-rw-r--r--sys/dev/usb/net/if_cue.c645
-rw-r--r--sys/dev/usb/net/if_cuereg.h132
-rw-r--r--sys/dev/usb/net/if_kue.c704
-rw-r--r--sys/dev/usb/net/if_kuefw.h685
-rw-r--r--sys/dev/usb/net/if_kuereg.h141
-rw-r--r--sys/dev/usb/net/if_rue.c913
-rw-r--r--sys/dev/usb/net/if_ruereg.h183
-rw-r--r--sys/dev/usb/net/if_udav.c856
-rw-r--r--sys/dev/usb/net/if_udavreg.h166
-rw-r--r--sys/dev/usb/net/usb_ethernet.c587
-rw-r--r--sys/dev/usb/net/usb_ethernet.h122
17 files changed, 8519 insertions, 0 deletions
diff --git a/sys/dev/usb/net/if_aue.c b/sys/dev/usb/net/if_aue.c
new file mode 100644
index 0000000..026fa7c
--- /dev/null
+++ b/sys/dev/usb/net/if_aue.c
@@ -0,0 +1,1054 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved.
+ *
+ * Copyright (c) 2006
+ * Alfred Perlstein <alfred@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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * ADMtek AN986 Pegasus and AN8511 Pegasus II USB to ethernet driver.
+ * Datasheet is available from http://www.admtek.com.tw.
+ *
+ * Written by Bill Paul <wpaul@ee.columbia.edu>
+ * Electrical Engineering Department
+ * Columbia University, New York City
+ *
+ * SMP locking by Alfred Perlstein <alfred@freebsd.org>.
+ * RED Inc.
+ */
+
+/*
+ * The Pegasus chip uses four USB "endpoints" to provide 10/100 ethernet
+ * support: the control endpoint for reading/writing registers, burst
+ * read endpoint for packet reception, burst write for packet transmission
+ * and one for "interrupts." The chip uses the same RX filter scheme
+ * as the other ADMtek ethernet parts: one perfect filter entry for the
+ * the station address and a 64-bit multicast hash table. The chip supports
+ * both MII and HomePNA attachments.
+ *
+ * Since the maximum data transfer speed of USB is supposed to be 12Mbps,
+ * you're never really going to get 100Mbps speeds from this device. I
+ * think the idea is to allow the device to connect to 10 or 100Mbps
+ * networks, not necessarily to provide 100Mbps performance. Also, since
+ * the controller uses an external PHY chip, it's possible that board
+ * designers might simply choose a 10Mbps PHY.
+ *
+ * Registers are accessed using usb2_ether_do_request(). Packet
+ * transfers are done using usb2_transfer() and friends.
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+
+#define USB_DEBUG_VAR aue_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/net/usb_ethernet.h>
+#include <dev/usb/net/if_auereg.h>
+
+#if USB_DEBUG
+static int aue_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, aue, CTLFLAG_RW, 0, "USB aue");
+SYSCTL_INT(_hw_usb2_aue, OID_AUTO, debug, CTLFLAG_RW, &aue_debug, 0,
+ "Debug level");
+#endif
+
+/*
+ * Various supported device vendors/products.
+ */
+static const struct usb2_device_id aue_devs[] = {
+ {USB_VPI(USB_VENDOR_3COM, USB_PRODUCT_3COM_3C460B, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_DSB650TX_PNA, 0)},
+ {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_UFE1000, AUE_FLAG_LSYS)},
+ {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX10, 0)},
+ {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX1, AUE_FLAG_PNA | AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX2, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX4, AUE_FLAG_PNA)},
+ {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX5, AUE_FLAG_PNA)},
+ {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX6, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX7, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX8, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX9, AUE_FLAG_PNA)},
+ {USB_VPI(USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_SS1001, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_USB320_EC, 0)},
+ {USB_VPI(USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII_2, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII_3, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII_4, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUS, AUE_FLAG_PNA | AUE_FLAG_DUAL_PHY)},
+ {USB_VPI(USB_VENDOR_AEI, USB_PRODUCT_AEI_FASTETHERNET, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_ALLIEDTELESYN, USB_PRODUCT_ALLIEDTELESYN_ATUSB100, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC110T, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_USB2LAN, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USB100, 0)},
+ {USB_VPI(USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBE100, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBEL100, 0)},
+ {USB_VPI(USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBLP100, AUE_FLAG_PNA)},
+ {USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TXS, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TX, 0)},
+ {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX1, AUE_FLAG_LSYS)},
+ {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX2, AUE_FLAG_LSYS | AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX3, AUE_FLAG_LSYS | AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX4, AUE_FLAG_LSYS | AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX_PNA, AUE_FLAG_PNA)},
+ {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX, AUE_FLAG_LSYS)},
+ {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650, AUE_FLAG_LSYS)},
+ {USB_VPI(USB_VENDOR_ELCON, USB_PRODUCT_ELCON_PLAN, AUE_FLAG_PNA | AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSB20, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBLTX, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX0, 0)},
+ {USB_VPI(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX1, AUE_FLAG_LSYS)},
+ {USB_VPI(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX2, 0)},
+ {USB_VPI(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX3, AUE_FLAG_LSYS)},
+ {USB_VPI(USB_VENDOR_ELSA, USB_PRODUCT_ELSA_USB2ETHERNET, 0)},
+ {USB_VPI(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNBR402W, 0)},
+ {USB_VPI(USB_VENDOR_HAWKING, USB_PRODUCT_HAWKING_UF100, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_HN210E, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETTXS, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETTX, 0)},
+ {USB_VPI(USB_VENDOR_KINGSTON, USB_PRODUCT_KINGSTON_KNU101TX, 0)},
+ {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB100H1, AUE_FLAG_LSYS | AUE_FLAG_PNA)},
+ {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB100TX, AUE_FLAG_LSYS)},
+ {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TA, AUE_FLAG_LSYS)},
+ {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TX1, AUE_FLAG_LSYS | AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TX2, AUE_FLAG_LSYS | AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10T, AUE_FLAG_LSYS)},
+ {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUA2TX5, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUATX1, 0)},
+ {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUATX5, 0)},
+ {USB_VPI(USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_MN110, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_FA101, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_SIEMENS, USB_PRODUCT_SIEMENS_SPEEDSTREAM, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_SIIG2, USB_PRODUCT_SIIG2_USBTOETHER, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_SMARTBRIDGES, USB_PRODUCT_SMARTBRIDGES_SMARTNIC, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_SMC, USB_PRODUCT_SMC_2202USB, 0)},
+ {USB_VPI(USB_VENDOR_SMC, USB_PRODUCT_SMC_2206USB, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_SOHOWARE, USB_PRODUCT_SOHOWARE_NUB100, 0)},
+ {USB_VPI(USB_VENDOR_SOHOWARE, USB_PRODUCT_SOHOWARE_NUB110, AUE_FLAG_PII)},
+};
+
+/* prototypes */
+
+static device_probe_t aue_probe;
+static device_attach_t aue_attach;
+static device_detach_t aue_detach;
+static device_shutdown_t aue_shutdown;
+static miibus_readreg_t aue_miibus_readreg;
+static miibus_writereg_t aue_miibus_writereg;
+static miibus_statchg_t aue_miibus_statchg;
+
+static usb2_callback_t aue_intr_callback;
+static usb2_callback_t aue_bulk_read_callback;
+static usb2_callback_t aue_bulk_write_callback;
+
+static usb2_ether_fn_t aue_attach_post;
+static usb2_ether_fn_t aue_init;
+static usb2_ether_fn_t aue_stop;
+static usb2_ether_fn_t aue_start;
+static usb2_ether_fn_t aue_tick;
+static usb2_ether_fn_t aue_setmulti;
+static usb2_ether_fn_t aue_setpromisc;
+
+static uint8_t aue_csr_read_1(struct aue_softc *, uint16_t);
+static uint16_t aue_csr_read_2(struct aue_softc *, uint16_t);
+static void aue_csr_write_1(struct aue_softc *, uint16_t, uint8_t);
+static void aue_csr_write_2(struct aue_softc *, uint16_t, uint16_t);
+static void aue_eeprom_getword(struct aue_softc *, int, uint16_t *);
+static void aue_read_eeprom(struct aue_softc *, uint8_t *, uint16_t,
+ uint16_t);
+static void aue_reset(struct aue_softc *);
+static void aue_reset_pegasus_II(struct aue_softc *);
+
+static int aue_ifmedia_upd(struct ifnet *);
+static void aue_ifmedia_sts(struct ifnet *, struct ifmediareq *);
+
+static const struct usb2_config aue_config[AUE_N_TRANSFER] = {
+
+ [AUE_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = (MCLBYTES + 2),
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = aue_bulk_write_callback,
+ .mh.timeout = 10000, /* 10 seconds */
+ },
+
+ [AUE_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = (MCLBYTES + 4 + ETHER_CRC_LEN),
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = aue_bulk_read_callback,
+ },
+
+ [AUE_INTR_DT_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.bufsize = 0, /* use wMaxPacketSize */
+ .mh.callback = aue_intr_callback,
+ },
+};
+
+static device_method_t aue_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, aue_probe),
+ DEVMETHOD(device_attach, aue_attach),
+ DEVMETHOD(device_detach, aue_detach),
+ DEVMETHOD(device_shutdown, aue_shutdown),
+
+ /* bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, aue_miibus_readreg),
+ DEVMETHOD(miibus_writereg, aue_miibus_writereg),
+ DEVMETHOD(miibus_statchg, aue_miibus_statchg),
+
+ {0, 0}
+};
+
+static driver_t aue_driver = {
+ .name = "aue",
+ .methods = aue_methods,
+ .size = sizeof(struct aue_softc)
+};
+
+static devclass_t aue_devclass;
+
+DRIVER_MODULE(aue, ushub, aue_driver, aue_devclass, NULL, 0);
+DRIVER_MODULE(miibus, aue, miibus_driver, miibus_devclass, 0, 0);
+MODULE_DEPEND(aue, uether, 1, 1, 1);
+MODULE_DEPEND(aue, usb, 1, 1, 1);
+MODULE_DEPEND(aue, ether, 1, 1, 1);
+MODULE_DEPEND(aue, miibus, 1, 1, 1);
+
+static const struct usb2_ether_methods aue_ue_methods = {
+ .ue_attach_post = aue_attach_post,
+ .ue_start = aue_start,
+ .ue_init = aue_init,
+ .ue_stop = aue_stop,
+ .ue_tick = aue_tick,
+ .ue_setmulti = aue_setmulti,
+ .ue_setpromisc = aue_setpromisc,
+ .ue_mii_upd = aue_ifmedia_upd,
+ .ue_mii_sts = aue_ifmedia_sts,
+};
+
+#define AUE_SETBIT(sc, reg, x) \
+ aue_csr_write_1(sc, reg, aue_csr_read_1(sc, reg) | (x))
+
+#define AUE_CLRBIT(sc, reg, x) \
+ aue_csr_write_1(sc, reg, aue_csr_read_1(sc, reg) & ~(x))
+
+static uint8_t
+aue_csr_read_1(struct aue_softc *sc, uint16_t reg)
+{
+ struct usb2_device_request req;
+ usb2_error_t err;
+ uint8_t val;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = AUE_UR_READREG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 1);
+
+ err = usb2_ether_do_request(&sc->sc_ue, &req, &val, 1000);
+ if (err)
+ return (0);
+ return (val);
+}
+
+static uint16_t
+aue_csr_read_2(struct aue_softc *sc, uint16_t reg)
+{
+ struct usb2_device_request req;
+ usb2_error_t err;
+ uint16_t val;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = AUE_UR_READREG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 2);
+
+ err = usb2_ether_do_request(&sc->sc_ue, &req, &val, 1000);
+ if (err)
+ return (0);
+ return (le16toh(val));
+}
+
+static void
+aue_csr_write_1(struct aue_softc *sc, uint16_t reg, uint8_t val)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = AUE_UR_WRITEREG;
+ req.wValue[0] = val;
+ req.wValue[1] = 0;
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 1);
+
+ if (usb2_ether_do_request(&sc->sc_ue, &req, &val, 1000)) {
+ /* error ignored */
+ }
+}
+
+static void
+aue_csr_write_2(struct aue_softc *sc, uint16_t reg, uint16_t val)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = AUE_UR_WRITEREG;
+ USETW(req.wValue, val);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 2);
+
+ val = htole16(val);
+
+ if (usb2_ether_do_request(&sc->sc_ue, &req, &val, 1000)) {
+ /* error ignored */
+ }
+}
+
+/*
+ * Read a word of data stored in the EEPROM at address 'addr.'
+ */
+static void
+aue_eeprom_getword(struct aue_softc *sc, int addr, uint16_t *dest)
+{
+ int i;
+ uint16_t word = 0;
+
+ aue_csr_write_1(sc, AUE_EE_REG, addr);
+ aue_csr_write_1(sc, AUE_EE_CTL, AUE_EECTL_READ);
+
+ for (i = 0; i != AUE_TIMEOUT; i++) {
+ if (aue_csr_read_1(sc, AUE_EE_CTL) & AUE_EECTL_DONE)
+ break;
+ if (usb2_ether_pause(&sc->sc_ue, hz / 100))
+ break;
+ }
+
+ if (i == AUE_TIMEOUT)
+ device_printf(sc->sc_ue.ue_dev, "EEPROM read timed out\n");
+
+ word = aue_csr_read_2(sc, AUE_EE_DATA);
+ *dest = word;
+}
+
+/*
+ * Read a sequence of words from the EEPROM.
+ */
+static void
+aue_read_eeprom(struct aue_softc *sc, uint8_t *dest,
+ uint16_t off, uint16_t len)
+{
+ uint16_t *ptr = (uint16_t *)dest;
+ int i;
+
+ for (i = 0; i != len; i++, ptr++)
+ aue_eeprom_getword(sc, off + i, ptr);
+}
+
+static int
+aue_miibus_readreg(device_t dev, int phy, int reg)
+{
+ struct aue_softc *sc = device_get_softc(dev);
+ int i, locked;
+ uint16_t val = 0;
+
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ AUE_LOCK(sc);
+
+ /*
+ * The Am79C901 HomePNA PHY actually contains two transceivers: a 1Mbps
+ * HomePNA PHY and a 10Mbps full/half duplex ethernet PHY with NWAY
+ * autoneg. However in the ADMtek adapter, only the 1Mbps PHY is
+ * actually connected to anything, so we ignore the 10Mbps one. It
+ * happens to be configured for MII address 3, so we filter that out.
+ */
+ if (sc->sc_flags & AUE_FLAG_DUAL_PHY) {
+ if (phy == 3)
+ goto done;
+#if 0
+ if (phy != 1)
+ goto done;
+#endif
+ }
+ aue_csr_write_1(sc, AUE_PHY_ADDR, phy);
+ aue_csr_write_1(sc, AUE_PHY_CTL, reg | AUE_PHYCTL_READ);
+
+ for (i = 0; i != AUE_TIMEOUT; i++) {
+ if (aue_csr_read_1(sc, AUE_PHY_CTL) & AUE_PHYCTL_DONE)
+ break;
+ if (usb2_ether_pause(&sc->sc_ue, hz / 100))
+ break;
+ }
+
+ if (i == AUE_TIMEOUT)
+ device_printf(sc->sc_ue.ue_dev, "MII read timed out\n");
+
+ val = aue_csr_read_2(sc, AUE_PHY_DATA);
+
+done:
+ if (!locked)
+ AUE_UNLOCK(sc);
+ return (val);
+}
+
+static int
+aue_miibus_writereg(device_t dev, int phy, int reg, int data)
+{
+ struct aue_softc *sc = device_get_softc(dev);
+ int i;
+ int locked;
+
+ if (phy == 3)
+ return (0);
+
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ AUE_LOCK(sc);
+
+ aue_csr_write_2(sc, AUE_PHY_DATA, data);
+ aue_csr_write_1(sc, AUE_PHY_ADDR, phy);
+ aue_csr_write_1(sc, AUE_PHY_CTL, reg | AUE_PHYCTL_WRITE);
+
+ for (i = 0; i != AUE_TIMEOUT; i++) {
+ if (aue_csr_read_1(sc, AUE_PHY_CTL) & AUE_PHYCTL_DONE)
+ break;
+ if (usb2_ether_pause(&sc->sc_ue, hz / 100))
+ break;
+ }
+
+ if (i == AUE_TIMEOUT)
+ device_printf(sc->sc_ue.ue_dev, "MII read timed out\n");
+
+ if (!locked)
+ AUE_UNLOCK(sc);
+ return (0);
+}
+
+static void
+aue_miibus_statchg(device_t dev)
+{
+ struct aue_softc *sc = device_get_softc(dev);
+ struct mii_data *mii = GET_MII(sc);
+ int locked;
+
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ AUE_LOCK(sc);
+
+ AUE_CLRBIT(sc, AUE_CTL0, AUE_CTL0_RX_ENB | AUE_CTL0_TX_ENB);
+ if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX)
+ AUE_SETBIT(sc, AUE_CTL1, AUE_CTL1_SPEEDSEL);
+ else
+ AUE_CLRBIT(sc, AUE_CTL1, AUE_CTL1_SPEEDSEL);
+
+ if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX)
+ AUE_SETBIT(sc, AUE_CTL1, AUE_CTL1_DUPLEX);
+ else
+ AUE_CLRBIT(sc, AUE_CTL1, AUE_CTL1_DUPLEX);
+
+ AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_RX_ENB | AUE_CTL0_TX_ENB);
+
+ /*
+ * Set the LED modes on the LinkSys adapter.
+ * This turns on the 'dual link LED' bin in the auxmode
+ * register of the Broadcom PHY.
+ */
+ if (sc->sc_flags & AUE_FLAG_LSYS) {
+ uint16_t auxmode;
+
+ auxmode = aue_miibus_readreg(dev, 0, 0x1b);
+ aue_miibus_writereg(dev, 0, 0x1b, auxmode | 0x04);
+ }
+ if (!locked)
+ AUE_UNLOCK(sc);
+}
+
+#define AUE_BITS 6
+static void
+aue_setmulti(struct usb2_ether *ue)
+{
+ struct aue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+ struct ifmultiaddr *ifma;
+ uint32_t h = 0;
+ uint32_t i;
+ uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ AUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
+ AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_ALLMULTI);
+ return;
+ }
+
+ AUE_CLRBIT(sc, AUE_CTL0, AUE_CTL0_ALLMULTI);
+
+ /* now program new ones */
+ IF_ADDR_LOCK(ifp);
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ h = ether_crc32_le(LLADDR((struct sockaddr_dl *)
+ ifma->ifma_addr), ETHER_ADDR_LEN) & ((1 << AUE_BITS) - 1);
+ hashtbl[(h >> 3)] |= 1 << (h & 0x7);
+ }
+ IF_ADDR_UNLOCK(ifp);
+
+ /* write the hashtable */
+ for (i = 0; i != 8; i++)
+ aue_csr_write_1(sc, AUE_MAR0 + i, hashtbl[i]);
+}
+
+static void
+aue_reset_pegasus_II(struct aue_softc *sc)
+{
+ /* Magic constants taken from Linux driver. */
+ aue_csr_write_1(sc, AUE_REG_1D, 0);
+ aue_csr_write_1(sc, AUE_REG_7B, 2);
+#if 0
+ if ((sc->sc_flags & HAS_HOME_PNA) && mii_mode)
+ aue_csr_write_1(sc, AUE_REG_81, 6);
+ else
+#endif
+ aue_csr_write_1(sc, AUE_REG_81, 2);
+}
+
+static void
+aue_reset(struct aue_softc *sc)
+{
+ int i;
+
+ AUE_SETBIT(sc, AUE_CTL1, AUE_CTL1_RESETMAC);
+
+ for (i = 0; i != AUE_TIMEOUT; i++) {
+ if (!(aue_csr_read_1(sc, AUE_CTL1) & AUE_CTL1_RESETMAC))
+ break;
+ if (usb2_ether_pause(&sc->sc_ue, hz / 100))
+ break;
+ }
+
+ if (i == AUE_TIMEOUT)
+ device_printf(sc->sc_ue.ue_dev, "reset failed\n");
+
+ /*
+ * The PHY(s) attached to the Pegasus chip may be held
+ * in reset until we flip on the GPIO outputs. Make sure
+ * to set the GPIO pins high so that the PHY(s) will
+ * be enabled.
+ *
+ * Note: We force all of the GPIO pins low first, *then*
+ * enable the ones we want.
+ */
+ aue_csr_write_1(sc, AUE_GPIO0, AUE_GPIO_OUT0|AUE_GPIO_SEL0);
+ aue_csr_write_1(sc, AUE_GPIO0, AUE_GPIO_OUT0|AUE_GPIO_SEL0|AUE_GPIO_SEL1);
+
+ if (sc->sc_flags & AUE_FLAG_LSYS) {
+ /* Grrr. LinkSys has to be different from everyone else. */
+ aue_csr_write_1(sc, AUE_GPIO0, AUE_GPIO_SEL0|AUE_GPIO_SEL1);
+ aue_csr_write_1(sc, AUE_GPIO0,
+ AUE_GPIO_SEL0|AUE_GPIO_SEL1|AUE_GPIO_OUT0);
+ }
+ if (sc->sc_flags & AUE_FLAG_PII)
+ aue_reset_pegasus_II(sc);
+
+ /* Wait a little while for the chip to get its brains in order: */
+ usb2_ether_pause(&sc->sc_ue, hz / 100);
+}
+
+static void
+aue_attach_post(struct usb2_ether *ue)
+{
+ struct aue_softc *sc = usb2_ether_getsc(ue);
+
+ /* reset the adapter */
+ aue_reset(sc);
+
+ /* get station address from the EEPROM */
+ aue_read_eeprom(sc, ue->ue_eaddr, 0, 3);
+}
+
+/*
+ * Probe for a Pegasus chip.
+ */
+static int
+aue_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST)
+ return (ENXIO);
+ if (uaa->info.bConfigIndex != AUE_CONFIG_INDEX)
+ return (ENXIO);
+ if (uaa->info.bIfaceIndex != AUE_IFACE_IDX)
+ return (ENXIO);
+ /*
+ * Belkin USB Bluetooth dongles of the F8T012xx1 model series conflict
+ * with older Belkin USB2LAN adapters. Skip if_aue if we detect one of
+ * the devices that look like Bluetooth adapters.
+ */
+ if (uaa->info.idVendor == USB_VENDOR_BELKIN &&
+ uaa->info.idProduct == USB_PRODUCT_BELKIN_F8T012 &&
+ uaa->info.bcdDevice == 0x0413)
+ return (ENXIO);
+
+ return (usb2_lookup_id_by_uaa(aue_devs, sizeof(aue_devs), uaa));
+}
+
+/*
+ * Attach the interface. Allocate softc structures, do ifmedia
+ * setup and ethernet/BPF attach.
+ */
+static int
+aue_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct aue_softc *sc = device_get_softc(dev);
+ struct usb2_ether *ue = &sc->sc_ue;
+ uint8_t iface_index;
+ int error;
+
+ sc->sc_flags = USB_GET_DRIVER_INFO(uaa);
+
+ if (uaa->info.bcdDevice >= 0x0201) {
+ /* XXX currently undocumented */
+ sc->sc_flags |= AUE_FLAG_VER_2;
+ }
+
+ device_set_usb2_desc(dev);
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ iface_index = AUE_IFACE_IDX;
+ error = usb2_transfer_setup(uaa->device, &iface_index,
+ sc->sc_xfer, aue_config, AUE_N_TRANSFER,
+ sc, &sc->sc_mtx);
+ if (error) {
+ device_printf(dev, "allocating USB transfers failed!\n");
+ goto detach;
+ }
+
+ ue->ue_sc = sc;
+ ue->ue_dev = dev;
+ ue->ue_udev = uaa->device;
+ ue->ue_mtx = &sc->sc_mtx;
+ ue->ue_methods = &aue_ue_methods;
+
+ error = usb2_ether_ifattach(ue);
+ if (error) {
+ device_printf(dev, "could not attach interface\n");
+ goto detach;
+ }
+ return (0); /* success */
+
+detach:
+ aue_detach(dev);
+ return (ENXIO); /* failure */
+}
+
+static int
+aue_detach(device_t dev)
+{
+ struct aue_softc *sc = device_get_softc(dev);
+ struct usb2_ether *ue = &sc->sc_ue;
+
+ usb2_transfer_unsetup(sc->sc_xfer, AUE_N_TRANSFER);
+ usb2_ether_ifdetach(ue);
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static void
+aue_intr_callback(struct usb2_xfer *xfer)
+{
+ struct aue_softc *sc = xfer->priv_sc;
+ struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue);
+ struct aue_intrpkt pkt;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) &&
+ xfer->actlen >= sizeof(pkt)) {
+
+ usb2_copy_out(xfer->frbuffers, 0, &pkt, sizeof(pkt));
+
+ if (pkt.aue_txstat0)
+ ifp->if_oerrors++;
+ if (pkt.aue_txstat0 & (AUE_TXSTAT0_LATECOLL &
+ AUE_TXSTAT0_EXCESSCOLL))
+ ifp->if_collisions++;
+ }
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+aue_bulk_read_callback(struct usb2_xfer *xfer)
+{
+ struct aue_softc *sc = xfer->priv_sc;
+ struct usb2_ether *ue = &sc->sc_ue;
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+ struct aue_rxpkt stat;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTFN(11, "received %d bytes\n", xfer->actlen);
+
+ if (sc->sc_flags & AUE_FLAG_VER_2) {
+
+ if (xfer->actlen == 0) {
+ ifp->if_ierrors++;
+ goto tr_setup;
+ }
+ } else {
+
+ if (xfer->actlen <= (sizeof(stat) + ETHER_CRC_LEN)) {
+ ifp->if_ierrors++;
+ goto tr_setup;
+ }
+ usb2_copy_out(xfer->frbuffers,
+ xfer->actlen - sizeof(stat), &stat, sizeof(stat));
+
+ /*
+ * turn off all the non-error bits in the rx status
+ * word:
+ */
+ stat.aue_rxstat &= AUE_RXSTAT_MASK;
+ if (stat.aue_rxstat) {
+ ifp->if_ierrors++;
+ goto tr_setup;
+ }
+ /* No errors; receive the packet. */
+ xfer->actlen -= (sizeof(stat) + ETHER_CRC_LEN);
+ }
+ usb2_ether_rxbuf(ue, xfer->frbuffers, 0, xfer->actlen);
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ usb2_ether_rxflush(ue);
+ return;
+
+ default: /* Error */
+ DPRINTF("bulk read error, %s\n",
+ usb2_errstr(xfer->error));
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+aue_bulk_write_callback(struct usb2_xfer *xfer)
+{
+ struct aue_softc *sc = xfer->priv_sc;
+ struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue);
+ struct mbuf *m;
+ uint8_t buf[2];
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTFN(11, "transfer of %d bytes complete\n", xfer->actlen);
+ ifp->if_opackets++;
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ if ((sc->sc_flags & AUE_FLAG_LINK) == 0) {
+ /*
+ * don't send anything if there is no link !
+ */
+ return;
+ }
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+
+ if (m == NULL)
+ return;
+ if (m->m_pkthdr.len > MCLBYTES)
+ m->m_pkthdr.len = MCLBYTES;
+ if (sc->sc_flags & AUE_FLAG_VER_2) {
+
+ xfer->frlengths[0] = m->m_pkthdr.len;
+
+ usb2_m_copy_in(xfer->frbuffers, 0,
+ m, 0, m->m_pkthdr.len);
+
+ } else {
+
+ xfer->frlengths[0] = (m->m_pkthdr.len + 2);
+
+ /*
+ * The ADMtek documentation says that the
+ * packet length is supposed to be specified
+ * in the first two bytes of the transfer,
+ * however it actually seems to ignore this
+ * info and base the frame size on the bulk
+ * transfer length.
+ */
+ buf[0] = (uint8_t)(m->m_pkthdr.len);
+ buf[1] = (uint8_t)(m->m_pkthdr.len >> 8);
+
+ usb2_copy_in(xfer->frbuffers, 0, buf, 2);
+
+ usb2_m_copy_in(xfer->frbuffers, 2,
+ m, 0, m->m_pkthdr.len);
+ }
+
+ /*
+ * if there's a BPF listener, bounce a copy
+ * of this frame to him:
+ */
+ BPF_MTAP(ifp, m);
+
+ m_freem(m);
+
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ DPRINTFN(11, "transfer error, %s\n",
+ usb2_errstr(xfer->error));
+
+ ifp->if_oerrors++;
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+aue_tick(struct usb2_ether *ue)
+{
+ struct aue_softc *sc = usb2_ether_getsc(ue);
+ struct mii_data *mii = GET_MII(sc);
+
+ AUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ mii_tick(mii);
+ if ((sc->sc_flags & AUE_FLAG_LINK) == 0
+ && mii->mii_media_status & IFM_ACTIVE &&
+ IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
+ sc->sc_flags |= AUE_FLAG_LINK;
+ aue_start(ue);
+ }
+}
+
+static void
+aue_start(struct usb2_ether *ue)
+{
+ struct aue_softc *sc = usb2_ether_getsc(ue);
+
+ /*
+ * start the USB transfers, if not already started:
+ */
+ usb2_transfer_start(sc->sc_xfer[AUE_INTR_DT_RD]);
+ usb2_transfer_start(sc->sc_xfer[AUE_BULK_DT_RD]);
+ usb2_transfer_start(sc->sc_xfer[AUE_BULK_DT_WR]);
+}
+
+static void
+aue_init(struct usb2_ether *ue)
+{
+ struct aue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+ int i;
+
+ AUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ /*
+ * Cancel pending I/O
+ */
+ aue_reset(sc);
+
+ /* Set MAC address */
+ for (i = 0; i != ETHER_ADDR_LEN; i++)
+ aue_csr_write_1(sc, AUE_PAR0 + i, IF_LLADDR(ifp)[i]);
+
+ /* update promiscuous setting */
+ aue_setpromisc(ue);
+
+ /* Load the multicast filter. */
+ aue_setmulti(ue);
+
+ /* Enable RX and TX */
+ aue_csr_write_1(sc, AUE_CTL0, AUE_CTL0_RXSTAT_APPEND | AUE_CTL0_RX_ENB);
+ AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_TX_ENB);
+ AUE_SETBIT(sc, AUE_CTL2, AUE_CTL2_EP3_CLR);
+
+ usb2_transfer_set_stall(sc->sc_xfer[AUE_BULK_DT_WR]);
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ aue_start(ue);
+}
+
+static void
+aue_setpromisc(struct usb2_ether *ue)
+{
+ struct aue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+
+ AUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ /* if we want promiscuous mode, set the allframes bit: */
+ if (ifp->if_flags & IFF_PROMISC)
+ AUE_SETBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC);
+ else
+ AUE_CLRBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC);
+}
+
+/*
+ * Set media options.
+ */
+static int
+aue_ifmedia_upd(struct ifnet *ifp)
+{
+ struct aue_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+
+ AUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ sc->sc_flags &= ~AUE_FLAG_LINK;
+ if (mii->mii_instance) {
+ struct mii_softc *miisc;
+
+ LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
+ mii_phy_reset(miisc);
+ }
+ mii_mediachg(mii);
+ return (0);
+}
+
+/*
+ * Report current media status.
+ */
+static void
+aue_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct aue_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+
+ AUE_LOCK(sc);
+ mii_pollstat(mii);
+ AUE_UNLOCK(sc);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+}
+
+/*
+ * Stop the adapter and free any mbufs allocated to the
+ * RX and TX lists.
+ */
+static void
+aue_stop(struct usb2_ether *ue)
+{
+ struct aue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+
+ AUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ sc->sc_flags &= ~AUE_FLAG_LINK;
+
+ /*
+ * stop all the transfers, if not already stopped:
+ */
+ usb2_transfer_stop(sc->sc_xfer[AUE_BULK_DT_WR]);
+ usb2_transfer_stop(sc->sc_xfer[AUE_BULK_DT_RD]);
+ usb2_transfer_stop(sc->sc_xfer[AUE_INTR_DT_RD]);
+
+ aue_csr_write_1(sc, AUE_CTL0, 0);
+ aue_csr_write_1(sc, AUE_CTL1, 0);
+ aue_reset(sc);
+}
+
+/*
+ * Stop all chip I/O so that the kernel's probe routines don't
+ * get confused by errant DMAs when rebooting.
+ */
+static int
+aue_shutdown(device_t dev)
+{
+ struct aue_softc *sc = device_get_softc(dev);
+
+ usb2_ether_ifshutdown(&sc->sc_ue);
+
+ return (0);
+}
diff --git a/sys/dev/usb/net/if_auereg.h b/sys/dev/usb/net/if_auereg.h
new file mode 100644
index 0000000..249c913
--- /dev/null
+++ b/sys/dev/usb/net/if_auereg.h
@@ -0,0 +1,220 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999
+ * Bill Paul <wpaul@ee.columbia.edu>. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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$
+ */
+
+/*
+ * Register definitions for ADMtek Pegasus AN986 USB to Ethernet
+ * chip. The Pegasus uses a total of four USB endpoints: the control
+ * endpoint (0), a bulk read endpoint for receiving packets (1),
+ * a bulk write endpoint for sending packets (2) and an interrupt
+ * endpoint for passing RX and TX status (3). Endpoint 0 is used
+ * to read and write the ethernet module's registers. All registers
+ * are 8 bits wide.
+ *
+ * Packet transfer is done in 64 byte chunks. The last chunk in a
+ * transfer is denoted by having a length less that 64 bytes. For
+ * the RX case, the data includes an optional RX status word.
+ */
+
+#define AUE_UR_READREG 0xF0
+#define AUE_UR_WRITEREG 0xF1
+
+#define AUE_CONFIG_INDEX 0 /* config number 1 */
+#define AUE_IFACE_IDX 0
+
+/*
+ * Note that while the ADMtek technically has four endpoints, the control
+ * endpoint (endpoint 0) is regarded as special by the USB code and drivers
+ * don't have direct access to it (we access it using usb2_do_request()
+ * when reading/writing registers. Consequently, our endpoint indexes
+ * don't match those in the ADMtek Pegasus manual: we consider the RX data
+ * endpoint to be index 0 and work up from there.
+ */
+enum {
+ AUE_BULK_DT_WR,
+ AUE_BULK_DT_RD,
+ AUE_INTR_DT_RD,
+ AUE_N_TRANSFER,
+};
+
+#define AUE_INTR_PKTLEN 0x8
+
+#define AUE_CTL0 0x00
+#define AUE_CTL1 0x01
+#define AUE_CTL2 0x02
+#define AUE_MAR0 0x08
+#define AUE_MAR1 0x09
+#define AUE_MAR2 0x0A
+#define AUE_MAR3 0x0B
+#define AUE_MAR4 0x0C
+#define AUE_MAR5 0x0D
+#define AUE_MAR6 0x0E
+#define AUE_MAR7 0x0F
+#define AUE_MAR AUE_MAR0
+#define AUE_PAR0 0x10
+#define AUE_PAR1 0x11
+#define AUE_PAR2 0x12
+#define AUE_PAR3 0x13
+#define AUE_PAR4 0x14
+#define AUE_PAR5 0x15
+#define AUE_PAR AUE_PAR0
+#define AUE_PAUSE0 0x18
+#define AUE_PAUSE1 0x19
+#define AUE_PAUSE AUE_PAUSE0
+#define AUE_RX_FLOWCTL_CNT 0x1A
+#define AUE_RX_FLOWCTL_FIFO 0x1B
+#define AUE_REG_1D 0x1D
+#define AUE_EE_REG 0x20
+#define AUE_EE_DATA0 0x21
+#define AUE_EE_DATA1 0x22
+#define AUE_EE_DATA AUE_EE_DATA0
+#define AUE_EE_CTL 0x23
+#define AUE_PHY_ADDR 0x25
+#define AUE_PHY_DATA0 0x26
+#define AUE_PHY_DATA1 0x27
+#define AUE_PHY_DATA AUE_PHY_DATA0
+#define AUE_PHY_CTL 0x28
+#define AUE_USB_STS 0x2A
+#define AUE_TXSTAT0 0x2B
+#define AUE_TXSTAT1 0x2C
+#define AUE_TXSTAT AUE_TXSTAT0
+#define AUE_RXSTAT 0x2D
+#define AUE_PKTLOST0 0x2E
+#define AUE_PKTLOST1 0x2F
+#define AUE_PKTLOST AUE_PKTLOST0
+
+#define AUE_REG_7B 0x7B
+#define AUE_GPIO0 0x7E
+#define AUE_GPIO1 0x7F
+#define AUE_REG_81 0x81
+
+#define AUE_CTL0_INCLUDE_RXCRC 0x01
+#define AUE_CTL0_ALLMULTI 0x02
+#define AUE_CTL0_STOP_BACKOFF 0x04
+#define AUE_CTL0_RXSTAT_APPEND 0x08
+#define AUE_CTL0_WAKEON_ENB 0x10
+#define AUE_CTL0_RXPAUSE_ENB 0x20
+#define AUE_CTL0_RX_ENB 0x40
+#define AUE_CTL0_TX_ENB 0x80
+
+#define AUE_CTL1_HOMELAN 0x04
+#define AUE_CTL1_RESETMAC 0x08
+#define AUE_CTL1_SPEEDSEL 0x10 /* 0 = 10mbps, 1 = 100mbps */
+#define AUE_CTL1_DUPLEX 0x20 /* 0 = half, 1 = full */
+#define AUE_CTL1_DELAYHOME 0x40
+
+#define AUE_CTL2_EP3_CLR 0x01 /* reading EP3 clrs status regs */
+#define AUE_CTL2_RX_BADFRAMES 0x02
+#define AUE_CTL2_RX_PROMISC 0x04
+#define AUE_CTL2_LOOPBACK 0x08
+#define AUE_CTL2_EEPROMWR_ENB 0x10
+#define AUE_CTL2_EEPROM_LOAD 0x20
+
+#define AUE_EECTL_WRITE 0x01
+#define AUE_EECTL_READ 0x02
+#define AUE_EECTL_DONE 0x04
+
+#define AUE_PHYCTL_PHYREG 0x1F
+#define AUE_PHYCTL_WRITE 0x20
+#define AUE_PHYCTL_READ 0x40
+#define AUE_PHYCTL_DONE 0x80
+
+#define AUE_USBSTS_SUSPEND 0x01
+#define AUE_USBSTS_RESUME 0x02
+
+#define AUE_TXSTAT0_JABTIMO 0x04
+#define AUE_TXSTAT0_CARLOSS 0x08
+#define AUE_TXSTAT0_NOCARRIER 0x10
+#define AUE_TXSTAT0_LATECOLL 0x20
+#define AUE_TXSTAT0_EXCESSCOLL 0x40
+#define AUE_TXSTAT0_UNDERRUN 0x80
+
+#define AUE_TXSTAT1_PKTCNT 0x0F
+#define AUE_TXSTAT1_FIFO_EMPTY 0x40
+#define AUE_TXSTAT1_FIFO_FULL 0x80
+
+#define AUE_RXSTAT_OVERRUN 0x01
+#define AUE_RXSTAT_PAUSE 0x02
+
+#define AUE_GPIO_IN0 0x01
+#define AUE_GPIO_OUT0 0x02
+#define AUE_GPIO_SEL0 0x04
+#define AUE_GPIO_IN1 0x08
+#define AUE_GPIO_OUT1 0x10
+#define AUE_GPIO_SEL1 0x20
+
+#define AUE_TIMEOUT 100 /* 10*ms */
+#define AUE_MIN_FRAMELEN 60
+
+#define AUE_RXSTAT_MCAST 0x01
+#define AUE_RXSTAT_GIANT 0x02
+#define AUE_RXSTAT_RUNT 0x04
+#define AUE_RXSTAT_CRCERR 0x08
+#define AUE_RXSTAT_DRIBBLE 0x10
+#define AUE_RXSTAT_MASK 0x1E
+
+#define GET_MII(sc) usb2_ether_getmii(&(sc)->sc_ue)
+
+struct aue_intrpkt {
+ uint8_t aue_txstat0;
+ uint8_t aue_txstat1;
+ uint8_t aue_rxstat;
+ uint8_t aue_rxlostpkt0;
+ uint8_t aue_rxlostpkt1;
+ uint8_t aue_wakeupstat;
+ uint8_t aue_rsvd;
+} __packed;
+
+struct aue_rxpkt {
+ uint16_t aue_pktlen;
+ uint8_t aue_rxstat;
+ uint8_t pad;
+} __packed;
+
+struct aue_softc {
+ struct usb2_ether sc_ue;
+ struct mtx sc_mtx;
+ struct usb2_xfer *sc_xfer[AUE_N_TRANSFER];
+
+ int sc_flags;
+#define AUE_FLAG_LSYS 0x0001 /* use Linksys reset */
+#define AUE_FLAG_PNA 0x0002 /* has Home PNA */
+#define AUE_FLAG_PII 0x0004 /* Pegasus II chip */
+#define AUE_FLAG_LINK 0x0008 /* wait for link to come up */
+#define AUE_FLAG_VER_2 0x0200 /* chip is version 2 */
+#define AUE_FLAG_DUAL_PHY 0x0400 /* chip has two transcivers */
+};
+
+#define AUE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define AUE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define AUE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t)
diff --git a/sys/dev/usb/net/if_axe.c b/sys/dev/usb/net/if_axe.c
new file mode 100644
index 0000000..0555e69
--- /dev/null
+++ b/sys/dev/usb/net/if_axe.c
@@ -0,0 +1,1076 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999, 2000-2003
+ * Bill Paul <wpaul@windriver.com>. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * ASIX Electronics AX88172/AX88178/AX88778 USB 2.0 ethernet driver.
+ * Used in the LinkSys USB200M and various other adapters.
+ *
+ * Manuals available from:
+ * http://www.asix.com.tw/datasheet/mac/Ax88172.PDF
+ * Note: you need the manual for the AX88170 chip (USB 1.x ethernet
+ * controller) to find the definitions for the RX control register.
+ * http://www.asix.com.tw/datasheet/mac/Ax88170.PDF
+ *
+ * Written by Bill Paul <wpaul@windriver.com>
+ * Senior Engineer
+ * Wind River Systems
+ */
+
+/*
+ * The AX88172 provides USB ethernet supports at 10 and 100Mbps.
+ * It uses an external PHY (reference designs use a RealTek chip),
+ * and has a 64-bit multicast hash filter. There is some information
+ * missing from the manual which one needs to know in order to make
+ * the chip function:
+ *
+ * - You must set bit 7 in the RX control register, otherwise the
+ * chip won't receive any packets.
+ * - You must initialize all 3 IPG registers, or you won't be able
+ * to send any packets.
+ *
+ * Note that this device appears to only support loading the station
+ * address via autload from the EEPROM (i.e. there's no way to manaully
+ * set it).
+ *
+ * (Adam Weinberger wanted me to name this driver if_gir.c.)
+ */
+
+/*
+ * Ax88178 and Ax88772 support backported from the OpenBSD driver.
+ * 2007/02/12, J.R. Oldroyd, fbsd@opal.com
+ *
+ * Manual here:
+ * http://www.asix.com.tw/FrootAttach/datasheet/AX88178_datasheet_Rev10.pdf
+ * http://www.asix.com.tw/FrootAttach/datasheet/AX88772_datasheet_Rev10.pdf
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+
+#define USB_DEBUG_VAR axe_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/net/usb_ethernet.h>
+#include <dev/usb/net/if_axereg.h>
+
+/*
+ * AXE_178_MAX_FRAME_BURST
+ * max frame burst size for Ax88178 and Ax88772
+ * 0 2048 bytes
+ * 1 4096 bytes
+ * 2 8192 bytes
+ * 3 16384 bytes
+ * use the largest your system can handle without USB stalling.
+ *
+ * NB: 88772 parts appear to generate lots of input errors with
+ * a 2K rx buffer and 8K is only slightly faster than 4K on an
+ * EHCI port on a T42 so change at your own risk.
+ */
+#define AXE_178_MAX_FRAME_BURST 1
+
+#if USB_DEBUG
+static int axe_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, axe, CTLFLAG_RW, 0, "USB axe");
+SYSCTL_INT(_hw_usb2_axe, OID_AUTO, debug, CTLFLAG_RW, &axe_debug, 0,
+ "Debug level");
+#endif
+
+/*
+ * Various supported device vendors/products.
+ */
+static const struct usb2_device_id axe_devs[] = {
+ {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_UF200, 0)},
+ {USB_VPI(USB_VENDOR_ACERCM, USB_PRODUCT_ACERCM_EP1427X2, 0)},
+ {USB_VPI(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_ETHERNET, AXE_FLAG_772)},
+ {USB_VPI(USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88172, 0)},
+ {USB_VPI(USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88178, AXE_FLAG_178)},
+ {USB_VPI(USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88772, AXE_FLAG_772)},
+ {USB_VPI(USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC210T, 0)},
+ {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D5055, AXE_FLAG_178)},
+ {USB_VPI(USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USB2AR, 0)},
+ {USB_VPI(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_USB200MV2, AXE_FLAG_772)},
+ {USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB2_TX, 0)},
+ {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DUBE100, 0)},
+ {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DUBE100B1, AXE_FLAG_772)},
+ {USB_VPI(USB_VENDOR_GOODWAY, USB_PRODUCT_GOODWAY_GWUSB2E, 0)},
+ {USB_VPI(USB_VENDOR_IODATA, USB_PRODUCT_IODATA_ETGUS2, AXE_FLAG_178)},
+ {USB_VPI(USB_VENDOR_JVC, USB_PRODUCT_JVC_MP_PRX1, 0)},
+ {USB_VPI(USB_VENDOR_LINKSYS2, USB_PRODUCT_LINKSYS2_USB200M, 0)},
+ {USB_VPI(USB_VENDOR_LINKSYS4, USB_PRODUCT_LINKSYS4_USB1000, AXE_FLAG_178)},
+ {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAU2KTX, 0)},
+ {USB_VPI(USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_FA120, 0)},
+ {USB_VPI(USB_VENDOR_OQO, USB_PRODUCT_OQO_ETHER01PLUS, AXE_FLAG_772)},
+ {USB_VPI(USB_VENDOR_PLANEX3, USB_PRODUCT_PLANEX3_GU1000T, AXE_FLAG_178)},
+ {USB_VPI(USB_VENDOR_SITECOM, USB_PRODUCT_SITECOM_LN029, 0)},
+ {USB_VPI(USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_LN028, AXE_FLAG_178)},
+ {USB_VPI(USB_VENDOR_SYSTEMTALKS, USB_PRODUCT_SYSTEMTALKS_SGCX2UL, 0)},
+};
+
+static device_probe_t axe_probe;
+static device_attach_t axe_attach;
+static device_detach_t axe_detach;
+static device_shutdown_t axe_shutdown;
+
+static usb2_callback_t axe_intr_callback;
+static usb2_callback_t axe_bulk_read_callback;
+static usb2_callback_t axe_bulk_write_callback;
+
+static miibus_readreg_t axe_miibus_readreg;
+static miibus_writereg_t axe_miibus_writereg;
+static miibus_statchg_t axe_miibus_statchg;
+
+static usb2_ether_fn_t axe_attach_post;
+static usb2_ether_fn_t axe_init;
+static usb2_ether_fn_t axe_stop;
+static usb2_ether_fn_t axe_start;
+static usb2_ether_fn_t axe_tick;
+static usb2_ether_fn_t axe_setmulti;
+static usb2_ether_fn_t axe_setpromisc;
+
+static int axe_ifmedia_upd(struct ifnet *);
+static void axe_ifmedia_sts(struct ifnet *, struct ifmediareq *);
+static int axe_cmd(struct axe_softc *, int, int, int, void *);
+static void axe_ax88178_init(struct axe_softc *);
+static void axe_ax88772_init(struct axe_softc *);
+static int axe_get_phyno(struct axe_softc *, int);
+
+static const struct usb2_config axe_config[AXE_N_TRANSFER] = {
+
+ [AXE_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = AXE_BULK_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = axe_bulk_write_callback,
+ .mh.timeout = 10000, /* 10 seconds */
+ },
+
+ [AXE_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+#if (MCLBYTES < 2048)
+#error "(MCLBYTES < 2048)"
+#endif
+ .mh.bufsize = MCLBYTES,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = axe_bulk_read_callback,
+ .mh.timeout = 0, /* no timeout */
+ },
+
+ [AXE_INTR_DT_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.bufsize = 0, /* use wMaxPacketSize */
+ .mh.callback = axe_intr_callback,
+ },
+};
+
+static device_method_t axe_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, axe_probe),
+ DEVMETHOD(device_attach, axe_attach),
+ DEVMETHOD(device_detach, axe_detach),
+ DEVMETHOD(device_shutdown, axe_shutdown),
+
+ /* bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, axe_miibus_readreg),
+ DEVMETHOD(miibus_writereg, axe_miibus_writereg),
+ DEVMETHOD(miibus_statchg, axe_miibus_statchg),
+
+ {0, 0}
+};
+
+static driver_t axe_driver = {
+ .name = "axe",
+ .methods = axe_methods,
+ .size = sizeof(struct axe_softc),
+};
+
+static devclass_t axe_devclass;
+
+DRIVER_MODULE(axe, ushub, axe_driver, axe_devclass, NULL, 0);
+DRIVER_MODULE(miibus, axe, miibus_driver, miibus_devclass, 0, 0);
+MODULE_DEPEND(axe, uether, 1, 1, 1);
+MODULE_DEPEND(axe, usb, 1, 1, 1);
+MODULE_DEPEND(axe, ether, 1, 1, 1);
+MODULE_DEPEND(axe, miibus, 1, 1, 1);
+
+static const struct usb2_ether_methods axe_ue_methods = {
+ .ue_attach_post = axe_attach_post,
+ .ue_start = axe_start,
+ .ue_init = axe_init,
+ .ue_stop = axe_stop,
+ .ue_tick = axe_tick,
+ .ue_setmulti = axe_setmulti,
+ .ue_setpromisc = axe_setpromisc,
+ .ue_mii_upd = axe_ifmedia_upd,
+ .ue_mii_sts = axe_ifmedia_sts,
+};
+
+static int
+axe_cmd(struct axe_softc *sc, int cmd, int index, int val, void *buf)
+{
+ struct usb2_device_request req;
+ usb2_error_t err;
+
+ AXE_LOCK_ASSERT(sc, MA_OWNED);
+
+ req.bmRequestType = (AXE_CMD_IS_WRITE(cmd) ?
+ UT_WRITE_VENDOR_DEVICE :
+ UT_READ_VENDOR_DEVICE);
+ req.bRequest = AXE_CMD_CMD(cmd);
+ USETW(req.wValue, val);
+ USETW(req.wIndex, index);
+ USETW(req.wLength, AXE_CMD_LEN(cmd));
+
+ err = usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000);
+
+ return (err);
+}
+
+static int
+axe_miibus_readreg(device_t dev, int phy, int reg)
+{
+ struct axe_softc *sc = device_get_softc(dev);
+ uint16_t val;
+ int locked;
+
+ if (sc->sc_phyno != phy)
+ return (0);
+
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ AXE_LOCK(sc);
+
+ axe_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL);
+ axe_cmd(sc, AXE_CMD_MII_READ_REG, reg, phy, &val);
+ axe_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL);
+
+ val = le16toh(val);
+ if ((sc->sc_flags & AXE_FLAG_772) != 0 && reg == MII_BMSR) {
+ /*
+ * BMSR of AX88772 indicates that it supports extended
+ * capability but the extended status register is
+ * revered for embedded ethernet PHY. So clear the
+ * extended capability bit of BMSR.
+ */
+ val &= ~BMSR_EXTCAP;
+ }
+
+ if (!locked)
+ AXE_UNLOCK(sc);
+ return (val);
+}
+
+static int
+axe_miibus_writereg(device_t dev, int phy, int reg, int val)
+{
+ struct axe_softc *sc = device_get_softc(dev);
+ int locked;
+
+ val = htole16(val);
+
+ if (sc->sc_phyno != phy)
+ return (0);
+
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ AXE_LOCK(sc);
+
+ axe_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL);
+ axe_cmd(sc, AXE_CMD_MII_WRITE_REG, reg, phy, &val);
+ axe_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL);
+
+ if (!locked)
+ AXE_UNLOCK(sc);
+ return (0);
+}
+
+static void
+axe_miibus_statchg(device_t dev)
+{
+ struct axe_softc *sc = device_get_softc(dev);
+ struct mii_data *mii = GET_MII(sc);
+ struct ifnet *ifp;
+ uint16_t val;
+ int err, locked;
+
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ AXE_LOCK(sc);
+
+ ifp = usb2_ether_getifp(&sc->sc_ue);
+ if (mii == NULL || ifp == NULL ||
+ (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ goto done;
+
+ sc->sc_flags &= ~AXE_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->sc_flags |= AXE_FLAG_LINK;
+ break;
+ case IFM_1000_T:
+ if ((sc->sc_flags & AXE_FLAG_178) == 0)
+ break;
+ sc->sc_flags |= AXE_FLAG_LINK;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* Lost link, do nothing. */
+ if ((sc->sc_flags & AXE_FLAG_LINK) == 0)
+ goto done;
+
+ val = 0;
+ if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0)
+ val |= AXE_MEDIA_FULL_DUPLEX;
+ if (sc->sc_flags & (AXE_FLAG_178 | AXE_FLAG_772)) {
+ val |= AXE_178_MEDIA_RX_EN | AXE_178_MEDIA_MAGIC;
+ if ((sc->sc_flags & AXE_FLAG_178) != 0)
+ val |= AXE_178_MEDIA_ENCK;
+ switch (IFM_SUBTYPE(mii->mii_media_active)) {
+ case IFM_1000_T:
+ val |= AXE_178_MEDIA_GMII | AXE_178_MEDIA_ENCK;
+ break;
+ case IFM_100_TX:
+ val |= AXE_178_MEDIA_100TX;
+ break;
+ case IFM_10_T:
+ /* doesn't need to be handled */
+ break;
+ }
+ }
+ err = axe_cmd(sc, AXE_CMD_WRITE_MEDIA, 0, val, NULL);
+ if (err)
+ device_printf(dev, "media change failed, error %d\n", err);
+done:
+ if (!locked)
+ AXE_UNLOCK(sc);
+}
+
+/*
+ * Set media options.
+ */
+static int
+axe_ifmedia_upd(struct ifnet *ifp)
+{
+ struct axe_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+ int error;
+
+ AXE_LOCK_ASSERT(sc, MA_OWNED);
+
+ if (mii->mii_instance) {
+ struct mii_softc *miisc;
+
+ LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
+ mii_phy_reset(miisc);
+ }
+ error = mii_mediachg(mii);
+ return (error);
+}
+
+/*
+ * Report current media status.
+ */
+static void
+axe_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct axe_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+
+ AXE_LOCK(sc);
+ mii_pollstat(mii);
+ AXE_UNLOCK(sc);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+}
+
+static void
+axe_setmulti(struct usb2_ether *ue)
+{
+ struct axe_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+ struct ifmultiaddr *ifma;
+ uint32_t h = 0;
+ uint16_t rxmode;
+ uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ AXE_LOCK_ASSERT(sc, MA_OWNED);
+
+ axe_cmd(sc, AXE_CMD_RXCTL_READ, 0, 0, &rxmode);
+ rxmode = le16toh(rxmode);
+
+ if (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) {
+ rxmode |= AXE_RXCMD_ALLMULTI;
+ axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL);
+ return;
+ }
+ rxmode &= ~AXE_RXCMD_ALLMULTI;
+
+ IF_ADDR_LOCK(ifp);
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
+ {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ h = ether_crc32_be(LLADDR((struct sockaddr_dl *)
+ ifma->ifma_addr), ETHER_ADDR_LEN) >> 26;
+ hashtbl[h / 8] |= 1 << (h % 8);
+ }
+ IF_ADDR_UNLOCK(ifp);
+
+ axe_cmd(sc, AXE_CMD_WRITE_MCAST, 0, 0, (void *)&hashtbl);
+ axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL);
+}
+
+static int
+axe_get_phyno(struct axe_softc *sc, int sel)
+{
+ int phyno;
+
+ switch (AXE_PHY_TYPE(sc->sc_phyaddrs[sel])) {
+ case PHY_TYPE_100_HOME:
+ case PHY_TYPE_GIG:
+ phyno = AXE_PHY_NO(sc->sc_phyaddrs[sel]);
+ break;
+ case PHY_TYPE_SPECIAL:
+ /* FALLTHROUGH */
+ case PHY_TYPE_RSVD:
+ /* FALLTHROUGH */
+ case PHY_TYPE_NON_SUP:
+ /* FALLTHROUGH */
+ default:
+ phyno = -1;
+ break;
+ }
+
+ return (phyno);
+}
+
+static void
+axe_ax88178_init(struct axe_softc *sc)
+{
+ int gpio0 = 0, phymode = 0;
+ uint16_t eeprom;
+
+ axe_cmd(sc, AXE_CMD_SROM_WR_ENABLE, 0, 0, NULL);
+ /* XXX magic */
+ axe_cmd(sc, AXE_CMD_SROM_READ, 0, 0x0017, &eeprom);
+ eeprom = le16toh(eeprom);
+ axe_cmd(sc, AXE_CMD_SROM_WR_DISABLE, 0, 0, NULL);
+
+ /* if EEPROM is invalid we have to use to GPIO0 */
+ if (eeprom == 0xffff) {
+ phymode = 0;
+ gpio0 = 1;
+ } else {
+ phymode = eeprom & 7;
+ gpio0 = (eeprom & 0x80) ? 0 : 1;
+ }
+
+ axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x008c, NULL);
+ usb2_ether_pause(&sc->sc_ue, hz / 16);
+
+ if ((eeprom >> 8) != 0x01) {
+ axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x003c, NULL);
+ usb2_ether_pause(&sc->sc_ue, hz / 32);
+
+ axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x001c, NULL);
+ usb2_ether_pause(&sc->sc_ue, hz / 3);
+
+ axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x003c, NULL);
+ usb2_ether_pause(&sc->sc_ue, hz / 32);
+ } else {
+ axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x0004, NULL);
+ usb2_ether_pause(&sc->sc_ue, hz / 32);
+
+ axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x000c, NULL);
+ usb2_ether_pause(&sc->sc_ue, hz / 32);
+ }
+
+ /* soft reset */
+ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_CLEAR, NULL);
+ usb2_ether_pause(&sc->sc_ue, hz / 4);
+
+ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0,
+ AXE_SW_RESET_PRL | AXE_178_RESET_MAGIC, NULL);
+ usb2_ether_pause(&sc->sc_ue, hz / 4);
+ /* Enable MII/GMII/RGMII interface to work with external PHY. */
+ axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0, NULL);
+ usb2_ether_pause(&sc->sc_ue, hz / 4);
+
+ axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL);
+}
+
+static void
+axe_ax88772_init(struct axe_softc *sc)
+{
+ axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x00b0, NULL);
+ usb2_ether_pause(&sc->sc_ue, hz / 16);
+
+ if (sc->sc_phyno == AXE_772_PHY_NO_EPHY) {
+ /* ask for the embedded PHY */
+ axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0x01, NULL);
+ usb2_ether_pause(&sc->sc_ue, hz / 64);
+
+ /* power down and reset state, pin reset state */
+ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0,
+ AXE_SW_RESET_CLEAR, NULL);
+ usb2_ether_pause(&sc->sc_ue, hz / 16);
+
+ /* power down/reset state, pin operating state */
+ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0,
+ AXE_SW_RESET_IPPD | AXE_SW_RESET_PRL, NULL);
+ usb2_ether_pause(&sc->sc_ue, hz / 4);
+
+ /* power up, reset */
+ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_PRL, NULL);
+
+ /* power up, operating */
+ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0,
+ AXE_SW_RESET_IPRL | AXE_SW_RESET_PRL, NULL);
+ } else {
+ /* ask for external PHY */
+ axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0x00, NULL);
+ usb2_ether_pause(&sc->sc_ue, hz / 64);
+
+ /* power down internal PHY */
+ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0,
+ AXE_SW_RESET_IPPD | AXE_SW_RESET_PRL, NULL);
+ }
+
+ usb2_ether_pause(&sc->sc_ue, hz / 4);
+ axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL);
+}
+
+static void
+axe_reset(struct axe_softc *sc)
+{
+ struct usb2_config_descriptor *cd;
+ usb2_error_t err;
+
+ cd = usb2_get_config_descriptor(sc->sc_ue.ue_udev);
+
+ err = usb2_req_set_config(sc->sc_ue.ue_udev, &sc->sc_mtx,
+ cd->bConfigurationValue);
+ if (err)
+ DPRINTF("reset failed (ignored)\n");
+
+ /* Wait a little while for the chip to get its brains in order. */
+ usb2_ether_pause(&sc->sc_ue, hz / 100);
+}
+
+static void
+axe_attach_post(struct usb2_ether *ue)
+{
+ struct axe_softc *sc = usb2_ether_getsc(ue);
+
+ /*
+ * Load PHY indexes first. Needed by axe_xxx_init().
+ */
+ axe_cmd(sc, AXE_CMD_READ_PHYID, 0, 0, sc->sc_phyaddrs);
+#if 1
+ device_printf(sc->sc_ue.ue_dev, "PHYADDR 0x%02x:0x%02x\n",
+ sc->sc_phyaddrs[0], sc->sc_phyaddrs[1]);
+#endif
+ sc->sc_phyno = axe_get_phyno(sc, AXE_PHY_SEL_PRI);
+ if (sc->sc_phyno == -1)
+ sc->sc_phyno = axe_get_phyno(sc, AXE_PHY_SEL_SEC);
+ if (sc->sc_phyno == -1) {
+ device_printf(sc->sc_ue.ue_dev,
+ "no valid PHY address found, assuming PHY address 0\n");
+ sc->sc_phyno = 0;
+ }
+
+ if (sc->sc_flags & AXE_FLAG_178)
+ axe_ax88178_init(sc);
+ else if (sc->sc_flags & AXE_FLAG_772)
+ axe_ax88772_init(sc);
+
+ /*
+ * Get station address.
+ */
+ if (sc->sc_flags & (AXE_FLAG_178 | AXE_FLAG_772))
+ axe_cmd(sc, AXE_178_CMD_READ_NODEID, 0, 0, ue->ue_eaddr);
+ else
+ axe_cmd(sc, AXE_172_CMD_READ_NODEID, 0, 0, ue->ue_eaddr);
+
+ /*
+ * Fetch IPG values.
+ */
+ axe_cmd(sc, AXE_CMD_READ_IPG012, 0, 0, sc->sc_ipgs);
+}
+
+/*
+ * Probe for a AX88172 chip.
+ */
+static int
+axe_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST)
+ return (ENXIO);
+ if (uaa->info.bConfigIndex != AXE_CONFIG_IDX)
+ return (ENXIO);
+ if (uaa->info.bIfaceIndex != AXE_IFACE_IDX)
+ return (ENXIO);
+
+ return (usb2_lookup_id_by_uaa(axe_devs, sizeof(axe_devs), uaa));
+}
+
+/*
+ * Attach the interface. Allocate softc structures, do ifmedia
+ * setup and ethernet/BPF attach.
+ */
+static int
+axe_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct axe_softc *sc = device_get_softc(dev);
+ struct usb2_ether *ue = &sc->sc_ue;
+ uint8_t iface_index;
+ int error;
+
+ sc->sc_flags = USB_GET_DRIVER_INFO(uaa);
+
+ device_set_usb2_desc(dev);
+
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ iface_index = AXE_IFACE_IDX;
+ error = usb2_transfer_setup(uaa->device, &iface_index, sc->sc_xfer,
+ axe_config, AXE_N_TRANSFER, sc, &sc->sc_mtx);
+ if (error) {
+ device_printf(dev, "allocating USB transfers failed!\n");
+ goto detach;
+ }
+
+ ue->ue_sc = sc;
+ ue->ue_dev = dev;
+ ue->ue_udev = uaa->device;
+ ue->ue_mtx = &sc->sc_mtx;
+ ue->ue_methods = &axe_ue_methods;
+
+ error = usb2_ether_ifattach(ue);
+ if (error) {
+ device_printf(dev, "could not attach interface\n");
+ goto detach;
+ }
+ return (0); /* success */
+
+detach:
+ axe_detach(dev);
+ return (ENXIO); /* failure */
+}
+
+static int
+axe_detach(device_t dev)
+{
+ struct axe_softc *sc = device_get_softc(dev);
+ struct usb2_ether *ue = &sc->sc_ue;
+
+ usb2_transfer_unsetup(sc->sc_xfer, AXE_N_TRANSFER);
+ usb2_ether_ifdetach(ue);
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static void
+axe_intr_callback(struct usb2_xfer *xfer)
+{
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+#if (AXE_BULK_BUF_SIZE >= 0x10000)
+#error "Please update axe_bulk_read_callback()!"
+#endif
+
+static void
+axe_bulk_read_callback(struct usb2_xfer *xfer)
+{
+ struct axe_softc *sc = xfer->priv_sc;
+ struct usb2_ether *ue = &sc->sc_ue;
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+ struct axe_sframe_hdr hdr;
+ int error, pos, len, adjust;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ pos = 0;
+ while (1) {
+ if (sc->sc_flags & (AXE_FLAG_772 | AXE_FLAG_178)) {
+ if (xfer->actlen < sizeof(hdr)) {
+ /* too little data */
+ break;
+ }
+ usb2_copy_out(xfer->frbuffers, pos, &hdr, sizeof(hdr));
+
+ if ((hdr.len ^ hdr.ilen) != 0xFFFF) {
+ /* we lost sync */
+ break;
+ }
+ xfer->actlen -= sizeof(hdr);
+ pos += sizeof(hdr);
+
+ len = le16toh(hdr.len);
+ if (len > xfer->actlen) {
+ /* invalid length */
+ break;
+ }
+ adjust = (len & 1);
+
+ } else {
+ len = xfer->actlen;
+ adjust = 0;
+ }
+ error = usb2_ether_rxbuf(ue, xfer->frbuffers, pos, len);
+ if (error)
+ break;
+
+ pos += len;
+ xfer->actlen -= len;
+
+ if (xfer->actlen <= adjust) {
+ /* we are finished */
+ goto tr_setup;
+ }
+ pos += adjust;
+ xfer->actlen -= adjust;
+ }
+
+ /* count an error */
+ ifp->if_ierrors++;
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ usb2_ether_rxflush(ue);
+ return;
+
+ default: /* Error */
+ DPRINTF("bulk read error, %s\n",
+ usb2_errstr(xfer->error));
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
+
+#if ((AXE_BULK_BUF_SIZE >= 0x10000) || (AXE_BULK_BUF_SIZE < (MCLBYTES+4)))
+#error "Please update axe_bulk_write_callback()!"
+#endif
+
+static void
+axe_bulk_write_callback(struct usb2_xfer *xfer)
+{
+ struct axe_softc *sc = xfer->priv_sc;
+ struct axe_sframe_hdr hdr;
+ struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue);
+ struct mbuf *m;
+ int pos;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTFN(11, "transfer complete\n");
+ ifp->if_opackets++;
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ if ((sc->sc_flags & AXE_FLAG_LINK) == 0) {
+ /*
+ * don't send anything if there is no link !
+ */
+ return;
+ }
+ pos = 0;
+
+ while (1) {
+
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+
+ if (m == NULL) {
+ if (pos > 0)
+ break; /* send out data */
+ return;
+ }
+ if (m->m_pkthdr.len > MCLBYTES) {
+ m->m_pkthdr.len = MCLBYTES;
+ }
+ if (sc->sc_flags & (AXE_FLAG_772 | AXE_FLAG_178)) {
+
+ hdr.len = htole16(m->m_pkthdr.len);
+ hdr.ilen = ~hdr.len;
+
+ usb2_copy_in(xfer->frbuffers, pos, &hdr, sizeof(hdr));
+
+ pos += sizeof(hdr);
+
+ /*
+ * NOTE: Some drivers force a short packet
+ * by appending a dummy header with zero
+ * length at then end of the USB transfer.
+ * This driver uses the
+ * USB_FORCE_SHORT_XFER flag instead.
+ */
+ }
+ usb2_m_copy_in(xfer->frbuffers, pos,
+ m, 0, m->m_pkthdr.len);
+
+ pos += m->m_pkthdr.len;
+
+ /*
+ * if there's a BPF listener, bounce a copy
+ * of this frame to him:
+ */
+ BPF_MTAP(ifp, m);
+
+ m_freem(m);
+
+ if (sc->sc_flags & (AXE_FLAG_772 | AXE_FLAG_178)) {
+ if (pos > (AXE_BULK_BUF_SIZE - MCLBYTES - sizeof(hdr))) {
+ /* send out frame(s) */
+ break;
+ }
+ } else {
+ /* send out frame */
+ break;
+ }
+ }
+
+ xfer->frlengths[0] = pos;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ DPRINTFN(11, "transfer error, %s\n",
+ usb2_errstr(xfer->error));
+
+ ifp->if_oerrors++;
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
+
+static void
+axe_tick(struct usb2_ether *ue)
+{
+ struct axe_softc *sc = usb2_ether_getsc(ue);
+ struct mii_data *mii = GET_MII(sc);
+
+ AXE_LOCK_ASSERT(sc, MA_OWNED);
+
+ mii_tick(mii);
+ if ((sc->sc_flags & AXE_FLAG_LINK) == 0) {
+ axe_miibus_statchg(ue->ue_dev);
+ if ((sc->sc_flags & AXE_FLAG_LINK) != 0)
+ axe_start(ue);
+ }
+}
+
+static void
+axe_start(struct usb2_ether *ue)
+{
+ struct axe_softc *sc = usb2_ether_getsc(ue);
+
+ /*
+ * start the USB transfers, if not already started:
+ */
+ usb2_transfer_start(sc->sc_xfer[AXE_INTR_DT_RD]);
+ usb2_transfer_start(sc->sc_xfer[AXE_BULK_DT_RD]);
+ usb2_transfer_start(sc->sc_xfer[AXE_BULK_DT_WR]);
+}
+
+static void
+axe_init(struct usb2_ether *ue)
+{
+ struct axe_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+ uint16_t rxmode;
+
+ AXE_LOCK_ASSERT(sc, MA_OWNED);
+
+ /* Cancel pending I/O */
+ axe_stop(ue);
+
+#ifdef notdef
+ /* Set MAC address */
+ axe_mac(sc, IF_LLADDR(ifp), 1);
+#endif
+
+ /* Set transmitter IPG values */
+ if (sc->sc_flags & (AXE_FLAG_178 | AXE_FLAG_772)) {
+ axe_cmd(sc, AXE_178_CMD_WRITE_IPG012, sc->sc_ipgs[2],
+ (sc->sc_ipgs[1] << 8) | (sc->sc_ipgs[0]), NULL);
+ } else {
+ axe_cmd(sc, AXE_172_CMD_WRITE_IPG0, 0, sc->sc_ipgs[0], NULL);
+ axe_cmd(sc, AXE_172_CMD_WRITE_IPG1, 0, sc->sc_ipgs[1], NULL);
+ axe_cmd(sc, AXE_172_CMD_WRITE_IPG2, 0, sc->sc_ipgs[2], NULL);
+ }
+
+ /* Enable receiver, set RX mode */
+ rxmode = (AXE_RXCMD_MULTICAST | AXE_RXCMD_ENABLE);
+ if (sc->sc_flags & (AXE_FLAG_178 | AXE_FLAG_772)) {
+ rxmode |= AXE_178_RXCMD_MFB_2048; /* chip default */
+ } else {
+ rxmode |= AXE_172_RXCMD_UNICAST;
+ }
+
+ /* If we want promiscuous mode, set the allframes bit. */
+ if (ifp->if_flags & IFF_PROMISC)
+ rxmode |= AXE_RXCMD_PROMISC;
+
+ if (ifp->if_flags & IFF_BROADCAST)
+ rxmode |= AXE_RXCMD_BROADCAST;
+
+ axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL);
+
+ /* Load the multicast filter. */
+ axe_setmulti(ue);
+
+ usb2_transfer_set_stall(sc->sc_xfer[AXE_BULK_DT_WR]);
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ axe_start(ue);
+}
+
+static void
+axe_setpromisc(struct usb2_ether *ue)
+{
+ struct axe_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+ uint16_t rxmode;
+
+ axe_cmd(sc, AXE_CMD_RXCTL_READ, 0, 0, &rxmode);
+
+ rxmode = le16toh(rxmode);
+
+ if (ifp->if_flags & IFF_PROMISC) {
+ rxmode |= AXE_RXCMD_PROMISC;
+ } else {
+ rxmode &= ~AXE_RXCMD_PROMISC;
+ }
+
+ axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL);
+
+ axe_setmulti(ue);
+}
+
+static void
+axe_stop(struct usb2_ether *ue)
+{
+ struct axe_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+
+ AXE_LOCK_ASSERT(sc, MA_OWNED);
+
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ sc->sc_flags &= ~AXE_FLAG_LINK;
+
+ /*
+ * stop all the transfers, if not already stopped:
+ */
+ usb2_transfer_stop(sc->sc_xfer[AXE_BULK_DT_WR]);
+ usb2_transfer_stop(sc->sc_xfer[AXE_BULK_DT_RD]);
+ usb2_transfer_stop(sc->sc_xfer[AXE_INTR_DT_RD]);
+
+ axe_reset(sc);
+}
+
+/*
+ * Stop all chip I/O so that the kernel's probe routines don't
+ * get confused by errant DMAs when rebooting.
+ */
+static int
+axe_shutdown(device_t dev)
+{
+ struct axe_softc *sc = device_get_softc(dev);
+
+ usb2_ether_ifshutdown(&sc->sc_ue);
+
+ return (0);
+}
diff --git a/sys/dev/usb/net/if_axereg.h b/sys/dev/usb/net/if_axereg.h
new file mode 100644
index 0000000..dc063e3
--- /dev/null
+++ b/sys/dev/usb/net/if_axereg.h
@@ -0,0 +1,196 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999, 2000-2003
+ * Bill Paul <wpaul@windriver.com>. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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$
+ */
+
+/*
+ * Definitions for the ASIX Electronics AX88172, AX88178
+ * and AX88772 to ethernet controllers.
+ */
+
+/*
+ * Vendor specific commands. ASIX conveniently doesn't document the 'set
+ * NODEID' command in their datasheet (thanks a lot guys).
+ * To make handling these commands easier, I added some extra data which is
+ * decided by the axe_cmd() routine. Commands are encoded in 16 bits, with
+ * the format: LDCC. L and D are both nibbles in the high byte. L represents
+ * the data length (0 to 15) and D represents the direction (0 for vendor read,
+ * 1 for vendor write). CC is the command byte, as specified in the manual.
+ */
+
+#define AXE_CMD_IS_WRITE(x) (((x) & 0x0F00) >> 8)
+#define AXE_CMD_LEN(x) (((x) & 0xF000) >> 12)
+#define AXE_CMD_CMD(x) ((x) & 0x00FF)
+
+#define AXE_172_CMD_READ_RXTX_SRAM 0x2002
+#define AXE_182_CMD_READ_RXTX_SRAM 0x8002
+#define AXE_172_CMD_WRITE_RX_SRAM 0x0103
+#define AXE_182_CMD_WRITE_RXTX_SRAM 0x8103
+#define AXE_172_CMD_WRITE_TX_SRAM 0x0104
+#define AXE_CMD_MII_OPMODE_SW 0x0106
+#define AXE_CMD_MII_READ_REG 0x2007
+#define AXE_CMD_MII_WRITE_REG 0x2108
+#define AXE_CMD_MII_READ_OPMODE 0x1009
+#define AXE_CMD_MII_OPMODE_HW 0x010A
+#define AXE_CMD_SROM_READ 0x200B
+#define AXE_CMD_SROM_WRITE 0x010C
+#define AXE_CMD_SROM_WR_ENABLE 0x010D
+#define AXE_CMD_SROM_WR_DISABLE 0x010E
+#define AXE_CMD_RXCTL_READ 0x200F
+#define AXE_CMD_RXCTL_WRITE 0x0110
+#define AXE_CMD_READ_IPG012 0x3011
+#define AXE_172_CMD_WRITE_IPG0 0x0112
+#define AXE_178_CMD_WRITE_IPG012 0x0112
+#define AXE_172_CMD_WRITE_IPG1 0x0113
+#define AXE_178_CMD_READ_NODEID 0x6013
+#define AXE_172_CMD_WRITE_IPG2 0x0114
+#define AXE_178_CMD_WRITE_NODEID 0x6114
+#define AXE_CMD_READ_MCAST 0x8015
+#define AXE_CMD_WRITE_MCAST 0x8116
+#define AXE_172_CMD_READ_NODEID 0x6017
+#define AXE_172_CMD_WRITE_NODEID 0x6118
+
+#define AXE_CMD_READ_PHYID 0x2019
+#define AXE_172_CMD_READ_MEDIA 0x101A
+#define AXE_178_CMD_READ_MEDIA 0x201A
+#define AXE_CMD_WRITE_MEDIA 0x011B
+#define AXE_CMD_READ_MONITOR_MODE 0x101C
+#define AXE_CMD_WRITE_MONITOR_MODE 0x011D
+#define AXE_CMD_READ_GPIO 0x101E
+#define AXE_CMD_WRITE_GPIO 0x011F
+
+#define AXE_CMD_SW_RESET_REG 0x0120
+#define AXE_CMD_SW_PHY_STATUS 0x0021
+#define AXE_CMD_SW_PHY_SELECT 0x0122
+
+#define AXE_SW_RESET_CLEAR 0x00
+#define AXE_SW_RESET_RR 0x01
+#define AXE_SW_RESET_RT 0x02
+#define AXE_SW_RESET_PRTE 0x04
+#define AXE_SW_RESET_PRL 0x08
+#define AXE_SW_RESET_BZ 0x10
+#define AXE_SW_RESET_IPRL 0x20
+#define AXE_SW_RESET_IPPD 0x40
+
+/* AX88178 documentation says to always write this bit... */
+#define AXE_178_RESET_MAGIC 0x40
+
+#define AXE_178_MEDIA_GMII 0x0001
+#define AXE_MEDIA_FULL_DUPLEX 0x0002
+#define AXE_172_MEDIA_TX_ABORT_ALLOW 0x0004
+
+/* AX88178/88772 documentation says to always write 1 to bit 2 */
+#define AXE_178_MEDIA_MAGIC 0x0004
+/* AX88772 documentation says to always write 0 to bit 3 */
+#define AXE_178_MEDIA_ENCK 0x0008
+#define AXE_172_MEDIA_FLOW_CONTROL_EN 0x0010
+#define AXE_178_MEDIA_RXFLOW_CONTROL_EN 0x0010
+#define AXE_178_MEDIA_TXFLOW_CONTROL_EN 0x0020
+#define AXE_178_MEDIA_JUMBO_EN 0x0040
+#define AXE_178_MEDIA_LTPF_ONLY 0x0080
+#define AXE_178_MEDIA_RX_EN 0x0100
+#define AXE_178_MEDIA_100TX 0x0200
+#define AXE_178_MEDIA_SBP 0x0800
+#define AXE_178_MEDIA_SUPERMAC 0x1000
+
+#define AXE_RXCMD_PROMISC 0x0001
+#define AXE_RXCMD_ALLMULTI 0x0002
+#define AXE_172_RXCMD_UNICAST 0x0004
+#define AXE_178_RXCMD_KEEP_INVALID_CRC 0x0004
+#define AXE_RXCMD_BROADCAST 0x0008
+#define AXE_RXCMD_MULTICAST 0x0010
+#define AXE_RXCMD_ENABLE 0x0080
+#define AXE_178_RXCMD_MFB_MASK 0x0300
+#define AXE_178_RXCMD_MFB_2048 0x0000
+#define AXE_178_RXCMD_MFB_4096 0x0100
+#define AXE_178_RXCMD_MFB_8192 0x0200
+#define AXE_178_RXCMD_MFB_16384 0x0300
+
+#define AXE_PHY_SEL_PRI 1
+#define AXE_PHY_SEL_SEC 0
+#define AXE_PHY_TYPE_MASK 0xE0
+#define AXE_PHY_TYPE_SHIFT 5
+#define AXE_PHY_TYPE(x) \
+ (((x) & AXE_PHY_TYPE_MASK) >> AXE_PHY_TYPE_SHIFT)
+
+#define PHY_TYPE_100_HOME 0 /* 10/100 or 1M HOME PHY */
+#define PHY_TYPE_GIG 1 /* Gigabit PHY */
+#define PHY_TYPE_SPECIAL 4 /* Special case */
+#define PHY_TYPE_RSVD 5 /* Reserved */
+#define PHY_TYPE_NON_SUP 7 /* Non-supported PHY */
+
+#define AXE_PHY_NO_MASK 0x1F
+#define AXE_PHY_NO(x) ((x) & AXE_PHY_NO_MASK)
+
+#define AXE_772_PHY_NO_EPHY 0x10 /* Embedded 10/100 PHY of AX88772 */
+
+#define AXE_BULK_BUF_SIZE 16384 /* bytes */
+
+#define AXE_CTL_READ 0x01
+#define AXE_CTL_WRITE 0x02
+
+#define AXE_CONFIG_IDX 0 /* config number 1 */
+#define AXE_IFACE_IDX 0
+
+struct axe_sframe_hdr {
+ uint16_t len;
+ uint16_t ilen;
+} __packed;
+
+#define GET_MII(sc) usb2_ether_getmii(&(sc)->sc_ue)
+
+/* The interrupt endpoint is currently unused by the ASIX part. */
+enum {
+ AXE_BULK_DT_WR,
+ AXE_BULK_DT_RD,
+ AXE_INTR_DT_RD,
+ AXE_N_TRANSFER,
+};
+
+struct axe_softc {
+ struct usb2_ether sc_ue;
+ struct mtx sc_mtx;
+ struct usb2_xfer *sc_xfer[AXE_N_TRANSFER];
+ int sc_phyno;
+
+ int sc_flags;
+#define AXE_FLAG_LINK 0x0001
+#define AXE_FLAG_772 0x1000 /* AX88772 */
+#define AXE_FLAG_178 0x2000 /* AX88178 */
+
+ uint8_t sc_ipgs[3];
+ uint8_t sc_phyaddrs[2];
+};
+
+#define AXE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define AXE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define AXE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t)
diff --git a/sys/dev/usb/net/if_cdce.c b/sys/dev/usb/net/if_cdce.c
new file mode 100644
index 0000000..4097bde
--- /dev/null
+++ b/sys/dev/usb/net/if_cdce.c
@@ -0,0 +1,771 @@
+/* $NetBSD: if_cdce.c,v 1.4 2004/10/24 12:50:54 augustss Exp $ */
+
+/*-
+ * Copyright (c) 1997, 1998, 1999, 2000-2003 Bill Paul <wpaul@windriver.com>
+ * Copyright (c) 2003-2005 Craig Boston
+ * Copyright (c) 2004 Daniel Hartmeier
+ * Copyright (c) 2009 Hans Petter Selasky
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul, THE VOICES IN HIS HEAD OR
+ * THE 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.
+ */
+
+/*
+ * USB Communication Device Class (Ethernet Networking Control Model)
+ * http://www.usb.org/developers/devclass_docs/usbcdc11.pdf
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+#include <dev/usb/usb_defs.h>
+
+#define USB_DEBUG_VAR cdce_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_parse.h>
+#include <dev/usb/usb_device.h>
+
+#include <dev/usb/net/usb_ethernet.h>
+#include <dev/usb/net/if_cdcereg.h>
+
+static device_probe_t cdce_probe;
+static device_attach_t cdce_attach;
+static device_detach_t cdce_detach;
+static device_shutdown_t cdce_shutdown;
+static device_suspend_t cdce_suspend;
+static device_resume_t cdce_resume;
+static usb_handle_request_t cdce_handle_request;
+
+static usb2_callback_t cdce_bulk_write_callback;
+static usb2_callback_t cdce_bulk_read_callback;
+static usb2_callback_t cdce_intr_read_callback;
+static usb2_callback_t cdce_intr_write_callback;
+
+static usb2_ether_fn_t cdce_attach_post;
+static usb2_ether_fn_t cdce_init;
+static usb2_ether_fn_t cdce_stop;
+static usb2_ether_fn_t cdce_start;
+static usb2_ether_fn_t cdce_setmulti;
+static usb2_ether_fn_t cdce_setpromisc;
+
+static uint32_t cdce_m_crc32(struct mbuf *, uint32_t, uint32_t);
+
+#if USB_DEBUG
+static int cdce_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, cdce, CTLFLAG_RW, 0, "USB CDC-Ethernet");
+SYSCTL_INT(_hw_usb2_cdce, OID_AUTO, debug, CTLFLAG_RW, &cdce_debug, 0,
+ "Debug level");
+#endif
+
+static const struct usb2_config cdce_config[CDCE_N_TRANSFER] = {
+
+ [CDCE_BULK_A] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .if_index = 0,
+ /* Host Mode */
+ .mh.frames = CDCE_FRAMES_MAX,
+ .mh.bufsize = (CDCE_FRAMES_MAX * MCLBYTES),
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,.ext_buffer = 1,},
+ .mh.callback = cdce_bulk_write_callback,
+ .mh.timeout = 10000, /* 10 seconds */
+ /* Device Mode */
+ .md.frames = CDCE_FRAMES_MAX,
+ .md.bufsize = (CDCE_FRAMES_MAX * MCLBYTES),
+ .md.flags = {.pipe_bof = 1,.short_frames_ok = 1,.short_xfer_ok = 1,.ext_buffer = 1,},
+ .md.callback = cdce_bulk_read_callback,
+ .md.timeout = 0, /* no timeout */
+ },
+
+ [CDCE_BULK_B] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .if_index = 0,
+ /* Host Mode */
+ .mh.frames = CDCE_FRAMES_MAX,
+ .mh.bufsize = (CDCE_FRAMES_MAX * MCLBYTES),
+ .mh.flags = {.pipe_bof = 1,.short_frames_ok = 1,.short_xfer_ok = 1,.ext_buffer = 1,},
+ .mh.callback = cdce_bulk_read_callback,
+ .mh.timeout = 0, /* no timeout */
+ /* Device Mode */
+ .md.frames = CDCE_FRAMES_MAX,
+ .md.bufsize = (CDCE_FRAMES_MAX * MCLBYTES),
+ .md.flags = {.pipe_bof = 1,.force_short_xfer = 1,.ext_buffer = 1,},
+ .md.callback = cdce_bulk_write_callback,
+ .md.timeout = 10000, /* 10 seconds */
+ },
+
+ [CDCE_INTR] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .if_index = 1,
+ /* Host Mode */
+ .mh.bufsize = CDCE_IND_SIZE_MAX,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,},
+ .mh.callback = cdce_intr_read_callback,
+ .mh.timeout = 0,
+ /* Device Mode */
+ .md.bufsize = CDCE_IND_SIZE_MAX,
+ .md.flags = {.pipe_bof = 1,.force_short_xfer = 1,.no_pipe_ok = 1,},
+ .md.callback = cdce_intr_write_callback,
+ .md.timeout = 10000, /* 10 seconds */
+ },
+};
+
+static device_method_t cdce_methods[] = {
+ /* USB interface */
+ DEVMETHOD(usb_handle_request, cdce_handle_request),
+
+ /* Device interface */
+ DEVMETHOD(device_probe, cdce_probe),
+ DEVMETHOD(device_attach, cdce_attach),
+ DEVMETHOD(device_detach, cdce_detach),
+ DEVMETHOD(device_suspend, cdce_suspend),
+ DEVMETHOD(device_resume, cdce_resume),
+ DEVMETHOD(device_shutdown, cdce_shutdown),
+
+ {0, 0}
+};
+
+static driver_t cdce_driver = {
+ .name = "cdce",
+ .methods = cdce_methods,
+ .size = sizeof(struct cdce_softc),
+};
+
+static devclass_t cdce_devclass;
+
+DRIVER_MODULE(cdce, ushub, cdce_driver, cdce_devclass, NULL, 0);
+MODULE_VERSION(cdce, 1);
+MODULE_DEPEND(cdce, uether, 1, 1, 1);
+MODULE_DEPEND(cdce, usb, 1, 1, 1);
+MODULE_DEPEND(cdce, ether, 1, 1, 1);
+
+static const struct usb2_ether_methods cdce_ue_methods = {
+ .ue_attach_post = cdce_attach_post,
+ .ue_start = cdce_start,
+ .ue_init = cdce_init,
+ .ue_stop = cdce_stop,
+ .ue_setmulti = cdce_setmulti,
+ .ue_setpromisc = cdce_setpromisc,
+};
+
+static const struct usb2_device_id cdce_devs[] = {
+ {USB_IF_CSI(UICLASS_CDC, UISUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL, 0)},
+ {USB_IF_CSI(UICLASS_CDC, UISUBCLASS_MOBILE_DIRECT_LINE_MODEL, 0)},
+
+ {USB_VPI(USB_VENDOR_ACERLABS, USB_PRODUCT_ACERLABS_M5632, CDCE_FLAG_NO_UNION)},
+ {USB_VPI(USB_VENDOR_AMBIT, USB_PRODUCT_AMBIT_NTL_250, CDCE_FLAG_NO_UNION)},
+ {USB_VPI(USB_VENDOR_COMPAQ, USB_PRODUCT_COMPAQ_IPAQLINUX, CDCE_FLAG_NO_UNION)},
+ {USB_VPI(USB_VENDOR_GMATE, USB_PRODUCT_GMATE_YP3X00, CDCE_FLAG_NO_UNION)},
+ {USB_VPI(USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_USBLAN, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)},
+ {USB_VPI(USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_USBLAN2, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)},
+ {USB_VPI(USB_VENDOR_NETCHIP, USB_PRODUCT_NETCHIP_ETHERNETGADGET, CDCE_FLAG_NO_UNION)},
+ {USB_VPI(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2501, CDCE_FLAG_NO_UNION)},
+ {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SL5500, CDCE_FLAG_ZAURUS)},
+ {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SL5600, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)},
+ {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SLA300, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)},
+ {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SLC700, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)},
+ {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SLC750, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)},
+};
+
+static int
+cdce_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ return (usb2_lookup_id_by_uaa(cdce_devs, sizeof(cdce_devs), uaa));
+}
+
+static void
+cdce_attach_post(struct usb2_ether *ue)
+{
+ /* no-op */
+ return;
+}
+
+static int
+cdce_attach(device_t dev)
+{
+ struct cdce_softc *sc = device_get_softc(dev);
+ struct usb2_ether *ue = &sc->sc_ue;
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct usb2_interface *iface;
+ const struct usb2_cdc_union_descriptor *ud;
+ const struct usb2_interface_descriptor *id;
+ const struct usb2_cdc_ethernet_descriptor *ued;
+ int error;
+ uint8_t i;
+ char eaddr_str[5 * ETHER_ADDR_LEN]; /* approx */
+
+ sc->sc_flags = USB_GET_DRIVER_INFO(uaa);
+
+ device_set_usb2_desc(dev);
+
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ if (sc->sc_flags & CDCE_FLAG_NO_UNION) {
+ sc->sc_ifaces_index[0] = uaa->info.bIfaceIndex;
+ sc->sc_ifaces_index[1] = uaa->info.bIfaceIndex;
+ sc->sc_data_iface_no = 0; /* not used */
+ goto alloc_transfers;
+ }
+ ud = usb2_find_descriptor
+ (uaa->device, NULL, uaa->info.bIfaceIndex,
+ UDESC_CS_INTERFACE, 0 - 1, UDESCSUB_CDC_UNION, 0 - 1);
+
+ if ((ud == NULL) || (ud->bLength < sizeof(*ud))) {
+ device_printf(dev, "no union descriptor!\n");
+ goto detach;
+ }
+ sc->sc_data_iface_no = ud->bSlaveInterface[0];
+
+ for (i = 0;; i++) {
+
+ iface = usb2_get_iface(uaa->device, i);
+
+ if (iface) {
+
+ id = usb2_get_interface_descriptor(iface);
+
+ if (id && (id->bInterfaceNumber ==
+ sc->sc_data_iface_no)) {
+ sc->sc_ifaces_index[0] = i;
+ sc->sc_ifaces_index[1] = uaa->info.bIfaceIndex;
+ usb2_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex);
+ break;
+ }
+ } else {
+ device_printf(dev, "no data interface found!\n");
+ goto detach;
+ }
+ }
+
+ /*
+ * <quote>
+ *
+ * The Data Class interface of a networking device shall have
+ * a minimum of two interface settings. The first setting
+ * (the default interface setting) includes no endpoints and
+ * therefore no networking traffic is exchanged whenever the
+ * default interface setting is selected. One or more
+ * additional interface settings are used for normal
+ * operation, and therefore each includes a pair of endpoints
+ * (one IN, and one OUT) to exchange network traffic. Select
+ * an alternate interface setting to initialize the network
+ * aspects of the device and to enable the exchange of
+ * network traffic.
+ *
+ * </quote>
+ *
+ * Some devices, most notably cable modems, include interface
+ * settings that have no IN or OUT endpoint, therefore loop
+ * through the list of all available interface settings
+ * looking for one with both IN and OUT endpoints.
+ */
+
+alloc_transfers:
+
+ for (i = 0; i != 32; i++) {
+
+ error = usb2_set_alt_interface_index
+ (uaa->device, sc->sc_ifaces_index[0], i);
+
+ if (error) {
+ device_printf(dev, "no valid alternate "
+ "setting found!\n");
+ goto detach;
+ }
+ error = usb2_transfer_setup
+ (uaa->device, sc->sc_ifaces_index,
+ sc->sc_xfer, cdce_config, CDCE_N_TRANSFER,
+ sc, &sc->sc_mtx);
+
+ if (error == 0) {
+ break;
+ }
+ }
+
+ ued = usb2_find_descriptor
+ (uaa->device, NULL, uaa->info.bIfaceIndex,
+ UDESC_CS_INTERFACE, 0 - 1, UDESCSUB_CDC_ENF, 0 - 1);
+
+ if ((ued == NULL) || (ued->bLength < sizeof(*ued))) {
+ error = USB_ERR_INVAL;
+ } else {
+ error = usb2_req_get_string_any(uaa->device, NULL,
+ eaddr_str, sizeof(eaddr_str), ued->iMacAddress);
+ }
+
+ if (error) {
+
+ /* fake MAC address */
+
+ device_printf(dev, "faking MAC address\n");
+ sc->sc_ue.ue_eaddr[0] = 0x2a;
+ memcpy(&sc->sc_ue.ue_eaddr[1], &ticks, sizeof(uint32_t));
+ sc->sc_ue.ue_eaddr[5] = device_get_unit(dev);
+
+ } else {
+
+ bzero(sc->sc_ue.ue_eaddr, sizeof(sc->sc_ue.ue_eaddr));
+
+ for (i = 0; i != (ETHER_ADDR_LEN * 2); i++) {
+
+ char c = eaddr_str[i];
+
+ if ('0' <= c && c <= '9')
+ c -= '0';
+ else if (c != 0)
+ c -= 'A' - 10;
+ else
+ break;
+
+ c &= 0xf;
+
+ if ((i & 1) == 0)
+ c <<= 4;
+ sc->sc_ue.ue_eaddr[i / 2] |= c;
+ }
+
+ if (uaa->usb2_mode == USB_MODE_DEVICE) {
+ /*
+ * Do not use the same MAC address like the peer !
+ */
+ sc->sc_ue.ue_eaddr[5] ^= 0xFF;
+ }
+ }
+
+ ue->ue_sc = sc;
+ ue->ue_dev = dev;
+ ue->ue_udev = uaa->device;
+ ue->ue_mtx = &sc->sc_mtx;
+ ue->ue_methods = &cdce_ue_methods;
+
+ error = usb2_ether_ifattach(ue);
+ if (error) {
+ device_printf(dev, "could not attach interface\n");
+ goto detach;
+ }
+ return (0); /* success */
+
+detach:
+ cdce_detach(dev);
+ return (ENXIO); /* failure */
+}
+
+static int
+cdce_detach(device_t dev)
+{
+ struct cdce_softc *sc = device_get_softc(dev);
+ struct usb2_ether *ue = &sc->sc_ue;
+
+ /* stop all USB transfers first */
+ usb2_transfer_unsetup(sc->sc_xfer, CDCE_N_TRANSFER);
+ usb2_ether_ifdetach(ue);
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static void
+cdce_start(struct usb2_ether *ue)
+{
+ struct cdce_softc *sc = usb2_ether_getsc(ue);
+
+ /*
+ * Start the USB transfers, if not already started:
+ */
+ usb2_transfer_start(sc->sc_xfer[CDCE_BULK_B]);
+ usb2_transfer_start(sc->sc_xfer[CDCE_BULK_A]);
+}
+
+static void
+cdce_free_queue(struct mbuf **ppm, uint8_t n)
+{
+ uint8_t x;
+ for (x = 0; x != n; x++) {
+ if (ppm[x] != NULL) {
+ m_freem(ppm[x]);
+ ppm[x] = NULL;
+ }
+ }
+}
+
+static void
+cdce_bulk_write_callback(struct usb2_xfer *xfer)
+{
+ struct cdce_softc *sc = xfer->priv_sc;
+ struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue);
+ struct mbuf *m;
+ struct mbuf *mt;
+ uint32_t crc;
+ uint8_t x;
+
+ DPRINTFN(1, "\n");
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTFN(11, "transfer complete: "
+ "%u bytes in %u frames\n", xfer->actlen,
+ xfer->aframes);
+
+ ifp->if_opackets++;
+
+ /* free all previous TX buffers */
+ cdce_free_queue(sc->sc_tx_buf, CDCE_FRAMES_MAX);
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ for (x = 0; x != CDCE_FRAMES_MAX; x++) {
+
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+
+ if (m == NULL)
+ break;
+
+ if (sc->sc_flags & CDCE_FLAG_ZAURUS) {
+ /*
+ * Zaurus wants a 32-bit CRC appended
+ * to every frame
+ */
+
+ crc = cdce_m_crc32(m, 0, m->m_pkthdr.len);
+ crc = htole32(crc);
+
+ if (!m_append(m, 4, (void *)&crc)) {
+ m_freem(m);
+ ifp->if_oerrors++;
+ continue;
+ }
+ }
+ if (m->m_len != m->m_pkthdr.len) {
+ mt = m_defrag(m, M_DONTWAIT);
+ if (mt == NULL) {
+ m_freem(m);
+ ifp->if_oerrors++;
+ continue;
+ }
+ m = mt;
+ }
+ if (m->m_pkthdr.len > MCLBYTES) {
+ m->m_pkthdr.len = MCLBYTES;
+ }
+ sc->sc_tx_buf[x] = m;
+ xfer->frlengths[x] = m->m_len;
+ usb2_set_frame_data(xfer, m->m_data, x);
+
+ /*
+ * If there's a BPF listener, bounce a copy of
+ * this frame to him:
+ */
+ BPF_MTAP(ifp, m);
+ }
+ if (x != 0) {
+ xfer->nframes = x;
+ usb2_start_hardware(xfer);
+ }
+ break;
+
+ default: /* Error */
+ DPRINTFN(11, "transfer error, %s\n",
+ usb2_errstr(xfer->error));
+
+ /* free all previous TX buffers */
+ cdce_free_queue(sc->sc_tx_buf, CDCE_FRAMES_MAX);
+
+ /* count output errors */
+ ifp->if_oerrors++;
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ break;
+ }
+}
+
+static int32_t
+cdce_m_crc32_cb(void *arg, void *src, uint32_t count)
+{
+ uint32_t *p_crc = arg;
+
+ *p_crc = crc32_raw(src, count, *p_crc);
+ return (0);
+}
+
+static uint32_t
+cdce_m_crc32(struct mbuf *m, uint32_t src_offset, uint32_t src_len)
+{
+ uint32_t crc = 0xFFFFFFFF;
+ int error;
+
+ error = m_apply(m, src_offset, src_len, cdce_m_crc32_cb, &crc);
+ return (crc ^ 0xFFFFFFFF);
+}
+
+static void
+cdce_init(struct usb2_ether *ue)
+{
+ struct cdce_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+
+ CDCE_LOCK_ASSERT(sc, MA_OWNED);
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+
+ /* start interrupt transfer */
+ usb2_transfer_start(sc->sc_xfer[CDCE_INTR]);
+
+ /* stall data write direction, which depends on USB mode */
+ if (usb2_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST)
+ usb2_transfer_set_stall(sc->sc_xfer[CDCE_BULK_A]);
+ else
+ usb2_transfer_set_stall(sc->sc_xfer[CDCE_BULK_B]);
+
+ /* start data transfers */
+ cdce_start(ue);
+}
+
+static void
+cdce_stop(struct usb2_ether *ue)
+{
+ struct cdce_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+
+ CDCE_LOCK_ASSERT(sc, MA_OWNED);
+
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+
+ /*
+ * stop all the transfers, if not already stopped:
+ */
+ usb2_transfer_stop(sc->sc_xfer[CDCE_BULK_A]);
+ usb2_transfer_stop(sc->sc_xfer[CDCE_BULK_B]);
+ usb2_transfer_stop(sc->sc_xfer[CDCE_INTR]);
+}
+
+static void
+cdce_setmulti(struct usb2_ether *ue)
+{
+ /* no-op */
+ return;
+}
+
+static void
+cdce_setpromisc(struct usb2_ether *ue)
+{
+ /* no-op */
+ return;
+}
+
+static int
+cdce_shutdown(device_t dev)
+{
+ struct cdce_softc *sc = device_get_softc(dev);
+
+ usb2_ether_ifshutdown(&sc->sc_ue);
+
+ return (0);
+}
+
+static int
+cdce_suspend(device_t dev)
+{
+ device_printf(dev, "Suspending\n");
+ return (0);
+}
+
+static int
+cdce_resume(device_t dev)
+{
+ device_printf(dev, "Resuming\n");
+ return (0);
+}
+
+static void
+cdce_bulk_read_callback(struct usb2_xfer *xfer)
+{
+ struct cdce_softc *sc = xfer->priv_sc;
+ struct mbuf *m;
+ uint8_t x;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ DPRINTF("received %u bytes in %u frames\n",
+ xfer->actlen, xfer->aframes);
+
+ for (x = 0; x != xfer->aframes; x++) {
+
+ m = sc->sc_rx_buf[x];
+ sc->sc_rx_buf[x] = NULL;
+
+ /* Strip off CRC added by Zaurus, if any */
+ if ((sc->sc_flags & CDCE_FLAG_ZAURUS) &&
+ (xfer->frlengths[x] >= 14))
+ xfer->frlengths[x] -= 4;
+
+ if (xfer->frlengths[x] < sizeof(struct ether_header)) {
+ m_freem(m);
+ continue;
+ }
+ /* queue up mbuf */
+ usb2_ether_rxmbuf(&sc->sc_ue, m, xfer->frlengths[x]);
+ }
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+ /*
+ * TODO: Implement support for multi frame transfers,
+ * when the USB hardware supports it.
+ */
+ for (x = 0; x != 1; x++) {
+ if (sc->sc_rx_buf[x] == NULL) {
+ m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+ if (m == NULL)
+ goto tr_stall;
+ sc->sc_rx_buf[x] = m;
+ /* adjust for ethernet */
+ m->m_len = m->m_pkthdr.len = MCLBYTES;
+ m_adj(m, ETHER_ALIGN);
+ } else {
+ m = sc->sc_rx_buf[x];
+ }
+
+ usb2_set_frame_data(xfer, m->m_data, x);
+ xfer->frlengths[x] = m->m_len;
+ }
+ /* set number of frames and start hardware */
+ xfer->nframes = x;
+ usb2_start_hardware(xfer);
+ /* flush any received frames */
+ usb2_ether_rxflush(&sc->sc_ue);
+ break;
+
+ default: /* Error */
+ DPRINTF("error = %s\n",
+ usb2_errstr(xfer->error));
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+tr_stall:
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ xfer->nframes = 0;
+ usb2_start_hardware(xfer);
+ break;
+ }
+
+ /* need to free the RX-mbufs when we are cancelled */
+ cdce_free_queue(sc->sc_rx_buf, CDCE_FRAMES_MAX);
+ break;
+ }
+}
+
+static void
+cdce_intr_read_callback(struct usb2_xfer *xfer)
+{
+ ; /* style fix */
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ DPRINTF("Received %d bytes\n",
+ xfer->actlen);
+
+ /* TODO: decode some indications */
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ break;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* start clear stall */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ break;
+ }
+}
+
+static void
+cdce_intr_write_callback(struct usb2_xfer *xfer)
+{
+ ; /* style fix */
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ DPRINTF("Transferred %d bytes\n", xfer->actlen);
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+#if 0
+ xfer->frlengths[0] = XXX;
+ usb2_start_hardware(xfer);
+#endif
+ break;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* start clear stall */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ break;
+ }
+}
+
+static int
+cdce_handle_request(device_t dev,
+ const void *req, void **pptr, uint16_t *plen,
+ uint16_t offset, uint8_t is_complete)
+{
+ return (ENXIO); /* use builtin handler */
+}
diff --git a/sys/dev/usb/net/if_cdcereg.h b/sys/dev/usb/net/if_cdcereg.h
new file mode 100644
index 0000000..dac5121
--- /dev/null
+++ b/sys/dev/usb/net/if_cdcereg.h
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (c) 2003-2005 Craig Boston
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul, THE VOICES IN HIS HEAD OR
+ * THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _USB_IF_CDCEREG_H_
+#define _USB_IF_CDCEREG_H_
+
+#define CDCE_FRAMES_MAX 8 /* units */
+#define CDCE_IND_SIZE_MAX 32 /* bytes */
+
+enum {
+ CDCE_BULK_A,
+ CDCE_BULK_B,
+ CDCE_INTR,
+ CDCE_N_TRANSFER,
+};
+
+struct cdce_softc {
+ struct usb2_ether sc_ue;
+ struct mtx sc_mtx;
+ struct usb2_xfer *sc_xfer[CDCE_N_TRANSFER];
+ struct mbuf *sc_rx_buf[CDCE_FRAMES_MAX];
+ struct mbuf *sc_tx_buf[CDCE_FRAMES_MAX];
+
+ int sc_flags;
+#define CDCE_FLAG_ZAURUS 0x0001
+#define CDCE_FLAG_NO_UNION 0x0002
+#define CDCE_FLAG_RX_DATA 0x0010
+
+ uint8_t sc_eaddr_str_index;
+ uint8_t sc_data_iface_no;
+ uint8_t sc_ifaces_index[2];
+};
+
+#define CDCE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define CDCE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define CDCE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t)
+#endif /* _USB_IF_CDCEREG_H_ */
diff --git a/sys/dev/usb/net/if_cue.c b/sys/dev/usb/net/if_cue.c
new file mode 100644
index 0000000..72ec92d
--- /dev/null
+++ b/sys/dev/usb/net/if_cue.c
@@ -0,0 +1,645 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * CATC USB-EL1210A USB to ethernet driver. Used in the CATC Netmate
+ * adapters and others.
+ *
+ * Written by Bill Paul <wpaul@ee.columbia.edu>
+ * Electrical Engineering Department
+ * Columbia University, New York City
+ */
+
+/*
+ * The CATC USB-EL1210A provides USB ethernet support at 10Mbps. The
+ * RX filter uses a 512-bit multicast hash table, single perfect entry
+ * for the station address, and promiscuous mode. Unlike the ADMtek
+ * and KLSI chips, the CATC ASIC supports read and write combining
+ * mode where multiple packets can be transfered using a single bulk
+ * transaction, which helps performance a great deal.
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+
+#define USB_DEBUG_VAR cue_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/net/usb_ethernet.h>
+#include <dev/usb/net/if_cuereg.h>
+
+/*
+ * Various supported device vendors/products.
+ */
+
+/* Belkin F5U111 adapter covered by NETMATE entry */
+
+static const struct usb2_device_id cue_devs[] = {
+ {USB_VPI(USB_VENDOR_CATC, USB_PRODUCT_CATC_NETMATE, 0)},
+ {USB_VPI(USB_VENDOR_CATC, USB_PRODUCT_CATC_NETMATE2, 0)},
+ {USB_VPI(USB_VENDOR_SMARTBRIDGES, USB_PRODUCT_SMARTBRIDGES_SMARTLINK, 0)},
+};
+
+/* prototypes */
+
+static device_probe_t cue_probe;
+static device_attach_t cue_attach;
+static device_detach_t cue_detach;
+static device_shutdown_t cue_shutdown;
+
+static usb2_callback_t cue_bulk_read_callback;
+static usb2_callback_t cue_bulk_write_callback;
+
+static usb2_ether_fn_t cue_attach_post;
+static usb2_ether_fn_t cue_init;
+static usb2_ether_fn_t cue_stop;
+static usb2_ether_fn_t cue_start;
+static usb2_ether_fn_t cue_tick;
+static usb2_ether_fn_t cue_setmulti;
+static usb2_ether_fn_t cue_setpromisc;
+
+static uint8_t cue_csr_read_1(struct cue_softc *, uint16_t);
+static uint16_t cue_csr_read_2(struct cue_softc *, uint8_t);
+static int cue_csr_write_1(struct cue_softc *, uint16_t, uint16_t);
+static int cue_mem(struct cue_softc *, uint8_t, uint16_t, void *, int);
+static int cue_getmac(struct cue_softc *, void *);
+static uint32_t cue_mchash(const uint8_t *);
+static void cue_reset(struct cue_softc *);
+
+#if USB_DEBUG
+static int cue_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, cue, CTLFLAG_RW, 0, "USB cue");
+SYSCTL_INT(_hw_usb2_cue, OID_AUTO, debug, CTLFLAG_RW, &cue_debug, 0,
+ "Debug level");
+#endif
+
+static const struct usb2_config cue_config[CUE_N_TRANSFER] = {
+
+ [CUE_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = (MCLBYTES + 2),
+ .mh.flags = {.pipe_bof = 1,},
+ .mh.callback = cue_bulk_write_callback,
+ .mh.timeout = 10000, /* 10 seconds */
+ },
+
+ [CUE_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = (MCLBYTES + 2),
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = cue_bulk_read_callback,
+ },
+};
+
+static device_method_t cue_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, cue_probe),
+ DEVMETHOD(device_attach, cue_attach),
+ DEVMETHOD(device_detach, cue_detach),
+ DEVMETHOD(device_shutdown, cue_shutdown),
+
+ {0, 0}
+};
+
+static driver_t cue_driver = {
+ .name = "cue",
+ .methods = cue_methods,
+ .size = sizeof(struct cue_softc),
+};
+
+static devclass_t cue_devclass;
+
+DRIVER_MODULE(cue, ushub, cue_driver, cue_devclass, NULL, 0);
+MODULE_DEPEND(cue, uether, 1, 1, 1);
+MODULE_DEPEND(cue, usb, 1, 1, 1);
+MODULE_DEPEND(cue, ether, 1, 1, 1);
+
+static const struct usb2_ether_methods cue_ue_methods = {
+ .ue_attach_post = cue_attach_post,
+ .ue_start = cue_start,
+ .ue_init = cue_init,
+ .ue_stop = cue_stop,
+ .ue_tick = cue_tick,
+ .ue_setmulti = cue_setmulti,
+ .ue_setpromisc = cue_setpromisc,
+};
+
+#define CUE_SETBIT(sc, reg, x) \
+ cue_csr_write_1(sc, reg, cue_csr_read_1(sc, reg) | (x))
+
+#define CUE_CLRBIT(sc, reg, x) \
+ cue_csr_write_1(sc, reg, cue_csr_read_1(sc, reg) & ~(x))
+
+static uint8_t
+cue_csr_read_1(struct cue_softc *sc, uint16_t reg)
+{
+ struct usb2_device_request req;
+ uint8_t val;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = CUE_CMD_READREG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 1);
+
+ if (usb2_ether_do_request(&sc->sc_ue, &req, &val, 1000)) {
+ /* ignore any errors */
+ }
+ return (val);
+}
+
+static uint16_t
+cue_csr_read_2(struct cue_softc *sc, uint8_t reg)
+{
+ struct usb2_device_request req;
+ uint16_t val;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = CUE_CMD_READREG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 2);
+
+ (void)usb2_ether_do_request(&sc->sc_ue, &req, &val, 1000);
+ return (le16toh(val));
+}
+
+static int
+cue_csr_write_1(struct cue_softc *sc, uint16_t reg, uint16_t val)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = CUE_CMD_WRITEREG;
+ USETW(req.wValue, val);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 0);
+
+ return (usb2_ether_do_request(&sc->sc_ue, &req, NULL, 1000));
+}
+
+static int
+cue_mem(struct cue_softc *sc, uint8_t cmd, uint16_t addr, void *buf, int len)
+{
+ struct usb2_device_request req;
+
+ if (cmd == CUE_CMD_READSRAM)
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ else
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = cmd;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, addr);
+ USETW(req.wLength, len);
+
+ return (usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000));
+}
+
+static int
+cue_getmac(struct cue_softc *sc, void *buf)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = CUE_CMD_GET_MACADDR;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, ETHER_ADDR_LEN);
+
+ return (usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000));
+}
+
+#define CUE_BITS 9
+
+static uint32_t
+cue_mchash(const uint8_t *addr)
+{
+ uint32_t crc;
+
+ /* Compute CRC for the address value. */
+ crc = ether_crc32_le(addr, ETHER_ADDR_LEN);
+
+ return (crc & ((1 << CUE_BITS) - 1));
+}
+
+static void
+cue_setpromisc(struct usb2_ether *ue)
+{
+ struct cue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+
+ CUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ /* if we want promiscuous mode, set the allframes bit */
+ if (ifp->if_flags & IFF_PROMISC)
+ CUE_SETBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC);
+ else
+ CUE_CLRBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC);
+
+ /* write multicast hash-bits */
+ cue_setmulti(ue);
+}
+
+static void
+cue_setmulti(struct usb2_ether *ue)
+{
+ struct cue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+ struct ifmultiaddr *ifma;
+ uint32_t h = 0, i;
+ uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ CUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
+ for (i = 0; i < 8; i++)
+ hashtbl[i] = 0xff;
+ cue_mem(sc, CUE_CMD_WRITESRAM, CUE_MCAST_TABLE_ADDR,
+ &hashtbl, 8);
+ return;
+ }
+
+ /* now program new ones */
+ IF_ADDR_LOCK(ifp);
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
+ {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ h = cue_mchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr));
+ hashtbl[h >> 3] |= 1 << (h & 0x7);
+ }
+ IF_ADDR_UNLOCK(ifp);
+
+ /*
+ * Also include the broadcast address in the filter
+ * so we can receive broadcast frames.
+ */
+ if (ifp->if_flags & IFF_BROADCAST) {
+ h = cue_mchash(ifp->if_broadcastaddr);
+ hashtbl[h >> 3] |= 1 << (h & 0x7);
+ }
+
+ cue_mem(sc, CUE_CMD_WRITESRAM, CUE_MCAST_TABLE_ADDR, &hashtbl, 8);
+}
+
+static void
+cue_reset(struct cue_softc *sc)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = CUE_CMD_RESET;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+
+ if (usb2_ether_do_request(&sc->sc_ue, &req, NULL, 1000)) {
+ /* ignore any errors */
+ }
+
+ /*
+ * wait a little while for the chip to get its brains in order:
+ */
+ usb2_ether_pause(&sc->sc_ue, hz / 100);
+}
+
+static void
+cue_attach_post(struct usb2_ether *ue)
+{
+ struct cue_softc *sc = usb2_ether_getsc(ue);
+
+ cue_getmac(sc, ue->ue_eaddr);
+}
+
+static int
+cue_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST)
+ return (ENXIO);
+ if (uaa->info.bConfigIndex != CUE_CONFIG_IDX)
+ return (ENXIO);
+ if (uaa->info.bIfaceIndex != CUE_IFACE_IDX)
+ return (ENXIO);
+
+ return (usb2_lookup_id_by_uaa(cue_devs, sizeof(cue_devs), uaa));
+}
+
+/*
+ * Attach the interface. Allocate softc structures, do ifmedia
+ * setup and ethernet/BPF attach.
+ */
+static int
+cue_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct cue_softc *sc = device_get_softc(dev);
+ struct usb2_ether *ue = &sc->sc_ue;
+ uint8_t iface_index;
+ int error;
+
+ device_set_usb2_desc(dev);
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ iface_index = CUE_IFACE_IDX;
+ error = usb2_transfer_setup(uaa->device, &iface_index,
+ sc->sc_xfer, cue_config, CUE_N_TRANSFER, sc, &sc->sc_mtx);
+ if (error) {
+ device_printf(dev, "allocating USB transfers failed!\n");
+ goto detach;
+ }
+
+ ue->ue_sc = sc;
+ ue->ue_dev = dev;
+ ue->ue_udev = uaa->device;
+ ue->ue_mtx = &sc->sc_mtx;
+ ue->ue_methods = &cue_ue_methods;
+
+ error = usb2_ether_ifattach(ue);
+ if (error) {
+ device_printf(dev, "could not attach interface\n");
+ goto detach;
+ }
+ return (0); /* success */
+
+detach:
+ cue_detach(dev);
+ return (ENXIO); /* failure */
+}
+
+static int
+cue_detach(device_t dev)
+{
+ struct cue_softc *sc = device_get_softc(dev);
+ struct usb2_ether *ue = &sc->sc_ue;
+
+ usb2_transfer_unsetup(sc->sc_xfer, CUE_N_TRANSFER);
+ usb2_ether_ifdetach(ue);
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static void
+cue_bulk_read_callback(struct usb2_xfer *xfer)
+{
+ struct cue_softc *sc = xfer->priv_sc;
+ struct usb2_ether *ue = &sc->sc_ue;
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+ uint8_t buf[2];
+ int len;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ if (xfer->actlen <= (2 + sizeof(struct ether_header))) {
+ ifp->if_ierrors++;
+ goto tr_setup;
+ }
+ usb2_copy_out(xfer->frbuffers, 0, buf, 2);
+ xfer->actlen -= 2;
+ len = buf[0] | (buf[1] << 8);
+ len = min(xfer->actlen, len);
+
+ usb2_ether_rxbuf(ue, xfer->frbuffers, 2, len);
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ usb2_ether_rxflush(ue);
+ return;
+
+ default: /* Error */
+ DPRINTF("bulk read error, %s\n",
+ usb2_errstr(xfer->error));
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
+
+static void
+cue_bulk_write_callback(struct usb2_xfer *xfer)
+{
+ struct cue_softc *sc = xfer->priv_sc;
+ struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue);
+ struct mbuf *m;
+ uint8_t buf[2];
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTFN(11, "transfer complete\n");
+ ifp->if_opackets++;
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+
+ if (m == NULL)
+ return;
+ if (m->m_pkthdr.len > MCLBYTES)
+ m->m_pkthdr.len = MCLBYTES;
+ xfer->frlengths[0] = (m->m_pkthdr.len + 2);
+
+ /* the first two bytes are the frame length */
+
+ buf[0] = (uint8_t)(m->m_pkthdr.len);
+ buf[1] = (uint8_t)(m->m_pkthdr.len >> 8);
+
+ usb2_copy_in(xfer->frbuffers, 0, buf, 2);
+
+ usb2_m_copy_in(xfer->frbuffers, 2,
+ m, 0, m->m_pkthdr.len);
+
+ /*
+ * If there's a BPF listener, bounce a copy of this frame
+ * to him.
+ */
+ BPF_MTAP(ifp, m);
+
+ m_freem(m);
+
+ usb2_start_hardware(xfer);
+
+ return;
+
+ default: /* Error */
+ DPRINTFN(11, "transfer error, %s\n",
+ usb2_errstr(xfer->error));
+
+ ifp->if_oerrors++;
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+cue_tick(struct usb2_ether *ue)
+{
+ struct cue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+
+ CUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ ifp->if_collisions += cue_csr_read_2(sc, CUE_TX_SINGLECOLL);
+ ifp->if_collisions += cue_csr_read_2(sc, CUE_TX_MULTICOLL);
+ ifp->if_collisions += cue_csr_read_2(sc, CUE_TX_EXCESSCOLL);
+
+ if (cue_csr_read_2(sc, CUE_RX_FRAMEERR))
+ ifp->if_ierrors++;
+}
+
+static void
+cue_start(struct usb2_ether *ue)
+{
+ struct cue_softc *sc = usb2_ether_getsc(ue);
+
+ /*
+ * start the USB transfers, if not already started:
+ */
+ usb2_transfer_start(sc->sc_xfer[CUE_BULK_DT_RD]);
+ usb2_transfer_start(sc->sc_xfer[CUE_BULK_DT_WR]);
+}
+
+static void
+cue_init(struct usb2_ether *ue)
+{
+ struct cue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+ int i;
+
+ CUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ /*
+ * Cancel pending I/O and free all RX/TX buffers.
+ */
+ cue_stop(ue);
+#if 0
+ cue_reset(sc);
+#endif
+ /* Set MAC address */
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ cue_csr_write_1(sc, CUE_PAR0 - i, IF_LLADDR(ifp)[i]);
+
+ /* Enable RX logic. */
+ cue_csr_write_1(sc, CUE_ETHCTL, CUE_ETHCTL_RX_ON | CUE_ETHCTL_MCAST_ON);
+
+ /* Load the multicast filter */
+ cue_setpromisc(ue);
+
+ /*
+ * Set the number of RX and TX buffers that we want
+ * to reserve inside the ASIC.
+ */
+ cue_csr_write_1(sc, CUE_RX_BUFPKTS, CUE_RX_FRAMES);
+ cue_csr_write_1(sc, CUE_TX_BUFPKTS, CUE_TX_FRAMES);
+
+ /* Set advanced operation modes. */
+ cue_csr_write_1(sc, CUE_ADVANCED_OPMODES,
+ CUE_AOP_EMBED_RXLEN | 0x01);/* 1 wait state */
+
+ /* Program the LED operation. */
+ cue_csr_write_1(sc, CUE_LEDCTL, CUE_LEDCTL_FOLLOW_LINK);
+
+ usb2_transfer_set_stall(sc->sc_xfer[CUE_BULK_DT_WR]);
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ cue_start(ue);
+}
+
+/*
+ * Stop the adapter and free any mbufs allocated to the
+ * RX and TX lists.
+ */
+static void
+cue_stop(struct usb2_ether *ue)
+{
+ struct cue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+
+ CUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+
+ /*
+ * stop all the transfers, if not already stopped:
+ */
+ usb2_transfer_stop(sc->sc_xfer[CUE_BULK_DT_WR]);
+ usb2_transfer_stop(sc->sc_xfer[CUE_BULK_DT_RD]);
+
+ cue_csr_write_1(sc, CUE_ETHCTL, 0);
+ cue_reset(sc);
+}
+
+/*
+ * Stop all chip I/O so that the kernel's probe routines don't
+ * get confused by errant DMAs when rebooting.
+ */
+static int
+cue_shutdown(device_t dev)
+{
+ struct cue_softc *sc = device_get_softc(dev);
+
+ usb2_ether_ifshutdown(&sc->sc_ue);
+
+ return (0);
+}
diff --git a/sys/dev/usb/net/if_cuereg.h b/sys/dev/usb/net/if_cuereg.h
new file mode 100644
index 0000000..ca3a816
--- /dev/null
+++ b/sys/dev/usb/net/if_cuereg.h
@@ -0,0 +1,132 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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$
+ */
+
+/*
+ * Definitions for the CATC Netmate II USB to ethernet controller.
+ */
+
+/* Vendor specific control commands. */
+#define CUE_CMD_RESET 0xF4
+#define CUE_CMD_GET_MACADDR 0xF2
+#define CUE_CMD_WRITEREG 0xFA
+#define CUE_CMD_READREG 0xFB
+#define CUE_CMD_READSRAM 0xF1
+#define CUE_CMD_WRITESRAM 0xFC
+/* Internal registers. */
+#define CUE_TX_BUFCNT 0x20
+#define CUE_RX_BUFCNT 0x21
+#define CUE_ADVANCED_OPMODES 0x22
+#define CUE_TX_BUFPKTS 0x23
+#define CUE_RX_BUFPKTS 0x24
+#define CUE_RX_MAXCHAIN 0x25
+#define CUE_ETHCTL 0x60
+#define CUE_ETHSTS 0x61
+#define CUE_PAR5 0x62
+#define CUE_PAR4 0x63
+#define CUE_PAR3 0x64
+#define CUE_PAR2 0x65
+#define CUE_PAR1 0x66
+#define CUE_PAR0 0x67
+/* Error counters, all 16 bits wide. */
+#define CUE_TX_SINGLECOLL 0x69
+#define CUE_TX_MULTICOLL 0x6B
+#define CUE_TX_EXCESSCOLL 0x6D
+#define CUE_RX_FRAMEERR 0x6F
+#define CUE_LEDCTL 0x81
+/* Advenced operating mode register. */
+#define CUE_AOP_SRAMWAITS 0x03
+#define CUE_AOP_EMBED_RXLEN 0x08
+#define CUE_AOP_RXCOMBINE 0x10
+#define CUE_AOP_TXCOMBINE 0x20
+#define CUE_AOP_EVEN_PKT_READS 0x40
+#define CUE_AOP_LOOPBK 0x80
+/* Ethernet control register. */
+#define CUE_ETHCTL_RX_ON 0x01
+#define CUE_ETHCTL_LINK_POLARITY 0x02
+#define CUE_ETHCTL_LINK_FORCE_OK 0x04
+#define CUE_ETHCTL_MCAST_ON 0x08
+#define CUE_ETHCTL_PROMISC 0x10
+/* Ethernet status register. */
+#define CUE_ETHSTS_NO_CARRIER 0x01
+#define CUE_ETHSTS_LATECOLL 0x02
+#define CUE_ETHSTS_EXCESSCOLL 0x04
+#define CUE_ETHSTS_TXBUF_AVAIL 0x08
+#define CUE_ETHSTS_BAD_POLARITY 0x10
+#define CUE_ETHSTS_LINK_OK 0x20
+/* LED control register. */
+#define CUE_LEDCTL_BLINK_1X 0x00
+#define CUE_LEDCTL_BLINK_2X 0x01
+#define CUE_LEDCTL_BLINK_QUARTER_ON 0x02
+#define CUE_LEDCTL_BLINK_QUARTER_OFF 0x03
+#define CUE_LEDCTL_OFF 0x04
+#define CUE_LEDCTL_FOLLOW_LINK 0x08
+
+/*
+ * Address in ASIC's internal SRAM where the multicast hash table lives.
+ * The table is 64 bytes long, giving us a 512-bit table. We have to set
+ * the bit that corresponds to the broadcast address in order to enable
+ * reception of broadcast frames.
+ */
+#define CUE_MCAST_TABLE_ADDR 0xFA80
+
+#define CUE_TIMEOUT 1000
+#define CUE_MIN_FRAMELEN 60
+#define CUE_RX_FRAMES 1
+#define CUE_TX_FRAMES 1
+
+#define CUE_CTL_READ 0x01
+#define CUE_CTL_WRITE 0x02
+
+#define CUE_CONFIG_IDX 0 /* config number 1 */
+#define CUE_IFACE_IDX 0
+
+/* The interrupt endpoint is currently unused by the KLSI part. */
+enum {
+ CUE_BULK_DT_WR,
+ CUE_BULK_DT_RD,
+ CUE_N_TRANSFER,
+};
+
+struct cue_softc {
+ struct usb2_ether sc_ue;
+ struct mtx sc_mtx;
+ struct usb2_xfer *sc_xfer[CUE_N_TRANSFER];
+
+ int sc_flags;
+#define CUE_FLAG_LINK 0x0001 /* got a link */
+};
+
+#define CUE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define CUE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define CUE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t)
diff --git a/sys/dev/usb/net/if_kue.c b/sys/dev/usb/net/if_kue.c
new file mode 100644
index 0000000..b97922f
--- /dev/null
+++ b/sys/dev/usb/net/if_kue.c
@@ -0,0 +1,704 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Kawasaki LSI KL5KUSB101B USB to ethernet adapter driver.
+ *
+ * Written by Bill Paul <wpaul@ee.columbia.edu>
+ * Electrical Engineering Department
+ * Columbia University, New York City
+ */
+
+/*
+ * The KLSI USB to ethernet adapter chip contains an USB serial interface,
+ * ethernet MAC and embedded microcontroller (called the QT Engine).
+ * The chip must have firmware loaded into it before it will operate.
+ * Packets are passed between the chip and host via bulk transfers.
+ * There is an interrupt endpoint mentioned in the software spec, however
+ * it's currently unused. This device is 10Mbps half-duplex only, hence
+ * there is no media selection logic. The MAC supports a 128 entry
+ * multicast filter, though the exact size of the filter can depend
+ * on the firmware. Curiously, while the software spec describes various
+ * ethernet statistics counters, my sample adapter and firmware combination
+ * claims not to support any statistics counters at all.
+ *
+ * Note that once we load the firmware in the device, we have to be
+ * careful not to load it again: if you restart your computer but
+ * leave the adapter attached to the USB controller, it may remain
+ * powered on and retain its firmware. In this case, we don't need
+ * to load the firmware a second time.
+ *
+ * Special thanks to Rob Furr for providing an ADS Technologies
+ * adapter for development and testing. No monkeys were harmed during
+ * the development of this driver.
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+
+#define USB_DEBUG_VAR kue_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/net/usb_ethernet.h>
+#include <dev/usb/net/if_kuereg.h>
+#include <dev/usb/net/if_kuefw.h>
+
+/*
+ * Various supported device vendors/products.
+ */
+static const struct usb2_device_id kue_devs[] = {
+ {USB_VPI(USB_VENDOR_3COM, USB_PRODUCT_3COM_3C19250, 0)},
+ {USB_VPI(USB_VENDOR_3COM, USB_PRODUCT_3COM_3C460, 0)},
+ {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_URE450, 0)},
+ {USB_VPI(USB_VENDOR_ADS, USB_PRODUCT_ADS_UBS10BT, 0)},
+ {USB_VPI(USB_VENDOR_ADS, USB_PRODUCT_ADS_UBS10BTX, 0)},
+ {USB_VPI(USB_VENDOR_AOX, USB_PRODUCT_AOX_USB101, 0)},
+ {USB_VPI(USB_VENDOR_ASANTE, USB_PRODUCT_ASANTE_EA, 0)},
+ {USB_VPI(USB_VENDOR_ATEN, USB_PRODUCT_ATEN_DSB650C, 0)},
+ {USB_VPI(USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC10T, 0)},
+ {USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_ETHER_USB_T, 0)},
+ {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650C, 0)},
+ {USB_VPI(USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_E45, 0)},
+ {USB_VPI(USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_XX1, 0)},
+ {USB_VPI(USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_XX2, 0)},
+ {USB_VPI(USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETT, 0)},
+ {USB_VPI(USB_VENDOR_JATON, USB_PRODUCT_JATON_EDA, 0)},
+ {USB_VPI(USB_VENDOR_KINGSTON, USB_PRODUCT_KINGSTON_XX1, 0)},
+ {USB_VPI(USB_VENDOR_KLSI, USB_PRODUCT_AOX_USB101, 0)},
+ {USB_VPI(USB_VENDOR_KLSI, USB_PRODUCT_KLSI_DUH3E10BT, 0)},
+ {USB_VPI(USB_VENDOR_KLSI, USB_PRODUCT_KLSI_DUH3E10BTN, 0)},
+ {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10T, 0)},
+ {USB_VPI(USB_VENDOR_MOBILITY, USB_PRODUCT_MOBILITY_EA, 0)},
+ {USB_VPI(USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_EA101, 0)},
+ {USB_VPI(USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_EA101X, 0)},
+ {USB_VPI(USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_ENET, 0)},
+ {USB_VPI(USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_ENET2, 0)},
+ {USB_VPI(USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_ENET3, 0)},
+ {USB_VPI(USB_VENDOR_PORTGEAR, USB_PRODUCT_PORTGEAR_EA8, 0)},
+ {USB_VPI(USB_VENDOR_PORTGEAR, USB_PRODUCT_PORTGEAR_EA9, 0)},
+ {USB_VPI(USB_VENDOR_PORTSMITH, USB_PRODUCT_PORTSMITH_EEA, 0)},
+ {USB_VPI(USB_VENDOR_SHARK, USB_PRODUCT_SHARK_PA, 0)},
+ {USB_VPI(USB_VENDOR_SILICOM, USB_PRODUCT_SILICOM_GPE, 0)},
+ {USB_VPI(USB_VENDOR_SILICOM, USB_PRODUCT_SILICOM_U2E, 0)},
+ {USB_VPI(USB_VENDOR_SMC, USB_PRODUCT_SMC_2102USB, 0)},
+};
+
+/* prototypes */
+
+static device_probe_t kue_probe;
+static device_attach_t kue_attach;
+static device_detach_t kue_detach;
+static device_shutdown_t kue_shutdown;
+
+static usb2_callback_t kue_bulk_read_callback;
+static usb2_callback_t kue_bulk_write_callback;
+
+static usb2_ether_fn_t kue_attach_post;
+static usb2_ether_fn_t kue_init;
+static usb2_ether_fn_t kue_stop;
+static usb2_ether_fn_t kue_start;
+static usb2_ether_fn_t kue_setmulti;
+static usb2_ether_fn_t kue_setpromisc;
+
+static int kue_do_request(struct kue_softc *,
+ struct usb2_device_request *, void *);
+static int kue_setword(struct kue_softc *, uint8_t, uint16_t);
+static int kue_ctl(struct kue_softc *, uint8_t, uint8_t, uint16_t,
+ void *, int);
+static int kue_load_fw(struct kue_softc *);
+static void kue_reset(struct kue_softc *);
+
+#if USB_DEBUG
+static int kue_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, kue, CTLFLAG_RW, 0, "USB kue");
+SYSCTL_INT(_hw_usb2_kue, OID_AUTO, debug, CTLFLAG_RW, &kue_debug, 0,
+ "Debug level");
+#endif
+
+static const struct usb2_config kue_config[KUE_N_TRANSFER] = {
+
+ [KUE_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = (MCLBYTES + 2 + 64),
+ .mh.flags = {.pipe_bof = 1,},
+ .mh.callback = kue_bulk_write_callback,
+ .mh.timeout = 10000, /* 10 seconds */
+ },
+
+ [KUE_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = (MCLBYTES + 2),
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = kue_bulk_read_callback,
+ .mh.timeout = 0, /* no timeout */
+ },
+};
+
+static device_method_t kue_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, kue_probe),
+ DEVMETHOD(device_attach, kue_attach),
+ DEVMETHOD(device_detach, kue_detach),
+ DEVMETHOD(device_shutdown, kue_shutdown),
+
+ {0, 0}
+};
+
+static driver_t kue_driver = {
+ .name = "kue",
+ .methods = kue_methods,
+ .size = sizeof(struct kue_softc),
+};
+
+static devclass_t kue_devclass;
+
+DRIVER_MODULE(kue, ushub, kue_driver, kue_devclass, NULL, 0);
+MODULE_DEPEND(kue, uether, 1, 1, 1);
+MODULE_DEPEND(kue, usb, 1, 1, 1);
+MODULE_DEPEND(kue, ether, 1, 1, 1);
+
+static const struct usb2_ether_methods kue_ue_methods = {
+ .ue_attach_post = kue_attach_post,
+ .ue_start = kue_start,
+ .ue_init = kue_init,
+ .ue_stop = kue_stop,
+ .ue_setmulti = kue_setmulti,
+ .ue_setpromisc = kue_setpromisc,
+};
+
+/*
+ * We have a custom do_request function which is almost like the
+ * regular do_request function, except it has a much longer timeout.
+ * Why? Because we need to make requests over the control endpoint
+ * to download the firmware to the device, which can take longer
+ * than the default timeout.
+ */
+static int
+kue_do_request(struct kue_softc *sc, struct usb2_device_request *req,
+ void *data)
+{
+ usb2_error_t err;
+
+ err = usb2_ether_do_request(&sc->sc_ue, req, data, 60000);
+
+ return (err);
+}
+
+static int
+kue_setword(struct kue_softc *sc, uint8_t breq, uint16_t word)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = breq;
+ USETW(req.wValue, word);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+
+ return (kue_do_request(sc, &req, NULL));
+}
+
+static int
+kue_ctl(struct kue_softc *sc, uint8_t rw, uint8_t breq,
+ uint16_t val, void *data, int len)
+{
+ struct usb2_device_request req;
+
+ if (rw == KUE_CTL_WRITE)
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ else
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+
+
+ req.bRequest = breq;
+ USETW(req.wValue, val);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, len);
+
+ return (kue_do_request(sc, &req, data));
+}
+
+static int
+kue_load_fw(struct kue_softc *sc)
+{
+ struct usb2_device_descriptor *dd;
+ uint16_t hwrev;
+ usb2_error_t err;
+
+ dd = usb2_get_device_descriptor(sc->sc_ue.ue_udev);
+ hwrev = UGETW(dd->bcdDevice);
+
+ /*
+ * First, check if we even need to load the firmware.
+ * If the device was still attached when the system was
+ * rebooted, it may already have firmware loaded in it.
+ * If this is the case, we don't need to do it again.
+ * And in fact, if we try to load it again, we'll hang,
+ * so we have to avoid this condition if we don't want
+ * to look stupid.
+ *
+ * We can test this quickly by checking the bcdRevision
+ * code. The NIC will return a different revision code if
+ * it's probed while the firmware is still loaded and
+ * running.
+ */
+ if (hwrev == 0x0202)
+ return(0);
+
+ /* Load code segment */
+ err = kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN,
+ 0, kue_code_seg, sizeof(kue_code_seg));
+ if (err) {
+ device_printf(sc->sc_ue.ue_dev, "failed to load code segment: %s\n",
+ usb2_errstr(err));
+ return(ENXIO);
+ }
+
+ /* Load fixup segment */
+ err = kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN,
+ 0, kue_fix_seg, sizeof(kue_fix_seg));
+ if (err) {
+ device_printf(sc->sc_ue.ue_dev, "failed to load fixup segment: %s\n",
+ usb2_errstr(err));
+ return(ENXIO);
+ }
+
+ /* Send trigger command. */
+ err = kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN,
+ 0, kue_trig_seg, sizeof(kue_trig_seg));
+ if (err) {
+ device_printf(sc->sc_ue.ue_dev, "failed to load trigger segment: %s\n",
+ usb2_errstr(err));
+ return(ENXIO);
+ }
+
+ return (0);
+}
+
+static void
+kue_setpromisc(struct usb2_ether *ue)
+{
+ struct kue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+
+ KUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ if (ifp->if_flags & IFF_PROMISC)
+ sc->sc_rxfilt |= KUE_RXFILT_PROMISC;
+ else
+ sc->sc_rxfilt &= ~KUE_RXFILT_PROMISC;
+
+ kue_setword(sc, KUE_CMD_SET_PKT_FILTER, sc->sc_rxfilt);
+}
+
+static void
+kue_setmulti(struct usb2_ether *ue)
+{
+ struct kue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+ struct ifmultiaddr *ifma;
+ int i = 0;
+
+ KUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
+ sc->sc_rxfilt |= KUE_RXFILT_ALLMULTI;
+ sc->sc_rxfilt &= ~KUE_RXFILT_MULTICAST;
+ kue_setword(sc, KUE_CMD_SET_PKT_FILTER, sc->sc_rxfilt);
+ return;
+ }
+
+ sc->sc_rxfilt &= ~KUE_RXFILT_ALLMULTI;
+
+ IF_ADDR_LOCK(ifp);
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
+ {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ /*
+ * If there are too many addresses for the
+ * internal filter, switch over to allmulti mode.
+ */
+ if (i == KUE_MCFILTCNT(sc))
+ break;
+ bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
+ KUE_MCFILT(sc, i), ETHER_ADDR_LEN);
+ i++;
+ }
+ IF_ADDR_UNLOCK(ifp);
+
+ if (i == KUE_MCFILTCNT(sc))
+ sc->sc_rxfilt |= KUE_RXFILT_ALLMULTI;
+ else {
+ sc->sc_rxfilt |= KUE_RXFILT_MULTICAST;
+ kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SET_MCAST_FILTERS,
+ i, sc->sc_mcfilters, i * ETHER_ADDR_LEN);
+ }
+
+ kue_setword(sc, KUE_CMD_SET_PKT_FILTER, sc->sc_rxfilt);
+}
+
+/*
+ * Issue a SET_CONFIGURATION command to reset the MAC. This should be
+ * done after the firmware is loaded into the adapter in order to
+ * bring it into proper operation.
+ */
+static void
+kue_reset(struct kue_softc *sc)
+{
+ struct usb2_config_descriptor *cd;
+ usb2_error_t err;
+
+ cd = usb2_get_config_descriptor(sc->sc_ue.ue_udev);
+
+ err = usb2_req_set_config(sc->sc_ue.ue_udev, &sc->sc_mtx,
+ cd->bConfigurationValue);
+ if (err)
+ DPRINTF("reset failed (ignored)\n");
+
+ /* wait a little while for the chip to get its brains in order */
+ usb2_ether_pause(&sc->sc_ue, hz / 100);
+}
+
+static void
+kue_attach_post(struct usb2_ether *ue)
+{
+ struct kue_softc *sc = usb2_ether_getsc(ue);
+ int error;
+
+ /* load the firmware into the NIC */
+ error = kue_load_fw(sc);
+ if (error) {
+ device_printf(sc->sc_ue.ue_dev, "could not load firmware\n");
+ /* ignore the error */
+ }
+
+ /* reset the adapter */
+ kue_reset(sc);
+
+ /* read ethernet descriptor */
+ kue_ctl(sc, KUE_CTL_READ, KUE_CMD_GET_ETHER_DESCRIPTOR,
+ 0, &sc->sc_desc, sizeof(sc->sc_desc));
+
+ /* copy in ethernet address */
+ memcpy(ue->ue_eaddr, sc->sc_desc.kue_macaddr, sizeof(ue->ue_eaddr));
+}
+
+/*
+ * Probe for a KLSI chip.
+ */
+static int
+kue_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST)
+ return (ENXIO);
+ if (uaa->info.bConfigIndex != KUE_CONFIG_IDX)
+ return (ENXIO);
+ if (uaa->info.bIfaceIndex != KUE_IFACE_IDX)
+ return (ENXIO);
+
+ return (usb2_lookup_id_by_uaa(kue_devs, sizeof(kue_devs), uaa));
+}
+
+/*
+ * Attach the interface. Allocate softc structures, do
+ * setup and ethernet/BPF attach.
+ */
+static int
+kue_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct kue_softc *sc = device_get_softc(dev);
+ struct usb2_ether *ue = &sc->sc_ue;
+ uint8_t iface_index;
+ int error;
+
+ device_set_usb2_desc(dev);
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ iface_index = KUE_IFACE_IDX;
+ error = usb2_transfer_setup(uaa->device, &iface_index,
+ sc->sc_xfer, kue_config, KUE_N_TRANSFER, sc, &sc->sc_mtx);
+ if (error) {
+ device_printf(dev, "allocating USB transfers failed!\n");
+ goto detach;
+ }
+
+ sc->sc_mcfilters = malloc(KUE_MCFILTCNT(sc) * ETHER_ADDR_LEN,
+ M_USBDEV, M_WAITOK);
+ if (sc->sc_mcfilters == NULL) {
+ device_printf(dev, "failed allocating USB memory!\n");
+ goto detach;
+ }
+
+ ue->ue_sc = sc;
+ ue->ue_dev = dev;
+ ue->ue_udev = uaa->device;
+ ue->ue_mtx = &sc->sc_mtx;
+ ue->ue_methods = &kue_ue_methods;
+
+ error = usb2_ether_ifattach(ue);
+ if (error) {
+ device_printf(dev, "could not attach interface\n");
+ goto detach;
+ }
+ return (0); /* success */
+
+detach:
+ kue_detach(dev);
+ return (ENXIO); /* failure */
+}
+
+static int
+kue_detach(device_t dev)
+{
+ struct kue_softc *sc = device_get_softc(dev);
+ struct usb2_ether *ue = &sc->sc_ue;
+
+ usb2_transfer_unsetup(sc->sc_xfer, KUE_N_TRANSFER);
+ usb2_ether_ifdetach(ue);
+ mtx_destroy(&sc->sc_mtx);
+ free(sc->sc_mcfilters, M_USBDEV);
+
+ return (0);
+}
+
+/*
+ * A frame has been uploaded: pass the resulting mbuf chain up to
+ * the higher level protocols.
+ */
+static void
+kue_bulk_read_callback(struct usb2_xfer *xfer)
+{
+ struct kue_softc *sc = xfer->priv_sc;
+ struct usb2_ether *ue = &sc->sc_ue;
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+ uint8_t buf[2];
+ int len;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ if (xfer->actlen <= (2 + sizeof(struct ether_header))) {
+ ifp->if_ierrors++;
+ goto tr_setup;
+ }
+ usb2_copy_out(xfer->frbuffers, 0, buf, 2);
+ xfer->actlen -= 2;
+ len = buf[0] | (buf[1] << 8);
+ len = min(xfer->actlen, len);
+
+ usb2_ether_rxbuf(ue, xfer->frbuffers, 2, len);
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ usb2_ether_rxflush(ue);
+ return;
+
+ default: /* Error */
+ DPRINTF("bulk read error, %s\n",
+ usb2_errstr(xfer->error));
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
+
+static void
+kue_bulk_write_callback(struct usb2_xfer *xfer)
+{
+ struct kue_softc *sc = xfer->priv_sc;
+ struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue);
+ struct mbuf *m;
+ int total_len;
+ int temp_len;
+ uint8_t buf[2];
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTFN(11, "transfer complete\n");
+ ifp->if_opackets++;
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+
+ if (m == NULL)
+ return;
+ if (m->m_pkthdr.len > MCLBYTES)
+ m->m_pkthdr.len = MCLBYTES;
+ temp_len = (m->m_pkthdr.len + 2);
+ total_len = (temp_len + (64 - (temp_len % 64)));
+
+ /* the first two bytes are the frame length */
+
+ buf[0] = (uint8_t)(m->m_pkthdr.len);
+ buf[1] = (uint8_t)(m->m_pkthdr.len >> 8);
+
+ usb2_copy_in(xfer->frbuffers, 0, buf, 2);
+
+ usb2_m_copy_in(xfer->frbuffers, 2,
+ m, 0, m->m_pkthdr.len);
+
+ usb2_bzero(xfer->frbuffers, temp_len,
+ total_len - temp_len);
+
+ xfer->frlengths[0] = total_len;
+
+ /*
+ * if there's a BPF listener, bounce a copy
+ * of this frame to him:
+ */
+ BPF_MTAP(ifp, m);
+
+ m_freem(m);
+
+ usb2_start_hardware(xfer);
+
+ return;
+
+ default: /* Error */
+ DPRINTFN(11, "transfer error, %s\n",
+ usb2_errstr(xfer->error));
+
+ ifp->if_oerrors++;
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
+
+static void
+kue_start(struct usb2_ether *ue)
+{
+ struct kue_softc *sc = usb2_ether_getsc(ue);
+
+ /*
+ * start the USB transfers, if not already started:
+ */
+ usb2_transfer_start(sc->sc_xfer[KUE_BULK_DT_RD]);
+ usb2_transfer_start(sc->sc_xfer[KUE_BULK_DT_WR]);
+}
+
+static void
+kue_init(struct usb2_ether *ue)
+{
+ struct kue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+
+ KUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ /* set MAC address */
+ kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SET_MAC,
+ 0, IF_LLADDR(ifp), ETHER_ADDR_LEN);
+
+ /* I'm not sure how to tune these. */
+#if 0
+ /*
+ * Leave this one alone for now; setting it
+ * wrong causes lockups on some machines/controllers.
+ */
+ kue_setword(sc, KUE_CMD_SET_SOFS, 1);
+#endif
+ kue_setword(sc, KUE_CMD_SET_URB_SIZE, 64);
+
+ /* load the multicast filter */
+ kue_setpromisc(ue);
+
+ usb2_transfer_set_stall(sc->sc_xfer[KUE_BULK_DT_WR]);
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ kue_start(ue);
+}
+
+static void
+kue_stop(struct usb2_ether *ue)
+{
+ struct kue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+
+ KUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+
+ /*
+ * stop all the transfers, if not already stopped:
+ */
+ usb2_transfer_stop(sc->sc_xfer[KUE_BULK_DT_WR]);
+ usb2_transfer_stop(sc->sc_xfer[KUE_BULK_DT_RD]);
+}
+
+/*
+ * Stop all chip I/O so that the kernel's probe routines don't
+ * get confused by errant DMAs when rebooting.
+ */
+static int
+kue_shutdown(device_t dev)
+{
+ struct kue_softc *sc = device_get_softc(dev);
+
+ usb2_ether_ifshutdown(&sc->sc_ue);
+
+ return (0);
+}
diff --git a/sys/dev/usb/net/if_kuefw.h b/sys/dev/usb/net/if_kuefw.h
new file mode 100644
index 0000000..2b055a9
--- /dev/null
+++ b/sys/dev/usb/net/if_kuefw.h
@@ -0,0 +1,685 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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$
+ */
+
+/*
+ * This file contains the firmware needed to make the KLSI chip work,
+ * along with a few constants related to the QT Engine microcontroller
+ * embedded in the KLSI part.
+ *
+ * Firmware is loaded using the vendor-specific 'send scan data'
+ * command (0xFF). The basic operation is that we must load the
+ * firmware, then issue some trigger commands to fix it up and start
+ * it running. There are three transfers: load the binary code,
+ * load the 'fixup' (data segment?), then issue a command to
+ * start the code firmware running. The data itself is prefixed by
+ * a 16-bit signature word, a 16-bit length value, a type byte
+ * and an interrupt (command) byte. The code segment is of type
+ * 0x02 (replacement interrupt vector data) and the fixup segment
+ * is of type 0x03 (replacement interrupt fixup data). The interrupt
+ * code is 0x64 (load new code). The length word is the total length
+ * of the segment minus 7. I precomputed the values and stuck them
+ * into the appropriate locations within the segments to save some
+ * work in the driver.
+ */
+
+/* QT controller data block types. */
+/* Write data into specific memory location. */
+#define KUE_QTBTYPE_WRITE_DATA 0x00
+/* Write data into interrupt vector location */
+#define KUE_QTBTYPE_WRITE_INTVEC 0x01
+/* Replace interrupt vector with this data */
+#define KUE_QTBTYPE_REPL_INTVEC 0x02
+/* Fixup interrupt vector code with this data */
+#define KUE_QTBTYPE_FIXUP_INTVEC 0x03
+/* Force jump to location */
+#define KUE_QTBTYPE_JUMP 0x04
+/* Force call to location */
+#define KUE_QTBTYPE_CALL 0x05
+/* Force interrupt call */
+#define KUE_QTBTYPE_CALLINTR 0x06
+/*
+ * Cause data to be written using the specified QT engine
+ * interrupt, from starting location in memory for a specified
+ * number of bytes.
+ */
+#define KUE_QTBTYPE_WRITE_WITH_INTR 0x07
+/* Cause data from stream to be written using specified QT interrupt. */
+#define KUE_QTBTYPE_WRITE_STR_WITH_INTR 0x08
+/* Cause data to be written to config locations. */
+/* Addresses assume 0xc000 offset. */
+#define KUE_QTBTYPE_WRITE_CONFIG 0x09
+
+#define KUE_QTINTR_LOAD_CODE 0x64
+#define KUE_QTINTR_TRIGGER_CODE 0x3B
+#define KUE_QTINTR_LOAD_CODE_HIGH 0x9C
+
+/* Firmware code segment */
+static unsigned char kue_code_seg[] =
+{
+ /******************************************/
+ /* NOTE: B6/C3 is data header signature */
+ /* 0xAA/0xBB is data length = total */
+ /* bytes - 7, 0xCC is type, 0xDD is */
+ /* interrupt to use. */
+ /******************************************/
+ 0xB6, 0xC3, 0xf7, 0x0e, 0x02, 0x64,
+ 0x9f, 0xcf, 0xbc, 0x08, 0xe7, 0x57, 0x00, 0x00,
+ 0x9a, 0x08, 0x97, 0xc1, 0xe7, 0x67, 0xff, 0x1f,
+ 0x28, 0xc0, 0xe7, 0x87, 0x00, 0x04, 0x24, 0xc0,
+ 0xe7, 0x67, 0xff, 0xf9, 0x22, 0xc0, 0x97, 0xcf,
+ 0xe7, 0x09, 0xa2, 0xc0, 0x94, 0x08, 0xd7, 0x09,
+ 0x00, 0xc0, 0xe7, 0x59, 0xba, 0x08, 0x94, 0x08,
+ 0x03, 0xc1, 0xe7, 0x67, 0xff, 0xf7, 0x24, 0xc0,
+ 0xe7, 0x05, 0x00, 0xc0, 0xa7, 0xcf, 0x92, 0x08,
+ 0xe7, 0x57, 0x00, 0x00, 0x8e, 0x08, 0xa7, 0xa1,
+ 0x8e, 0x08, 0x97, 0xcf, 0xe7, 0x57, 0x00, 0x00,
+ 0xf2, 0x09, 0x0a, 0xc0, 0xe7, 0x57, 0x00, 0x00,
+ 0xa4, 0xc0, 0xa7, 0xc0, 0x56, 0x08, 0x9f, 0xaf,
+ 0x70, 0x09, 0xe7, 0x07, 0x00, 0x00, 0xf2, 0x09,
+ 0xe7, 0x57, 0xff, 0xff, 0x90, 0x08, 0x9f, 0xa0,
+ 0x40, 0x00, 0xe7, 0x59, 0x90, 0x08, 0x94, 0x08,
+ 0x9f, 0xa0, 0x40, 0x00, 0xc8, 0x09, 0xa2, 0x08,
+ 0x08, 0x62, 0x9f, 0xa1, 0x14, 0x0a, 0xe7, 0x57,
+ 0x00, 0x00, 0x52, 0x08, 0xa7, 0xc0, 0x56, 0x08,
+ 0x9f, 0xaf, 0x04, 0x00, 0xe7, 0x57, 0x00, 0x00,
+ 0x8e, 0x08, 0xa7, 0xc1, 0x56, 0x08, 0xc0, 0x09,
+ 0xa8, 0x08, 0x00, 0x60, 0x05, 0xc4, 0xc0, 0x59,
+ 0x94, 0x08, 0x02, 0xc0, 0x9f, 0xaf, 0xee, 0x00,
+ 0xe7, 0x59, 0xae, 0x08, 0x94, 0x08, 0x02, 0xc1,
+ 0x9f, 0xaf, 0xf6, 0x00, 0x9f, 0xaf, 0x9e, 0x03,
+ 0xef, 0x57, 0x00, 0x00, 0xf0, 0x09, 0x9f, 0xa1,
+ 0xde, 0x01, 0xe7, 0x57, 0x00, 0x00, 0x78, 0x08,
+ 0x9f, 0xa0, 0xe4, 0x03, 0x9f, 0xaf, 0x2c, 0x04,
+ 0xa7, 0xcf, 0x56, 0x08, 0x48, 0x02, 0xe7, 0x09,
+ 0x94, 0x08, 0xa8, 0x08, 0xc8, 0x37, 0x04, 0x00,
+ 0x9f, 0xaf, 0x68, 0x04, 0x97, 0xcf, 0xe7, 0x57,
+ 0x00, 0x00, 0xa6, 0x08, 0x97, 0xc0, 0xd7, 0x09,
+ 0x00, 0xc0, 0xc1, 0xdf, 0xc8, 0x09, 0x9c, 0x08,
+ 0x08, 0x62, 0x1d, 0xc0, 0x27, 0x04, 0x9c, 0x08,
+ 0x10, 0x94, 0xf0, 0x07, 0xee, 0x09, 0x02, 0x00,
+ 0xc1, 0x07, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00,
+ 0xf0, 0x07, 0x44, 0x01, 0x06, 0x00, 0x50, 0xaf,
+ 0xe7, 0x09, 0x94, 0x08, 0xae, 0x08, 0xe7, 0x17,
+ 0x14, 0x00, 0xae, 0x08, 0xe7, 0x67, 0xff, 0x07,
+ 0xae, 0x08, 0xe7, 0x07, 0xff, 0xff, 0xa8, 0x08,
+ 0xe7, 0x07, 0x00, 0x00, 0xa6, 0x08, 0xe7, 0x05,
+ 0x00, 0xc0, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0,
+ 0xc1, 0xdf, 0x48, 0x02, 0xd0, 0x09, 0x9c, 0x08,
+ 0x27, 0x02, 0x9c, 0x08, 0xe7, 0x09, 0x20, 0xc0,
+ 0xee, 0x09, 0xe7, 0xd0, 0xee, 0x09, 0xe7, 0x05,
+ 0x00, 0xc0, 0x97, 0xcf, 0x48, 0x02, 0xc8, 0x37,
+ 0x04, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x60,
+ 0x21, 0xc0, 0xc0, 0x37, 0x3e, 0x00, 0x23, 0xc9,
+ 0xc0, 0x57, 0xb4, 0x05, 0x1b, 0xc8, 0xc0, 0x17,
+ 0x3f, 0x00, 0xc0, 0x67, 0xc0, 0xff, 0x30, 0x00,
+ 0x08, 0x00, 0xf0, 0x07, 0x00, 0x00, 0x04, 0x00,
+ 0x00, 0x02, 0xc0, 0x17, 0x4c, 0x00, 0x30, 0x00,
+ 0x06, 0x00, 0xf0, 0x07, 0xbe, 0x01, 0x0a, 0x00,
+ 0x48, 0x02, 0xc1, 0x07, 0x02, 0x00, 0xd7, 0x09,
+ 0x00, 0xc0, 0xc1, 0xdf, 0x51, 0xaf, 0xe7, 0x05,
+ 0x00, 0xc0, 0x97, 0xcf, 0x9f, 0xaf, 0x68, 0x04,
+ 0x9f, 0xaf, 0xe4, 0x03, 0x97, 0xcf, 0x9f, 0xaf,
+ 0xe4, 0x03, 0xc9, 0x37, 0x04, 0x00, 0xc1, 0xdf,
+ 0xc8, 0x09, 0x70, 0x08, 0x50, 0x02, 0x67, 0x02,
+ 0x70, 0x08, 0xd1, 0x07, 0x00, 0x00, 0xc0, 0xdf,
+ 0x9f, 0xaf, 0xde, 0x01, 0x97, 0xcf, 0xe7, 0x57,
+ 0x00, 0x00, 0xaa, 0x08, 0x97, 0xc1, 0xe7, 0x57,
+ 0x01, 0x00, 0x7a, 0x08, 0x97, 0xc0, 0xc8, 0x09,
+ 0x6e, 0x08, 0x08, 0x62, 0x97, 0xc0, 0x00, 0x02,
+ 0xc0, 0x17, 0x0e, 0x00, 0x27, 0x00, 0x34, 0x01,
+ 0x27, 0x0c, 0x0c, 0x00, 0x36, 0x01, 0xef, 0x57,
+ 0x00, 0x00, 0xf0, 0x09, 0x9f, 0xc0, 0xbe, 0x02,
+ 0xe7, 0x57, 0x00, 0x00, 0xb0, 0x08, 0x97, 0xc1,
+ 0xe7, 0x07, 0x09, 0x00, 0x12, 0xc0, 0xe7, 0x77,
+ 0x00, 0x08, 0x20, 0xc0, 0x9f, 0xc1, 0xb6, 0x02,
+ 0xe7, 0x57, 0x09, 0x00, 0x12, 0xc0, 0x77, 0xc9,
+ 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0xe7, 0x77,
+ 0x00, 0x08, 0x20, 0xc0, 0x2f, 0xc1, 0xe7, 0x07,
+ 0x00, 0x00, 0x42, 0xc0, 0xe7, 0x07, 0x05, 0x00,
+ 0x90, 0xc0, 0xc8, 0x07, 0x0a, 0x00, 0xe7, 0x77,
+ 0x04, 0x00, 0x20, 0xc0, 0x09, 0xc1, 0x08, 0xda,
+ 0x7a, 0xc1, 0xe7, 0x07, 0x00, 0x01, 0x42, 0xc0,
+ 0xe7, 0x07, 0x04, 0x00, 0x90, 0xc0, 0x1a, 0xcf,
+ 0xe7, 0x07, 0x01, 0x00, 0x7a, 0x08, 0x00, 0xd8,
+ 0x27, 0x50, 0x34, 0x01, 0x17, 0xc1, 0xe7, 0x77,
+ 0x02, 0x00, 0x20, 0xc0, 0x79, 0xc1, 0x27, 0x50,
+ 0x34, 0x01, 0x10, 0xc1, 0xe7, 0x77, 0x02, 0x00,
+ 0x20, 0xc0, 0x79, 0xc0, 0x9f, 0xaf, 0xd8, 0x02,
+ 0xe7, 0x05, 0x00, 0xc0, 0x00, 0x60, 0x9f, 0xc0,
+ 0xde, 0x01, 0x97, 0xcf, 0xe7, 0x07, 0x01, 0x00,
+ 0xb8, 0x08, 0x06, 0xcf, 0xe7, 0x07, 0x30, 0x0e,
+ 0x02, 0x00, 0xe7, 0x07, 0x50, 0xc3, 0x12, 0xc0,
+ 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, 0xe7, 0x07,
+ 0x01, 0x00, 0xb8, 0x08, 0x97, 0xcf, 0xe7, 0x07,
+ 0x50, 0xc3, 0x12, 0xc0, 0xe7, 0x07, 0x30, 0x0e,
+ 0x02, 0x00, 0xe7, 0x07, 0x01, 0x00, 0x7a, 0x08,
+ 0xe7, 0x07, 0x05, 0x00, 0x90, 0xc0, 0x97, 0xcf,
+ 0xe7, 0x07, 0x00, 0x01, 0x42, 0xc0, 0xe7, 0x07,
+ 0x04, 0x00, 0x90, 0xc0, 0xe7, 0x07, 0x00, 0x00,
+ 0x7a, 0x08, 0xe7, 0x57, 0x0f, 0x00, 0xb2, 0x08,
+ 0x13, 0xc1, 0x9f, 0xaf, 0x2e, 0x08, 0xca, 0x09,
+ 0xac, 0x08, 0xf2, 0x17, 0x01, 0x00, 0x5c, 0x00,
+ 0xf2, 0x27, 0x00, 0x00, 0x5e, 0x00, 0xe7, 0x07,
+ 0x00, 0x00, 0xb2, 0x08, 0xe7, 0x07, 0x01, 0x00,
+ 0xb4, 0x08, 0xc0, 0x07, 0xff, 0xff, 0x97, 0xcf,
+ 0x9f, 0xaf, 0x4c, 0x03, 0xc0, 0x69, 0xb4, 0x08,
+ 0x57, 0x00, 0x9f, 0xde, 0x33, 0x00, 0xc1, 0x05,
+ 0x27, 0xd8, 0xb2, 0x08, 0x27, 0xd2, 0xb4, 0x08,
+ 0xe7, 0x87, 0x01, 0x00, 0xb4, 0x08, 0xe7, 0x67,
+ 0xff, 0x03, 0xb4, 0x08, 0x00, 0x60, 0x97, 0xc0,
+ 0xe7, 0x07, 0x01, 0x00, 0xb0, 0x08, 0x27, 0x00,
+ 0x12, 0xc0, 0x97, 0xcf, 0xc0, 0x09, 0xb6, 0x08,
+ 0x00, 0xd2, 0x02, 0xc3, 0xc0, 0x97, 0x05, 0x80,
+ 0x27, 0x00, 0xb6, 0x08, 0xc0, 0x99, 0x82, 0x08,
+ 0xc0, 0x99, 0xa2, 0xc0, 0x97, 0xcf, 0xe7, 0x07,
+ 0x00, 0x00, 0xb0, 0x08, 0xc0, 0xdf, 0x97, 0xcf,
+ 0xc8, 0x09, 0x72, 0x08, 0x08, 0x62, 0x02, 0xc0,
+ 0x10, 0x64, 0x07, 0xc1, 0xe7, 0x07, 0x00, 0x00,
+ 0x64, 0x08, 0xe7, 0x07, 0xc8, 0x05, 0x24, 0x00,
+ 0x97, 0xcf, 0x27, 0x04, 0x72, 0x08, 0xc8, 0x17,
+ 0x0e, 0x00, 0x27, 0x02, 0x64, 0x08, 0xe7, 0x07,
+ 0xd6, 0x05, 0x24, 0x00, 0x97, 0xcf, 0xd7, 0x09,
+ 0x00, 0xc0, 0xc1, 0xdf, 0xe7, 0x57, 0x00, 0x00,
+ 0x62, 0x08, 0x13, 0xc1, 0x9f, 0xaf, 0x70, 0x03,
+ 0xe7, 0x57, 0x00, 0x00, 0x64, 0x08, 0x13, 0xc0,
+ 0xe7, 0x09, 0x64, 0x08, 0x30, 0x01, 0xe7, 0x07,
+ 0xf2, 0x05, 0x32, 0x01, 0xe7, 0x07, 0x10, 0x00,
+ 0x96, 0xc0, 0xe7, 0x09, 0x64, 0x08, 0x62, 0x08,
+ 0x04, 0xcf, 0xe7, 0x57, 0x00, 0x00, 0x64, 0x08,
+ 0x02, 0xc1, 0x9f, 0xaf, 0x70, 0x03, 0xe7, 0x05,
+ 0x00, 0xc0, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0,
+ 0xc1, 0xdf, 0xc8, 0x09, 0x72, 0x08, 0x27, 0x02,
+ 0x78, 0x08, 0x08, 0x62, 0x03, 0xc1, 0xe7, 0x05,
+ 0x00, 0xc0, 0x97, 0xcf, 0x27, 0x04, 0x72, 0x08,
+ 0xe7, 0x05, 0x00, 0xc0, 0xf0, 0x07, 0x40, 0x00,
+ 0x08, 0x00, 0xf0, 0x07, 0x00, 0x00, 0x04, 0x00,
+ 0x00, 0x02, 0xc0, 0x17, 0x0c, 0x00, 0x30, 0x00,
+ 0x06, 0x00, 0xf0, 0x07, 0x64, 0x01, 0x0a, 0x00,
+ 0xc8, 0x17, 0x04, 0x00, 0xc1, 0x07, 0x02, 0x00,
+ 0x51, 0xaf, 0x97, 0xcf, 0xe7, 0x57, 0x00, 0x00,
+ 0x6a, 0x08, 0x97, 0xc0, 0xc1, 0xdf, 0xc8, 0x09,
+ 0x6a, 0x08, 0x27, 0x04, 0x6a, 0x08, 0x27, 0x52,
+ 0x6c, 0x08, 0x03, 0xc1, 0xe7, 0x07, 0x6a, 0x08,
+ 0x6c, 0x08, 0xc0, 0xdf, 0x17, 0x02, 0xc8, 0x17,
+ 0x0e, 0x00, 0x9f, 0xaf, 0x16, 0x05, 0xc8, 0x05,
+ 0x00, 0x60, 0x03, 0xc0, 0x9f, 0xaf, 0x80, 0x04,
+ 0x97, 0xcf, 0x9f, 0xaf, 0x68, 0x04, 0x97, 0xcf,
+ 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x08, 0x62,
+ 0x1c, 0xc0, 0xd0, 0x09, 0x72, 0x08, 0x27, 0x02,
+ 0x72, 0x08, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf,
+ 0x97, 0x02, 0xca, 0x09, 0xac, 0x08, 0xf2, 0x17,
+ 0x01, 0x00, 0x04, 0x00, 0xf2, 0x27, 0x00, 0x00,
+ 0x06, 0x00, 0xca, 0x17, 0x2c, 0x00, 0xf8, 0x77,
+ 0x01, 0x00, 0x0e, 0x00, 0x06, 0xc0, 0xca, 0xd9,
+ 0xf8, 0x57, 0xff, 0x00, 0x0e, 0x00, 0x01, 0xc1,
+ 0xca, 0xd9, 0x22, 0x1c, 0x0c, 0x00, 0xe2, 0x27,
+ 0x00, 0x00, 0xe2, 0x17, 0x01, 0x00, 0xe2, 0x27,
+ 0x00, 0x00, 0xca, 0x05, 0x00, 0x0c, 0x0c, 0x00,
+ 0xc0, 0x17, 0x41, 0x00, 0xc0, 0x67, 0xc0, 0xff,
+ 0x30, 0x00, 0x08, 0x00, 0x00, 0x02, 0xc0, 0x17,
+ 0x0c, 0x00, 0x30, 0x00, 0x06, 0x00, 0xf0, 0x07,
+ 0xdc, 0x00, 0x0a, 0x00, 0xf0, 0x07, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x0c, 0x08, 0x00, 0x40, 0xd1,
+ 0x01, 0x00, 0xc0, 0x19, 0xa6, 0x08, 0xc0, 0x59,
+ 0x98, 0x08, 0x04, 0xc9, 0x49, 0xaf, 0x9f, 0xaf,
+ 0xee, 0x00, 0x4a, 0xaf, 0x67, 0x10, 0xa6, 0x08,
+ 0xc8, 0x17, 0x04, 0x00, 0xc1, 0x07, 0x01, 0x00,
+ 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x50, 0xaf,
+ 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, 0xc0, 0x07,
+ 0x01, 0x00, 0xc1, 0x09, 0x7c, 0x08, 0xc1, 0x77,
+ 0x01, 0x00, 0x97, 0xc1, 0xd8, 0x77, 0x01, 0x00,
+ 0x12, 0xc0, 0xc9, 0x07, 0x4c, 0x08, 0x9f, 0xaf,
+ 0x64, 0x05, 0x04, 0xc1, 0xc1, 0x77, 0x08, 0x00,
+ 0x13, 0xc0, 0x97, 0xcf, 0xc1, 0x77, 0x02, 0x00,
+ 0x97, 0xc1, 0xc1, 0x77, 0x10, 0x00, 0x0c, 0xc0,
+ 0x9f, 0xaf, 0x86, 0x05, 0x97, 0xcf, 0xc1, 0x77,
+ 0x04, 0x00, 0x06, 0xc0, 0xc9, 0x07, 0x7e, 0x08,
+ 0x9f, 0xaf, 0x64, 0x05, 0x97, 0xc0, 0x00, 0xcf,
+ 0x00, 0x90, 0x97, 0xcf, 0x50, 0x54, 0x97, 0xc1,
+ 0x70, 0x5c, 0x02, 0x00, 0x02, 0x00, 0x97, 0xc1,
+ 0x70, 0x5c, 0x04, 0x00, 0x04, 0x00, 0x97, 0xcf,
+ 0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x18, 0x00,
+ 0x0c, 0x00, 0x06, 0x00, 0x00, 0x00, 0xcb, 0x09,
+ 0x88, 0x08, 0xcc, 0x09, 0x8a, 0x08, 0x0b, 0x53,
+ 0x11, 0xc0, 0xc9, 0x02, 0xca, 0x07, 0x78, 0x05,
+ 0x9f, 0xaf, 0x64, 0x05, 0x97, 0xc0, 0x0a, 0xc8,
+ 0x82, 0x08, 0x0a, 0xcf, 0x82, 0x08, 0x9f, 0xaf,
+ 0x64, 0x05, 0x97, 0xc0, 0x05, 0xc2, 0x89, 0x30,
+ 0x82, 0x60, 0x78, 0xc1, 0x00, 0x90, 0x97, 0xcf,
+ 0x89, 0x10, 0x09, 0x53, 0x79, 0xc2, 0x89, 0x30,
+ 0x82, 0x08, 0x7a, 0xcf, 0xc0, 0xdf, 0x97, 0xcf,
+ 0xe7, 0x09, 0x96, 0xc0, 0x66, 0x08, 0xe7, 0x09,
+ 0x98, 0xc0, 0x68, 0x08, 0x0f, 0xcf, 0xe7, 0x09,
+ 0x96, 0xc0, 0x66, 0x08, 0xe7, 0x09, 0x98, 0xc0,
+ 0x68, 0x08, 0xe7, 0x09, 0x64, 0x08, 0x30, 0x01,
+ 0xe7, 0x07, 0xf2, 0x05, 0x32, 0x01, 0xe7, 0x07,
+ 0x10, 0x00, 0x96, 0xc0, 0xd7, 0x09, 0x00, 0xc0,
+ 0x17, 0x02, 0xc8, 0x09, 0x62, 0x08, 0xc8, 0x37,
+ 0x0e, 0x00, 0xe7, 0x57, 0x04, 0x00, 0x68, 0x08,
+ 0x3d, 0xc0, 0xe7, 0x87, 0x00, 0x08, 0x24, 0xc0,
+ 0xe7, 0x09, 0x94, 0x08, 0xba, 0x08, 0xe7, 0x17,
+ 0x64, 0x00, 0xba, 0x08, 0xe7, 0x67, 0xff, 0x07,
+ 0xba, 0x08, 0xe7, 0x77, 0x2a, 0x00, 0x66, 0x08,
+ 0x30, 0xc0, 0x97, 0x02, 0xca, 0x09, 0xac, 0x08,
+ 0xe7, 0x77, 0x20, 0x00, 0x66, 0x08, 0x0e, 0xc0,
+ 0xf2, 0x17, 0x01, 0x00, 0x10, 0x00, 0xf2, 0x27,
+ 0x00, 0x00, 0x12, 0x00, 0xe7, 0x77, 0x0a, 0x00,
+ 0x66, 0x08, 0xca, 0x05, 0x1e, 0xc0, 0x97, 0x02,
+ 0xca, 0x09, 0xac, 0x08, 0xf2, 0x17, 0x01, 0x00,
+ 0x0c, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x0e, 0x00,
+ 0xe7, 0x77, 0x02, 0x00, 0x66, 0x08, 0x07, 0xc0,
+ 0xf2, 0x17, 0x01, 0x00, 0x44, 0x00, 0xf2, 0x27,
+ 0x00, 0x00, 0x46, 0x00, 0x06, 0xcf, 0xf2, 0x17,
+ 0x01, 0x00, 0x60, 0x00, 0xf2, 0x27, 0x00, 0x00,
+ 0x62, 0x00, 0xca, 0x05, 0x9f, 0xaf, 0x68, 0x04,
+ 0x0f, 0xcf, 0x57, 0x02, 0x09, 0x02, 0xf1, 0x09,
+ 0x68, 0x08, 0x0c, 0x00, 0xf1, 0xda, 0x0c, 0x00,
+ 0xc8, 0x09, 0x6c, 0x08, 0x50, 0x02, 0x67, 0x02,
+ 0x6c, 0x08, 0xd1, 0x07, 0x00, 0x00, 0xc9, 0x05,
+ 0xe7, 0x09, 0x64, 0x08, 0x62, 0x08, 0xe7, 0x57,
+ 0x00, 0x00, 0x62, 0x08, 0x02, 0xc0, 0x9f, 0xaf,
+ 0x70, 0x03, 0xc8, 0x05, 0xe7, 0x05, 0x00, 0xc0,
+ 0xc0, 0xdf, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0,
+ 0x17, 0x00, 0x17, 0x02, 0x97, 0x02, 0xc0, 0x09,
+ 0x92, 0xc0, 0xe7, 0x87, 0x00, 0x08, 0x24, 0xc0,
+ 0xe7, 0x09, 0x94, 0x08, 0xba, 0x08, 0xe7, 0x17,
+ 0x64, 0x00, 0xba, 0x08, 0xe7, 0x67, 0xff, 0x07,
+ 0xba, 0x08, 0xe7, 0x07, 0x04, 0x00, 0x90, 0xc0,
+ 0xca, 0x09, 0xac, 0x08, 0xe7, 0x07, 0x00, 0x00,
+ 0x7a, 0x08, 0xe7, 0x07, 0x66, 0x03, 0x02, 0x00,
+ 0xc0, 0x77, 0x02, 0x00, 0x10, 0xc0, 0xef, 0x57,
+ 0x00, 0x00, 0xf0, 0x09, 0x04, 0xc0, 0x9f, 0xaf,
+ 0xd8, 0x02, 0x9f, 0xcf, 0x12, 0x08, 0xf2, 0x17,
+ 0x01, 0x00, 0x50, 0x00, 0xf2, 0x27, 0x00, 0x00,
+ 0x52, 0x00, 0x9f, 0xcf, 0x12, 0x08, 0xef, 0x57,
+ 0x00, 0x00, 0xf0, 0x09, 0x08, 0xc0, 0xe7, 0x57,
+ 0x00, 0x00, 0xb8, 0x08, 0xe7, 0x07, 0x00, 0x00,
+ 0xb8, 0x08, 0x0a, 0xc0, 0x03, 0xcf, 0xc0, 0x77,
+ 0x10, 0x00, 0x06, 0xc0, 0xf2, 0x17, 0x01, 0x00,
+ 0x58, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x5a, 0x00,
+ 0xc0, 0x77, 0x80, 0x00, 0x06, 0xc0, 0xf2, 0x17,
+ 0x01, 0x00, 0x70, 0x00, 0xf2, 0x27, 0x00, 0x00,
+ 0x72, 0x00, 0xc0, 0x77, 0x08, 0x00, 0x1d, 0xc1,
+ 0xf2, 0x17, 0x01, 0x00, 0x08, 0x00, 0xf2, 0x27,
+ 0x00, 0x00, 0x0a, 0x00, 0xc0, 0x77, 0x00, 0x02,
+ 0x06, 0xc0, 0xf2, 0x17, 0x01, 0x00, 0x64, 0x00,
+ 0xf2, 0x27, 0x00, 0x00, 0x66, 0x00, 0xc0, 0x77,
+ 0x40, 0x00, 0x06, 0xc0, 0xf2, 0x17, 0x01, 0x00,
+ 0x5c, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x5e, 0x00,
+ 0xc0, 0x77, 0x01, 0x00, 0x01, 0xc0, 0x37, 0xcf,
+ 0x36, 0xcf, 0xf2, 0x17, 0x01, 0x00, 0x00, 0x00,
+ 0xf2, 0x27, 0x00, 0x00, 0x02, 0x00, 0xef, 0x57,
+ 0x00, 0x00, 0xf0, 0x09, 0x18, 0xc0, 0xe7, 0x57,
+ 0x01, 0x00, 0xb2, 0x08, 0x0e, 0xc2, 0x07, 0xc8,
+ 0xf2, 0x17, 0x01, 0x00, 0x50, 0x00, 0xf2, 0x27,
+ 0x00, 0x00, 0x52, 0x00, 0x06, 0xcf, 0xf2, 0x17,
+ 0x01, 0x00, 0x54, 0x00, 0xf2, 0x27, 0x00, 0x00,
+ 0x56, 0x00, 0xe7, 0x07, 0x00, 0x00, 0xb2, 0x08,
+ 0xe7, 0x07, 0x01, 0x00, 0xb4, 0x08, 0xc8, 0x09,
+ 0x34, 0x01, 0xca, 0x17, 0x14, 0x00, 0xd8, 0x77,
+ 0x01, 0x00, 0x05, 0xc0, 0xca, 0xd9, 0xd8, 0x57,
+ 0xff, 0x00, 0x01, 0xc0, 0xca, 0xd9, 0xe2, 0x19,
+ 0x94, 0xc0, 0xe2, 0x27, 0x00, 0x00, 0xe2, 0x17,
+ 0x01, 0x00, 0xe2, 0x27, 0x00, 0x00, 0x9f, 0xaf,
+ 0x2e, 0x08, 0x9f, 0xaf, 0xde, 0x01, 0xe7, 0x57,
+ 0x00, 0x00, 0xaa, 0x08, 0x9f, 0xa1, 0xf0, 0x0b,
+ 0xca, 0x05, 0xc8, 0x05, 0xc0, 0x05, 0xe7, 0x05,
+ 0x00, 0xc0, 0xc0, 0xdf, 0x97, 0xcf, 0xc8, 0x09,
+ 0x6e, 0x08, 0x08, 0x62, 0x97, 0xc0, 0x27, 0x04,
+ 0x6e, 0x08, 0x27, 0x52, 0x70, 0x08, 0x03, 0xc1,
+ 0xe7, 0x07, 0x6e, 0x08, 0x70, 0x08, 0x9f, 0xaf,
+ 0x68, 0x04, 0x97, 0xcf, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x33, 0xcc,
+ 0x00, 0x00, 0x00, 0x00, 0xe7, 0x57, 0x00, 0x80,
+ 0xb2, 0x00, 0x06, 0xc2, 0xe7, 0x07, 0x52, 0x0e,
+ 0x12, 0x00, 0xe7, 0x07, 0x98, 0x0e, 0xb2, 0x00,
+ 0xe7, 0x07, 0xa4, 0x09, 0xf2, 0x02, 0xc8, 0x09,
+ 0xb4, 0x00, 0xf8, 0x07, 0x02, 0x00, 0x0d, 0x00,
+ 0xd7, 0x09, 0x0e, 0xc0, 0xe7, 0x07, 0x00, 0x00,
+ 0x0e, 0xc0, 0xc8, 0x09, 0xdc, 0x00, 0xf0, 0x07,
+ 0xff, 0xff, 0x09, 0x00, 0xf0, 0x07, 0xfb, 0x13,
+ 0x0b, 0x00, 0xe7, 0x09, 0xc0, 0x00, 0x58, 0x08,
+ 0xe7, 0x09, 0xbe, 0x00, 0x54, 0x08, 0xe7, 0x09,
+ 0x10, 0x00, 0x92, 0x08, 0xc8, 0x07, 0xb4, 0x09,
+ 0x9f, 0xaf, 0x8c, 0x09, 0x9f, 0xaf, 0xe2, 0x0b,
+ 0xc0, 0x07, 0x80, 0x01, 0x44, 0xaf, 0x27, 0x00,
+ 0x88, 0x08, 0x27, 0x00, 0x8a, 0x08, 0x27, 0x00,
+ 0x8c, 0x08, 0xc0, 0x07, 0x74, 0x00, 0x44, 0xaf,
+ 0x27, 0x00, 0xac, 0x08, 0x08, 0x00, 0x00, 0x90,
+ 0xc1, 0x07, 0x1d, 0x00, 0x20, 0x00, 0x20, 0x00,
+ 0x01, 0xda, 0x7c, 0xc1, 0x9f, 0xaf, 0x8a, 0x0b,
+ 0xc0, 0x07, 0x4c, 0x00, 0x48, 0xaf, 0x27, 0x00,
+ 0x56, 0x08, 0x9f, 0xaf, 0x72, 0x0c, 0xe7, 0x07,
+ 0x00, 0x80, 0x96, 0x08, 0xef, 0x57, 0x00, 0x00,
+ 0xf0, 0x09, 0x03, 0xc0, 0xe7, 0x07, 0x01, 0x00,
+ 0x1c, 0xc0, 0xe7, 0x05, 0x0e, 0xc0, 0x97, 0xcf,
+ 0x49, 0xaf, 0xe7, 0x87, 0x43, 0x00, 0x0e, 0xc0,
+ 0xe7, 0x07, 0xff, 0xff, 0x94, 0x08, 0x9f, 0xaf,
+ 0x8a, 0x0c, 0xc0, 0x07, 0x01, 0x00, 0x60, 0xaf,
+ 0x4a, 0xaf, 0x97, 0xcf, 0x00, 0x08, 0x09, 0x08,
+ 0x11, 0x08, 0x00, 0xda, 0x7c, 0xc1, 0x97, 0xcf,
+ 0x67, 0x04, 0xcc, 0x02, 0xc0, 0xdf, 0x51, 0x94,
+ 0xb1, 0xaf, 0x06, 0x00, 0xc1, 0xdf, 0xc9, 0x09,
+ 0xcc, 0x02, 0x49, 0x62, 0x75, 0xc1, 0xc0, 0xdf,
+ 0xa7, 0xcf, 0xd6, 0x02, 0x0e, 0x00, 0x24, 0x00,
+ 0xd6, 0x05, 0x22, 0x00, 0xc4, 0x06, 0xd0, 0x00,
+ 0xf0, 0x0b, 0xaa, 0x00, 0x0e, 0x0a, 0xbe, 0x00,
+ 0x2c, 0x0c, 0x10, 0x00, 0x20, 0x00, 0x04, 0x00,
+ 0xc4, 0x05, 0x02, 0x00, 0x66, 0x03, 0x06, 0x00,
+ 0x00, 0x00, 0x24, 0xc0, 0x04, 0x04, 0x28, 0xc0,
+ 0xfe, 0xfb, 0x1e, 0xc0, 0x00, 0x04, 0x22, 0xc0,
+ 0xff, 0xf0, 0xc0, 0x00, 0x60, 0x0b, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0x34, 0x0a, 0x3e, 0x0a,
+ 0x9e, 0x0a, 0xa8, 0x0a, 0xce, 0x0a, 0xd2, 0x0a,
+ 0xd6, 0x0a, 0x00, 0x0b, 0x10, 0x0b, 0x1e, 0x0b,
+ 0x20, 0x0b, 0x28, 0x0b, 0x28, 0x0b, 0x27, 0x02,
+ 0xa2, 0x08, 0x97, 0xcf, 0xe7, 0x07, 0x00, 0x00,
+ 0xa2, 0x08, 0x0a, 0x0e, 0x01, 0x00, 0xca, 0x57,
+ 0x0e, 0x00, 0x9f, 0xc3, 0x2a, 0x0b, 0xca, 0x37,
+ 0x00, 0x00, 0x9f, 0xc2, 0x2a, 0x0b, 0x0a, 0xd2,
+ 0xb2, 0xcf, 0xf4, 0x09, 0xc8, 0x09, 0xde, 0x00,
+ 0x07, 0x06, 0x9f, 0xcf, 0x3c, 0x0b, 0xf0, 0x57,
+ 0x80, 0x01, 0x06, 0x00, 0x9f, 0xc8, 0x2a, 0x0b,
+ 0x27, 0x0c, 0x02, 0x00, 0x86, 0x08, 0xc0, 0x09,
+ 0x88, 0x08, 0x27, 0x00, 0x8a, 0x08, 0xe7, 0x07,
+ 0x00, 0x00, 0x84, 0x08, 0x27, 0x00, 0x5c, 0x08,
+ 0x00, 0x1c, 0x06, 0x00, 0x27, 0x00, 0x8c, 0x08,
+ 0x41, 0x90, 0x67, 0x50, 0x86, 0x08, 0x0d, 0xc0,
+ 0x67, 0x00, 0x5a, 0x08, 0x27, 0x0c, 0x06, 0x00,
+ 0x5e, 0x08, 0xe7, 0x07, 0x8a, 0x0a, 0x60, 0x08,
+ 0xc8, 0x07, 0x5a, 0x08, 0x41, 0x90, 0x51, 0xaf,
+ 0x97, 0xcf, 0x9f, 0xaf, 0xac, 0x0e, 0xe7, 0x09,
+ 0x8c, 0x08, 0x8a, 0x08, 0xe7, 0x09, 0x86, 0x08,
+ 0x84, 0x08, 0x59, 0xaf, 0x97, 0xcf, 0x27, 0x0c,
+ 0x02, 0x00, 0x7c, 0x08, 0x59, 0xaf, 0x97, 0xcf,
+ 0x09, 0x0c, 0x02, 0x00, 0x09, 0xda, 0x49, 0xd2,
+ 0xc9, 0x19, 0xac, 0x08, 0xc8, 0x07, 0x5a, 0x08,
+ 0xe0, 0x07, 0x00, 0x00, 0x60, 0x02, 0xe0, 0x07,
+ 0x04, 0x00, 0xd0, 0x07, 0x9a, 0x0a, 0x48, 0xdb,
+ 0x41, 0x90, 0x50, 0xaf, 0x97, 0xcf, 0x59, 0xaf,
+ 0x97, 0xcf, 0x59, 0xaf, 0x97, 0xcf, 0xf0, 0x57,
+ 0x06, 0x00, 0x06, 0x00, 0x26, 0xc1, 0xe7, 0x07,
+ 0x7e, 0x08, 0x5c, 0x08, 0x41, 0x90, 0x67, 0x00,
+ 0x5a, 0x08, 0x27, 0x0c, 0x06, 0x00, 0x5e, 0x08,
+ 0xe7, 0x07, 0x5c, 0x0b, 0x60, 0x08, 0xc8, 0x07,
+ 0x5a, 0x08, 0x41, 0x90, 0x51, 0xaf, 0x97, 0xcf,
+ 0x07, 0x0c, 0x06, 0x00, 0xc7, 0x57, 0x06, 0x00,
+ 0x10, 0xc1, 0xc8, 0x07, 0x7e, 0x08, 0x16, 0xcf,
+ 0x00, 0x0c, 0x02, 0x00, 0x00, 0xda, 0x40, 0xd1,
+ 0x27, 0x00, 0x98, 0x08, 0x1f, 0xcf, 0x1e, 0xcf,
+ 0x27, 0x0c, 0x02, 0x00, 0xa4, 0x08, 0x1a, 0xcf,
+ 0x00, 0xcf, 0x27, 0x02, 0x20, 0x01, 0xe7, 0x07,
+ 0x08, 0x00, 0x22, 0x01, 0xe7, 0x07, 0x13, 0x00,
+ 0xb0, 0xc0, 0x97, 0xcf, 0x41, 0x90, 0x67, 0x00,
+ 0x5a, 0x08, 0xe7, 0x01, 0x5e, 0x08, 0x27, 0x02,
+ 0x5c, 0x08, 0xe7, 0x07, 0x5c, 0x0b, 0x60, 0x08,
+ 0xc8, 0x07, 0x5a, 0x08, 0xc1, 0x07, 0x00, 0x80,
+ 0x50, 0xaf, 0x97, 0xcf, 0x59, 0xaf, 0x97, 0xcf,
+ 0x00, 0x60, 0x05, 0xc0, 0xe7, 0x07, 0x00, 0x00,
+ 0x9a, 0x08, 0xa7, 0xcf, 0x58, 0x08, 0x9f, 0xaf,
+ 0xe2, 0x0b, 0xe7, 0x07, 0x01, 0x00, 0x9a, 0x08,
+ 0x49, 0xaf, 0xd7, 0x09, 0x00, 0xc0, 0x07, 0xaf,
+ 0xe7, 0x05, 0x00, 0xc0, 0x4a, 0xaf, 0xa7, 0xcf,
+ 0x58, 0x08, 0xc0, 0x07, 0x40, 0x00, 0x44, 0xaf,
+ 0x27, 0x00, 0xa0, 0x08, 0x08, 0x00, 0xc0, 0x07,
+ 0x20, 0x00, 0x20, 0x94, 0x00, 0xda, 0x7d, 0xc1,
+ 0xc0, 0x07, 0xfe, 0x7f, 0x44, 0xaf, 0x40, 0x00,
+ 0x41, 0x90, 0xc0, 0x37, 0x08, 0x00, 0xdf, 0xde,
+ 0x50, 0x06, 0xc0, 0x57, 0x10, 0x00, 0x02, 0xc2,
+ 0xc0, 0x07, 0x10, 0x00, 0x27, 0x00, 0x76, 0x08,
+ 0x41, 0x90, 0x9f, 0xde, 0x40, 0x06, 0x44, 0xaf,
+ 0x27, 0x00, 0x74, 0x08, 0xc0, 0x09, 0x76, 0x08,
+ 0x41, 0x90, 0x00, 0xd2, 0x00, 0xd8, 0x9f, 0xde,
+ 0x08, 0x00, 0x44, 0xaf, 0x27, 0x00, 0x9e, 0x08,
+ 0x97, 0xcf, 0xe7, 0x87, 0x00, 0x84, 0x28, 0xc0,
+ 0xe7, 0x67, 0xff, 0xf3, 0x24, 0xc0, 0x97, 0xcf,
+ 0xe7, 0x87, 0x01, 0x00, 0xaa, 0x08, 0xe7, 0x57,
+ 0x00, 0x00, 0x7a, 0x08, 0x97, 0xc1, 0x9f, 0xaf,
+ 0xe2, 0x0b, 0xe7, 0x87, 0x00, 0x06, 0x22, 0xc0,
+ 0xe7, 0x07, 0x00, 0x00, 0x90, 0xc0, 0xe7, 0x67,
+ 0xfe, 0xff, 0x3e, 0xc0, 0xe7, 0x07, 0x2e, 0x00,
+ 0x0a, 0xc0, 0xe7, 0x87, 0x01, 0x00, 0x3e, 0xc0,
+ 0xe7, 0x07, 0xff, 0xff, 0x94, 0x08, 0x9f, 0xaf,
+ 0xf0, 0x0c, 0x97, 0xcf, 0x17, 0x00, 0xa7, 0xaf,
+ 0x54, 0x08, 0xc0, 0x05, 0x27, 0x00, 0x52, 0x08,
+ 0xe7, 0x87, 0x01, 0x00, 0xaa, 0x08, 0x9f, 0xaf,
+ 0xe2, 0x0b, 0xe7, 0x07, 0x0c, 0x00, 0x40, 0xc0,
+ 0x9f, 0xaf, 0xf0, 0x0c, 0xe7, 0x07, 0x00, 0x00,
+ 0x78, 0x08, 0x00, 0x90, 0xe7, 0x09, 0x88, 0x08,
+ 0x8a, 0x08, 0x27, 0x00, 0x84, 0x08, 0x27, 0x00,
+ 0x7c, 0x08, 0x9f, 0xaf, 0x8a, 0x0c, 0xe7, 0x07,
+ 0x00, 0x00, 0xb2, 0x02, 0xe7, 0x07, 0x00, 0x00,
+ 0xb4, 0x02, 0xc0, 0x07, 0x06, 0x00, 0xc8, 0x09,
+ 0xde, 0x00, 0xc8, 0x17, 0x03, 0x00, 0xc9, 0x07,
+ 0x7e, 0x08, 0x29, 0x0a, 0x00, 0xda, 0x7d, 0xc1,
+ 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf,
+ 0x00, 0x90, 0x27, 0x00, 0x6a, 0x08, 0xe7, 0x07,
+ 0x6a, 0x08, 0x6c, 0x08, 0x27, 0x00, 0x6e, 0x08,
+ 0xe7, 0x07, 0x6e, 0x08, 0x70, 0x08, 0x27, 0x00,
+ 0x78, 0x08, 0x27, 0x00, 0x62, 0x08, 0x27, 0x00,
+ 0x64, 0x08, 0xc8, 0x09, 0x74, 0x08, 0xc1, 0x09,
+ 0x76, 0x08, 0xc9, 0x07, 0x72, 0x08, 0x11, 0x02,
+ 0x09, 0x02, 0xc8, 0x17, 0x40, 0x06, 0x01, 0xda,
+ 0x7a, 0xc1, 0x51, 0x94, 0xc8, 0x09, 0x9e, 0x08,
+ 0xc9, 0x07, 0x9c, 0x08, 0xc1, 0x09, 0x76, 0x08,
+ 0x01, 0xd2, 0x01, 0xd8, 0x11, 0x02, 0x09, 0x02,
+ 0xc8, 0x17, 0x08, 0x00, 0x01, 0xda, 0x7a, 0xc1,
+ 0x51, 0x94, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf,
+ 0xe7, 0x57, 0x00, 0x00, 0x52, 0x08, 0x97, 0xc0,
+ 0x9f, 0xaf, 0x04, 0x00, 0xe7, 0x09, 0x94, 0x08,
+ 0x90, 0x08, 0xe7, 0x57, 0xff, 0xff, 0x90, 0x08,
+ 0x04, 0xc1, 0xe7, 0x07, 0xf0, 0x0c, 0x8e, 0x08,
+ 0x97, 0xcf, 0xe7, 0x17, 0x32, 0x00, 0x90, 0x08,
+ 0xe7, 0x67, 0xff, 0x07, 0x90, 0x08, 0xe7, 0x07,
+ 0x26, 0x0d, 0x8e, 0x08, 0x97, 0xcf, 0xd7, 0x09,
+ 0x00, 0xc0, 0xc1, 0xdf, 0xe7, 0x57, 0x00, 0x00,
+ 0x96, 0x08, 0x23, 0xc0, 0xe7, 0x07, 0x00, 0x80,
+ 0x80, 0xc0, 0xe7, 0x07, 0x04, 0x00, 0x90, 0xc0,
+ 0xe7, 0x07, 0x00, 0x00, 0x80, 0xc0, 0xe7, 0x07,
+ 0x00, 0x80, 0x80, 0xc0, 0xc0, 0x07, 0x00, 0x00,
+ 0xc0, 0x07, 0x00, 0x00, 0xc0, 0x07, 0x00, 0x00,
+ 0xe7, 0x07, 0x00, 0x00, 0x80, 0xc0, 0xe7, 0x07,
+ 0x00, 0x80, 0x80, 0xc0, 0xe7, 0x07, 0x00, 0x80,
+ 0x40, 0xc0, 0xc0, 0x07, 0x00, 0x00, 0xe7, 0x07,
+ 0x00, 0x00, 0x40, 0xc0, 0xe7, 0x07, 0x00, 0x00,
+ 0x80, 0xc0, 0xef, 0x57, 0x00, 0x00, 0xf1, 0x09,
+ 0x9f, 0xa0, 0xc0, 0x0d, 0xe7, 0x07, 0x04, 0x00,
+ 0x90, 0xc0, 0xe7, 0x07, 0x00, 0x02, 0x40, 0xc0,
+ 0xe7, 0x07, 0x0c, 0x02, 0x40, 0xc0, 0xe7, 0x07,
+ 0x00, 0x00, 0x96, 0x08, 0xe7, 0x07, 0x00, 0x00,
+ 0x8e, 0x08, 0xe7, 0x07, 0x00, 0x00, 0xaa, 0x08,
+ 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x9f, 0xaf,
+ 0x9e, 0x03, 0xe7, 0x05, 0x00, 0xc0, 0x9f, 0xaf,
+ 0xde, 0x01, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf,
+ 0x9f, 0xaf, 0xde, 0x0d, 0xef, 0x77, 0x00, 0x00,
+ 0xf1, 0x09, 0x97, 0xc1, 0x9f, 0xaf, 0xde, 0x0d,
+ 0xef, 0x77, 0x00, 0x00, 0xf1, 0x09, 0x97, 0xc1,
+ 0xef, 0x07, 0x01, 0x00, 0xf1, 0x09, 0xe7, 0x87,
+ 0x00, 0x08, 0x1e, 0xc0, 0xe7, 0x87, 0x00, 0x08,
+ 0x22, 0xc0, 0xe7, 0x67, 0xff, 0xf7, 0x22, 0xc0,
+ 0xe7, 0x77, 0x00, 0x08, 0x20, 0xc0, 0x11, 0xc0,
+ 0xe7, 0x67, 0xff, 0xf7, 0x1e, 0xc0, 0xe7, 0x87,
+ 0x00, 0x08, 0x22, 0xc0, 0xe7, 0x67, 0xff, 0xf7,
+ 0x22, 0xc0, 0xe7, 0x77, 0x00, 0x08, 0x20, 0xc0,
+ 0x04, 0xc1, 0xe7, 0x87, 0x00, 0x08, 0x22, 0xc0,
+ 0x97, 0xcf, 0xe7, 0x07, 0x01, 0x01, 0xf0, 0x09,
+ 0xef, 0x57, 0x18, 0x00, 0xfe, 0xff, 0x97, 0xc2,
+ 0xef, 0x07, 0x00, 0x00, 0xf0, 0x09, 0x97, 0xcf,
+ 0xd7, 0x09, 0x00, 0xc0, 0x17, 0x00, 0x17, 0x02,
+ 0x97, 0x02, 0xe7, 0x57, 0x00, 0x00, 0x7a, 0x08,
+ 0x06, 0xc0, 0xc0, 0x09, 0x92, 0xc0, 0xc0, 0x77,
+ 0x09, 0x02, 0x9f, 0xc1, 0xea, 0x06, 0x9f, 0xcf,
+ 0x20, 0x08, 0xd7, 0x09, 0x0e, 0xc0, 0xe7, 0x07,
+ 0x00, 0x00, 0x0e, 0xc0, 0x9f, 0xaf, 0x66, 0x0e,
+ 0xe7, 0x05, 0x0e, 0xc0, 0x97, 0xcf, 0xd7, 0x09,
+ 0x00, 0xc0, 0x17, 0x02, 0xc8, 0x09, 0xb0, 0xc0,
+ 0xe7, 0x67, 0xfe, 0x7f, 0xb0, 0xc0, 0xc8, 0x77,
+ 0x00, 0x20, 0x9f, 0xc1, 0x64, 0xeb, 0xe7, 0x57,
+ 0x00, 0x00, 0xc8, 0x02, 0x9f, 0xc1, 0x80, 0xeb,
+ 0xc8, 0x99, 0xca, 0x02, 0xc8, 0x67, 0x04, 0x00,
+ 0x9f, 0xc1, 0x96, 0xeb, 0x9f, 0xcf, 0x4c, 0xeb,
+ 0xe7, 0x07, 0x00, 0x00, 0xa6, 0xc0, 0xe7, 0x09,
+ 0xb0, 0xc0, 0xc8, 0x02, 0xe7, 0x07, 0x03, 0x00,
+ 0xb0, 0xc0, 0x97, 0xcf, 0xc0, 0x09, 0x86, 0x08,
+ 0xc0, 0x37, 0x01, 0x00, 0x97, 0xc9, 0xc9, 0x09,
+ 0x88, 0x08, 0x02, 0x00, 0x41, 0x90, 0x48, 0x02,
+ 0xc9, 0x17, 0x06, 0x00, 0x9f, 0xaf, 0x64, 0x05,
+ 0x9f, 0xa2, 0xd6, 0x0e, 0x02, 0xda, 0x77, 0xc1,
+ 0x41, 0x60, 0x71, 0xc1, 0x97, 0xcf, 0x17, 0x02,
+ 0x57, 0x02, 0x43, 0x04, 0x21, 0x04, 0xe0, 0x00,
+ 0x43, 0x04, 0x21, 0x04, 0xe0, 0x00, 0x43, 0x04,
+ 0x21, 0x04, 0xe0, 0x00, 0xc1, 0x07, 0x01, 0x00,
+ 0xc9, 0x05, 0xc8, 0x05, 0x97, 0xcf,
+ 0, 0
+};
+
+/* Firmware fixup (data?) segment */
+static unsigned char kue_fix_seg[] =
+{
+ /******************************************/
+ /* NOTE: B6/C3 is data header signature */
+ /* 0xAA/0xBB is data length = total */
+ /* bytes - 7, 0xCC is type, 0xDD is */
+ /* interrupt to use. */
+ /******************************************/
+ 0xB6, 0xC3, 0xc9, 0x02, 0x03, 0x64,
+ 0x02, 0x00, 0x08, 0x00, 0x24, 0x00, 0x2e, 0x00,
+ 0x2c, 0x00, 0x3e, 0x00, 0x44, 0x00, 0x48, 0x00,
+ 0x50, 0x00, 0x5c, 0x00, 0x60, 0x00, 0x66, 0x00,
+ 0x6c, 0x00, 0x70, 0x00, 0x76, 0x00, 0x74, 0x00,
+ 0x7a, 0x00, 0x7e, 0x00, 0x84, 0x00, 0x8a, 0x00,
+ 0x8e, 0x00, 0x92, 0x00, 0x98, 0x00, 0x9c, 0x00,
+ 0xa0, 0x00, 0xa8, 0x00, 0xae, 0x00, 0xb4, 0x00,
+ 0xb2, 0x00, 0xba, 0x00, 0xbe, 0x00, 0xc4, 0x00,
+ 0xc8, 0x00, 0xce, 0x00, 0xd2, 0x00, 0xd6, 0x00,
+ 0xda, 0x00, 0xe2, 0x00, 0xe0, 0x00, 0xea, 0x00,
+ 0xf2, 0x00, 0xfe, 0x00, 0x06, 0x01, 0x0c, 0x01,
+ 0x1a, 0x01, 0x24, 0x01, 0x22, 0x01, 0x2a, 0x01,
+ 0x30, 0x01, 0x36, 0x01, 0x3c, 0x01, 0x4e, 0x01,
+ 0x52, 0x01, 0x58, 0x01, 0x5c, 0x01, 0x9c, 0x01,
+ 0xb6, 0x01, 0xba, 0x01, 0xc0, 0x01, 0xca, 0x01,
+ 0xd0, 0x01, 0xda, 0x01, 0xe2, 0x01, 0xea, 0x01,
+ 0xf0, 0x01, 0x0a, 0x02, 0x0e, 0x02, 0x14, 0x02,
+ 0x26, 0x02, 0x6c, 0x02, 0x8e, 0x02, 0x98, 0x02,
+ 0xa0, 0x02, 0xa6, 0x02, 0xba, 0x02, 0xc6, 0x02,
+ 0xce, 0x02, 0xe8, 0x02, 0xee, 0x02, 0xf4, 0x02,
+ 0xf8, 0x02, 0x0a, 0x03, 0x10, 0x03, 0x1a, 0x03,
+ 0x1e, 0x03, 0x2a, 0x03, 0x2e, 0x03, 0x34, 0x03,
+ 0x3a, 0x03, 0x44, 0x03, 0x4e, 0x03, 0x5a, 0x03,
+ 0x5e, 0x03, 0x6a, 0x03, 0x72, 0x03, 0x80, 0x03,
+ 0x84, 0x03, 0x8c, 0x03, 0x94, 0x03, 0x98, 0x03,
+ 0xa8, 0x03, 0xae, 0x03, 0xb4, 0x03, 0xba, 0x03,
+ 0xce, 0x03, 0xcc, 0x03, 0xd6, 0x03, 0xdc, 0x03,
+ 0xec, 0x03, 0xf0, 0x03, 0xfe, 0x03, 0x1c, 0x04,
+ 0x30, 0x04, 0x38, 0x04, 0x3c, 0x04, 0x40, 0x04,
+ 0x48, 0x04, 0x46, 0x04, 0x54, 0x04, 0x5e, 0x04,
+ 0x64, 0x04, 0x74, 0x04, 0x78, 0x04, 0x84, 0x04,
+ 0xd8, 0x04, 0xec, 0x04, 0xf0, 0x04, 0xf8, 0x04,
+ 0xfe, 0x04, 0x1c, 0x05, 0x2c, 0x05, 0x30, 0x05,
+ 0x4a, 0x05, 0x56, 0x05, 0x5a, 0x05, 0x88, 0x05,
+ 0x8c, 0x05, 0x96, 0x05, 0x9a, 0x05, 0xa8, 0x05,
+ 0xcc, 0x05, 0xd2, 0x05, 0xda, 0x05, 0xe0, 0x05,
+ 0xe4, 0x05, 0xfc, 0x05, 0x06, 0x06, 0x14, 0x06,
+ 0x12, 0x06, 0x1a, 0x06, 0x20, 0x06, 0x26, 0x06,
+ 0x2e, 0x06, 0x34, 0x06, 0x48, 0x06, 0x52, 0x06,
+ 0x64, 0x06, 0x86, 0x06, 0x90, 0x06, 0x9a, 0x06,
+ 0xa0, 0x06, 0xac, 0x06, 0xaa, 0x06, 0xb2, 0x06,
+ 0xb8, 0x06, 0xdc, 0x06, 0xda, 0x06, 0xe2, 0x06,
+ 0xe8, 0x06, 0xf2, 0x06, 0xf8, 0x06, 0xfc, 0x06,
+ 0x0a, 0x07, 0x10, 0x07, 0x14, 0x07, 0x24, 0x07,
+ 0x2a, 0x07, 0x32, 0x07, 0x38, 0x07, 0xb2, 0x07,
+ 0xba, 0x07, 0xde, 0x07, 0xe4, 0x07, 0x10, 0x08,
+ 0x14, 0x08, 0x1a, 0x08, 0x1e, 0x08, 0x30, 0x08,
+ 0x38, 0x08, 0x3c, 0x08, 0x44, 0x08, 0x42, 0x08,
+ 0x48, 0x08, 0xc6, 0x08, 0xcc, 0x08, 0xd2, 0x08,
+ 0xfe, 0x08, 0x04, 0x09, 0x0a, 0x09, 0x0e, 0x09,
+ 0x12, 0x09, 0x16, 0x09, 0x20, 0x09, 0x24, 0x09,
+ 0x28, 0x09, 0x32, 0x09, 0x46, 0x09, 0x4a, 0x09,
+ 0x50, 0x09, 0x54, 0x09, 0x5a, 0x09, 0x60, 0x09,
+ 0x7c, 0x09, 0x80, 0x09, 0xb8, 0x09, 0xbc, 0x09,
+ 0xc0, 0x09, 0xc4, 0x09, 0xc8, 0x09, 0xcc, 0x09,
+ 0xd0, 0x09, 0xd4, 0x09, 0xec, 0x09, 0xf4, 0x09,
+ 0xf6, 0x09, 0xf8, 0x09, 0xfa, 0x09, 0xfc, 0x09,
+ 0xfe, 0x09, 0x00, 0x0a, 0x02, 0x0a, 0x04, 0x0a,
+ 0x06, 0x0a, 0x08, 0x0a, 0x0a, 0x0a, 0x0c, 0x0a,
+ 0x10, 0x0a, 0x18, 0x0a, 0x24, 0x0a, 0x2c, 0x0a,
+ 0x32, 0x0a, 0x3c, 0x0a, 0x46, 0x0a, 0x4c, 0x0a,
+ 0x50, 0x0a, 0x54, 0x0a, 0x5a, 0x0a, 0x5e, 0x0a,
+ 0x66, 0x0a, 0x6c, 0x0a, 0x72, 0x0a, 0x78, 0x0a,
+ 0x7e, 0x0a, 0x7c, 0x0a, 0x82, 0x0a, 0x8c, 0x0a,
+ 0x92, 0x0a, 0x90, 0x0a, 0x98, 0x0a, 0x96, 0x0a,
+ 0xa2, 0x0a, 0xb2, 0x0a, 0xb6, 0x0a, 0xc4, 0x0a,
+ 0xe2, 0x0a, 0xe0, 0x0a, 0xe8, 0x0a, 0xee, 0x0a,
+ 0xf4, 0x0a, 0xf2, 0x0a, 0xf8, 0x0a, 0x0c, 0x0b,
+ 0x1a, 0x0b, 0x24, 0x0b, 0x40, 0x0b, 0x44, 0x0b,
+ 0x48, 0x0b, 0x4e, 0x0b, 0x4c, 0x0b, 0x52, 0x0b,
+ 0x68, 0x0b, 0x6c, 0x0b, 0x70, 0x0b, 0x76, 0x0b,
+ 0x88, 0x0b, 0x92, 0x0b, 0xbe, 0x0b, 0xca, 0x0b,
+ 0xce, 0x0b, 0xde, 0x0b, 0xf4, 0x0b, 0xfa, 0x0b,
+ 0x00, 0x0c, 0x24, 0x0c, 0x28, 0x0c, 0x30, 0x0c,
+ 0x36, 0x0c, 0x3c, 0x0c, 0x40, 0x0c, 0x4a, 0x0c,
+ 0x50, 0x0c, 0x58, 0x0c, 0x56, 0x0c, 0x5c, 0x0c,
+ 0x60, 0x0c, 0x64, 0x0c, 0x80, 0x0c, 0x94, 0x0c,
+ 0x9a, 0x0c, 0x98, 0x0c, 0x9e, 0x0c, 0xa4, 0x0c,
+ 0xa2, 0x0c, 0xa8, 0x0c, 0xac, 0x0c, 0xb0, 0x0c,
+ 0xb4, 0x0c, 0xb8, 0x0c, 0xbc, 0x0c, 0xce, 0x0c,
+ 0xd2, 0x0c, 0xd6, 0x0c, 0xf4, 0x0c, 0xfa, 0x0c,
+ 0x00, 0x0d, 0xfe, 0x0c, 0x06, 0x0d, 0x0e, 0x0d,
+ 0x0c, 0x0d, 0x16, 0x0d, 0x1c, 0x0d, 0x22, 0x0d,
+ 0x20, 0x0d, 0x30, 0x0d, 0x7e, 0x0d, 0x82, 0x0d,
+ 0x9a, 0x0d, 0xa0, 0x0d, 0xa6, 0x0d, 0xb0, 0x0d,
+ 0xb8, 0x0d, 0xc2, 0x0d, 0xc8, 0x0d, 0xce, 0x0d,
+ 0xd4, 0x0d, 0xdc, 0x0d, 0x1e, 0x0e, 0x2c, 0x0e,
+ 0x3e, 0x0e, 0x4c, 0x0e, 0x50, 0x0e, 0x5e, 0x0e,
+ 0xae, 0x0e, 0xb8, 0x0e, 0xc6, 0x0e, 0xca, 0x0e,
+ 0, 0
+};
+
+/* Fixup command. */
+#define KUE_TRIGCMD_OFFSET 5
+static unsigned char kue_trig_seg[] = {
+ 0xb6, 0xc3, 0x01, 0x00, 0x06, 0x64, 0x00, 0x00
+};
diff --git a/sys/dev/usb/net/if_kuereg.h b/sys/dev/usb/net/if_kuereg.h
new file mode 100644
index 0000000..8650687
--- /dev/null
+++ b/sys/dev/usb/net/if_kuereg.h
@@ -0,0 +1,141 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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$
+ */
+
+/*
+ * Definitions for the KLSI KL5KUSB101B USB to ethernet controller.
+ * The KLSI part is controlled via vendor control requests, the structure
+ * of which depend a bit on the firmware running on the internal
+ * microcontroller. The one exception is the 'send scan data' command,
+ * which is used to load the firmware.
+ */
+
+#define KUE_CMD_GET_ETHER_DESCRIPTOR 0x00
+#define KUE_CMD_SET_MCAST_FILTERS 0x01
+#define KUE_CMD_SET_PKT_FILTER 0x02
+#define KUE_CMD_GET_ETHERSTATS 0x03
+#define KUE_CMD_GET_GPIO 0x04
+#define KUE_CMD_SET_GPIO 0x05
+#define KUE_CMD_SET_MAC 0x06
+#define KUE_CMD_GET_MAC 0x07
+#define KUE_CMD_SET_URB_SIZE 0x08
+#define KUE_CMD_SET_SOFS 0x09
+#define KUE_CMD_SET_EVEN_PKTS 0x0A
+#define KUE_CMD_SEND_SCAN 0xFF
+
+struct kue_ether_desc {
+ uint8_t kue_len;
+ uint8_t kue_rsvd0;
+ uint8_t kue_rsvd1;
+ uint8_t kue_macaddr[ETHER_ADDR_LEN];
+ uint8_t kue_etherstats[4];
+ uint8_t kue_maxseg[2];
+ uint8_t kue_mcastfilt[2];
+ uint8_t kue_rsvd2;
+} __packed;
+
+#define KUE_ETHERSTATS(x) UGETDW((x)->sc_desc.kue_etherstats)
+#define KUE_MAXSEG(x) UGETW((x)->sc_desc.kue_maxseg)
+#define KUE_MCFILTCNT(x) (UGETW((x)->sc_desc.kue_mcastfilt) & 0x7FFF)
+#define KUE_MCFILT(x, y) \
+ (char *)&(sc->sc_mcfilters[y * ETHER_ADDR_LEN])
+
+#define KUE_STAT_TX_OK 0x00000001
+#define KUE_STAT_RX_OK 0x00000002
+#define KUE_STAT_TX_ERR 0x00000004
+#define KUE_STAT_RX_ERR 0x00000008
+#define KUE_STAT_RX_NOBUF 0x00000010
+#define KUE_STAT_TX_UCAST_BYTES 0x00000020
+#define KUE_STAT_TX_UCAST_FRAMES 0x00000040
+#define KUE_STAT_TX_MCAST_BYTES 0x00000080
+#define KUE_STAT_TX_MCAST_FRAMES 0x00000100
+#define KUE_STAT_TX_BCAST_BYTES 0x00000200
+#define KUE_STAT_TX_BCAST_FRAMES 0x00000400
+#define KUE_STAT_RX_UCAST_BYTES 0x00000800
+#define KUE_STAT_RX_UCAST_FRAMES 0x00001000
+#define KUE_STAT_RX_MCAST_BYTES 0x00002000
+#define KUE_STAT_RX_MCAST_FRAMES 0x00004000
+#define KUE_STAT_RX_BCAST_BYTES 0x00008000
+#define KUE_STAT_RX_BCAST_FRAMES 0x00010000
+#define KUE_STAT_RX_CRCERR 0x00020000
+#define KUE_STAT_TX_QUEUE_LENGTH 0x00040000
+#define KUE_STAT_RX_ALIGNERR 0x00080000
+#define KUE_STAT_TX_SINGLECOLL 0x00100000
+#define KUE_STAT_TX_MULTICOLL 0x00200000
+#define KUE_STAT_TX_DEFERRED 0x00400000
+#define KUE_STAT_TX_MAXCOLLS 0x00800000
+#define KUE_STAT_RX_OVERRUN 0x01000000
+#define KUE_STAT_TX_UNDERRUN 0x02000000
+#define KUE_STAT_TX_SQE_ERR 0x04000000
+#define KUE_STAT_TX_CARRLOSS 0x08000000
+#define KUE_STAT_RX_LATECOLL 0x10000000
+
+#define KUE_RXFILT_PROMISC 0x0001
+#define KUE_RXFILT_ALLMULTI 0x0002
+#define KUE_RXFILT_UNICAST 0x0004
+#define KUE_RXFILT_BROADCAST 0x0008
+#define KUE_RXFILT_MULTICAST 0x0010
+
+#define KUE_TIMEOUT 1000
+#define KUE_MIN_FRAMELEN 60
+
+#define KUE_CTL_READ 0x01
+#define KUE_CTL_WRITE 0x02
+
+#define KUE_CONFIG_IDX 0 /* config number 1 */
+#define KUE_IFACE_IDX 0
+
+/* The interrupt endpoint is currently unused by the KLSI part. */
+#define KUE_ENDPT_MAX 4
+enum {
+ KUE_BULK_DT_WR,
+ KUE_BULK_DT_RD,
+ KUE_N_TRANSFER,
+};
+
+struct kue_softc {
+ struct usb2_ether sc_ue;
+ struct mtx sc_mtx;
+ struct kue_ether_desc sc_desc;
+ struct usb2_xfer *sc_xfer[KUE_N_TRANSFER];
+ uint8_t *sc_mcfilters;
+
+ int sc_flags;
+#define KUE_FLAG_LINK 0x0001
+
+ uint16_t sc_rxfilt;
+};
+
+#define KUE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define KUE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define KUE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t)
diff --git a/sys/dev/usb/net/if_rue.c b/sys/dev/usb/net/if_rue.c
new file mode 100644
index 0000000..1d0f6ee
--- /dev/null
+++ b/sys/dev/usb/net/if_rue.c
@@ -0,0 +1,913 @@
+/*-
+ * Copyright (c) 2001-2003, Shunsuke Akiyama <akiyama@FreeBSD.org>.
+ * Copyright (c) 1997, 1998, 1999, 2000 Bill Paul <wpaul@ee.columbia.edu>.
+ * 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.
+ */
+/*-
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * RealTek RTL8150 USB to fast ethernet controller driver.
+ * Datasheet is available from
+ * ftp://ftp.realtek.com.tw/lancard/data_sheet/8150/.
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+
+#define USB_DEBUG_VAR rue_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/net/usb_ethernet.h>
+#include <dev/usb/net/if_ruereg.h>
+
+#if USB_DEBUG
+static int rue_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, rue, CTLFLAG_RW, 0, "USB rue");
+SYSCTL_INT(_hw_usb2_rue, OID_AUTO, debug, CTLFLAG_RW,
+ &rue_debug, 0, "Debug level");
+#endif
+
+/*
+ * Various supported device vendors/products.
+ */
+
+static const struct usb2_device_id rue_devs[] = {
+ {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAKTX, 0)},
+ {USB_VPI(USB_VENDOR_REALTEK, USB_PRODUCT_REALTEK_USBKR100, 0)},
+};
+
+/* prototypes */
+
+static device_probe_t rue_probe;
+static device_attach_t rue_attach;
+static device_detach_t rue_detach;
+static device_shutdown_t rue_shutdown;
+
+static miibus_readreg_t rue_miibus_readreg;
+static miibus_writereg_t rue_miibus_writereg;
+static miibus_statchg_t rue_miibus_statchg;
+
+static usb2_callback_t rue_intr_callback;
+static usb2_callback_t rue_bulk_read_callback;
+static usb2_callback_t rue_bulk_write_callback;
+
+static usb2_ether_fn_t rue_attach_post;
+static usb2_ether_fn_t rue_init;
+static usb2_ether_fn_t rue_stop;
+static usb2_ether_fn_t rue_start;
+static usb2_ether_fn_t rue_tick;
+static usb2_ether_fn_t rue_setmulti;
+static usb2_ether_fn_t rue_setpromisc;
+
+static int rue_read_mem(struct rue_softc *, uint16_t, void *, int);
+static int rue_write_mem(struct rue_softc *, uint16_t, void *, int);
+static uint8_t rue_csr_read_1(struct rue_softc *, uint16_t);
+static uint16_t rue_csr_read_2(struct rue_softc *, uint16_t);
+static int rue_csr_write_1(struct rue_softc *, uint16_t, uint8_t);
+static int rue_csr_write_2(struct rue_softc *, uint16_t, uint16_t);
+static int rue_csr_write_4(struct rue_softc *, int, uint32_t);
+
+static void rue_reset(struct rue_softc *);
+static int rue_ifmedia_upd(struct ifnet *);
+static void rue_ifmedia_sts(struct ifnet *, struct ifmediareq *);
+
+static const struct usb2_config rue_config[RUE_N_TRANSFER] = {
+
+ [RUE_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = MCLBYTES,
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = rue_bulk_write_callback,
+ .mh.timeout = 10000, /* 10 seconds */
+ },
+
+ [RUE_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = (MCLBYTES + 4),
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = rue_bulk_read_callback,
+ .mh.timeout = 0, /* no timeout */
+ },
+
+ [RUE_INTR_DT_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.bufsize = 0, /* use wMaxPacketSize */
+ .mh.callback = rue_intr_callback,
+ },
+};
+
+static device_method_t rue_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, rue_probe),
+ DEVMETHOD(device_attach, rue_attach),
+ DEVMETHOD(device_detach, rue_detach),
+ DEVMETHOD(device_shutdown, rue_shutdown),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, rue_miibus_readreg),
+ DEVMETHOD(miibus_writereg, rue_miibus_writereg),
+ DEVMETHOD(miibus_statchg, rue_miibus_statchg),
+
+ {0, 0}
+};
+
+static driver_t rue_driver = {
+ .name = "rue",
+ .methods = rue_methods,
+ .size = sizeof(struct rue_softc),
+};
+
+static devclass_t rue_devclass;
+
+DRIVER_MODULE(rue, ushub, rue_driver, rue_devclass, NULL, 0);
+DRIVER_MODULE(miibus, rue, miibus_driver, miibus_devclass, 0, 0);
+MODULE_DEPEND(rue, uether, 1, 1, 1);
+MODULE_DEPEND(rue, usb, 1, 1, 1);
+MODULE_DEPEND(rue, ether, 1, 1, 1);
+MODULE_DEPEND(rue, miibus, 1, 1, 1);
+
+static const struct usb2_ether_methods rue_ue_methods = {
+ .ue_attach_post = rue_attach_post,
+ .ue_start = rue_start,
+ .ue_init = rue_init,
+ .ue_stop = rue_stop,
+ .ue_tick = rue_tick,
+ .ue_setmulti = rue_setmulti,
+ .ue_setpromisc = rue_setpromisc,
+ .ue_mii_upd = rue_ifmedia_upd,
+ .ue_mii_sts = rue_ifmedia_sts,
+};
+
+#define RUE_SETBIT(sc, reg, x) \
+ rue_csr_write_1(sc, reg, rue_csr_read_1(sc, reg) | (x))
+
+#define RUE_CLRBIT(sc, reg, x) \
+ rue_csr_write_1(sc, reg, rue_csr_read_1(sc, reg) & ~(x))
+
+static int
+rue_read_mem(struct rue_softc *sc, uint16_t addr, void *buf, int len)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = UR_SET_ADDRESS;
+ USETW(req.wValue, addr);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, len);
+
+ return (usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000));
+}
+
+static int
+rue_write_mem(struct rue_softc *sc, uint16_t addr, void *buf, int len)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UR_SET_ADDRESS;
+ USETW(req.wValue, addr);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, len);
+
+ return (usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000));
+}
+
+static uint8_t
+rue_csr_read_1(struct rue_softc *sc, uint16_t reg)
+{
+ uint8_t val;
+
+ rue_read_mem(sc, reg, &val, 1);
+ return (val);
+}
+
+static uint16_t
+rue_csr_read_2(struct rue_softc *sc, uint16_t reg)
+{
+ uint8_t val[2];
+
+ rue_read_mem(sc, reg, &val, 2);
+ return (UGETW(val));
+}
+
+static int
+rue_csr_write_1(struct rue_softc *sc, uint16_t reg, uint8_t val)
+{
+ return (rue_write_mem(sc, reg, &val, 1));
+}
+
+static int
+rue_csr_write_2(struct rue_softc *sc, uint16_t reg, uint16_t val)
+{
+ uint8_t temp[2];
+
+ USETW(temp, val);
+ return (rue_write_mem(sc, reg, &temp, 2));
+}
+
+static int
+rue_csr_write_4(struct rue_softc *sc, int reg, uint32_t val)
+{
+ uint8_t temp[4];
+
+ USETDW(temp, val);
+ return (rue_write_mem(sc, reg, &temp, 4));
+}
+
+static int
+rue_miibus_readreg(device_t dev, int phy, int reg)
+{
+ struct rue_softc *sc = device_get_softc(dev);
+ uint16_t rval;
+ uint16_t ruereg;
+ int locked;
+
+ if (phy != 0) /* RTL8150 supports PHY == 0, only */
+ return (0);
+
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ RUE_LOCK(sc);
+
+ switch (reg) {
+ case MII_BMCR:
+ ruereg = RUE_BMCR;
+ break;
+ case MII_BMSR:
+ ruereg = RUE_BMSR;
+ break;
+ case MII_ANAR:
+ ruereg = RUE_ANAR;
+ break;
+ case MII_ANER:
+ ruereg = RUE_AER;
+ break;
+ case MII_ANLPAR:
+ ruereg = RUE_ANLP;
+ break;
+ case MII_PHYIDR1:
+ case MII_PHYIDR2:
+ rval = 0;
+ goto done;
+ default:
+ if (RUE_REG_MIN <= reg && reg <= RUE_REG_MAX) {
+ rval = rue_csr_read_1(sc, reg);
+ goto done;
+ }
+ device_printf(sc->sc_ue.ue_dev, "bad phy register\n");
+ rval = 0;
+ goto done;
+ }
+
+ rval = rue_csr_read_2(sc, ruereg);
+done:
+ if (!locked)
+ RUE_UNLOCK(sc);
+ return (rval);
+}
+
+static int
+rue_miibus_writereg(device_t dev, int phy, int reg, int data)
+{
+ struct rue_softc *sc = device_get_softc(dev);
+ uint16_t ruereg;
+ int locked;
+
+ if (phy != 0) /* RTL8150 supports PHY == 0, only */
+ return (0);
+
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ RUE_LOCK(sc);
+
+ switch (reg) {
+ case MII_BMCR:
+ ruereg = RUE_BMCR;
+ break;
+ case MII_BMSR:
+ ruereg = RUE_BMSR;
+ break;
+ case MII_ANAR:
+ ruereg = RUE_ANAR;
+ break;
+ case MII_ANER:
+ ruereg = RUE_AER;
+ break;
+ case MII_ANLPAR:
+ ruereg = RUE_ANLP;
+ break;
+ case MII_PHYIDR1:
+ case MII_PHYIDR2:
+ goto done;
+ default:
+ if (RUE_REG_MIN <= reg && reg <= RUE_REG_MAX) {
+ rue_csr_write_1(sc, reg, data);
+ goto done;
+ }
+ device_printf(sc->sc_ue.ue_dev, " bad phy register\n");
+ goto done;
+ }
+ rue_csr_write_2(sc, ruereg, data);
+done:
+ if (!locked)
+ RUE_UNLOCK(sc);
+ return (0);
+}
+
+static void
+rue_miibus_statchg(device_t dev)
+{
+ /*
+ * When the code below is enabled the card starts doing weird
+ * things after link going from UP to DOWN and back UP.
+ *
+ * Looks like some of register writes below messes up PHY
+ * interface.
+ *
+ * No visible regressions were found after commenting this code
+ * out, so that disable it for good.
+ */
+#if 0
+ struct rue_softc *sc = device_get_softc(dev);
+ struct mii_data *mii = GET_MII(sc);
+ uint16_t bmcr;
+ int locked;
+
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ RUE_LOCK(sc);
+
+ RUE_CLRBIT(sc, RUE_CR, (RUE_CR_RE | RUE_CR_TE));
+
+ bmcr = rue_csr_read_2(sc, RUE_BMCR);
+
+ if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX)
+ bmcr |= RUE_BMCR_SPD_SET;
+ else
+ bmcr &= ~RUE_BMCR_SPD_SET;
+
+ if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX)
+ bmcr |= RUE_BMCR_DUPLEX;
+ else
+ bmcr &= ~RUE_BMCR_DUPLEX;
+
+ rue_csr_write_2(sc, RUE_BMCR, bmcr);
+
+ RUE_SETBIT(sc, RUE_CR, (RUE_CR_RE | RUE_CR_TE));
+
+ if (!locked)
+ RUE_UNLOCK(sc);
+#endif
+}
+
+static void
+rue_setpromisc(struct usb2_ether *ue)
+{
+ struct rue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+
+ RUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ /* If we want promiscuous mode, set the allframes bit. */
+ if (ifp->if_flags & IFF_PROMISC)
+ RUE_SETBIT(sc, RUE_RCR, RUE_RCR_AAP);
+ else
+ RUE_CLRBIT(sc, RUE_RCR, RUE_RCR_AAP);
+}
+
+/*
+ * Program the 64-bit multicast hash filter.
+ */
+static void
+rue_setmulti(struct usb2_ether *ue)
+{
+ struct rue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+ uint16_t rxcfg;
+ int h = 0;
+ uint32_t hashes[2] = { 0, 0 };
+ struct ifmultiaddr *ifma;
+ int mcnt = 0;
+
+ RUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ rxcfg = rue_csr_read_2(sc, RUE_RCR);
+
+ if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
+ rxcfg |= (RUE_RCR_AAM | RUE_RCR_AAP);
+ rxcfg &= ~RUE_RCR_AM;
+ rue_csr_write_2(sc, RUE_RCR, rxcfg);
+ rue_csr_write_4(sc, RUE_MAR0, 0xFFFFFFFF);
+ rue_csr_write_4(sc, RUE_MAR4, 0xFFFFFFFF);
+ return;
+ }
+
+ /* first, zot all the existing hash bits */
+ rue_csr_write_4(sc, RUE_MAR0, 0);
+ rue_csr_write_4(sc, RUE_MAR4, 0);
+
+ /* now program new ones */
+ IF_ADDR_LOCK(ifp);
+ TAILQ_FOREACH (ifma, &ifp->if_multiaddrs, ifma_link)
+ {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ h = ether_crc32_be(LLADDR((struct sockaddr_dl *)
+ ifma->ifma_addr), ETHER_ADDR_LEN) >> 26;
+ if (h < 32)
+ hashes[0] |= (1 << h);
+ else
+ hashes[1] |= (1 << (h - 32));
+ mcnt++;
+ }
+ IF_ADDR_UNLOCK(ifp);
+
+ if (mcnt)
+ rxcfg |= RUE_RCR_AM;
+ else
+ rxcfg &= ~RUE_RCR_AM;
+
+ rxcfg &= ~(RUE_RCR_AAM | RUE_RCR_AAP);
+
+ rue_csr_write_2(sc, RUE_RCR, rxcfg);
+ rue_csr_write_4(sc, RUE_MAR0, hashes[0]);
+ rue_csr_write_4(sc, RUE_MAR4, hashes[1]);
+}
+
+static void
+rue_reset(struct rue_softc *sc)
+{
+ int i;
+
+ rue_csr_write_1(sc, RUE_CR, RUE_CR_SOFT_RST);
+
+ for (i = 0; i != RUE_TIMEOUT; i++) {
+ if (usb2_ether_pause(&sc->sc_ue, hz / 1000))
+ break;
+ if (!(rue_csr_read_1(sc, RUE_CR) & RUE_CR_SOFT_RST))
+ break;
+ }
+ if (i == RUE_TIMEOUT)
+ device_printf(sc->sc_ue.ue_dev, "reset never completed!\n");
+
+ usb2_ether_pause(&sc->sc_ue, hz / 100);
+}
+
+static void
+rue_attach_post(struct usb2_ether *ue)
+{
+ struct rue_softc *sc = usb2_ether_getsc(ue);
+
+ /* reset the adapter */
+ rue_reset(sc);
+
+ /* get station address from the EEPROM */
+ rue_read_mem(sc, RUE_EEPROM_IDR0, ue->ue_eaddr, ETHER_ADDR_LEN);
+}
+
+/*
+ * Probe for a RTL8150 chip.
+ */
+static int
+rue_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST)
+ return (ENXIO);
+ if (uaa->info.bConfigIndex != RUE_CONFIG_IDX)
+ return (ENXIO);
+ if (uaa->info.bIfaceIndex != RUE_IFACE_IDX)
+ return (ENXIO);
+
+ return (usb2_lookup_id_by_uaa(rue_devs, sizeof(rue_devs), uaa));
+}
+
+/*
+ * Attach the interface. Allocate softc structures, do ifmedia
+ * setup and ethernet/BPF attach.
+ */
+static int
+rue_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct rue_softc *sc = device_get_softc(dev);
+ struct usb2_ether *ue = &sc->sc_ue;
+ uint8_t iface_index;
+ int error;
+
+ device_set_usb2_desc(dev);
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ iface_index = RUE_IFACE_IDX;
+ error = usb2_transfer_setup(uaa->device, &iface_index,
+ sc->sc_xfer, rue_config, RUE_N_TRANSFER,
+ sc, &sc->sc_mtx);
+ if (error) {
+ device_printf(dev, "allocating USB transfers failed!\n");
+ goto detach;
+ }
+
+ ue->ue_sc = sc;
+ ue->ue_dev = dev;
+ ue->ue_udev = uaa->device;
+ ue->ue_mtx = &sc->sc_mtx;
+ ue->ue_methods = &rue_ue_methods;
+
+ error = usb2_ether_ifattach(ue);
+ if (error) {
+ device_printf(dev, "could not attach interface\n");
+ goto detach;
+ }
+ return (0); /* success */
+
+detach:
+ rue_detach(dev);
+ return (ENXIO); /* failure */
+}
+
+static int
+rue_detach(device_t dev)
+{
+ struct rue_softc *sc = device_get_softc(dev);
+ struct usb2_ether *ue = &sc->sc_ue;
+
+ usb2_transfer_unsetup(sc->sc_xfer, RUE_N_TRANSFER);
+ usb2_ether_ifdetach(ue);
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static void
+rue_intr_callback(struct usb2_xfer *xfer)
+{
+ struct rue_softc *sc = xfer->priv_sc;
+ struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue);
+ struct rue_intrpkt pkt;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ if (ifp && (ifp->if_drv_flags & IFF_DRV_RUNNING) &&
+ (xfer->actlen >= sizeof(pkt))) {
+
+ usb2_copy_out(xfer->frbuffers, 0, &pkt, sizeof(pkt));
+
+ ifp->if_ierrors += pkt.rue_rxlost_cnt;
+ ifp->if_ierrors += pkt.rue_crcerr_cnt;
+ ifp->if_collisions += pkt.rue_col_cnt;
+ }
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+rue_bulk_read_callback(struct usb2_xfer *xfer)
+{
+ struct rue_softc *sc = xfer->priv_sc;
+ struct usb2_ether *ue = &sc->sc_ue;
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+ uint16_t status;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ if (xfer->actlen < 4) {
+ ifp->if_ierrors++;
+ goto tr_setup;
+ }
+ usb2_copy_out(xfer->frbuffers, xfer->actlen - 4,
+ &status, sizeof(status));
+ xfer->actlen -= 4;
+
+ /* check recieve packet was valid or not */
+ status = le16toh(status);
+ if ((status & RUE_RXSTAT_VALID) == 0) {
+ ifp->if_ierrors++;
+ goto tr_setup;
+ }
+ usb2_ether_rxbuf(ue, xfer->frbuffers, 0, xfer->actlen);
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ usb2_ether_rxflush(ue);
+ return;
+
+ default: /* Error */
+ DPRINTF("bulk read error, %s\n",
+ usb2_errstr(xfer->error));
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+rue_bulk_write_callback(struct usb2_xfer *xfer)
+{
+ struct rue_softc *sc = xfer->priv_sc;
+ struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue);
+ struct mbuf *m;
+ int temp_len;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTFN(11, "transfer complete\n");
+ ifp->if_opackets++;
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ if ((sc->sc_flags & RUE_FLAG_LINK) == 0) {
+ /*
+ * don't send anything if there is no link !
+ */
+ return;
+ }
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+
+ if (m == NULL)
+ return;
+ if (m->m_pkthdr.len > MCLBYTES)
+ m->m_pkthdr.len = MCLBYTES;
+ temp_len = m->m_pkthdr.len;
+
+ usb2_m_copy_in(xfer->frbuffers, 0,
+ m, 0, m->m_pkthdr.len);
+
+ /*
+ * This is an undocumented behavior.
+ * RTL8150 chip doesn't send frame length smaller than
+ * RUE_MIN_FRAMELEN (60) byte packet.
+ */
+ if (temp_len < RUE_MIN_FRAMELEN) {
+ usb2_bzero(xfer->frbuffers, temp_len,
+ RUE_MIN_FRAMELEN - temp_len);
+ temp_len = RUE_MIN_FRAMELEN;
+ }
+ xfer->frlengths[0] = temp_len;
+
+ /*
+ * if there's a BPF listener, bounce a copy
+ * of this frame to him:
+ */
+ BPF_MTAP(ifp, m);
+
+ m_freem(m);
+
+ usb2_start_hardware(xfer);
+
+ return;
+
+ default: /* Error */
+ DPRINTFN(11, "transfer error, %s\n",
+ usb2_errstr(xfer->error));
+
+ ifp->if_oerrors++;
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+rue_tick(struct usb2_ether *ue)
+{
+ struct rue_softc *sc = usb2_ether_getsc(ue);
+ struct mii_data *mii = GET_MII(sc);
+
+ RUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ mii_tick(mii);
+ if ((sc->sc_flags & RUE_FLAG_LINK) == 0
+ && mii->mii_media_status & IFM_ACTIVE &&
+ IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
+ sc->sc_flags |= RUE_FLAG_LINK;
+ rue_start(ue);
+ }
+}
+
+static void
+rue_start(struct usb2_ether *ue)
+{
+ struct rue_softc *sc = usb2_ether_getsc(ue);
+
+ /*
+ * start the USB transfers, if not already started:
+ */
+ usb2_transfer_start(sc->sc_xfer[RUE_INTR_DT_RD]);
+ usb2_transfer_start(sc->sc_xfer[RUE_BULK_DT_RD]);
+ usb2_transfer_start(sc->sc_xfer[RUE_BULK_DT_WR]);
+}
+
+static void
+rue_init(struct usb2_ether *ue)
+{
+ struct rue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+
+ RUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ /*
+ * Cancel pending I/O
+ */
+ rue_reset(sc);
+
+ /* Set MAC address */
+ rue_write_mem(sc, RUE_IDR0, IF_LLADDR(ifp), ETHER_ADDR_LEN);
+
+ rue_stop(ue);
+
+ /*
+ * Set the initial TX and RX configuration.
+ */
+ rue_csr_write_1(sc, RUE_TCR, RUE_TCR_CONFIG);
+ rue_csr_write_2(sc, RUE_RCR, RUE_RCR_CONFIG|RUE_RCR_AB);
+
+ /* Load the multicast filter */
+ rue_setpromisc(ue);
+ /* Load the multicast filter. */
+ rue_setmulti(ue);
+
+ /* Enable RX and TX */
+ rue_csr_write_1(sc, RUE_CR, (RUE_CR_TE | RUE_CR_RE | RUE_CR_EP3CLREN));
+
+ usb2_transfer_set_stall(sc->sc_xfer[RUE_BULK_DT_WR]);
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ rue_start(ue);
+}
+
+/*
+ * Set media options.
+ */
+static int
+rue_ifmedia_upd(struct ifnet *ifp)
+{
+ struct rue_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+
+ RUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ sc->sc_flags &= ~RUE_FLAG_LINK;
+ if (mii->mii_instance) {
+ struct mii_softc *miisc;
+
+ LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
+ mii_phy_reset(miisc);
+ }
+ mii_mediachg(mii);
+ return (0);
+}
+
+/*
+ * Report current media status.
+ */
+static void
+rue_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct rue_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+
+ RUE_LOCK(sc);
+ mii_pollstat(mii);
+ RUE_UNLOCK(sc);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+}
+
+static void
+rue_stop(struct usb2_ether *ue)
+{
+ struct rue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+
+ RUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ sc->sc_flags &= ~RUE_FLAG_LINK;
+
+ /*
+ * stop all the transfers, if not already stopped:
+ */
+ usb2_transfer_stop(sc->sc_xfer[RUE_BULK_DT_WR]);
+ usb2_transfer_stop(sc->sc_xfer[RUE_BULK_DT_RD]);
+ usb2_transfer_stop(sc->sc_xfer[RUE_INTR_DT_RD]);
+
+ rue_csr_write_1(sc, RUE_CR, 0x00);
+
+ rue_reset(sc);
+}
+
+/*
+ * Stop all chip I/O so that the kernel's probe routines don't
+ * get confused by errant DMAs when rebooting.
+ */
+static int
+rue_shutdown(device_t dev)
+{
+ struct rue_softc *sc = device_get_softc(dev);
+
+ usb2_ether_ifshutdown(&sc->sc_ue);
+
+ return (0);
+}
diff --git a/sys/dev/usb/net/if_ruereg.h b/sys/dev/usb/net/if_ruereg.h
new file mode 100644
index 0000000..a94d45a
--- /dev/null
+++ b/sys/dev/usb/net/if_ruereg.h
@@ -0,0 +1,183 @@
+/*-
+ * Copyright (c) 2001-2003, Shunsuke Akiyama <akiyama@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define RUE_CONFIG_IDX 0 /* config number 1 */
+#define RUE_IFACE_IDX 0
+
+#define RUE_INTR_PKTLEN 0x8
+
+#define RUE_TIMEOUT 50
+#define RUE_MIN_FRAMELEN 60
+
+/* Registers. */
+#define RUE_IDR0 0x0120
+#define RUE_IDR1 0x0121
+#define RUE_IDR2 0x0122
+#define RUE_IDR3 0x0123
+#define RUE_IDR4 0x0124
+#define RUE_IDR5 0x0125
+
+#define RUE_MAR0 0x0126
+#define RUE_MAR1 0x0127
+#define RUE_MAR2 0x0128
+#define RUE_MAR3 0x0129
+#define RUE_MAR4 0x012A
+#define RUE_MAR5 0x012B
+#define RUE_MAR6 0x012C
+#define RUE_MAR7 0x012D
+
+#define RUE_CR 0x012E /* B, R/W */
+#define RUE_CR_SOFT_RST 0x10
+#define RUE_CR_RE 0x08
+#define RUE_CR_TE 0x04
+#define RUE_CR_EP3CLREN 0x02
+
+#define RUE_TCR 0x012F /* B, R/W */
+#define RUE_TCR_TXRR1 0x80
+#define RUE_TCR_TXRR0 0x40
+#define RUE_TCR_IFG1 0x10
+#define RUE_TCR_IFG0 0x08
+#define RUE_TCR_NOCRC 0x01
+#define RUE_TCR_CONFIG (RUE_TCR_TXRR1 | RUE_TCR_TXRR0 | \
+ RUE_TCR_IFG1 | RUE_TCR_IFG0)
+
+#define RUE_RCR 0x0130 /* W, R/W */
+#define RUE_RCR_TAIL 0x80
+#define RUE_RCR_AER 0x40
+#define RUE_RCR_AR 0x20
+#define RUE_RCR_AM 0x10
+#define RUE_RCR_AB 0x08
+#define RUE_RCR_AD 0x04
+#define RUE_RCR_AAM 0x02
+#define RUE_RCR_AAP 0x01
+#define RUE_RCR_CONFIG (RUE_RCR_TAIL | RUE_RCR_AD)
+
+#define RUE_TSR 0x0132
+#define RUE_RSR 0x0133
+#define RUE_CON0 0x0135
+#define RUE_CON1 0x0136
+#define RUE_MSR 0x0137
+#define RUE_PHYADD 0x0138
+#define RUE_PHYDAT 0x0139
+
+#define RUE_PHYCNT 0x013B /* B, R/W */
+#define RUE_PHYCNT_PHYOWN 0x40
+#define RUE_PHYCNT_RWCR 0x20
+
+#define RUE_GPPC 0x013D
+#define RUE_WAKECNT 0x013E
+
+#define RUE_BMCR 0x0140
+#define RUE_BMCR_SPD_SET 0x2000
+#define RUE_BMCR_DUPLEX 0x0100
+
+#define RUE_BMSR 0x0142
+
+#define RUE_ANAR 0x0144 /* W, R/W */
+#define RUE_ANAR_PAUSE 0x0400
+
+#define RUE_ANLP 0x0146 /* W, R/O */
+#define RUE_ANLP_PAUSE 0x0400
+
+#define RUE_AER 0x0148
+
+#define RUE_NWAYT 0x014A
+#define RUE_CSCR 0x014C
+
+#define RUE_CRC0 0x014E
+#define RUE_CRC1 0x0150
+#define RUE_CRC2 0x0152
+#define RUE_CRC3 0x0154
+#define RUE_CRC4 0x0156
+
+#define RUE_BYTEMASK0 0x0158
+#define RUE_BYTEMASK1 0x0160
+#define RUE_BYTEMASK2 0x0168
+#define RUE_BYTEMASK3 0x0170
+#define RUE_BYTEMASK4 0x0178
+
+#define RUE_PHY1 0x0180
+#define RUE_PHY2 0x0184
+
+#define RUE_TW1 0x0186
+
+#define RUE_REG_MIN 0x0120
+#define RUE_REG_MAX 0x0189
+
+/* EEPROM address declarations. */
+#define RUE_EEPROM_BASE 0x1200
+#define RUE_EEPROM_IDR0 (RUE_EEPROM_BASE + 0x02)
+#define RUE_EEPROM_IDR1 (RUE_EEPROM_BASE + 0x03)
+#define RUE_EEPROM_IDR2 (RUE_EEPROM_BASE + 0x03)
+#define RUE_EEPROM_IDR3 (RUE_EEPROM_BASE + 0x03)
+#define RUE_EEPROM_IDR4 (RUE_EEPROM_BASE + 0x03)
+#define RUE_EEPROM_IDR5 (RUE_EEPROM_BASE + 0x03)
+#define RUE_EEPROM_INTERVAL (RUE_EEPROM_BASE + 0x17)
+
+#define RUE_RXSTAT_VALID (0x01 << 12)
+#define RUE_RXSTAT_RUNT (0x02 << 12)
+#define RUE_RXSTAT_PMATCH (0x04 << 12)
+#define RUE_RXSTAT_MCAST (0x08 << 12)
+
+#define GET_MII(sc) usb2_ether_getmii(&(sc)->sc_ue)
+
+struct rue_intrpkt {
+ uint8_t rue_tsr;
+ uint8_t rue_rsr;
+ uint8_t rue_gep_msr;
+ uint8_t rue_waksr;
+ uint8_t rue_txok_cnt;
+ uint8_t rue_rxlost_cnt;
+ uint8_t rue_crcerr_cnt;
+ uint8_t rue_col_cnt;
+} __packed;
+
+struct rue_type {
+ uint16_t rue_vid;
+ uint16_t rue_did;
+};
+
+enum {
+ RUE_BULK_DT_WR,
+ RUE_BULK_DT_RD,
+ RUE_INTR_DT_RD,
+ RUE_N_TRANSFER,
+};
+
+struct rue_softc {
+ struct usb2_ether sc_ue;
+ struct mtx sc_mtx;
+ struct usb2_xfer *sc_xfer[RUE_N_TRANSFER];
+
+ int sc_flags;
+#define RUE_FLAG_LINK 0x0001
+};
+
+#define RUE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define RUE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define RUE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t)
diff --git a/sys/dev/usb/net/if_udav.c b/sys/dev/usb/net/if_udav.c
new file mode 100644
index 0000000..82cec80
--- /dev/null
+++ b/sys/dev/usb/net/if_udav.c
@@ -0,0 +1,856 @@
+/* $NetBSD: if_udav.c,v 1.2 2003/09/04 15:17:38 tsutsui Exp $ */
+/* $nabe: if_udav.c,v 1.3 2003/08/21 16:57:19 nabe Exp $ */
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2003
+ * Shingo WATANABE <nabe@nabechan.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.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * 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.
+ *
+ */
+
+/*
+ * DM9601(DAVICOM USB to Ethernet MAC Controller with Integrated 10/100 PHY)
+ * The spec can be found at the following url.
+ * http://www.davicom.com.tw/big5/download/Data%20Sheet/DM9601-DS-P01-930914.pdf
+ */
+
+/*
+ * TODO:
+ * Interrupt Endpoint support
+ * External PHYs
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+
+#define USB_DEBUG_VAR udav_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/net/usb_ethernet.h>
+#include <dev/usb/net/if_udavreg.h>
+
+/* prototypes */
+
+static device_probe_t udav_probe;
+static device_attach_t udav_attach;
+static device_detach_t udav_detach;
+static device_shutdown_t udav_shutdown;
+
+static usb2_callback_t udav_bulk_write_callback;
+static usb2_callback_t udav_bulk_read_callback;
+static usb2_callback_t udav_intr_callback;
+
+static usb2_ether_fn_t udav_attach_post;
+static usb2_ether_fn_t udav_init;
+static usb2_ether_fn_t udav_stop;
+static usb2_ether_fn_t udav_start;
+static usb2_ether_fn_t udav_tick;
+static usb2_ether_fn_t udav_setmulti;
+static usb2_ether_fn_t udav_setpromisc;
+
+static int udav_csr_read(struct udav_softc *, uint16_t, void *, int);
+static int udav_csr_write(struct udav_softc *, uint16_t, void *, int);
+static uint8_t udav_csr_read1(struct udav_softc *, uint16_t);
+static int udav_csr_write1(struct udav_softc *, uint16_t, uint8_t);
+static void udav_reset(struct udav_softc *);
+static int udav_ifmedia_upd(struct ifnet *);
+static void udav_ifmedia_status(struct ifnet *, struct ifmediareq *);
+
+static miibus_readreg_t udav_miibus_readreg;
+static miibus_writereg_t udav_miibus_writereg;
+static miibus_statchg_t udav_miibus_statchg;
+
+static const struct usb2_config udav_config[UDAV_N_TRANSFER] = {
+
+ [UDAV_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = (MCLBYTES + 2),
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = udav_bulk_write_callback,
+ .mh.timeout = 10000, /* 10 seconds */
+ },
+
+ [UDAV_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = (MCLBYTES + 3),
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = udav_bulk_read_callback,
+ .mh.timeout = 0, /* no timeout */
+ },
+
+ [UDAV_INTR_DT_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.bufsize = 0, /* use wMaxPacketSize */
+ .mh.callback = udav_intr_callback,
+ },
+};
+
+static device_method_t udav_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, udav_probe),
+ DEVMETHOD(device_attach, udav_attach),
+ DEVMETHOD(device_detach, udav_detach),
+ DEVMETHOD(device_shutdown, udav_shutdown),
+
+ /* bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, udav_miibus_readreg),
+ DEVMETHOD(miibus_writereg, udav_miibus_writereg),
+ DEVMETHOD(miibus_statchg, udav_miibus_statchg),
+
+ {0, 0}
+};
+
+static driver_t udav_driver = {
+ .name = "udav",
+ .methods = udav_methods,
+ .size = sizeof(struct udav_softc),
+};
+
+static devclass_t udav_devclass;
+
+DRIVER_MODULE(udav, ushub, udav_driver, udav_devclass, NULL, 0);
+DRIVER_MODULE(miibus, udav, miibus_driver, miibus_devclass, 0, 0);
+MODULE_DEPEND(udav, uether, 1, 1, 1);
+MODULE_DEPEND(udav, usb, 1, 1, 1);
+MODULE_DEPEND(udav, ether, 1, 1, 1);
+MODULE_DEPEND(udav, miibus, 1, 1, 1);
+
+static const struct usb2_ether_methods udav_ue_methods = {
+ .ue_attach_post = udav_attach_post,
+ .ue_start = udav_start,
+ .ue_init = udav_init,
+ .ue_stop = udav_stop,
+ .ue_tick = udav_tick,
+ .ue_setmulti = udav_setmulti,
+ .ue_setpromisc = udav_setpromisc,
+ .ue_mii_upd = udav_ifmedia_upd,
+ .ue_mii_sts = udav_ifmedia_status,
+};
+
+#if USB_DEBUG
+static int udav_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, udav, CTLFLAG_RW, 0, "USB udav");
+SYSCTL_INT(_hw_usb2_udav, OID_AUTO, debug, CTLFLAG_RW, &udav_debug, 0,
+ "Debug level");
+#endif
+
+#define UDAV_SETBIT(sc, reg, x) \
+ udav_csr_write1(sc, reg, udav_csr_read1(sc, reg) | (x))
+
+#define UDAV_CLRBIT(sc, reg, x) \
+ udav_csr_write1(sc, reg, udav_csr_read1(sc, reg) & ~(x))
+
+static const struct usb2_device_id udav_devs[] = {
+ /* ShanTou DM9601 USB NIC */
+ {USB_VPI(USB_VENDOR_SHANTOU, USB_PRODUCT_SHANTOU_DM9601, 0)},
+ /* ShanTou ST268 USB NIC */
+ {USB_VPI(USB_VENDOR_SHANTOU, USB_PRODUCT_SHANTOU_ST268, 0)},
+ /* Corega USB-TXC */
+ {USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TXC, 0)},
+};
+
+static void
+udav_attach_post(struct usb2_ether *ue)
+{
+ struct udav_softc *sc = usb2_ether_getsc(ue);
+
+ /* reset the adapter */
+ udav_reset(sc);
+
+ /* Get Ethernet Address */
+ udav_csr_read(sc, UDAV_PAR, ue->ue_eaddr, ETHER_ADDR_LEN);
+}
+
+static int
+udav_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST)
+ return (ENXIO);
+ if (uaa->info.bConfigIndex != UDAV_CONFIG_INDEX)
+ return (ENXIO);
+ if (uaa->info.bIfaceIndex != UDAV_IFACE_INDEX)
+ return (ENXIO);
+
+ return (usb2_lookup_id_by_uaa(udav_devs, sizeof(udav_devs), uaa));
+}
+
+static int
+udav_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct udav_softc *sc = device_get_softc(dev);
+ struct usb2_ether *ue = &sc->sc_ue;
+ uint8_t iface_index;
+ int error;
+
+ sc->sc_flags = USB_GET_DRIVER_INFO(uaa);
+
+ device_set_usb2_desc(dev);
+
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ iface_index = UDAV_IFACE_INDEX;
+ error = usb2_transfer_setup(uaa->device, &iface_index,
+ sc->sc_xfer, udav_config, UDAV_N_TRANSFER, sc, &sc->sc_mtx);
+ if (error) {
+ device_printf(dev, "allocating USB transfers failed!\n");
+ goto detach;
+ }
+
+ ue->ue_sc = sc;
+ ue->ue_dev = dev;
+ ue->ue_udev = uaa->device;
+ ue->ue_mtx = &sc->sc_mtx;
+ ue->ue_methods = &udav_ue_methods;
+
+ error = usb2_ether_ifattach(ue);
+ if (error) {
+ device_printf(dev, "could not attach interface\n");
+ goto detach;
+ }
+
+ return (0); /* success */
+
+detach:
+ udav_detach(dev);
+ return (ENXIO); /* failure */
+}
+
+static int
+udav_detach(device_t dev)
+{
+ struct udav_softc *sc = device_get_softc(dev);
+ struct usb2_ether *ue = &sc->sc_ue;
+
+ usb2_transfer_unsetup(sc->sc_xfer, UDAV_N_TRANSFER);
+ usb2_ether_ifdetach(ue);
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+#if 0
+static int
+udav_mem_read(struct udav_softc *sc, uint16_t offset, void *buf,
+ int len)
+{
+ struct usb2_device_request req;
+
+ len &= 0xff;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = UDAV_REQ_MEM_READ;
+ USETW(req.wValue, 0x0000);
+ USETW(req.wIndex, offset);
+ USETW(req.wLength, len);
+
+ return (usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000));
+}
+
+static int
+udav_mem_write(struct udav_softc *sc, uint16_t offset, void *buf,
+ int len)
+{
+ struct usb2_device_request req;
+
+ len &= 0xff;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UDAV_REQ_MEM_WRITE;
+ USETW(req.wValue, 0x0000);
+ USETW(req.wIndex, offset);
+ USETW(req.wLength, len);
+
+ return (usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000));
+}
+
+static int
+udav_mem_write1(struct udav_softc *sc, uint16_t offset,
+ uint8_t ch)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UDAV_REQ_MEM_WRITE1;
+ USETW(req.wValue, ch);
+ USETW(req.wIndex, offset);
+ USETW(req.wLength, 0x0000);
+
+ return (usb2_ether_do_request(&sc->sc_ue, &req, NULL, 1000));
+}
+#endif
+
+static int
+udav_csr_read(struct udav_softc *sc, uint16_t offset, void *buf, int len)
+{
+ struct usb2_device_request req;
+
+ len &= 0xff;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = UDAV_REQ_REG_READ;
+ USETW(req.wValue, 0x0000);
+ USETW(req.wIndex, offset);
+ USETW(req.wLength, len);
+
+ return (usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000));
+}
+
+static int
+udav_csr_write(struct udav_softc *sc, uint16_t offset, void *buf, int len)
+{
+ struct usb2_device_request req;
+
+ offset &= 0xff;
+ len &= 0xff;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UDAV_REQ_REG_WRITE;
+ USETW(req.wValue, 0x0000);
+ USETW(req.wIndex, offset);
+ USETW(req.wLength, len);
+
+ return (usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000));
+}
+
+static uint8_t
+udav_csr_read1(struct udav_softc *sc, uint16_t offset)
+{
+ uint8_t val;
+
+ udav_csr_read(sc, offset, &val, 1);
+ return (val);
+}
+
+static int
+udav_csr_write1(struct udav_softc *sc, uint16_t offset,
+ uint8_t ch)
+{
+ struct usb2_device_request req;
+
+ offset &= 0xff;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UDAV_REQ_REG_WRITE1;
+ USETW(req.wValue, ch);
+ USETW(req.wIndex, offset);
+ USETW(req.wLength, 0x0000);
+
+ return (usb2_ether_do_request(&sc->sc_ue, &req, NULL, 1000));
+}
+
+static void
+udav_init(struct usb2_ether *ue)
+{
+ struct udav_softc *sc = ue->ue_sc;
+ struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue);
+
+ UDAV_LOCK_ASSERT(sc, MA_OWNED);
+
+ /*
+ * Cancel pending I/O
+ */
+ udav_stop(ue);
+
+ /* set MAC address */
+ udav_csr_write(sc, UDAV_PAR, IF_LLADDR(ifp), ETHER_ADDR_LEN);
+
+ /* initialize network control register */
+
+ /* disable loopback */
+ UDAV_CLRBIT(sc, UDAV_NCR, UDAV_NCR_LBK0 | UDAV_NCR_LBK1);
+
+ /* Initialize RX control register */
+ UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_DIS_LONG | UDAV_RCR_DIS_CRC);
+
+ /* load multicast filter and update promiscious mode bit */
+ udav_setpromisc(ue);
+
+ /* enable RX */
+ UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_RXEN);
+
+ /* clear POWER_DOWN state of internal PHY */
+ UDAV_SETBIT(sc, UDAV_GPCR, UDAV_GPCR_GEP_CNTL0);
+ UDAV_CLRBIT(sc, UDAV_GPR, UDAV_GPR_GEPIO0);
+
+ usb2_transfer_set_stall(sc->sc_xfer[UDAV_BULK_DT_WR]);
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ udav_start(ue);
+}
+
+static void
+udav_reset(struct udav_softc *sc)
+{
+ int i;
+
+ /* Select PHY */
+#if 1
+ /*
+ * XXX: force select internal phy.
+ * external phy routines are not tested.
+ */
+ UDAV_CLRBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY);
+#else
+ if (sc->sc_flags & UDAV_EXT_PHY)
+ UDAV_SETBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY);
+ else
+ UDAV_CLRBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY);
+#endif
+
+ UDAV_SETBIT(sc, UDAV_NCR, UDAV_NCR_RST);
+
+ for (i = 0; i < UDAV_TX_TIMEOUT; i++) {
+ if (!(udav_csr_read1(sc, UDAV_NCR) & UDAV_NCR_RST))
+ break;
+ if (usb2_ether_pause(&sc->sc_ue, hz / 100))
+ break;
+ }
+
+ usb2_ether_pause(&sc->sc_ue, hz / 100);
+}
+
+#define UDAV_BITS 6
+static void
+udav_setmulti(struct usb2_ether *ue)
+{
+ struct udav_softc *sc = ue->ue_sc;
+ struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue);
+ struct ifmultiaddr *ifma;
+ uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ int h = 0;
+
+ UDAV_LOCK_ASSERT(sc, MA_OWNED);
+
+ if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
+ UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_ALL|UDAV_RCR_PRMSC);
+ return;
+ }
+
+ /* first, zot all the existing hash bits */
+ memset(hashtbl, 0x00, sizeof(hashtbl));
+ hashtbl[7] |= 0x80; /* broadcast address */
+ udav_csr_write(sc, UDAV_MAR, hashtbl, sizeof(hashtbl));
+
+ /* now program new ones */
+ IF_ADDR_LOCK(ifp);
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
+ {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ h = ether_crc32_be(LLADDR((struct sockaddr_dl *)
+ ifma->ifma_addr), ETHER_ADDR_LEN) >> 26;
+ hashtbl[h / 8] |= 1 << (h % 8);
+ }
+ IF_ADDR_UNLOCK(ifp);
+
+ /* disable all multicast */
+ UDAV_CLRBIT(sc, UDAV_RCR, UDAV_RCR_ALL);
+
+ /* write hash value to the register */
+ udav_csr_write(sc, UDAV_MAR, hashtbl, sizeof(hashtbl));
+}
+
+static void
+udav_setpromisc(struct usb2_ether *ue)
+{
+ struct udav_softc *sc = ue->ue_sc;
+ struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue);
+ uint8_t rxmode;
+
+ rxmode = udav_csr_read1(sc, UDAV_RCR);
+ rxmode &= ~(UDAV_RCR_ALL | UDAV_RCR_PRMSC);
+
+ if (ifp->if_flags & IFF_PROMISC)
+ rxmode |= UDAV_RCR_ALL | UDAV_RCR_PRMSC;
+ else if (ifp->if_flags & IFF_ALLMULTI)
+ rxmode |= UDAV_RCR_ALL;
+
+ /* write new mode bits */
+ udav_csr_write1(sc, UDAV_RCR, rxmode);
+}
+
+static void
+udav_start(struct usb2_ether *ue)
+{
+ struct udav_softc *sc = ue->ue_sc;
+
+ /*
+ * start the USB transfers, if not already started:
+ */
+ usb2_transfer_start(sc->sc_xfer[UDAV_INTR_DT_RD]);
+ usb2_transfer_start(sc->sc_xfer[UDAV_BULK_DT_RD]);
+ usb2_transfer_start(sc->sc_xfer[UDAV_BULK_DT_WR]);
+}
+
+static void
+udav_bulk_write_callback(struct usb2_xfer *xfer)
+{
+ struct udav_softc *sc = xfer->priv_sc;
+ struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue);
+ struct mbuf *m;
+ int extra_len;
+ int temp_len;
+ uint8_t buf[2];
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTFN(11, "transfer complete\n");
+ ifp->if_opackets++;
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ if ((sc->sc_flags & UDAV_FLAG_LINK) == 0) {
+ /*
+ * don't send anything if there is no link !
+ */
+ return;
+ }
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+
+ if (m == NULL)
+ return;
+ if (m->m_pkthdr.len > MCLBYTES)
+ m->m_pkthdr.len = MCLBYTES;
+ if (m->m_pkthdr.len < UDAV_MIN_FRAME_LEN) {
+ extra_len = UDAV_MIN_FRAME_LEN - m->m_pkthdr.len;
+ } else {
+ extra_len = 0;
+ }
+
+ temp_len = (m->m_pkthdr.len + extra_len);
+
+ /*
+ * the frame length is specified in the first 2 bytes of the
+ * buffer
+ */
+ buf[0] = (uint8_t)(temp_len);
+ buf[1] = (uint8_t)(temp_len >> 8);
+
+ temp_len += 2;
+
+ usb2_copy_in(xfer->frbuffers, 0, buf, 2);
+
+ usb2_m_copy_in(xfer->frbuffers, 2,
+ m, 0, m->m_pkthdr.len);
+
+ if (extra_len) {
+ usb2_bzero(xfer->frbuffers, temp_len - extra_len,
+ extra_len);
+ }
+ /*
+ * if there's a BPF listener, bounce a copy
+ * of this frame to him:
+ */
+ BPF_MTAP(ifp, m);
+
+ m_freem(m);
+
+ xfer->frlengths[0] = temp_len;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ DPRINTFN(11, "transfer error, %s\n",
+ usb2_errstr(xfer->error));
+
+ ifp->if_oerrors++;
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+udav_bulk_read_callback(struct usb2_xfer *xfer)
+{
+ struct udav_softc *sc = xfer->priv_sc;
+ struct usb2_ether *ue = &sc->sc_ue;
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+ struct udav_rxpkt stat;
+ int len;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ if (xfer->actlen < sizeof(stat) + ETHER_CRC_LEN) {
+ ifp->if_ierrors++;
+ goto tr_setup;
+ }
+ usb2_copy_out(xfer->frbuffers, 0, &stat, sizeof(stat));
+ xfer->actlen -= sizeof(stat);
+ len = min(xfer->actlen, le16toh(stat.pktlen));
+ len -= ETHER_CRC_LEN;
+
+ if (stat.rxstat & UDAV_RSR_LCS) {
+ ifp->if_collisions++;
+ goto tr_setup;
+ }
+ if (stat.rxstat & UDAV_RSR_ERR) {
+ ifp->if_ierrors++;
+ goto tr_setup;
+ }
+ usb2_ether_rxbuf(ue, xfer->frbuffers, sizeof(stat), len);
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ usb2_ether_rxflush(ue);
+ return;
+
+ default: /* Error */
+ DPRINTF("bulk read error, %s\n",
+ usb2_errstr(xfer->error));
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+udav_intr_callback(struct usb2_xfer *xfer)
+{
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+udav_stop(struct usb2_ether *ue)
+{
+ struct udav_softc *sc = ue->ue_sc;
+ struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue);
+
+ UDAV_LOCK_ASSERT(sc, MA_OWNED);
+
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ sc->sc_flags &= ~UDAV_FLAG_LINK;
+
+ /*
+ * stop all the transfers, if not already stopped:
+ */
+ usb2_transfer_stop(sc->sc_xfer[UDAV_BULK_DT_WR]);
+ usb2_transfer_stop(sc->sc_xfer[UDAV_BULK_DT_RD]);
+ usb2_transfer_stop(sc->sc_xfer[UDAV_INTR_DT_RD]);
+
+ udav_reset(sc);
+}
+
+static int
+udav_ifmedia_upd(struct ifnet *ifp)
+{
+ struct udav_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+
+ UDAV_LOCK_ASSERT(sc, MA_OWNED);
+
+ sc->sc_flags &= ~UDAV_FLAG_LINK;
+ if (mii->mii_instance) {
+ struct mii_softc *miisc;
+
+ LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
+ mii_phy_reset(miisc);
+ }
+ mii_mediachg(mii);
+ return (0);
+}
+
+static void
+udav_ifmedia_status(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct udav_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+
+ UDAV_LOCK(sc);
+ mii_pollstat(mii);
+ UDAV_UNLOCK(sc);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+}
+
+static void
+udav_tick(struct usb2_ether *ue)
+{
+ struct udav_softc *sc = ue->ue_sc;
+ struct mii_data *mii = GET_MII(sc);
+
+ UDAV_LOCK_ASSERT(sc, MA_OWNED);
+
+ mii_tick(mii);
+ if ((sc->sc_flags & UDAV_FLAG_LINK) == 0
+ && mii->mii_media_status & IFM_ACTIVE &&
+ IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
+ sc->sc_flags |= UDAV_FLAG_LINK;
+ udav_start(ue);
+ }
+}
+
+static int
+udav_miibus_readreg(device_t dev, int phy, int reg)
+{
+ struct udav_softc *sc = device_get_softc(dev);
+ uint16_t data16;
+ uint8_t val[2];
+ int locked;
+
+ /* XXX: one PHY only for the internal PHY */
+ if (phy != 0)
+ return (0);
+
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ UDAV_LOCK(sc);
+
+ /* select internal PHY and set PHY register address */
+ udav_csr_write1(sc, UDAV_EPAR,
+ UDAV_EPAR_PHY_ADR0 | (reg & UDAV_EPAR_EROA_MASK));
+
+ /* select PHY operation and start read command */
+ udav_csr_write1(sc, UDAV_EPCR, UDAV_EPCR_EPOS | UDAV_EPCR_ERPRR);
+
+ /* XXX: should we wait? */
+
+ /* end read command */
+ UDAV_CLRBIT(sc, UDAV_EPCR, UDAV_EPCR_ERPRR);
+
+ /* retrieve the result from data registers */
+ udav_csr_read(sc, UDAV_EPDRL, val, 2);
+
+ data16 = (val[0] | (val[1] << 8));
+
+ DPRINTFN(11, "phy=%d reg=0x%04x => 0x%04x\n",
+ phy, reg, data16);
+
+ if (!locked)
+ UDAV_UNLOCK(sc);
+ return (data16);
+}
+
+static int
+udav_miibus_writereg(device_t dev, int phy, int reg, int data)
+{
+ struct udav_softc *sc = device_get_softc(dev);
+ uint8_t val[2];
+ int locked;
+
+ /* XXX: one PHY only for the internal PHY */
+ if (phy != 0)
+ return (0);
+
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ UDAV_LOCK(sc);
+
+ /* select internal PHY and set PHY register address */
+ udav_csr_write1(sc, UDAV_EPAR,
+ UDAV_EPAR_PHY_ADR0 | (reg & UDAV_EPAR_EROA_MASK));
+
+ /* put the value to the data registers */
+ val[0] = (data & 0xff);
+ val[1] = (data >> 8) & 0xff;
+ udav_csr_write(sc, UDAV_EPDRL, val, 2);
+
+ /* select PHY operation and start write command */
+ udav_csr_write1(sc, UDAV_EPCR, UDAV_EPCR_EPOS | UDAV_EPCR_ERPRW);
+
+ /* XXX: should we wait? */
+
+ /* end write command */
+ UDAV_CLRBIT(sc, UDAV_EPCR, UDAV_EPCR_ERPRW);
+
+ if (!locked)
+ UDAV_UNLOCK(sc);
+ return (0);
+}
+
+static void
+udav_miibus_statchg(device_t dev)
+{
+ /* nothing to do */
+}
+
+/*
+ * Stop all chip I/O so that the kernel's probe routines don't
+ * get confused by errant DMAs when rebooting.
+ */
+static int
+udav_shutdown(device_t dev)
+{
+ struct udav_softc *sc = device_get_softc(dev);
+
+ usb2_ether_ifshutdown(&sc->sc_ue);
+
+ return (0);
+}
diff --git a/sys/dev/usb/net/if_udavreg.h b/sys/dev/usb/net/if_udavreg.h
new file mode 100644
index 0000000..d652f5b
--- /dev/null
+++ b/sys/dev/usb/net/if_udavreg.h
@@ -0,0 +1,166 @@
+/* $NetBSD: if_udavreg.h,v 1.2 2003/09/04 15:17:39 tsutsui Exp $ */
+/* $nabe: if_udavreg.h,v 1.2 2003/08/21 16:26:40 nabe Exp $ */
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2003
+ * Shingo WATANABE <nabe@nabechan.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.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * 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.
+ *
+ */
+
+#define UDAV_IFACE_INDEX 0
+#define UDAV_CONFIG_INDEX 0 /* config number 1 */
+
+#define UDAV_TX_TIMEOUT 1000
+#define UDAV_TIMEOUT 10000
+
+#define UDAV_TX_TIMEOUT 1000
+#define UDAV_TIMEOUT 10000
+
+/* Packet length */
+#define UDAV_MIN_FRAME_LEN 60
+
+/* Request */
+#define UDAV_REQ_REG_READ 0x00 /* Read from register(s) */
+#define UDAV_REQ_REG_WRITE 0x01 /* Write to register(s) */
+#define UDAV_REQ_REG_WRITE1 0x03 /* Write to a register */
+
+#define UDAV_REQ_MEM_READ 0x02 /* Read from memory */
+#define UDAV_REQ_MEM_WRITE 0x05 /* Write to memory */
+#define UDAV_REQ_MEM_WRITE1 0x07 /* Write a byte to memory */
+
+/* Registers */
+#define UDAV_NCR 0x00 /* Network Control Register */
+#define UDAV_NCR_EXT_PHY (1<<7) /* Select External PHY */
+#define UDAV_NCR_WAKEEN (1<<6) /* Wakeup Event Enable */
+#define UDAV_NCR_FCOL (1<<4) /* Force Collision Mode */
+#define UDAV_NCR_FDX (1<<3) /* Full-Duplex Mode (RO on Int. PHY) */
+#define UDAV_NCR_LBK1 (1<<2) /* Lookback Mode */
+#define UDAV_NCR_LBK0 (1<<1) /* Lookback Mode */
+#define UDAV_NCR_RST (1<<0) /* Software reset */
+
+#define UDAV_RCR 0x05 /* RX Control Register */
+#define UDAV_RCR_WTDIS (1<<6) /* Watchdog Timer Disable */
+#define UDAV_RCR_DIS_LONG (1<<5) /* Discard Long Packet(over 1522Byte) */
+#define UDAV_RCR_DIS_CRC (1<<4) /* Discard CRC Error Packet */
+#define UDAV_RCR_ALL (1<<3) /* Pass All Multicast */
+#define UDAV_RCR_RUNT (1<<2) /* Pass Runt Packet */
+#define UDAV_RCR_PRMSC (1<<1) /* Promiscuous Mode */
+#define UDAV_RCR_RXEN (1<<0) /* RX Enable */
+
+#define UDAV_RSR 0x06 /* RX Status Register */
+#define UDAV_RSR_RF (1<<7) /* Runt Frame */
+#define UDAV_RSR_MF (1<<6) /* Multicast Frame */
+#define UDAV_RSR_LCS (1<<5) /* Late Collision Seen */
+#define UDAV_RSR_RWTO (1<<4) /* Receive Watchdog Time-Out */
+#define UDAV_RSR_PLE (1<<3) /* Physical Layer Error */
+#define UDAV_RSR_AE (1<<2) /* Alignment Error */
+#define UDAV_RSR_CE (1<<1) /* CRC Error */
+#define UDAV_RSR_FOE (1<<0) /* FIFO Overflow Error */
+#define UDAV_RSR_ERR (UDAV_RSR_RF | UDAV_RSR_LCS | \
+ UDAV_RSR_RWTO | UDAV_RSR_PLE | \
+ UDAV_RSR_AE | UDAV_RSR_CE | UDAV_RSR_FOE)
+
+#define UDAV_EPCR 0x0b /* EEPROM & PHY Control Register */
+#define UDAV_EPCR_REEP (1<<5) /* Reload EEPROM */
+#define UDAV_EPCR_WEP (1<<4) /* Write EEPROM enable */
+#define UDAV_EPCR_EPOS (1<<3) /* EEPROM or PHY Operation Select */
+#define UDAV_EPCR_ERPRR (1<<2) /* EEPROM/PHY Register Read Command */
+#define UDAV_EPCR_ERPRW (1<<1) /* EEPROM/PHY Register Write Command */
+#define UDAV_EPCR_ERRE (1<<0) /* EEPROM/PHY Access Status */
+
+#define UDAV_EPAR 0x0c /* EEPROM & PHY Control Register */
+#define UDAV_EPAR_PHY_ADR1 (1<<7) /* PHY Address bit 1 */
+#define UDAV_EPAR_PHY_ADR0 (1<<6) /* PHY Address bit 0 */
+#define UDAV_EPAR_EROA (1<<0) /* EEPROM Word/PHY Register Address */
+#define UDAV_EPAR_EROA_MASK (0x1f) /* [5:0] */
+
+#define UDAV_EPDRL 0x0d /* EEPROM & PHY Data Register */
+#define UDAV_EPDRH 0x0e /* EEPROM & PHY Data Register */
+
+#define UDAV_PAR0 0x10 /* Ethernet Address, load from EEPROM */
+#define UDAV_PAR1 0x11 /* Ethernet Address, load from EEPROM */
+#define UDAV_PAR2 0x12 /* Ethernet Address, load from EEPROM */
+#define UDAV_PAR3 0x13 /* Ethernet Address, load from EEPROM */
+#define UDAV_PAR4 0x14 /* Ethernet Address, load from EEPROM */
+#define UDAV_PAR5 0x15 /* Ethernet Address, load from EEPROM */
+#define UDAV_PAR UDAV_PAR0
+
+#define UDAV_MAR0 0x16 /* Multicast Register */
+#define UDAV_MAR1 0x17 /* Multicast Register */
+#define UDAV_MAR2 0x18 /* Multicast Register */
+#define UDAV_MAR3 0x19 /* Multicast Register */
+#define UDAV_MAR4 0x1a /* Multicast Register */
+#define UDAV_MAR5 0x1b /* Multicast Register */
+#define UDAV_MAR6 0x1c /* Multicast Register */
+#define UDAV_MAR7 0x1d /* Multicast Register */
+#define UDAV_MAR UDAV_MAR0
+
+#define UDAV_GPCR 0x1e /* General purpose control register */
+#define UDAV_GPCR_GEP_CNTL6 (1<<6) /* General purpose control 6 */
+#define UDAV_GPCR_GEP_CNTL5 (1<<5) /* General purpose control 5 */
+#define UDAV_GPCR_GEP_CNTL4 (1<<4) /* General purpose control 4 */
+#define UDAV_GPCR_GEP_CNTL3 (1<<3) /* General purpose control 3 */
+#define UDAV_GPCR_GEP_CNTL2 (1<<2) /* General purpose control 2 */
+#define UDAV_GPCR_GEP_CNTL1 (1<<1) /* General purpose control 1 */
+#define UDAV_GPCR_GEP_CNTL0 (1<<0) /* General purpose control 0 */
+
+#define UDAV_GPR 0x1f /* General purpose register */
+#define UDAV_GPR_GEPIO6 (1<<6) /* General purpose 6 */
+#define UDAV_GPR_GEPIO5 (1<<5) /* General purpose 5 */
+#define UDAV_GPR_GEPIO4 (1<<4) /* General purpose 4 */
+#define UDAV_GPR_GEPIO3 (1<<3) /* General purpose 3 */
+#define UDAV_GPR_GEPIO2 (1<<2) /* General purpose 2 */
+#define UDAV_GPR_GEPIO1 (1<<1) /* General purpose 1 */
+#define UDAV_GPR_GEPIO0 (1<<0) /* General purpose 0 */
+
+#define GET_MII(sc) usb2_ether_getmii(&(sc)->sc_ue)
+
+struct udav_rxpkt {
+ uint8_t rxstat;
+ uint16_t pktlen;
+} __packed;
+
+enum {
+ UDAV_BULK_DT_WR,
+ UDAV_BULK_DT_RD,
+ UDAV_INTR_DT_RD,
+ UDAV_N_TRANSFER,
+};
+
+struct udav_softc {
+ struct usb2_ether sc_ue;
+ struct mtx sc_mtx;
+ struct usb2_xfer *sc_xfer[UDAV_N_TRANSFER];
+
+ int sc_flags;
+#define UDAV_FLAG_LINK 0x0001
+#define UDAV_FLAG_EXT_PHY 0x0040
+};
+
+#define UDAV_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define UDAV_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define UDAV_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t)
diff --git a/sys/dev/usb/net/usb_ethernet.c b/sys/dev/usb/net/usb_ethernet.c
new file mode 100644
index 0000000..ac4b701
--- /dev/null
+++ b/sys/dev/usb/net/usb_ethernet.c
@@ -0,0 +1,587 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2009 Andrew Thompson (thompsa@FreeBSD.org)
+ *
+ * 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 <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_endian.h>
+#include <dev/usb/usb.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/net/usb_ethernet.h>
+
+SYSCTL_NODE(_net, OID_AUTO, ue, CTLFLAG_RD, 0, "USB Ethernet parameters");
+
+#define UE_LOCK(_ue) mtx_lock((_ue)->ue_mtx)
+#define UE_UNLOCK(_ue) mtx_unlock((_ue)->ue_mtx)
+#define UE_LOCK_ASSERT(_ue, t) mtx_assert((_ue)->ue_mtx, t)
+
+MODULE_DEPEND(uether, usb, 1, 1, 1);
+MODULE_DEPEND(uether, miibus, 1, 1, 1);
+
+static struct unrhdr *ueunit;
+
+static usb2_proc_callback_t ue_attach_post_task;
+static usb2_proc_callback_t ue_promisc_task;
+static usb2_proc_callback_t ue_setmulti_task;
+static usb2_proc_callback_t ue_ifmedia_task;
+static usb2_proc_callback_t ue_tick_task;
+static usb2_proc_callback_t ue_start_task;
+static usb2_proc_callback_t ue_stop_task;
+
+static void ue_init(void *);
+static void ue_start(struct ifnet *);
+static int ue_ifmedia_upd(struct ifnet *);
+static void ue_watchdog(void *);
+
+/*
+ * Return values:
+ * 0: success
+ * Else: device has been detached
+ */
+uint8_t
+usb2_ether_pause(struct usb2_ether *ue, unsigned int _ticks)
+{
+ if (usb2_proc_is_gone(&ue->ue_tq)) {
+ /* nothing to do */
+ return (1);
+ }
+ usb2_pause_mtx(ue->ue_mtx, _ticks);
+ return (0);
+}
+
+static void
+ue_queue_command(struct usb2_ether *ue,
+ usb2_proc_callback_t *fn,
+ struct usb2_proc_msg *t0, struct usb2_proc_msg *t1)
+{
+ struct usb2_ether_cfg_task *task;
+
+ UE_LOCK_ASSERT(ue, MA_OWNED);
+
+ if (usb2_proc_is_gone(&ue->ue_tq)) {
+ return; /* nothing to do */
+ }
+ /*
+ * NOTE: The task cannot get executed before we drop the
+ * "sc_mtx" mutex. It is safe to update fields in the message
+ * structure after that the message got queued.
+ */
+ task = (struct usb2_ether_cfg_task *)
+ usb2_proc_msignal(&ue->ue_tq, t0, t1);
+
+ /* Setup callback and self pointers */
+ task->hdr.pm_callback = fn;
+ task->ue = ue;
+
+ /*
+ * Start and stop must be synchronous!
+ */
+ if ((fn == ue_start_task) || (fn == ue_stop_task))
+ usb2_proc_mwait(&ue->ue_tq, t0, t1);
+}
+
+struct ifnet *
+usb2_ether_getifp(struct usb2_ether *ue)
+{
+ return (ue->ue_ifp);
+}
+
+struct mii_data *
+usb2_ether_getmii(struct usb2_ether *ue)
+{
+ return (device_get_softc(ue->ue_miibus));
+}
+
+void *
+usb2_ether_getsc(struct usb2_ether *ue)
+{
+ return (ue->ue_sc);
+}
+
+static int
+ue_sysctl_parent(SYSCTL_HANDLER_ARGS)
+{
+ struct usb2_ether *ue = arg1;
+ const char *name;
+
+ name = device_get_nameunit(ue->ue_dev);
+ return SYSCTL_OUT(req, name, strlen(name));
+}
+
+int
+usb2_ether_ifattach(struct usb2_ether *ue)
+{
+ int error;
+
+ /* check some critical parameters */
+ if ((ue->ue_dev == NULL) ||
+ (ue->ue_udev == NULL) ||
+ (ue->ue_mtx == NULL) ||
+ (ue->ue_methods == NULL))
+ return (EINVAL);
+
+ error = usb2_proc_create(&ue->ue_tq, ue->ue_mtx,
+ device_get_nameunit(ue->ue_dev), USB_PRI_MED);
+ if (error) {
+ device_printf(ue->ue_dev, "could not setup taskqueue\n");
+ goto error;
+ }
+
+ /* fork rest of the attach code */
+ UE_LOCK(ue);
+ ue_queue_command(ue, ue_attach_post_task,
+ &ue->ue_sync_task[0].hdr,
+ &ue->ue_sync_task[1].hdr);
+ UE_UNLOCK(ue);
+
+error:
+ return (error);
+}
+
+static void
+ue_attach_post_task(struct usb2_proc_msg *_task)
+{
+ struct usb2_ether_cfg_task *task =
+ (struct usb2_ether_cfg_task *)_task;
+ struct usb2_ether *ue = task->ue;
+ struct ifnet *ifp;
+ int error;
+ char num[14]; /* sufficient for 32 bits */
+
+ /* first call driver's post attach routine */
+ ue->ue_methods->ue_attach_post(ue);
+
+ UE_UNLOCK(ue);
+
+ ue->ue_unit = alloc_unr(ueunit);
+ usb2_callout_init_mtx(&ue->ue_watchdog, ue->ue_mtx, 0);
+ sysctl_ctx_init(&ue->ue_sysctl_ctx);
+
+ ifp = if_alloc(IFT_ETHER);
+ if (ifp == NULL) {
+ device_printf(ue->ue_dev, "could not allocate ifnet\n");
+ goto error;
+ }
+
+ ifp->if_softc = ue;
+ if_initname(ifp, "ue", ue->ue_unit);
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ if (ue->ue_methods->ue_ioctl != NULL)
+ ifp->if_ioctl = ue->ue_methods->ue_ioctl;
+ else
+ ifp->if_ioctl = usb2_ether_ioctl;
+ ifp->if_start = ue_start;
+ ifp->if_init = ue_init;
+ IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
+ ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN;
+ IFQ_SET_READY(&ifp->if_snd);
+ ue->ue_ifp = ifp;
+
+ if (ue->ue_methods->ue_mii_upd != NULL &&
+ ue->ue_methods->ue_mii_sts != NULL) {
+ mtx_lock(&Giant); /* device_xxx() depends on this */
+ error = mii_phy_probe(ue->ue_dev, &ue->ue_miibus,
+ ue_ifmedia_upd, ue->ue_methods->ue_mii_sts);
+ mtx_unlock(&Giant);
+ if (error) {
+ device_printf(ue->ue_dev, "MII without any PHY\n");
+ goto error;
+ }
+ }
+
+ if_printf(ifp, "<USB Ethernet> on %s\n", device_get_nameunit(ue->ue_dev));
+ ether_ifattach(ifp, ue->ue_eaddr);
+
+ snprintf(num, sizeof(num), "%u", ue->ue_unit);
+ ue->ue_sysctl_oid = SYSCTL_ADD_NODE(&ue->ue_sysctl_ctx,
+ &SYSCTL_NODE_CHILDREN(_net, ue),
+ OID_AUTO, num, CTLFLAG_RD, NULL, "");
+ SYSCTL_ADD_PROC(&ue->ue_sysctl_ctx,
+ SYSCTL_CHILDREN(ue->ue_sysctl_oid), OID_AUTO,
+ "%parent", CTLFLAG_RD, ue, 0,
+ ue_sysctl_parent, "A", "parent device");
+
+ UE_LOCK(ue);
+ return;
+
+error:
+ free_unr(ueunit, ue->ue_unit);
+ if (ue->ue_ifp != NULL) {
+ if_free(ue->ue_ifp);
+ ue->ue_ifp = NULL;
+ }
+ UE_LOCK(ue);
+ return;
+}
+
+void
+usb2_ether_ifdetach(struct usb2_ether *ue)
+{
+ struct ifnet *ifp;
+
+ /* wait for any post attach or other command to complete */
+ usb2_proc_drain(&ue->ue_tq);
+
+ /* read "ifnet" pointer after taskqueue drain */
+ ifp = ue->ue_ifp;
+
+ if (ifp != NULL) {
+
+ /* we are not running any more */
+ UE_LOCK(ue);
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ UE_UNLOCK(ue);
+
+ /* drain any callouts */
+ usb2_callout_drain(&ue->ue_watchdog);
+
+ /* detach miibus */
+ if (ue->ue_miibus != NULL) {
+ mtx_lock(&Giant); /* device_xxx() depends on this */
+ device_delete_child(ue->ue_dev, ue->ue_miibus);
+ mtx_unlock(&Giant);
+ }
+
+ /* detach ethernet */
+ ether_ifdetach(ifp);
+
+ /* free interface instance */
+ if_free(ifp);
+
+ /* free sysctl */
+ sysctl_ctx_free(&ue->ue_sysctl_ctx);
+
+ /* free unit */
+ free_unr(ueunit, ue->ue_unit);
+ }
+
+ /* free taskqueue, if any */
+ usb2_proc_free(&ue->ue_tq);
+}
+
+void
+usb2_ether_ifshutdown(struct usb2_ether *ue)
+{
+ struct ifnet *ifp = ue->ue_ifp;
+
+ UE_LOCK(ue);
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ ue_queue_command(ue, ue_stop_task,
+ &ue->ue_sync_task[0].hdr,
+ &ue->ue_sync_task[1].hdr);
+ UE_UNLOCK(ue);
+}
+
+uint8_t
+usb2_ether_is_gone(struct usb2_ether *ue)
+{
+ return (usb2_proc_is_gone(&ue->ue_tq));
+}
+
+static void
+ue_init(void *arg)
+{
+ struct usb2_ether *ue = arg;
+
+ UE_LOCK(ue);
+ ue_queue_command(ue, ue_start_task,
+ &ue->ue_sync_task[0].hdr,
+ &ue->ue_sync_task[1].hdr);
+ UE_UNLOCK(ue);
+}
+
+static void
+ue_start_task(struct usb2_proc_msg *_task)
+{
+ struct usb2_ether_cfg_task *task =
+ (struct usb2_ether_cfg_task *)_task;
+ struct usb2_ether *ue = task->ue;
+ struct ifnet *ifp = ue->ue_ifp;
+
+ UE_LOCK_ASSERT(ue, MA_OWNED);
+
+ ue->ue_methods->ue_init(ue);
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ return;
+
+ if (ue->ue_methods->ue_tick != NULL)
+ usb2_callout_reset(&ue->ue_watchdog, hz, ue_watchdog, ue);
+}
+
+static void
+ue_stop_task(struct usb2_proc_msg *_task)
+{
+ struct usb2_ether_cfg_task *task =
+ (struct usb2_ether_cfg_task *)_task;
+ struct usb2_ether *ue = task->ue;
+
+ UE_LOCK_ASSERT(ue, MA_OWNED);
+
+ usb2_callout_stop(&ue->ue_watchdog);
+
+ ue->ue_methods->ue_stop(ue);
+}
+
+static void
+ue_start(struct ifnet *ifp)
+{
+ struct usb2_ether *ue = ifp->if_softc;
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ return;
+
+ UE_LOCK(ue);
+ ue->ue_methods->ue_start(ue);
+ UE_UNLOCK(ue);
+}
+
+static void
+ue_promisc_task(struct usb2_proc_msg *_task)
+{
+ struct usb2_ether_cfg_task *task =
+ (struct usb2_ether_cfg_task *)_task;
+ struct usb2_ether *ue = task->ue;
+
+ ue->ue_methods->ue_setpromisc(ue);
+}
+
+static void
+ue_setmulti_task(struct usb2_proc_msg *_task)
+{
+ struct usb2_ether_cfg_task *task =
+ (struct usb2_ether_cfg_task *)_task;
+ struct usb2_ether *ue = task->ue;
+
+ ue->ue_methods->ue_setmulti(ue);
+}
+
+static int
+ue_ifmedia_upd(struct ifnet *ifp)
+{
+ struct usb2_ether *ue = ifp->if_softc;
+
+ /* Defer to process context */
+ UE_LOCK(ue);
+ ue_queue_command(ue, ue_ifmedia_task,
+ &ue->ue_media_task[0].hdr,
+ &ue->ue_media_task[1].hdr);
+ UE_UNLOCK(ue);
+
+ return (0);
+}
+
+static void
+ue_ifmedia_task(struct usb2_proc_msg *_task)
+{
+ struct usb2_ether_cfg_task *task =
+ (struct usb2_ether_cfg_task *)_task;
+ struct usb2_ether *ue = task->ue;
+ struct ifnet *ifp = ue->ue_ifp;
+
+ ue->ue_methods->ue_mii_upd(ifp);
+}
+
+static void
+ue_watchdog(void *arg)
+{
+ struct usb2_ether *ue = arg;
+ struct ifnet *ifp = ue->ue_ifp;
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ return;
+
+ ue_queue_command(ue, ue_tick_task,
+ &ue->ue_tick_task[0].hdr,
+ &ue->ue_tick_task[1].hdr);
+
+ usb2_callout_reset(&ue->ue_watchdog, hz, ue_watchdog, ue);
+}
+
+static void
+ue_tick_task(struct usb2_proc_msg *_task)
+{
+ struct usb2_ether_cfg_task *task =
+ (struct usb2_ether_cfg_task *)_task;
+ struct usb2_ether *ue = task->ue;
+ struct ifnet *ifp = ue->ue_ifp;
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ return;
+
+ ue->ue_methods->ue_tick(ue);
+}
+
+int
+usb2_ether_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
+{
+ struct usb2_ether *ue = ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *)data;
+ struct mii_data *mii;
+ int error = 0;
+
+ switch (command) {
+ case SIOCSIFFLAGS:
+ UE_LOCK(ue);
+ if (ifp->if_flags & IFF_UP) {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ ue_queue_command(ue, ue_promisc_task,
+ &ue->ue_promisc_task[0].hdr,
+ &ue->ue_promisc_task[1].hdr);
+ else
+ ue_queue_command(ue, ue_start_task,
+ &ue->ue_sync_task[0].hdr,
+ &ue->ue_sync_task[1].hdr);
+ } else {
+ ue_queue_command(ue, ue_stop_task,
+ &ue->ue_sync_task[0].hdr,
+ &ue->ue_sync_task[1].hdr);
+ }
+ UE_UNLOCK(ue);
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ UE_LOCK(ue);
+ ue_queue_command(ue, ue_setmulti_task,
+ &ue->ue_multi_task[0].hdr,
+ &ue->ue_multi_task[1].hdr);
+ UE_UNLOCK(ue);
+ break;
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ if (ue->ue_miibus != NULL) {
+ mii = device_get_softc(ue->ue_miibus);
+ error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
+ } else
+ error = ether_ioctl(ifp, command, data);
+ break;
+ default:
+ error = ether_ioctl(ifp, command, data);
+ break;
+ }
+ return (error);
+}
+
+static int
+usb2_ether_modevent(module_t mod, int type, void *data)
+{
+
+ switch (type) {
+ case MOD_LOAD:
+ ueunit = new_unrhdr(0, INT_MAX, NULL);
+ break;
+ case MOD_UNLOAD:
+ break;
+ default:
+ return (EOPNOTSUPP);
+ }
+ return (0);
+}
+static moduledata_t usb2_ether_mod = {
+ "uether",
+ usb2_ether_modevent,
+ 0
+};
+
+int
+usb2_ether_rxmbuf(struct usb2_ether *ue, struct mbuf *m,
+ unsigned int len)
+{
+ struct ifnet *ifp = ue->ue_ifp;
+
+ UE_LOCK_ASSERT(ue, MA_OWNED);
+
+ /* finalize mbuf */
+ ifp->if_ipackets++;
+ m->m_pkthdr.rcvif = ifp;
+ m->m_pkthdr.len = m->m_len = len;
+
+ /* enqueue for later when the lock can be released */
+ _IF_ENQUEUE(&ue->ue_rxq, m);
+ return (0);
+}
+
+int
+usb2_ether_rxbuf(struct usb2_ether *ue, struct usb2_page_cache *pc,
+ unsigned int offset, unsigned int len)
+{
+ struct ifnet *ifp = ue->ue_ifp;
+ struct mbuf *m;
+
+ UE_LOCK_ASSERT(ue, MA_OWNED);
+
+ if (len < ETHER_HDR_LEN || len > MCLBYTES)
+ return (1);
+
+ m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+ if (m == NULL) {
+ ifp->if_ierrors++;
+ return (ENOMEM);
+ }
+
+ m_adj(m, ETHER_ALIGN);
+ usb2_copy_out(pc, offset, mtod(m, uint8_t *), len);
+
+ /* finalize mbuf */
+ ifp->if_ipackets++;
+ m->m_pkthdr.rcvif = ifp;
+ m->m_pkthdr.len = m->m_len = len;
+
+ /* enqueue for later when the lock can be released */
+ _IF_ENQUEUE(&ue->ue_rxq, m);
+ return (0);
+}
+
+void
+usb2_ether_rxflush(struct usb2_ether *ue)
+{
+ struct ifnet *ifp = ue->ue_ifp;
+ struct mbuf *m;
+
+ UE_LOCK_ASSERT(ue, MA_OWNED);
+
+ for (;;) {
+ _IF_DEQUEUE(&ue->ue_rxq, m);
+ if (m == NULL)
+ break;
+
+ /*
+ * The USB xfer has been resubmitted so its safe to unlock now.
+ */
+ UE_UNLOCK(ue);
+ ifp->if_input(ifp, m);
+ UE_LOCK(ue);
+ }
+}
+
+DECLARE_MODULE(uether, usb2_ether_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
+MODULE_VERSION(uether, 1);
diff --git a/sys/dev/usb/net/usb_ethernet.h b/sys/dev/usb/net/usb_ethernet.h
new file mode 100644
index 0000000..0ee36f2
--- /dev/null
+++ b/sys/dev/usb/net/usb_ethernet.h
@@ -0,0 +1,122 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 Hans Petter Selasky. 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.
+ */
+
+#ifndef _USB2_ETHERNET_H_
+#define _USB2_ETHERNET_H_
+
+#include "opt_inet.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/limits.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/bpf.h>
+#include <net/ethernet.h>
+
+#include "miibus_if.h"
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+struct usb2_ether;
+struct usb2_device_request;
+
+typedef void (usb2_ether_fn_t)(struct usb2_ether *);
+
+struct usb2_ether_methods {
+ usb2_ether_fn_t *ue_attach_post;
+ usb2_ether_fn_t *ue_start;
+ usb2_ether_fn_t *ue_init;
+ usb2_ether_fn_t *ue_stop;
+ usb2_ether_fn_t *ue_setmulti;
+ usb2_ether_fn_t *ue_setpromisc;
+ usb2_ether_fn_t *ue_tick;
+ int (*ue_mii_upd)(struct ifnet *);
+ void (*ue_mii_sts)(struct ifnet *,
+ struct ifmediareq *);
+ int (*ue_ioctl)(struct ifnet *, u_long, caddr_t);
+
+};
+
+struct usb2_ether_cfg_task {
+ struct usb2_proc_msg hdr;
+ struct usb2_ether *ue;
+};
+
+struct usb2_ether {
+ /* NOTE: the "ue_ifp" pointer must be first --hps */
+ struct ifnet *ue_ifp;
+ struct mtx *ue_mtx;
+ const struct usb2_ether_methods *ue_methods;
+ struct sysctl_oid *ue_sysctl_oid;
+ void *ue_sc;
+ struct usb2_device *ue_udev; /* used by usb2_ether_do_request() */
+ device_t ue_dev;
+ device_t ue_miibus;
+
+ struct usb2_process ue_tq;
+ struct sysctl_ctx_list ue_sysctl_ctx;
+ struct ifqueue ue_rxq;
+ struct usb2_callout ue_watchdog;
+ struct usb2_ether_cfg_task ue_sync_task[2];
+ struct usb2_ether_cfg_task ue_media_task[2];
+ struct usb2_ether_cfg_task ue_multi_task[2];
+ struct usb2_ether_cfg_task ue_promisc_task[2];
+ struct usb2_ether_cfg_task ue_tick_task[2];
+
+ int ue_unit;
+
+ /* ethernet address from eeprom */
+ uint8_t ue_eaddr[ETHER_ADDR_LEN];
+};
+
+#define usb2_ether_do_request(ue,req,data,timo) \
+ usb2_do_request_proc((ue)->ue_udev,&(ue)->ue_tq,req,data,0,NULL,timo)
+
+uint8_t usb2_ether_pause(struct usb2_ether *, unsigned int);
+struct ifnet *usb2_ether_getifp(struct usb2_ether *);
+struct mii_data *usb2_ether_getmii(struct usb2_ether *);
+void *usb2_ether_getsc(struct usb2_ether *);
+int usb2_ether_ifattach(struct usb2_ether *);
+void usb2_ether_ifdetach(struct usb2_ether *);
+int usb2_ether_ioctl(struct ifnet *, u_long, caddr_t);
+int usb2_ether_rxmbuf(struct usb2_ether *, struct mbuf *,
+ unsigned int);
+int usb2_ether_rxbuf(struct usb2_ether *,
+ struct usb2_page_cache *,
+ unsigned int, unsigned int);
+void usb2_ether_rxflush(struct usb2_ether *);
+void usb2_ether_ifshutdown(struct usb2_ether *);
+uint8_t usb2_ether_is_gone(struct usb2_ether *);
+#endif /* _USB2_ETHERNET_H_ */
OpenPOWER on IntegriCloud